From 5d0b3e15821fb8d95ee50f49a79622378ab623cb Mon Sep 17 00:00:00 2001 From: Nikolas Klauser Date: Tue, 28 Jan 2025 08:41:31 +0100 Subject: [PATCH 1/3] [clang] Extend diagnose_if to accept more detailed warning information, take 2 (#119712) This is take two of #70976. This iteration of the patch makes sure that custom diagnostics without any warning group don't get promoted by `-Werror` or `-Wfatal-errors`. This implements parts of the extension proposed in https://discourse.llvm.org/t/exposing-the-diagnostic-engine-to-c/73092/7. Specifically, this makes it possible to specify a diagnostic group in an optional third argument. (cherry picked from commit 0865ecc5150b9a55ba1f9e30b6d463a66ac362a6) --- clang-tools-extra/clangd/Diagnostics.cpp | 27 +- clang-tools-extra/clangd/Diagnostics.h | 8 +- clang-tools-extra/clangd/ParsedAST.cpp | 6 +- clang-tools-extra/clangd/Preamble.cpp | 4 +- .../clangd/unittests/ConfigCompileTests.cpp | 47 ++- clang/include/clang/Basic/Attr.td | 12 +- clang/include/clang/Basic/Diagnostic.h | 8 +- .../clang/Basic/DiagnosticCategories.h | 5 +- clang/include/clang/Basic/DiagnosticIDs.h | 155 ++++++++-- .../clang/Basic/DiagnosticSemaKinds.td | 6 + clang/lib/Basic/Diagnostic.cpp | 22 +- clang/lib/Basic/DiagnosticIDs.cpp | 284 ++++++++++-------- clang/lib/Frontend/LogDiagnosticPrinter.cpp | 4 +- .../Frontend/SerializedDiagnosticPrinter.cpp | 12 +- clang/lib/Frontend/TextDiagnosticPrinter.cpp | 10 +- clang/lib/Sema/Sema.cpp | 5 +- clang/lib/Sema/SemaCUDA.cpp | 4 +- clang/lib/Sema/SemaDeclAttr.cpp | 26 +- clang/lib/Sema/SemaOverload.cpp | 33 +- .../lib/Sema/SemaTemplateInstantiateDecl.cpp | 3 +- clang/lib/Serialization/ASTReader.cpp | 2 +- clang/lib/Serialization/ASTWriter.cpp | 2 +- .../StaticAnalyzer/Core/TextDiagnostics.cpp | 1 - .../Frontend/custom-diag-werror-interaction.c | 9 + clang/test/Sema/diagnose_if.c | 8 +- .../SemaCXX/diagnose_if-warning-group.cpp | 63 ++++ clang/tools/diagtool/ListWarnings.cpp | 7 +- clang/tools/diagtool/ShowEnabledWarnings.cpp | 6 +- clang/tools/libclang/CXStoredDiagnostic.cpp | 4 +- flang/lib/Frontend/TextDiagnosticPrinter.cpp | 3 +- 30 files changed, 562 insertions(+), 224 deletions(-) create mode 100644 clang/test/Frontend/custom-diag-werror-interaction.c create mode 100644 clang/test/SemaCXX/diagnose_if-warning-group.cpp diff --git a/clang-tools-extra/clangd/Diagnostics.cpp b/clang-tools-extra/clangd/Diagnostics.cpp index d5eca083eb651..09cf6be472a06 100644 --- a/clang-tools-extra/clangd/Diagnostics.cpp +++ b/clang-tools-extra/clangd/Diagnostics.cpp @@ -579,7 +579,17 @@ std::vector StoreDiags::take(const clang::tidy::ClangTidyContext *Tidy) { for (auto &Diag : Output) { if (const char *ClangDiag = getDiagnosticCode(Diag.ID)) { // Warnings controlled by -Wfoo are better recognized by that name. - StringRef Warning = DiagnosticIDs::getWarningOptionForDiag(Diag.ID); + StringRef Warning = [&] { + if (OrigSrcMgr) { + return OrigSrcMgr->getDiagnostics() + .getDiagnosticIDs() + ->getWarningOptionForDiag(Diag.ID); + } + if (!DiagnosticIDs::IsCustomDiag(Diag.ID)) + return DiagnosticIDs{}.getWarningOptionForDiag(Diag.ID); + return StringRef{}; + }(); + if (!Warning.empty()) { Diag.Name = ("-W" + Warning).str(); } else { @@ -896,20 +906,23 @@ void StoreDiags::flushLastDiag() { Output.push_back(std::move(*LastDiag)); } -bool isBuiltinDiagnosticSuppressed(unsigned ID, - const llvm::StringSet<> &Suppress, - const LangOptions &LangOpts) { +bool isDiagnosticSuppressed(const clang::Diagnostic &Diag, + const llvm::StringSet<> &Suppress, + const LangOptions &LangOpts) { // Don't complain about header-only stuff in mainfiles if it's a header. // FIXME: would be cleaner to suppress in clang, once we decide whether the // behavior should be to silently-ignore or respect the pragma. - if (ID == diag::pp_pragma_sysheader_in_main_file && LangOpts.IsHeaderFile) + if (Diag.getID() == diag::pp_pragma_sysheader_in_main_file && + LangOpts.IsHeaderFile) return true; - if (const char *CodePtr = getDiagnosticCode(ID)) { + if (const char *CodePtr = getDiagnosticCode(Diag.getID())) { if (Suppress.contains(normalizeSuppressedCode(CodePtr))) return true; } - StringRef Warning = DiagnosticIDs::getWarningOptionForDiag(ID); + StringRef Warning = + Diag.getDiags()->getDiagnosticIDs()->getWarningOptionForDiag( + Diag.getID()); if (!Warning.empty() && Suppress.contains(Warning)) return true; return false; diff --git a/clang-tools-extra/clangd/Diagnostics.h b/clang-tools-extra/clangd/Diagnostics.h index d4c0478c63a5c..c45d8dc3aa6ce 100644 --- a/clang-tools-extra/clangd/Diagnostics.h +++ b/clang-tools-extra/clangd/Diagnostics.h @@ -181,11 +181,11 @@ class StoreDiags : public DiagnosticConsumer { }; /// Determine whether a (non-clang-tidy) diagnostic is suppressed by config. -bool isBuiltinDiagnosticSuppressed(unsigned ID, - const llvm::StringSet<> &Suppressed, - const LangOptions &); +bool isDiagnosticSuppressed(const clang::Diagnostic &Diag, + const llvm::StringSet<> &Suppressed, + const LangOptions &); /// Take a user-specified diagnostic code, and convert it to a normalized form -/// stored in the config and consumed by isBuiltinDiagnosticsSuppressed. +/// stored in the config and consumed by isDiagnosticsSuppressed. /// /// (This strips err_ and -W prefix so we can match with or without them.) llvm::StringRef normalizeSuppressedCode(llvm::StringRef); diff --git a/clang-tools-extra/clangd/ParsedAST.cpp b/clang-tools-extra/clangd/ParsedAST.cpp index a2f1504db7e88..fa05291151eef 100644 --- a/clang-tools-extra/clangd/ParsedAST.cpp +++ b/clang-tools-extra/clangd/ParsedAST.cpp @@ -340,7 +340,7 @@ void applyWarningOptions(llvm::ArrayRef ExtraArgs, if (Enable) { if (Diags.getDiagnosticLevel(ID, SourceLocation()) < DiagnosticsEngine::Warning) { - auto Group = DiagnosticIDs::getGroupForDiag(ID); + auto Group = Diags.getDiagnosticIDs()->getGroupForDiag(ID); if (!Group || !EnabledGroups(*Group)) continue; Diags.setSeverity(ID, diag::Severity::Warning, SourceLocation()); @@ -583,8 +583,8 @@ ParsedAST::build(llvm::StringRef Filename, const ParseInputs &Inputs, ASTDiags.setLevelAdjuster([&](DiagnosticsEngine::Level DiagLevel, const clang::Diagnostic &Info) { if (Cfg.Diagnostics.SuppressAll || - isBuiltinDiagnosticSuppressed(Info.getID(), Cfg.Diagnostics.Suppress, - Clang->getLangOpts())) + isDiagnosticSuppressed(Info, Cfg.Diagnostics.Suppress, + Clang->getLangOpts())) return DiagnosticsEngine::Ignored; auto It = OverriddenSeverity.find(Info.getID()); diff --git a/clang-tools-extra/clangd/Preamble.cpp b/clang-tools-extra/clangd/Preamble.cpp index dd13b1a9e5613..84e8fec342829 100644 --- a/clang-tools-extra/clangd/Preamble.cpp +++ b/clang-tools-extra/clangd/Preamble.cpp @@ -621,8 +621,8 @@ buildPreamble(PathRef FileName, CompilerInvocation CI, PreambleDiagnostics.setLevelAdjuster([&](DiagnosticsEngine::Level DiagLevel, const clang::Diagnostic &Info) { if (Cfg.Diagnostics.SuppressAll || - isBuiltinDiagnosticSuppressed(Info.getID(), Cfg.Diagnostics.Suppress, - CI.getLangOpts())) + isDiagnosticSuppressed(Info, Cfg.Diagnostics.Suppress, + CI.getLangOpts())) return DiagnosticsEngine::Ignored; switch (Info.getID()) { case diag::warn_no_newline_eof: diff --git a/clang-tools-extra/clangd/unittests/ConfigCompileTests.cpp b/clang-tools-extra/clangd/unittests/ConfigCompileTests.cpp index 4ecfdf0184ab4..cf9b42828568d 100644 --- a/clang-tools-extra/clangd/unittests/ConfigCompileTests.cpp +++ b/clang-tools-extra/clangd/unittests/ConfigCompileTests.cpp @@ -298,20 +298,41 @@ TEST_F(ConfigCompileTests, DiagnosticSuppression) { "unreachable-code", "unused-variable", "typecheck_bool_condition", "unexpected_friend", "warn_alloca")); - EXPECT_TRUE(isBuiltinDiagnosticSuppressed( - diag::warn_unreachable, Conf.Diagnostics.Suppress, LangOptions())); + clang::DiagnosticsEngine DiagEngine(new DiagnosticIDs, nullptr, + new clang::IgnoringDiagConsumer); + + using Diag = clang::Diagnostic; + { + auto D = DiagEngine.Report(diag::warn_unreachable); + EXPECT_TRUE(isDiagnosticSuppressed( + Diag{&DiagEngine, D}, Conf.Diagnostics.Suppress, LangOptions())); + } // Subcategory not respected/suppressed. - EXPECT_FALSE(isBuiltinDiagnosticSuppressed( - diag::warn_unreachable_break, Conf.Diagnostics.Suppress, LangOptions())); - EXPECT_TRUE(isBuiltinDiagnosticSuppressed( - diag::warn_unused_variable, Conf.Diagnostics.Suppress, LangOptions())); - EXPECT_TRUE(isBuiltinDiagnosticSuppressed(diag::err_typecheck_bool_condition, - Conf.Diagnostics.Suppress, - LangOptions())); - EXPECT_TRUE(isBuiltinDiagnosticSuppressed( - diag::err_unexpected_friend, Conf.Diagnostics.Suppress, LangOptions())); - EXPECT_TRUE(isBuiltinDiagnosticSuppressed( - diag::warn_alloca, Conf.Diagnostics.Suppress, LangOptions())); + { + auto D = DiagEngine.Report(diag::warn_unreachable_break); + EXPECT_FALSE(isDiagnosticSuppressed( + Diag{&DiagEngine, D}, Conf.Diagnostics.Suppress, LangOptions())); + } + { + auto D = DiagEngine.Report(diag::warn_unused_variable); + EXPECT_TRUE(isDiagnosticSuppressed( + Diag{&DiagEngine, D}, Conf.Diagnostics.Suppress, LangOptions())); + } + { + auto D = DiagEngine.Report(diag::err_typecheck_bool_condition); + EXPECT_TRUE(isDiagnosticSuppressed( + Diag{&DiagEngine, D}, Conf.Diagnostics.Suppress, LangOptions())); + } + { + auto D = DiagEngine.Report(diag::err_unexpected_friend); + EXPECT_TRUE(isDiagnosticSuppressed( + Diag{&DiagEngine, D}, Conf.Diagnostics.Suppress, LangOptions())); + } + { + auto D = DiagEngine.Report(diag::warn_alloca); + EXPECT_TRUE(isDiagnosticSuppressed( + Diag{&DiagEngine, D}, Conf.Diagnostics.Suppress, LangOptions())); + } Frag.Diagnostics.Suppress.emplace_back("*"); EXPECT_TRUE(compileAndApply()); diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 930770601aa5a..cb824ced7caa6 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -3496,18 +3496,16 @@ def DiagnoseIf : InheritableAttr { let Spellings = [GNU<"diagnose_if">]; let Subjects = SubjectList<[Function, ObjCMethod, ObjCProperty]>; let Args = [ExprArgument<"Cond">, StringArgument<"Message">, - EnumArgument<"DiagnosticType", "DiagnosticType", + EnumArgument<"DefaultSeverity", + "DefaultSeverity", /*is_string=*/true, - ["error", "warning"], - ["DT_Error", "DT_Warning"]>, + ["error", "warning"], + ["DS_error", "DS_warning"]>, + StringArgument<"WarningGroup", /*optional*/ 1>, BoolArgument<"ArgDependent", 0, /*fake*/ 1>, DeclArgument]; let InheritEvenIfAlreadyPresent = 1; let LateParsed = LateAttrParseStandard; - let AdditionalMembers = [{ - bool isError() const { return diagnosticType == DT_Error; } - bool isWarning() const { return diagnosticType == DT_Warning; } - }]; let TemplateDependent = 1; let Documentation = [DiagnoseIfDocs]; } diff --git a/clang/include/clang/Basic/Diagnostic.h b/clang/include/clang/Basic/Diagnostic.h index 1e5626229ce2e..e9ae146ba665e 100644 --- a/clang/include/clang/Basic/Diagnostic.h +++ b/clang/include/clang/Basic/Diagnostic.h @@ -337,10 +337,12 @@ class DiagnosticsEngine : public RefCountedBase { // Map extensions to warnings or errors? diag::Severity ExtBehavior = diag::Severity::Ignored; - DiagState() + DiagnosticIDs &DiagIDs; + + DiagState(DiagnosticIDs &DiagIDs) : IgnoreAllWarnings(false), EnableAllWarnings(false), WarningsAsErrors(false), ErrorsAsFatal(false), - SuppressSystemWarnings(false) {} + SuppressSystemWarnings(false), DiagIDs(DiagIDs) {} using iterator = llvm::DenseMap::iterator; using const_iterator = @@ -871,6 +873,8 @@ class DiagnosticsEngine : public RefCountedBase { /// \param FormatString A fixed diagnostic format string that will be hashed /// and mapped to a unique DiagID. template + // TODO: Deprecate this once all uses are removed from Clang. + // [[deprecated("Use a CustomDiagDesc instead of a Level")]] unsigned getCustomDiagID(Level L, const char (&FormatString)[N]) { return Diags->getCustomDiagID((DiagnosticIDs::Level)L, StringRef(FormatString, N - 1)); diff --git a/clang/include/clang/Basic/DiagnosticCategories.h b/clang/include/clang/Basic/DiagnosticCategories.h index 14be326f7515f..839f8dee3ca89 100644 --- a/clang/include/clang/Basic/DiagnosticCategories.h +++ b/clang/include/clang/Basic/DiagnosticCategories.h @@ -21,11 +21,12 @@ namespace clang { }; enum class Group { -#define DIAG_ENTRY(GroupName, FlagNameOffset, Members, SubGroups, Docs) \ - GroupName, +#define DIAG_ENTRY(GroupName, FlagNameOffset, Members, SubGroups, Docs) \ + GroupName, #include "clang/Basic/DiagnosticGroups.inc" #undef CATEGORY #undef DIAG_ENTRY + NUM_GROUPS }; } // end namespace diag } // end namespace clang diff --git a/clang/include/clang/Basic/DiagnosticIDs.h b/clang/include/clang/Basic/DiagnosticIDs.h index bdaebbd57fa3d..ac27ed72bcf33 100644 --- a/clang/include/clang/Basic/DiagnosticIDs.h +++ b/clang/include/clang/Basic/DiagnosticIDs.h @@ -14,9 +14,11 @@ #ifndef LLVM_CLANG_BASIC_DIAGNOSTICIDS_H #define LLVM_CLANG_BASIC_DIAGNOSTICIDS_H +#include "clang/Basic/DiagnosticCategories.h" #include "clang/Basic/LLVM.h" #include "llvm/ADT/IntrusiveRefCntPtr.h" #include "llvm/ADT/StringRef.h" +#include "llvm/Support/ErrorHandling.h" #include #include @@ -85,7 +87,7 @@ namespace clang { /// to either Ignore (nothing), Remark (emit a remark), Warning /// (emit a warning) or Error (emit as an error). It allows clients to /// map ERRORs to Error or Fatal (stop emitting diagnostics after this one). - enum class Severity { + enum class Severity : uint8_t { // NOTE: 0 means "uncomputed". Ignored = 1, ///< Do not present this diagnostic, ignore it. Remark = 2, ///< Present this diagnostic as a remark. @@ -182,13 +184,96 @@ class DiagnosticMapping { class DiagnosticIDs : public RefCountedBase { public: /// The level of the diagnostic, after it has been through mapping. - enum Level { - Ignored, Note, Remark, Warning, Error, Fatal + enum Level : uint8_t { Ignored, Note, Remark, Warning, Error, Fatal }; + + // Diagnostic classes. + enum Class { + CLASS_INVALID = 0x00, + CLASS_NOTE = 0x01, + CLASS_REMARK = 0x02, + CLASS_WARNING = 0x03, + CLASS_EXTENSION = 0x04, + CLASS_ERROR = 0x05 + }; + + static bool IsCustomDiag(diag::kind Diag) { + return Diag >= diag::DIAG_UPPER_LIMIT; + } + + class CustomDiagDesc { + LLVM_PREFERRED_TYPE(diag::Severity) + unsigned DefaultSeverity : 3; + LLVM_PREFERRED_TYPE(Class) + unsigned DiagClass : 3; + LLVM_PREFERRED_TYPE(bool) + unsigned ShowInSystemHeader : 1; + LLVM_PREFERRED_TYPE(bool) + unsigned ShowInSystemMacro : 1; + LLVM_PREFERRED_TYPE(bool) + unsigned HasGroup : 1; + diag::Group Group; + std::string Description; + + auto get_as_tuple() const { + return std::tuple(DefaultSeverity, DiagClass, ShowInSystemHeader, + ShowInSystemMacro, HasGroup, Group, + std::string_view{Description}); + } + + public: + CustomDiagDesc(diag::Severity DefaultSeverity, std::string Description, + unsigned Class = CLASS_WARNING, + bool ShowInSystemHeader = false, + bool ShowInSystemMacro = false, + std::optional Group = std::nullopt) + : DefaultSeverity(static_cast(DefaultSeverity)), + DiagClass(Class), ShowInSystemHeader(ShowInSystemHeader), + ShowInSystemMacro(ShowInSystemMacro), HasGroup(Group != std::nullopt), + Group(Group.value_or(diag::Group{})), + Description(std::move(Description)) {} + + std::optional GetGroup() const { + if (HasGroup) + return Group; + return std::nullopt; + } + + diag::Severity GetDefaultSeverity() const { + return static_cast(DefaultSeverity); + } + + Class GetClass() const { return static_cast(DiagClass); } + std::string_view GetDescription() const { return Description; } + bool ShouldShowInSystemHeader() const { return ShowInSystemHeader; } + + friend bool operator==(const CustomDiagDesc &lhs, + const CustomDiagDesc &rhs) { + return lhs.get_as_tuple() == rhs.get_as_tuple(); + } + + friend bool operator<(const CustomDiagDesc &lhs, + const CustomDiagDesc &rhs) { + return lhs.get_as_tuple() < rhs.get_as_tuple(); + } + }; + + struct GroupInfo { + LLVM_PREFERRED_TYPE(diag::Severity) + unsigned Severity : 3; + LLVM_PREFERRED_TYPE(bool) + unsigned HasNoWarningAsError : 1; }; private: /// Information for uniquing and looking up custom diags. std::unique_ptr CustomDiagInfo; + std::unique_ptr GroupInfos = []() { + auto GIs = std::make_unique( + static_cast(diag::Group::NUM_GROUPS)); + for (size_t i = 0; i != static_cast(diag::Group::NUM_GROUPS); ++i) + GIs[i] = {{}, false}; + return GIs; + }(); public: DiagnosticIDs(); @@ -203,7 +288,35 @@ class DiagnosticIDs : public RefCountedBase { // FIXME: Replace this function with a create-only facilty like // createCustomDiagIDFromFormatString() to enforce safe usage. At the time of // writing, nearly all callers of this function were invalid. - unsigned getCustomDiagID(Level L, StringRef FormatString); + unsigned getCustomDiagID(CustomDiagDesc Diag); + + // TODO: Deprecate this once all uses are removed from LLVM + // [[deprecated("Use a CustomDiagDesc instead of a Level")]] + unsigned getCustomDiagID(Level Level, StringRef Message) { + return getCustomDiagID([&]() -> CustomDiagDesc { + switch (Level) { + case DiagnosticIDs::Level::Ignored: + return {diag::Severity::Ignored, std::string(Message), CLASS_WARNING, + /*ShowInSystemHeader*/ true, /*ShowInSystemMacro=*/true}; + case DiagnosticIDs::Level::Note: + return {diag::Severity::Fatal, std::string(Message), CLASS_NOTE, + /*ShowInSystemHeader*/ true, /*ShowInSystemMacro=*/true}; + case DiagnosticIDs::Level::Remark: + return {diag::Severity::Remark, std::string(Message), CLASS_REMARK, + /*ShowInSystemHeader*/ true, /*ShowInSystemMacro=*/true}; + case DiagnosticIDs::Level::Warning: + return {diag::Severity::Warning, std::string(Message), CLASS_WARNING, + /*ShowInSystemHeader*/ true, /*ShowInSystemMacro=*/true}; + case DiagnosticIDs::Level::Error: + return {diag::Severity::Error, std::string(Message), CLASS_ERROR, + /*ShowInSystemHeader*/ true, /*ShowInSystemMacro=*/true}; + case DiagnosticIDs::Level::Fatal: + return {diag::Severity::Fatal, std::string(Message), CLASS_ERROR, + /*ShowInSystemHeader*/ true, /*ShowInSystemMacro=*/true}; + } + llvm_unreachable("Fully covered switch above!"); + }()); + } //===--------------------------------------------------------------------===// // Diagnostic classification and reporting interfaces. @@ -215,35 +328,36 @@ class DiagnosticIDs : public RefCountedBase { /// Return true if the unmapped diagnostic levelof the specified /// diagnostic ID is a Warning or Extension. /// - /// This only works on builtin diagnostics, not custom ones, and is not - /// legal to call on NOTEs. - static bool isBuiltinWarningOrExtension(unsigned DiagID); + /// This is not legal to call on NOTEs. + bool isWarningOrExtension(unsigned DiagID) const; /// Return true if the specified diagnostic is mapped to errors by /// default. - static bool isDefaultMappingAsError(unsigned DiagID); + bool isDefaultMappingAsError(unsigned DiagID) const; /// Get the default mapping for this diagnostic. - static DiagnosticMapping getDefaultMapping(unsigned DiagID); + DiagnosticMapping getDefaultMapping(unsigned DiagID) const; + + void initCustomDiagMapping(DiagnosticMapping &, unsigned DiagID); - /// Determine whether the given built-in diagnostic ID is a Note. - static bool isBuiltinNote(unsigned DiagID); + /// Determine whether the given diagnostic ID is a Note. + bool isNote(unsigned DiagID) const; - /// Determine whether the given built-in diagnostic ID is for an + /// Determine whether the given diagnostic ID is for an /// extension of some sort. - static bool isBuiltinExtensionDiag(unsigned DiagID) { + bool isExtensionDiag(unsigned DiagID) const { bool ignored; - return isBuiltinExtensionDiag(DiagID, ignored); + return isExtensionDiag(DiagID, ignored); } - /// Determine whether the given built-in diagnostic ID is for an + /// Determine whether the given diagnostic ID is for an /// extension of some sort, and whether it is enabled by default. /// /// This also returns EnabledByDefault, which is set to indicate whether the /// diagnostic is ignored by default (in which case -pedantic enables it) or /// treated as a warning/error by default. /// - static bool isBuiltinExtensionDiag(unsigned DiagID, bool &EnabledByDefault); + bool isExtensionDiag(unsigned DiagID, bool &EnabledByDefault) const; /// Given a group ID, returns the flag that toggles the group. /// For example, for Group::DeprecatedDeclarations, returns @@ -253,19 +367,22 @@ class DiagnosticIDs : public RefCountedBase { /// Given a diagnostic group ID, return its documentation. static StringRef getWarningOptionDocumentation(diag::Group GroupID); + void setGroupSeverity(StringRef Group, diag::Severity); + void setGroupNoWarningsAsError(StringRef Group, bool); + /// Given a group ID, returns the flag that toggles the group. /// For example, for "deprecated-declarations", returns /// Group::DeprecatedDeclarations. static std::optional getGroupForWarningOption(StringRef); /// Return the lowest-level group that contains the specified diagnostic. - static std::optional getGroupForDiag(unsigned DiagID); + std::optional getGroupForDiag(unsigned DiagID) const; /// Return the lowest-level warning option that enables the specified /// diagnostic. /// /// If there is no -Wfoo flag that controls the diagnostic, this returns null. - static StringRef getWarningOptionForDiag(unsigned DiagID); + StringRef getWarningOptionForDiag(unsigned DiagID); /// Return the category number that a specified \p DiagID belongs to, /// or 0 if no category. @@ -366,6 +483,8 @@ class DiagnosticIDs : public RefCountedBase { getDiagnosticSeverity(unsigned DiagID, SourceLocation Loc, const DiagnosticsEngine &Diag) const LLVM_READONLY; + Class getDiagClass(unsigned DiagID) const; + /// Used to report a diagnostic that is finally fully formed. /// /// \returns \c true if the diagnostic was emitted, \c false if it was diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 82e2341f5be6f..9db7a502a87cf 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -2951,9 +2951,15 @@ def ext_constexpr_function_never_constant_expr : ExtWarn< "constant expression">, InGroup>, DefaultError; def err_attr_cond_never_constant_expr : Error< "%0 attribute expression never produces a constant expression">; +def err_diagnose_if_unknown_warning : Error<"unknown warning group '%0'">; def err_diagnose_if_invalid_diagnostic_type : Error< "invalid diagnostic type for 'diagnose_if'; use \"error\" or \"warning\" " "instead">; +def err_diagnose_if_unknown_option : Error<"unknown diagnostic option">; +def err_diagnose_if_expected_equals : Error< + "expected '=' after diagnostic option">; +def err_diagnose_if_unexpected_value : Error< + "unexpected value; use 'true' or 'false'">; def err_constexpr_body_no_return : Error< "no return statement in %select{constexpr|consteval}0 function">; def err_constexpr_return_missing_expr : Error< diff --git a/clang/lib/Basic/Diagnostic.cpp b/clang/lib/Basic/Diagnostic.cpp index 92c2f1f20e7d1..18cfb230c5e59 100644 --- a/clang/lib/Basic/Diagnostic.cpp +++ b/clang/lib/Basic/Diagnostic.cpp @@ -146,7 +146,7 @@ void DiagnosticsEngine::Reset(bool soft /*=false*/) { // Create a DiagState and DiagStatePoint representing diagnostic changes // through command-line. - DiagStates.emplace_back(); + DiagStates.emplace_back(*Diags); DiagStatesByLoc.appendFirst(&DiagStates.back()); } } @@ -174,8 +174,11 @@ DiagnosticsEngine::DiagState::getOrAddMapping(diag::kind Diag) { DiagMap.insert(std::make_pair(Diag, DiagnosticMapping())); // Initialize the entry if we added it. - if (Result.second) - Result.first->second = DiagnosticIDs::getDefaultMapping(Diag); + if (Result.second) { + Result.first->second = DiagIDs.getDefaultMapping(Diag); + if (DiagnosticIDs::IsCustomDiag(Diag)) + DiagIDs.initCustomDiagMapping(Result.first->second, Diag); + } return Result.first->second; } @@ -317,7 +320,8 @@ void DiagnosticsEngine::DiagStateMap::dump(SourceManager &SrcMgr, for (auto &Mapping : *Transition.State) { StringRef Option = - DiagnosticIDs::getWarningOptionForDiag(Mapping.first); + SrcMgr.getDiagnostics().Diags->getWarningOptionForDiag( + Mapping.first); if (!DiagName.empty() && DiagName != Option) continue; @@ -361,9 +365,7 @@ void DiagnosticsEngine::PushDiagStatePoint(DiagState *State, void DiagnosticsEngine::setSeverity(diag::kind Diag, diag::Severity Map, SourceLocation L) { - assert(Diag < diag::DIAG_UPPER_LIMIT && - "Can only map builtin diagnostics"); - assert((Diags->isBuiltinWarningOrExtension(Diag) || + assert((Diags->isWarningOrExtension(Diag) || (Map == diag::Severity::Fatal || Map == diag::Severity::Error)) && "Cannot map errors into warnings!"); assert((L.isInvalid() || SourceMgr) && "No SourceMgr for valid location"); @@ -415,6 +417,8 @@ bool DiagnosticsEngine::setSeverityForGroup(diag::Flavor Flavor, if (Diags->getDiagnosticsInGroup(Flavor, Group, GroupDiags)) return true; + Diags->setGroupSeverity(Group, Map); + // Set the mapping. for (diag::kind Diag : GroupDiags) setSeverity(Diag, Map, Loc); @@ -437,6 +441,7 @@ bool DiagnosticsEngine::setDiagnosticGroupWarningAsError(StringRef Group, if (Enabled) return setSeverityForGroup(diag::Flavor::WarningOrError, Group, diag::Severity::Error); + Diags->setGroupSeverity(Group, diag::Severity::Warning); // Otherwise, we want to set the diagnostic mapping's "no Werror" bit, and // potentially downgrade anything already mapped to be a warning. @@ -468,6 +473,7 @@ bool DiagnosticsEngine::setDiagnosticGroupErrorAsFatal(StringRef Group, if (Enabled) return setSeverityForGroup(diag::Flavor::WarningOrError, Group, diag::Severity::Fatal); + Diags->setGroupSeverity(Group, diag::Severity::Error); // Otherwise, we want to set the diagnostic mapping's "no Wfatal-errors" bit, // and potentially downgrade anything already mapped to be a fatal error. @@ -500,7 +506,7 @@ void DiagnosticsEngine::setSeverityForAll(diag::Flavor Flavor, // Set the mapping. for (diag::kind Diag : AllDiags) - if (Diags->isBuiltinWarningOrExtension(Diag)) + if (Diags->isWarningOrExtension(Diag)) setSeverity(Diag, Map, Loc); } diff --git a/clang/lib/Basic/DiagnosticIDs.cpp b/clang/lib/Basic/DiagnosticIDs.cpp index d66877ba7ea4e..ade4e0ab1a5a5 100644 --- a/clang/lib/Basic/DiagnosticIDs.cpp +++ b/clang/lib/Basic/DiagnosticIDs.cpp @@ -105,13 +105,12 @@ const uint32_t StaticDiagInfoDescriptionOffsets[] = { #undef DIAG }; -// Diagnostic classes. enum DiagnosticClass { - CLASS_NOTE = 0x01, - CLASS_REMARK = 0x02, - CLASS_WARNING = 0x03, - CLASS_EXTENSION = 0x04, - CLASS_ERROR = 0x05 + CLASS_NOTE = DiagnosticIDs::CLASS_NOTE, + CLASS_REMARK = DiagnosticIDs::CLASS_REMARK, + CLASS_WARNING = DiagnosticIDs::CLASS_WARNING, + CLASS_EXTENSION = DiagnosticIDs::CLASS_EXTENSION, + CLASS_ERROR = DiagnosticIDs::CLASS_ERROR, }; struct StaticDiagInfoRec { @@ -275,11 +274,60 @@ CATEGORY(CAS, INSTALLAPI) return Found; } -DiagnosticMapping DiagnosticIDs::getDefaultMapping(unsigned DiagID) { +//===----------------------------------------------------------------------===// +// Custom Diagnostic information +//===----------------------------------------------------------------------===// + +namespace clang { +namespace diag { +using CustomDiagDesc = DiagnosticIDs::CustomDiagDesc; +class CustomDiagInfo { + std::vector DiagInfo; + std::map DiagIDs; + std::map> GroupToDiags; + +public: + /// getDescription - Return the description of the specified custom + /// diagnostic. + const CustomDiagDesc &getDescription(unsigned DiagID) const { + assert(DiagID - DIAG_UPPER_LIMIT < DiagInfo.size() && + "Invalid diagnostic ID"); + return DiagInfo[DiagID - DIAG_UPPER_LIMIT]; + } + + unsigned getOrCreateDiagID(DiagnosticIDs::CustomDiagDesc D) { + // Check to see if it already exists. + std::map::iterator I = DiagIDs.lower_bound(D); + if (I != DiagIDs.end() && I->first == D) + return I->second; + + // If not, assign a new ID. + unsigned ID = DiagInfo.size() + DIAG_UPPER_LIMIT; + DiagIDs.insert(std::make_pair(D, ID)); + DiagInfo.push_back(D); + if (auto Group = D.GetGroup()) + GroupToDiags[*Group].emplace_back(ID); + return ID; + } + + ArrayRef getDiagsInGroup(diag::Group G) const { + if (auto Diags = GroupToDiags.find(G); Diags != GroupToDiags.end()) + return Diags->second; + return {}; + } +}; + +} // namespace diag +} // namespace clang + +DiagnosticMapping DiagnosticIDs::getDefaultMapping(unsigned DiagID) const { DiagnosticMapping Info = DiagnosticMapping::Make( diag::Severity::Fatal, /*IsUser=*/false, /*IsPragma=*/false); - if (const StaticDiagInfoRec *StaticInfo = GetDiagInfo(DiagID)) { + if (IsCustomDiag(DiagID)) { + Info.setSeverity( + CustomDiagInfo->getDescription(DiagID).GetDefaultSeverity()); + } else if (const StaticDiagInfoRec *StaticInfo = GetDiagInfo(DiagID)) { Info.setSeverity((diag::Severity)StaticInfo->DefaultSeverity); if (StaticInfo->WarnNoWerror) { @@ -292,6 +340,22 @@ DiagnosticMapping DiagnosticIDs::getDefaultMapping(unsigned DiagID) { return Info; } +void DiagnosticIDs::initCustomDiagMapping(DiagnosticMapping &Mapping, + unsigned DiagID) { + assert(IsCustomDiag(DiagID)); + const auto &Diag = CustomDiagInfo->getDescription(DiagID); + if (auto Group = Diag.GetGroup()) { + GroupInfo GroupInfo = GroupInfos[static_cast(*Group)]; + if (static_cast(GroupInfo.Severity) != diag::Severity()) + Mapping.setSeverity(static_cast(GroupInfo.Severity)); + Mapping.setNoWarningAsError(GroupInfo.HasNoWarningAsError); + } else { + Mapping.setSeverity(Diag.GetDefaultSeverity()); + Mapping.setNoWarningAsError(true); + Mapping.setNoErrorAsFatal(true); + } +} + /// getCategoryNumberForDiag - Return the category number that a specified /// DiagID belongs to, or 0 if no category. unsigned DiagnosticIDs::getCategoryNumberForDiag(unsigned DiagID) { @@ -349,61 +413,6 @@ bool DiagnosticIDs::isDeferrable(unsigned DiagID) { return false; } -/// getBuiltinDiagClass - Return the class field of the diagnostic. -/// -static unsigned getBuiltinDiagClass(unsigned DiagID) { - if (const StaticDiagInfoRec *Info = GetDiagInfo(DiagID)) - return Info->Class; - return ~0U; -} - -//===----------------------------------------------------------------------===// -// Custom Diagnostic information -//===----------------------------------------------------------------------===// - -namespace clang { - namespace diag { - class CustomDiagInfo { - typedef std::pair DiagDesc; - std::vector DiagInfo; - std::map DiagIDs; - public: - - /// getDescription - Return the description of the specified custom - /// diagnostic. - StringRef getDescription(unsigned DiagID) const { - assert(DiagID - DIAG_UPPER_LIMIT < DiagInfo.size() && - "Invalid diagnostic ID"); - return DiagInfo[DiagID-DIAG_UPPER_LIMIT].second; - } - - /// getLevel - Return the level of the specified custom diagnostic. - DiagnosticIDs::Level getLevel(unsigned DiagID) const { - assert(DiagID - DIAG_UPPER_LIMIT < DiagInfo.size() && - "Invalid diagnostic ID"); - return DiagInfo[DiagID-DIAG_UPPER_LIMIT].first; - } - - unsigned getOrCreateDiagID(DiagnosticIDs::Level L, StringRef Message, - DiagnosticIDs &Diags) { - DiagDesc D(L, std::string(Message)); - // Check to see if it already exists. - std::map::iterator I = DiagIDs.lower_bound(D); - if (I != DiagIDs.end() && I->first == D) - return I->second; - - // If not, assign a new ID. - unsigned ID = DiagInfo.size()+DIAG_UPPER_LIMIT; - DiagIDs.insert(std::make_pair(D, ID)); - DiagInfo.push_back(D); - return ID; - } - }; - - } // end diag namespace -} // end clang namespace - - //===----------------------------------------------------------------------===// // Common Diagnostic implementation //===----------------------------------------------------------------------===// @@ -418,38 +427,32 @@ DiagnosticIDs::~DiagnosticIDs() {} /// /// \param FormatString A fixed diagnostic format string that will be hashed and /// mapped to a unique DiagID. -unsigned DiagnosticIDs::getCustomDiagID(Level L, StringRef FormatString) { +unsigned DiagnosticIDs::getCustomDiagID(CustomDiagDesc Diag) { if (!CustomDiagInfo) CustomDiagInfo.reset(new diag::CustomDiagInfo()); - return CustomDiagInfo->getOrCreateDiagID(L, FormatString, *this); + return CustomDiagInfo->getOrCreateDiagID(Diag); } - -/// isBuiltinWarningOrExtension - Return true if the unmapped diagnostic -/// level of the specified diagnostic ID is a Warning or Extension. -/// This only works on builtin diagnostics, not custom ones, and is not legal to -/// call on NOTEs. -bool DiagnosticIDs::isBuiltinWarningOrExtension(unsigned DiagID) { - return DiagID < diag::DIAG_UPPER_LIMIT && - getBuiltinDiagClass(DiagID) != CLASS_ERROR; +bool DiagnosticIDs::isWarningOrExtension(unsigned DiagID) const { + return DiagID < diag::DIAG_UPPER_LIMIT + ? getDiagClass(DiagID) != CLASS_ERROR + : CustomDiagInfo->getDescription(DiagID).GetClass() != CLASS_ERROR; } /// Determine whether the given built-in diagnostic ID is a /// Note. -bool DiagnosticIDs::isBuiltinNote(unsigned DiagID) { - return DiagID < diag::DIAG_UPPER_LIMIT && - getBuiltinDiagClass(DiagID) == CLASS_NOTE; +bool DiagnosticIDs::isNote(unsigned DiagID) const { + return DiagID < diag::DIAG_UPPER_LIMIT && getDiagClass(DiagID) == CLASS_NOTE; } -/// isBuiltinExtensionDiag - Determine whether the given built-in diagnostic +/// isExtensionDiag - Determine whether the given built-in diagnostic /// ID is for an extension of some sort. This also returns EnabledByDefault, /// which is set to indicate whether the diagnostic is ignored by default (in /// which case -pedantic enables it) or treated as a warning/error by default. /// -bool DiagnosticIDs::isBuiltinExtensionDiag(unsigned DiagID, - bool &EnabledByDefault) { - if (DiagID >= diag::DIAG_UPPER_LIMIT || - getBuiltinDiagClass(DiagID) != CLASS_EXTENSION) +bool DiagnosticIDs::isExtensionDiag(unsigned DiagID, + bool &EnabledByDefault) const { + if (IsCustomDiag(DiagID) || getDiagClass(DiagID) != CLASS_EXTENSION) return false; EnabledByDefault = @@ -457,10 +460,7 @@ bool DiagnosticIDs::isBuiltinExtensionDiag(unsigned DiagID, return true; } -bool DiagnosticIDs::isDefaultMappingAsError(unsigned DiagID) { - if (DiagID >= diag::DIAG_UPPER_LIMIT) - return false; - +bool DiagnosticIDs::isDefaultMappingAsError(unsigned DiagID) const { return getDefaultMapping(DiagID).getSeverity() >= diag::Severity::Error; } @@ -470,7 +470,7 @@ StringRef DiagnosticIDs::getDescription(unsigned DiagID) const { if (const StaticDiagInfoRec *Info = GetDiagInfo(DiagID)) return Info->getDescription(); assert(CustomDiagInfo && "Invalid CustomDiagInfo"); - return CustomDiagInfo->getDescription(DiagID); + return CustomDiagInfo->getDescription(DiagID).GetDescription(); } static DiagnosticIDs::Level toLevel(diag::Severity SV) { @@ -495,13 +495,7 @@ static DiagnosticIDs::Level toLevel(diag::Severity SV) { DiagnosticIDs::Level DiagnosticIDs::getDiagnosticLevel(unsigned DiagID, SourceLocation Loc, const DiagnosticsEngine &Diag) const { - // Handle custom diagnostics, which cannot be mapped. - if (DiagID >= diag::DIAG_UPPER_LIMIT) { - assert(CustomDiagInfo && "Invalid CustomDiagInfo"); - return CustomDiagInfo->getLevel(DiagID); - } - - unsigned DiagClass = getBuiltinDiagClass(DiagID); + unsigned DiagClass = getDiagClass(DiagID); if (DiagClass == CLASS_NOTE) return DiagnosticIDs::Note; return toLevel(getDiagnosticSeverity(DiagID, Loc, Diag)); } @@ -515,7 +509,8 @@ DiagnosticIDs::getDiagnosticLevel(unsigned DiagID, SourceLocation Loc, diag::Severity DiagnosticIDs::getDiagnosticSeverity(unsigned DiagID, SourceLocation Loc, const DiagnosticsEngine &Diag) const { - assert(getBuiltinDiagClass(DiagID) != CLASS_NOTE); + bool IsCustomDiag = DiagnosticIDs::IsCustomDiag(DiagID); + assert(getDiagClass(DiagID) != CLASS_NOTE); // Specific non-error diagnostics may be mapped to various levels from ignored // to error. Errors can only be mapped to fatal. @@ -523,7 +518,7 @@ DiagnosticIDs::getDiagnosticSeverity(unsigned DiagID, SourceLocation Loc, // Get the mapping information, or compute it lazily. DiagnosticsEngine::DiagState *State = Diag.GetDiagStateForLoc(Loc); - DiagnosticMapping &Mapping = State->getOrAddMapping((diag::kind)DiagID); + DiagnosticMapping Mapping = State->getOrAddMapping((diag::kind)DiagID); // TODO: Can a null severity really get here? if (Mapping.getSeverity() != diag::Severity()) @@ -531,14 +526,15 @@ DiagnosticIDs::getDiagnosticSeverity(unsigned DiagID, SourceLocation Loc, // Upgrade ignored diagnostics if -Weverything is enabled. if (State->EnableAllWarnings && Result == diag::Severity::Ignored && - !Mapping.isUser() && getBuiltinDiagClass(DiagID) != CLASS_REMARK) + !Mapping.isUser() && + (IsCustomDiag || getDiagClass(DiagID) != CLASS_REMARK)) Result = diag::Severity::Warning; // Ignore -pedantic diagnostics inside __extension__ blocks. // (The diagnostics controlled by -pedantic are the extension diagnostics // that are not enabled by default.) bool EnabledByDefault = false; - bool IsExtensionDiag = isBuiltinExtensionDiag(DiagID, EnabledByDefault); + bool IsExtensionDiag = isExtensionDiag(DiagID, EnabledByDefault); if (Diag.AllExtensionsSilenced && IsExtensionDiag && !EnabledByDefault) return diag::Severity::Ignored; @@ -556,10 +552,12 @@ DiagnosticIDs::getDiagnosticSeverity(unsigned DiagID, SourceLocation Loc, // as well as disabling all messages which are currently mapped to Warning // (whether by default or downgraded from Error via e.g. -Wno-error or #pragma // diagnostic.) + // FIXME: Should -w be ignored for custom warnings without a group? if (State->IgnoreAllWarnings) { - if (Result == diag::Severity::Warning || - (Result >= diag::Severity::Error && - !isDefaultMappingAsError((diag::kind)DiagID))) + if ((!IsCustomDiag || CustomDiagInfo->getDescription(DiagID).GetGroup()) && + (Result == diag::Severity::Warning || + (Result >= diag::Severity::Error && + !isDefaultMappingAsError((diag::kind)DiagID)))) return diag::Severity::Ignored; } @@ -581,9 +579,10 @@ DiagnosticIDs::getDiagnosticSeverity(unsigned DiagID, SourceLocation Loc, Diag.CurDiagID != diag::fatal_too_many_errors && Diag.FatalsAsError) Result = diag::Severity::Error; - // Custom diagnostics always are emitted in system headers. bool ShowInSystemHeader = - !GetDiagInfo(DiagID) || GetDiagInfo(DiagID)->WarnShowInSystemHeader; + IsCustomDiag + ? CustomDiagInfo->getDescription(DiagID).ShouldShowInSystemHeader() + : !GetDiagInfo(DiagID) || GetDiagInfo(DiagID)->WarnShowInSystemHeader; // If we are in a system header, we ignore it. We look at the diagnostic class // because we also want to ignore extensions and warnings in -Werror and @@ -603,6 +602,15 @@ DiagnosticIDs::getDiagnosticSeverity(unsigned DiagID, SourceLocation Loc, return Result; } +DiagnosticIDs::Class DiagnosticIDs::getDiagClass(unsigned DiagID) const { + if (IsCustomDiag(DiagID)) + return Class(CustomDiagInfo->getDescription(DiagID).GetClass()); + + if (const StaticDiagInfoRec *Info = GetDiagInfo(DiagID)) + return Class(Info->Class); + return CLASS_INVALID; +} + #define GET_DIAG_ARRAYS #include "clang/Basic/DiagnosticGroups.inc" #undef GET_DIAG_ARRAYS @@ -648,7 +656,12 @@ DiagnosticIDs::getGroupForWarningOption(StringRef Name) { return static_cast(Found - OptionTable); } -std::optional DiagnosticIDs::getGroupForDiag(unsigned DiagID) { +std::optional +DiagnosticIDs::getGroupForDiag(unsigned DiagID) const { + if (IsCustomDiag(DiagID)) { + assert(CustomDiagInfo); + return CustomDiagInfo->getDescription(DiagID).GetGroup(); + } if (const StaticDiagInfoRec *Info = GetDiagInfo(DiagID)) return static_cast(Info->getOptionGroupIndex()); return std::nullopt; @@ -679,7 +692,8 @@ std::vector DiagnosticIDs::getDiagnosticFlags() { /// were filtered out due to having the wrong flavor. static bool getDiagnosticsInGroup(diag::Flavor Flavor, const WarningOption *Group, - SmallVectorImpl &Diags) { + SmallVectorImpl &Diags, + diag::CustomDiagInfo *CustomDiagInfo) { // An empty group is considered to be a warning group: we have empty groups // for GCC compatibility, and GCC does not have remarks. if (!Group->Members && !Group->SubGroups) @@ -698,9 +712,14 @@ static bool getDiagnosticsInGroup(diag::Flavor Flavor, // Add the members of the subgroups. const int16_t *SubGroups = DiagSubGroups + Group->SubGroups; - for (; *SubGroups != (int16_t)-1; ++SubGroups) + for (; *SubGroups != (int16_t)-1; ++SubGroups) { + if (CustomDiagInfo) + llvm::copy( + CustomDiagInfo->getDiagsInGroup(static_cast(*SubGroups)), + std::back_inserter(Diags)); NotFound &= getDiagnosticsInGroup(Flavor, &OptionTable[(short)*SubGroups], - Diags); + Diags, CustomDiagInfo); + } return NotFound; } @@ -708,12 +727,49 @@ static bool getDiagnosticsInGroup(diag::Flavor Flavor, bool DiagnosticIDs::getDiagnosticsInGroup(diag::Flavor Flavor, StringRef Group, SmallVectorImpl &Diags) const { - if (std::optional G = getGroupForWarningOption(Group)) - return ::getDiagnosticsInGroup( - Flavor, &OptionTable[static_cast(*G)], Diags); + if (std::optional G = getGroupForWarningOption(Group)) { + if (CustomDiagInfo) + llvm::copy(CustomDiagInfo->getDiagsInGroup(*G), + std::back_inserter(Diags)); + return ::getDiagnosticsInGroup(Flavor, + &OptionTable[static_cast(*G)], + Diags, CustomDiagInfo.get()); + } return true; } +template +static void forEachSubGroupImpl(const WarningOption *Group, Func func) { + for (const int16_t *SubGroups = DiagSubGroups + Group->SubGroups; + *SubGroups != -1; ++SubGroups) { + func(static_cast(*SubGroups)); + forEachSubGroupImpl(&OptionTable[*SubGroups], std::move(func)); + } +} + +template +static void forEachSubGroup(diag::Group Group, Func func) { + const WarningOption *WarningOpt = &OptionTable[static_cast(Group)]; + func(static_cast(Group)); + ::forEachSubGroupImpl(WarningOpt, std::move(func)); +} + +void DiagnosticIDs::setGroupSeverity(StringRef Group, diag::Severity Sev) { + if (std::optional G = getGroupForWarningOption(Group)) { + ::forEachSubGroup(*G, [&](size_t SubGroup) { + GroupInfos[SubGroup].Severity = static_cast(Sev); + }); + } +} + +void DiagnosticIDs::setGroupNoWarningsAsError(StringRef Group, bool Val) { + if (std::optional G = getGroupForWarningOption(Group)) { + ::forEachSubGroup(*G, [&](size_t SubGroup) { + GroupInfos[static_cast(*G)].HasNoWarningAsError = Val; + }); + } +} + void DiagnosticIDs::getAllDiagnostics(diag::Flavor Flavor, std::vector &Diags) { for (unsigned i = 0; i != StaticDiagInfoSize; ++i) @@ -736,7 +792,7 @@ StringRef DiagnosticIDs::getNearestOption(diag::Flavor Flavor, // Don't suggest groups that are not of this kind. llvm::SmallVector Diags; - if (::getDiagnosticsInGroup(Flavor, &O, Diags) || Diags.empty()) + if (::getDiagnosticsInGroup(Flavor, &O, Diags, nullptr) || Diags.empty()) continue; if (Distance == BestDistance) { @@ -849,14 +905,8 @@ void DiagnosticIDs::EmitDiag(DiagnosticsEngine &Diag, Level DiagLevel) const { } bool DiagnosticIDs::isUnrecoverable(unsigned DiagID) const { - if (DiagID >= diag::DIAG_UPPER_LIMIT) { - assert(CustomDiagInfo && "Invalid CustomDiagInfo"); - // Custom diagnostics. - return CustomDiagInfo->getLevel(DiagID) >= DiagnosticIDs::Error; - } - // Only errors may be unrecoverable. - if (getBuiltinDiagClass(DiagID) < CLASS_ERROR) + if (getDiagClass(DiagID) < CLASS_ERROR) return false; if (DiagID == diag::err_unavailable || diff --git a/clang/lib/Frontend/LogDiagnosticPrinter.cpp b/clang/lib/Frontend/LogDiagnosticPrinter.cpp index 469d1c22633aa..4e963af837f01 100644 --- a/clang/lib/Frontend/LogDiagnosticPrinter.cpp +++ b/clang/lib/Frontend/LogDiagnosticPrinter.cpp @@ -129,7 +129,8 @@ void LogDiagnosticPrinter::HandleDiagnostic(DiagnosticsEngine::Level Level, DE.DiagnosticLevel = Level; DE.WarningOption = - std::string(DiagnosticIDs::getWarningOptionForDiag(DE.DiagnosticID)); + std::string(Info.getDiags()->getDiagnosticIDs()->getWarningOptionForDiag( + DE.DiagnosticID)); // Format the message. SmallString<100> MessageStr; @@ -160,4 +161,3 @@ void LogDiagnosticPrinter::HandleDiagnostic(DiagnosticsEngine::Level Level, // Record the diagnostic entry. Entries.push_back(DE); } - diff --git a/clang/lib/Frontend/SerializedDiagnosticPrinter.cpp b/clang/lib/Frontend/SerializedDiagnosticPrinter.cpp index 5cc6b3549a6a3..911485dce21f7 100644 --- a/clang/lib/Frontend/SerializedDiagnosticPrinter.cpp +++ b/clang/lib/Frontend/SerializedDiagnosticPrinter.cpp @@ -210,7 +210,7 @@ class SDiagsWriter : public DiagnosticConsumer { /// Emit the string information for diagnostic flags. unsigned getEmitDiagnosticFlag(DiagnosticsEngine::Level DiagLevel, - unsigned DiagID = 0); + const Diagnostic *Diag = nullptr); unsigned getEmitDiagnosticFlag(StringRef DiagName); @@ -563,11 +563,13 @@ unsigned SDiagsWriter::getEmitCategory(unsigned int category) { } unsigned SDiagsWriter::getEmitDiagnosticFlag(DiagnosticsEngine::Level DiagLevel, - unsigned DiagID) { - if (DiagLevel == DiagnosticsEngine::Note) + const Diagnostic *Diag) { + if (!Diag || DiagLevel == DiagnosticsEngine::Note) return 0; // No flag for notes. - StringRef FlagName = DiagnosticIDs::getWarningOptionForDiag(DiagID); + StringRef FlagName = + Diag->getDiags()->getDiagnosticIDs()->getWarningOptionForDiag( + Diag->getID()); return getEmitDiagnosticFlag(FlagName); } @@ -685,7 +687,7 @@ void SDiagsWriter::EmitDiagnosticMessage(FullSourceLoc Loc, PresumedLoc PLoc, unsigned DiagID = DiagnosticIDs::getCategoryNumberForDiag(Info->getID()); Record.push_back(getEmitCategory(DiagID)); // Emit the diagnostic flag string lazily and get the mapped ID. - Record.push_back(getEmitDiagnosticFlag(Level, Info->getID())); + Record.push_back(getEmitDiagnosticFlag(Level, Info)); } else { Record.push_back(getEmitCategory()); Record.push_back(getEmitDiagnosticFlag(Level)); diff --git a/clang/lib/Frontend/TextDiagnosticPrinter.cpp b/clang/lib/Frontend/TextDiagnosticPrinter.cpp index b2fb762537573..c2fea3d03f0c0 100644 --- a/clang/lib/Frontend/TextDiagnosticPrinter.cpp +++ b/clang/lib/Frontend/TextDiagnosticPrinter.cpp @@ -70,13 +70,17 @@ static void printDiagnosticOptions(raw_ostream &OS, // flag it as such. Note that diagnostics could also have been mapped by a // pragma, but we don't currently have a way to distinguish this. if (Level == DiagnosticsEngine::Error && - DiagnosticIDs::isBuiltinWarningOrExtension(Info.getID()) && - !DiagnosticIDs::isDefaultMappingAsError(Info.getID())) { + Info.getDiags()->getDiagnosticIDs()->isWarningOrExtension( + Info.getID()) && + !Info.getDiags()->getDiagnosticIDs()->isDefaultMappingAsError( + Info.getID())) { OS << " [-Werror"; Started = true; } - StringRef Opt = DiagnosticIDs::getWarningOptionForDiag(Info.getID()); + StringRef Opt = + Info.getDiags()->getDiagnosticIDs()->getWarningOptionForDiag( + Info.getID()); if (!Opt.empty()) { OS << (Started ? "," : " [") << (Level == DiagnosticsEngine::Remark ? "-R" : "-W") << Opt; diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index 0e6c3bffae893..ab3f92ef2cc67 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -1884,7 +1884,7 @@ void Sema::EmitCurrentDiagnostic(unsigned DiagID) { // that is different from the last template instantiation where // we emitted an error, print a template instantiation // backtrace. - if (!DiagnosticIDs::isBuiltinNote(DiagID)) + if (!Diags.getDiagnosticIDs()->isNote(DiagID)) PrintContextStack(); } @@ -1898,7 +1898,8 @@ bool Sema::hasUncompilableErrorOccurred() const { if (Loc == DeviceDeferredDiags.end()) return false; for (auto PDAt : Loc->second) { - if (DiagnosticIDs::isDefaultMappingAsError(PDAt.second.getDiagID())) + if (Diags.getDiagnosticIDs()->isDefaultMappingAsError( + PDAt.second.getDiagID())) return true; } return false; diff --git a/clang/lib/Sema/SemaCUDA.cpp b/clang/lib/Sema/SemaCUDA.cpp index 580b9872c6a1d..248c3ad992938 100644 --- a/clang/lib/Sema/SemaCUDA.cpp +++ b/clang/lib/Sema/SemaCUDA.cpp @@ -835,7 +835,7 @@ SemaBase::SemaDiagnosticBuilder SemaCUDA::DiagIfDeviceCode(SourceLocation Loc, if (!getLangOpts().CUDAIsDevice) return SemaDiagnosticBuilder::K_Nop; if (SemaRef.IsLastErrorImmediate && - getDiagnostics().getDiagnosticIDs()->isBuiltinNote(DiagID)) + getDiagnostics().getDiagnosticIDs()->isNote(DiagID)) return SemaDiagnosticBuilder::K_Immediate; return (SemaRef.getEmissionStatus(CurFunContext) == Sema::FunctionEmissionStatus::Emitted) @@ -866,7 +866,7 @@ Sema::SemaDiagnosticBuilder SemaCUDA::DiagIfHostCode(SourceLocation Loc, if (getLangOpts().CUDAIsDevice) return SemaDiagnosticBuilder::K_Nop; if (SemaRef.IsLastErrorImmediate && - getDiagnostics().getDiagnosticIDs()->isBuiltinNote(DiagID)) + getDiagnostics().getDiagnosticIDs()->isNote(DiagID)) return SemaDiagnosticBuilder::K_Immediate; return (SemaRef.getEmissionStatus(CurFunContext) == Sema::FunctionEmissionStatus::Emitted) diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index bba91d580a082..ec4d0e8ff4426 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -858,22 +858,38 @@ static void handleDiagnoseIfAttr(Sema &S, Decl *D, const ParsedAttr &AL) { if (!checkFunctionConditionAttr(S, D, AL, Cond, Msg)) return; - StringRef DiagTypeStr; - if (!S.checkStringLiteralArgumentAttr(AL, 2, DiagTypeStr)) + StringRef DefaultSevStr; + if (!S.checkStringLiteralArgumentAttr(AL, 2, DefaultSevStr)) return; - DiagnoseIfAttr::DiagnosticType DiagType; - if (!DiagnoseIfAttr::ConvertStrToDiagnosticType(DiagTypeStr, DiagType)) { + DiagnoseIfAttr::DefaultSeverity DefaultSev; + if (!DiagnoseIfAttr::ConvertStrToDefaultSeverity(DefaultSevStr, DefaultSev)) { S.Diag(AL.getArgAsExpr(2)->getBeginLoc(), diag::err_diagnose_if_invalid_diagnostic_type); return; } + StringRef WarningGroup; + SmallVector Options; + if (AL.getNumArgs() > 3) { + if (!S.checkStringLiteralArgumentAttr(AL, 3, WarningGroup)) + return; + if (WarningGroup.empty() || + !S.getDiagnostics().getDiagnosticIDs()->getGroupForWarningOption( + WarningGroup)) { + S.Diag(AL.getArgAsExpr(3)->getBeginLoc(), + diag::err_diagnose_if_unknown_warning) + << WarningGroup; + return; + } + } + bool ArgDependent = false; if (const auto *FD = dyn_cast(D)) ArgDependent = ArgumentDependenceChecker(FD).referencesArgs(Cond); D->addAttr(::new (S.Context) DiagnoseIfAttr( - S.Context, AL, Cond, Msg, DiagType, ArgDependent, cast(D))); + S.Context, AL, Cond, Msg, DefaultSev, WarningGroup, ArgDependent, + cast(D))); } static void handleNoBuiltinAttr(Sema &S, Decl *D, const ParsedAttr &AL) { diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 11ceaa98f3cfb..0e880641a61a3 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -7234,8 +7234,10 @@ static bool diagnoseDiagnoseIfAttrsWith(Sema &S, const NamedDecl *ND, return false; auto WarningBegin = std::stable_partition( - Attrs.begin(), Attrs.end(), - [](const DiagnoseIfAttr *DIA) { return DIA->isError(); }); + Attrs.begin(), Attrs.end(), [](const DiagnoseIfAttr *DIA) { + return DIA->getDefaultSeverity() == DiagnoseIfAttr::DS_error && + DIA->getWarningGroup().empty(); + }); // Note that diagnose_if attributes are late-parsed, so they appear in the // correct order (unlike enable_if attributes). @@ -7249,11 +7251,32 @@ static bool diagnoseDiagnoseIfAttrsWith(Sema &S, const NamedDecl *ND, return true; } + auto ToSeverity = [](DiagnoseIfAttr::DefaultSeverity Sev) { + switch (Sev) { + case DiagnoseIfAttr::DS_warning: + return diag::Severity::Warning; + case DiagnoseIfAttr::DS_error: + return diag::Severity::Error; + } + llvm_unreachable("Fully covered switch above!"); + }; + for (const auto *DIA : llvm::make_range(WarningBegin, Attrs.end())) if (IsSuccessful(DIA)) { - S.Diag(Loc, diag::warn_diagnose_if_succeeded) << DIA->getMessage(); - S.Diag(DIA->getLocation(), diag::note_from_diagnose_if) - << DIA->getParent() << DIA->getCond()->getSourceRange(); + if (DIA->getWarningGroup().empty() && + DIA->getDefaultSeverity() == DiagnoseIfAttr::DS_warning) { + S.Diag(Loc, diag::warn_diagnose_if_succeeded) << DIA->getMessage(); + S.Diag(DIA->getLocation(), diag::note_from_diagnose_if) + << DIA->getParent() << DIA->getCond()->getSourceRange(); + } else { + auto DiagGroup = S.Diags.getDiagnosticIDs()->getGroupForWarningOption( + DIA->getWarningGroup()); + assert(DiagGroup); + auto DiagID = S.Diags.getDiagnosticIDs()->getCustomDiagID( + {ToSeverity(DIA->getDefaultSeverity()), "%0", + DiagnosticIDs::CLASS_WARNING, false, false, *DiagGroup}); + S.Diag(Loc, DiagID) << DIA->getMessage(); + } } return false; diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index bcdf6fed0f365..061907be5eb3e 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -282,7 +282,8 @@ static void instantiateDependentDiagnoseIfAttr( if (Cond) New->addAttr(new (S.getASTContext()) DiagnoseIfAttr( S.getASTContext(), *DIA, Cond, DIA->getMessage(), - DIA->getDiagnosticType(), DIA->getArgDependent(), New)); + DIA->getDefaultSeverity(), DIA->getWarningGroup(), + DIA->getArgDependent(), New)); } // Constructs and adds to New a new instance of CUDALaunchBoundsAttr using diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index 1aab800ce423d..bf4cdf0983432 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -6711,7 +6711,7 @@ void ASTReader::ReadPragmaDiagnosticMappings(DiagnosticsEngine &Diag) { // command line (-w, -Weverything, -Werror, ...) along with any explicit // -Wblah flags. unsigned Flags = Record[Idx++]; - DiagState Initial; + DiagState Initial(*Diag.getDiagnosticIDs()); Initial.SuppressSystemWarnings = Flags & 1; Flags >>= 1; Initial.ErrorsAsFatal = Flags & 1; Flags >>= 1; Initial.WarningsAsErrors = Flags & 1; Flags >>= 1; diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index dcd3532ae4024..c70a29cb505e3 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -3253,7 +3253,7 @@ void ASTWriter::WritePragmaDiagnosticMappings(const DiagnosticsEngine &Diag, // Skip default mappings. We have a mapping for every diagnostic ever // emitted, regardless of whether it was customized. if (!I.second.isPragma() && - I.second == DiagnosticIDs::getDefaultMapping(I.first)) + I.second == Diag.getDiagnosticIDs()->getDefaultMapping(I.first)) continue; Mappings.push_back(I); } diff --git a/clang/lib/StaticAnalyzer/Core/TextDiagnostics.cpp b/clang/lib/StaticAnalyzer/Core/TextDiagnostics.cpp index 71268af22e242..7cdd545e61b32 100644 --- a/clang/lib/StaticAnalyzer/Core/TextDiagnostics.cpp +++ b/clang/lib/StaticAnalyzer/Core/TextDiagnostics.cpp @@ -91,7 +91,6 @@ class TextDiagnostics : public PathDiagnosticConsumer { ? " [" + PD->getCheckerName() + "]" : "") .str(); - reportPiece(WarnID, PD->getLocation().asLocation(), (PD->getShortDescription() + WarningMsg).str(), PD->path.back()->getRanges(), PD->path.back()->getFixits()); diff --git a/clang/test/Frontend/custom-diag-werror-interaction.c b/clang/test/Frontend/custom-diag-werror-interaction.c new file mode 100644 index 0000000000000..997c8c11ff0e0 --- /dev/null +++ b/clang/test/Frontend/custom-diag-werror-interaction.c @@ -0,0 +1,9 @@ +// RUN: %clang_cc1 -emit-llvm-only -fprofile-instrument=clang -fcoverage-mcdc -Werror -Wno-unused-value %s -verify + +int foo(int x); + +int main(void) { + int a, b, c; + a && foo( b && c ); // expected-warning{{unsupported MC/DC boolean expression; contains an operation with a nested boolean expression. Expression will not be covered}} + return 0; +} diff --git a/clang/test/Sema/diagnose_if.c b/clang/test/Sema/diagnose_if.c index 4df39916c031e..e9b8497d5ca4e 100644 --- a/clang/test/Sema/diagnose_if.c +++ b/clang/test/Sema/diagnose_if.c @@ -2,10 +2,10 @@ #define _diagnose_if(...) __attribute__((diagnose_if(__VA_ARGS__))) -void failure1(void) _diagnose_if(); // expected-error{{exactly 3 arguments}} -void failure2(void) _diagnose_if(0); // expected-error{{exactly 3 arguments}} -void failure3(void) _diagnose_if(0, ""); // expected-error{{exactly 3 arguments}} -void failure4(void) _diagnose_if(0, "", "error", 1); // expected-error{{exactly 3 arguments}} +void failure1(void) _diagnose_if(); // expected-error{{at least 3 arguments}} +void failure2(void) _diagnose_if(0); // expected-error{{at least 3 arguments}} +void failure3(void) _diagnose_if(0, ""); // expected-error{{at least 3 arguments}} +void failure4(void) _diagnose_if(0, "", "error", 1); // expected-error{{expected string literal as argument}} void failure5(void) _diagnose_if(0, 0, "error"); // expected-error{{expected string literal as argument of 'diagnose_if' attribute}} void failure6(void) _diagnose_if(0, "", "invalid"); // expected-error{{invalid diagnostic type for 'diagnose_if'; use "error" or "warning" instead}} void failure7(void) _diagnose_if(0, "", "ERROR"); // expected-error{{invalid diagnostic type}} diff --git a/clang/test/SemaCXX/diagnose_if-warning-group.cpp b/clang/test/SemaCXX/diagnose_if-warning-group.cpp new file mode 100644 index 0000000000000..a39c0c0c33c9e --- /dev/null +++ b/clang/test/SemaCXX/diagnose_if-warning-group.cpp @@ -0,0 +1,63 @@ +// RUN: %clang_cc1 %s -verify=expected,wall -fno-builtin -Wno-pedantic -Werror=comment -Wno-error=abi -Wfatal-errors=assume -Wno-fatal-errors=assume -Wno-format +// RUN: %clang_cc1 %s -verify=expected,wno-all,pedantic,format -fno-builtin -Wno-all -Werror=comment -Wno-error=abi -Werror=assume -Wformat + +#define diagnose_if(...) __attribute__((diagnose_if(__VA_ARGS__))) + +#ifndef EMTY_WARNING_GROUP +void bougus_warning() diagnose_if(true, "oh no", "warning", "bogus warning") {} // expected-error {{unknown warning group 'bogus warning'}} + +void show_in_system_header() diagnose_if(true, "oh no", "warning", "assume", "Banane") {} // expected-error {{'diagnose_if' attribute takes no more than 4 arguments}} +#endif // EMTY_WARNING_GROUP + +template +void diagnose_if_wcomma() diagnose_if(b, "oh no", "warning", "comma") {} + +template +void diagnose_if_wcomment() diagnose_if(b, "oh no", "warning", "comment") {} + +void empty_warning_group() diagnose_if(true, "oh no", "warning", "") {} // expected-error {{unknown warning group ''}} +void empty_warning_group_error() diagnose_if(true, "oh no", "error", "") {} // expected-error {{unknown warning group ''}} + +void diagnose_if_wabi_default_error() diagnose_if(true, "ABI stuff", "error", "abi") {} +void diagnose_assume() diagnose_if(true, "Assume diagnostic", "warning", "assume") {} + +void Wall() diagnose_if(true, "oh no", "warning", "all") {} +void Wpedantic() diagnose_if(true, "oh no", "warning", "pedantic") {} +void Wformat_extra_args() diagnose_if(true, "oh no", "warning", "format-extra-args") {} + +void call() { + diagnose_if_wcomma(); // expected-warning {{oh no}} + diagnose_if_wcomma(); + diagnose_if_wcomment(); // expected-error {{oh no}} + diagnose_if_wcomment(); + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wcomma" + diagnose_if_wcomma(); + diagnose_if_wcomment(); // expected-error {{oh no}} +#pragma clang diagnostic pop + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wcomment" + diagnose_if_wcomma(); // expected-warning {{oh no}} + diagnose_if_wcomment(); +#pragma clang diagnostic pop + + diagnose_if_wcomma(); // expected-warning {{oh no}} + diagnose_if_wcomment(); // expected-error {{oh no}} + + diagnose_if_wabi_default_error(); // expected-warning {{ABI stuff}} + diagnose_assume(); // expected-error {{Assume diagnostic}} + + // Make sure that the -Wassume diagnostic isn't fatal + diagnose_if_wabi_default_error(); // expected-warning {{ABI stuff}} + + Wall(); // wall-warning {{oh no}} + Wpedantic(); // pedantic-warning {{oh no}} + Wformat_extra_args(); // format-warning {{oh no}} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wformat" + Wformat_extra_args(); +#pragma clang diagnostic pop +} diff --git a/clang/tools/diagtool/ListWarnings.cpp b/clang/tools/diagtool/ListWarnings.cpp index a71f6e3a66c8e..9f9647126dd8a 100644 --- a/clang/tools/diagtool/ListWarnings.cpp +++ b/clang/tools/diagtool/ListWarnings.cpp @@ -53,13 +53,13 @@ int ListWarnings::run(unsigned int argc, char **argv, llvm::raw_ostream &out) { for (const DiagnosticRecord &DR : getBuiltinDiagnosticsByName()) { const unsigned diagID = DR.DiagID; - if (DiagnosticIDs::isBuiltinNote(diagID)) + if (DiagnosticIDs{}.isNote(diagID)) continue; - if (!DiagnosticIDs::isBuiltinWarningOrExtension(diagID)) + if (!DiagnosticIDs{}.isWarningOrExtension(diagID)) continue; - Entry entry(DR.getName(), DiagnosticIDs::getWarningOptionForDiag(diagID)); + Entry entry(DR.getName(), DiagnosticIDs{}.getWarningOptionForDiag(diagID)); if (entry.Flag.empty()) Unflagged.push_back(entry); @@ -97,4 +97,3 @@ int ListWarnings::run(unsigned int argc, char **argv, llvm::raw_ostream &out) { return 0; } - diff --git a/clang/tools/diagtool/ShowEnabledWarnings.cpp b/clang/tools/diagtool/ShowEnabledWarnings.cpp index 66a295db054c3..caf67223921d4 100644 --- a/clang/tools/diagtool/ShowEnabledWarnings.cpp +++ b/clang/tools/diagtool/ShowEnabledWarnings.cpp @@ -117,10 +117,10 @@ int ShowEnabledWarnings::run(unsigned int argc, char **argv, raw_ostream &Out) { for (const DiagnosticRecord &DR : getBuiltinDiagnosticsByName()) { unsigned DiagID = DR.DiagID; - if (DiagnosticIDs::isBuiltinNote(DiagID)) + if (DiagnosticIDs{}.isNote(DiagID)) continue; - if (!DiagnosticIDs::isBuiltinWarningOrExtension(DiagID)) + if (!DiagnosticIDs{}.isWarningOrExtension(DiagID)) continue; DiagnosticsEngine::Level DiagLevel = @@ -128,7 +128,7 @@ int ShowEnabledWarnings::run(unsigned int argc, char **argv, raw_ostream &Out) { if (DiagLevel == DiagnosticsEngine::Ignored) continue; - StringRef WarningOpt = DiagnosticIDs::getWarningOptionForDiag(DiagID); + StringRef WarningOpt = DiagnosticIDs{}.getWarningOptionForDiag(DiagID); Active.push_back(PrettyDiag(DR.getName(), WarningOpt, DiagLevel)); } diff --git a/clang/tools/libclang/CXStoredDiagnostic.cpp b/clang/tools/libclang/CXStoredDiagnostic.cpp index f46df9bbb8294..72db2c8a55e6e 100644 --- a/clang/tools/libclang/CXStoredDiagnostic.cpp +++ b/clang/tools/libclang/CXStoredDiagnostic.cpp @@ -51,7 +51,9 @@ CXString CXStoredDiagnostic::getSpelling() const { CXString CXStoredDiagnostic::getDiagnosticOption(CXString *Disable) const { unsigned ID = Diag.getID(); - StringRef Option = DiagnosticIDs::getWarningOptionForDiag(ID); + if (DiagnosticIDs::IsCustomDiag(ID)) + return cxstring::createEmpty(); + StringRef Option = DiagnosticIDs{}.getWarningOptionForDiag(ID); if (!Option.empty()) { if (Disable) *Disable = cxstring::createDup((Twine("-Wno-") + Option).str()); diff --git a/flang/lib/Frontend/TextDiagnosticPrinter.cpp b/flang/lib/Frontend/TextDiagnosticPrinter.cpp index 1e6414f38648e..e4d4cbe91b8f4 100644 --- a/flang/lib/Frontend/TextDiagnosticPrinter.cpp +++ b/flang/lib/Frontend/TextDiagnosticPrinter.cpp @@ -39,7 +39,8 @@ static void printRemarkOption(llvm::raw_ostream &os, clang::DiagnosticsEngine::Level level, const clang::Diagnostic &info) { llvm::StringRef opt = - clang::DiagnosticIDs::getWarningOptionForDiag(info.getID()); + info.getDiags()->getDiagnosticIDs()->getWarningOptionForDiag( + info.getID()); if (!opt.empty()) { // We still need to check if the level is a Remark since, an unknown option // warning could be printed i.e. [-Wunknown-warning-option] From 69aedb48133f63242c3984aec68056fa761d7131 Mon Sep 17 00:00:00 2001 From: Ben Langmuir Date: Thu, 19 Sep 2024 10:56:19 -0700 Subject: [PATCH 2/3] [cas] Fix caching of diagnostics using getCustomDiagID Custom diagnostics need to be serialized so that they can be registered with the diagnostic engine during replay. If any custom diagnostics are emitted, we capture all the known custom diagnostics. While this could theoretically be wasteful, in practice custom diagnostics are created only when they will be emitted. Note: this also fixes clang/test/CAS/analyze-action.c. (cherry picked from commit 61d414a7527a5fa9c077cac309c40ab28f85a129) (cherry picked from commit ee2a43d94fd1ce344e980c1e8a2528dabca2c094) --- clang/include/clang/Basic/Diagnostic.h | 12 +++ clang/include/clang/Basic/DiagnosticIDs.h | 4 + clang/lib/Basic/DiagnosticIDs.cpp | 16 ++++ clang/lib/Frontend/CachedDiagnostics.cpp | 101 ++++++++++++++++++++++ clang/test/CAS/custom-diags.m | 14 +++ 5 files changed, 147 insertions(+) create mode 100644 clang/test/CAS/custom-diags.m diff --git a/clang/include/clang/Basic/Diagnostic.h b/clang/include/clang/Basic/Diagnostic.h index e9ae146ba665e..e96aeefb9aeb4 100644 --- a/clang/include/clang/Basic/Diagnostic.h +++ b/clang/include/clang/Basic/Diagnostic.h @@ -880,6 +880,18 @@ class DiagnosticsEngine : public RefCountedBase { StringRef(FormatString, N - 1)); } + unsigned getCustomDiagID(DiagnosticIDs::CustomDiagDesc Desc) { + return Diags->getCustomDiagID(std::move(Desc)); + } + + std::optional getMaxCustomDiagID() const { + return Diags->getMaxCustomDiagID(); + } + const DiagnosticIDs::CustomDiagDesc & + getCustomDiagDesc(unsigned DiagID) const { + return Diags->getCustomDiagDesc(DiagID); + } + /// Converts a diagnostic argument (as an intptr_t) into the string /// that represents it. void ConvertArgToString(ArgumentKind Kind, intptr_t Val, diff --git a/clang/include/clang/Basic/DiagnosticIDs.h b/clang/include/clang/Basic/DiagnosticIDs.h index ac27ed72bcf33..a711c64a5d414 100644 --- a/clang/include/clang/Basic/DiagnosticIDs.h +++ b/clang/include/clang/Basic/DiagnosticIDs.h @@ -245,6 +245,7 @@ class DiagnosticIDs : public RefCountedBase { Class GetClass() const { return static_cast(DiagClass); } std::string_view GetDescription() const { return Description; } bool ShouldShowInSystemHeader() const { return ShowInSystemHeader; } + bool ShouldShowInSystemMacro() const { return ShowInSystemMacro; } friend bool operator==(const CustomDiagDesc &lhs, const CustomDiagDesc &rhs) { @@ -318,6 +319,9 @@ class DiagnosticIDs : public RefCountedBase { }()); } + std::optional getMaxCustomDiagID() const; + const CustomDiagDesc &getCustomDiagDesc(unsigned DiagID) const; + //===--------------------------------------------------------------------===// // Diagnostic classification and reporting interfaces. // diff --git a/clang/lib/Basic/DiagnosticIDs.cpp b/clang/lib/Basic/DiagnosticIDs.cpp index ade4e0ab1a5a5..e53df8ac5d9da 100644 --- a/clang/lib/Basic/DiagnosticIDs.cpp +++ b/clang/lib/Basic/DiagnosticIDs.cpp @@ -315,6 +315,10 @@ class CustomDiagInfo { return Diags->second; return {}; } + + unsigned getMaxCustomDiagID() const { + return DIAG_UPPER_LIMIT + DiagInfo.size(); + } }; } // namespace diag @@ -433,6 +437,18 @@ unsigned DiagnosticIDs::getCustomDiagID(CustomDiagDesc Diag) { return CustomDiagInfo->getOrCreateDiagID(Diag); } +std::optional DiagnosticIDs::getMaxCustomDiagID() const { + if (CustomDiagInfo) + return CustomDiagInfo->getMaxCustomDiagID(); + return std::nullopt; +} + +const DiagnosticIDs::CustomDiagDesc & +DiagnosticIDs::getCustomDiagDesc(unsigned DiagID) const { + assert(IsCustomDiag(DiagID)); + return CustomDiagInfo->getDescription(DiagID); +} + bool DiagnosticIDs::isWarningOrExtension(unsigned DiagID) const { return DiagID < diag::DIAG_UPPER_LIMIT ? getDiagClass(DiagID) != CLASS_ERROR diff --git a/clang/lib/Frontend/CachedDiagnostics.cpp b/clang/lib/Frontend/CachedDiagnostics.cpp index f0891b039ee84..194cb2137751f 100644 --- a/clang/lib/Frontend/CachedDiagnostics.cpp +++ b/clang/lib/Frontend/CachedDiagnostics.cpp @@ -108,15 +108,38 @@ struct Diagnostic { std::vector FixIts; }; +struct CustomDiagDesc { + diag::Severity DefaultSeverity; + DiagnosticIDs::Class DiagClass; + bool ShowInSystemHeader; + bool ShowInSystemMacro; + std::string Description; + std::optional Group; + CustomDiagDesc() = default; + CustomDiagDesc(const DiagnosticIDs::CustomDiagDesc &Desc) + : DefaultSeverity(Desc.GetDefaultSeverity()), DiagClass(Desc.GetClass()), + ShowInSystemHeader(Desc.ShouldShowInSystemHeader()), + ShowInSystemMacro(Desc.ShouldShowInSystemMacro()), + Description(Desc.GetDescription()), Group(Desc.GetGroup()) {} + + DiagnosticIDs::CustomDiagDesc getDesc() const { + return DiagnosticIDs::CustomDiagDesc(DefaultSeverity, Description, + DiagClass, ShowInSystemHeader, + ShowInSystemMacro, Group); + } +}; + struct Diagnostics { std::vector SLocEntries; std::vector Diags; + std::vector CustomDiags; size_t getNumDiags() const { return Diags.size(); } void clear() { SLocEntries.clear(); Diags.clear(); + CustomDiags.clear(); } }; @@ -198,6 +221,10 @@ struct CachedDiagnosticSerializer { /// produced it. std::optional serializeEmittedDiagnostics(); Error deserializeCachedDiagnostics(StringRef Buffer); + + /// Capture any custom diagnostics registerd by \p Diags so that they can be + /// later serialized. + void captureCustomDiags(const DiagnosticsEngine &Diags); }; } // anonymous namespace @@ -456,6 +483,46 @@ template <> struct MappingTraits { } }; +template <> struct ScalarEnumerationTraits { + static void enumeration(IO &io, diag::Severity &value) { + io.enumCase(value, "ignored", diag::Severity::Ignored); + io.enumCase(value, "remark", diag::Severity::Remark); + io.enumCase(value, "warning", diag::Severity::Warning); + io.enumCase(value, "error", diag::Severity::Error); + io.enumCase(value, "fatal", diag::Severity::Fatal); + } +}; +template <> struct ScalarEnumerationTraits { + static void enumeration(IO &io, DiagnosticIDs::Class &value) { + io.enumCase(value, "invalid", DiagnosticIDs::CLASS_INVALID); + io.enumCase(value, "note", DiagnosticIDs::CLASS_NOTE); + io.enumCase(value, "remark", DiagnosticIDs::CLASS_REMARK); + io.enumCase(value, "warning", DiagnosticIDs::CLASS_WARNING); + io.enumCase(value, "extension", DiagnosticIDs::CLASS_EXTENSION); + io.enumCase(value, "error", DiagnosticIDs::CLASS_ERROR); + } +}; +template <> struct ScalarEnumerationTraits { + static void enumeration(IO &io, diag::Group &value) { +#define DIAG_ENTRY(GroupName, FlagNameOffset, Members, SubGroups, Docs) \ + io.enumCase(value, #GroupName, diag::Group::GroupName); +#include "clang/Basic/DiagnosticGroups.inc" +#undef CATEGORY +#undef DIAG_ENTRY + } +}; + +template <> struct MappingTraits { + static void mapping(IO &io, cached_diagnostics::CustomDiagDesc &DiagDesc) { + io.mapRequired("severity", DiagDesc.DefaultSeverity); + io.mapRequired("class", DiagDesc.DiagClass); + io.mapRequired("show_in_system_header", DiagDesc.ShowInSystemHeader); + io.mapRequired("show_in_system_macro", DiagDesc.ShowInSystemMacro); + io.mapRequired("description", DiagDesc.Description); + io.mapOptional("group", DiagDesc.Group); + } +}; + template <> struct MappingTraits { static void mapping(IO &io, cached_diagnostics::SLocEntry::FileInfo &s) { io.mapRequired("filename", s.Filename); @@ -537,6 +604,7 @@ template <> struct MappingTraits { static void mapping(IO &io, cached_diagnostics::Diagnostics &s) { io.mapRequired("sloc_entries", s.SLocEntries); io.mapRequired("diagnostics", s.Diags); + io.mapRequired("custom_diagnostics", s.CustomDiags); } }; } // namespace llvm::yaml @@ -545,6 +613,28 @@ LLVM_YAML_IS_SEQUENCE_VECTOR(cached_diagnostics::SLocEntry) LLVM_YAML_IS_SEQUENCE_VECTOR(cached_diagnostics::Diagnostic) LLVM_YAML_IS_SEQUENCE_VECTOR(cached_diagnostics::Range) LLVM_YAML_IS_SEQUENCE_VECTOR(cached_diagnostics::FixItHint) +LLVM_YAML_IS_SEQUENCE_VECTOR(cached_diagnostics::CustomDiagDesc) + +void CachedDiagnosticSerializer::captureCustomDiags( + const DiagnosticsEngine &Diags) { + auto MaxCustomDiagID = Diags.getMaxCustomDiagID(); + if (!MaxCustomDiagID) + return; + + // Capture any custom diagnostics we have not already seen. + unsigned FirstUnknownDiag = + diag::DIAG_UPPER_LIMIT + CachedDiags.CustomDiags.size(); + for (unsigned DiagID = FirstUnknownDiag; DiagID < *MaxCustomDiagID; + ++DiagID) { + auto Desc = Diags.getCustomDiagDesc(DiagID); + CachedDiags.CustomDiags.push_back(Desc); + + // Forward the custom diagnostic to the Serializer's diagnostic engine. + auto SerializerDiagID = DiagEngine.getCustomDiagID(Desc); + assert(SerializerDiagID == DiagID && "mismatched custom diags"); + (void)SerializerDiagID; + } +} std::optional CachedDiagnosticSerializer::serializeEmittedDiagnostics() { @@ -613,6 +703,13 @@ Error CachedDiagnosticSerializer::deserializeCachedDiagnostics( if (YIn.error()) return createStringError(YIn.error(), "failed deserializing cached diagnostics"); + + assert(DiagEngine.getMaxCustomDiagID() == std::nullopt && + "existing custom diagnostics will conflict"); + for (const auto &CustomDiag : CachedDiags.CustomDiags) { + (void)DiagEngine.getCustomDiagID(CustomDiag.getDesc()); + } + return Error::success(); } @@ -661,6 +758,10 @@ struct CachingDiagnosticsProcessor::DiagnosticsConsumer if (shouldCacheDiagnostic(Level, Info)) { unsigned DiagIdx = Serializer.addDiag(StoredDiagnostic(Level, Info)); StoredDiagnostic NewDiag = Serializer.getDiag(DiagIdx); + + if (DiagnosticIDs::IsCustomDiag(NewDiag.getID())) + Serializer.captureCustomDiags(*Info.getDiags()); + // Pass the converted diagnostic to the original consumer. We do this // because: // 1. It ensures that the rendered diagnostics will use the same diff --git a/clang/test/CAS/custom-diags.m b/clang/test/CAS/custom-diags.m new file mode 100644 index 0000000000000..5bac91f050a1a --- /dev/null +++ b/clang/test/CAS/custom-diags.m @@ -0,0 +1,14 @@ +// RUN: rm -rf %t && mkdir -p %t + +// RUN: not %clang_cc1 -triple arm64-apple-macosx12 -fsyntax-only %s 2> %t/diags-orig + +// RUN: %clang -cc1depscan -fdepscan=inline -fdepscan-include-tree -o %t/t.rsp -cc1-args \ +// RUN: -cc1 -triple arm64-apple-macosx12 -fcas-path %t/cas -fsyntax-only %s +// RUN: not %clang @%t/t.rsp 2> %t/diags-cached + +// RUN: diff -u %t/diags-orig %t/diags-cached + +// RUN: FileCheck %s -input-file %t/diags-cached + +const char s8[] = @encode(__SVInt8_t); +// CHECK: cannot yet @encode type __SVInt8_t From 58d344d48e96c10ed516d09486f6a09149d662f8 Mon Sep 17 00:00:00 2001 From: Ben Langmuir Date: Tue, 22 Apr 2025 09:56:05 -0700 Subject: [PATCH 3/3] [6.2] Fix clangd unittest build failure after 5d0b3e1582 --- .../clangd/unittests/ConfigCompileTests.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/clang-tools-extra/clangd/unittests/ConfigCompileTests.cpp b/clang-tools-extra/clangd/unittests/ConfigCompileTests.cpp index cf9b42828568d..021d731f8f176 100644 --- a/clang-tools-extra/clangd/unittests/ConfigCompileTests.cpp +++ b/clang-tools-extra/clangd/unittests/ConfigCompileTests.cpp @@ -305,33 +305,33 @@ TEST_F(ConfigCompileTests, DiagnosticSuppression) { { auto D = DiagEngine.Report(diag::warn_unreachable); EXPECT_TRUE(isDiagnosticSuppressed( - Diag{&DiagEngine, D}, Conf.Diagnostics.Suppress, LangOptions())); + Diag{&DiagEngine}, Conf.Diagnostics.Suppress, LangOptions())); } // Subcategory not respected/suppressed. { auto D = DiagEngine.Report(diag::warn_unreachable_break); EXPECT_FALSE(isDiagnosticSuppressed( - Diag{&DiagEngine, D}, Conf.Diagnostics.Suppress, LangOptions())); + Diag{&DiagEngine}, Conf.Diagnostics.Suppress, LangOptions())); } { auto D = DiagEngine.Report(diag::warn_unused_variable); EXPECT_TRUE(isDiagnosticSuppressed( - Diag{&DiagEngine, D}, Conf.Diagnostics.Suppress, LangOptions())); + Diag{&DiagEngine}, Conf.Diagnostics.Suppress, LangOptions())); } { auto D = DiagEngine.Report(diag::err_typecheck_bool_condition); EXPECT_TRUE(isDiagnosticSuppressed( - Diag{&DiagEngine, D}, Conf.Diagnostics.Suppress, LangOptions())); + Diag{&DiagEngine}, Conf.Diagnostics.Suppress, LangOptions())); } { auto D = DiagEngine.Report(diag::err_unexpected_friend); EXPECT_TRUE(isDiagnosticSuppressed( - Diag{&DiagEngine, D}, Conf.Diagnostics.Suppress, LangOptions())); + Diag{&DiagEngine}, Conf.Diagnostics.Suppress, LangOptions())); } { auto D = DiagEngine.Report(diag::warn_alloca); EXPECT_TRUE(isDiagnosticSuppressed( - Diag{&DiagEngine, D}, Conf.Diagnostics.Suppress, LangOptions())); + Diag{&DiagEngine}, Conf.Diagnostics.Suppress, LangOptions())); } Frag.Diagnostics.Suppress.emplace_back("*");