From 030c6da7af826b641db005be925b20f956c3a6bb Mon Sep 17 00:00:00 2001 From: Nikolas Klauser Date: Thu, 12 Sep 2024 20:15:01 +0200 Subject: [PATCH] [clang] Extend diagnose_if to accept more detailed warning information (#70976) 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. --- 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 | 153 ++++++++-- .../clang/Basic/DiagnosticSemaKinds.td | 6 + clang/lib/Basic/Diagnostic.cpp | 22 +- clang/lib/Basic/DiagnosticIDs.cpp | 280 ++++++++++-------- 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 | 32 +- .../lib/Sema/SemaTemplateInstantiateDecl.cpp | 3 +- clang/lib/Serialization/ASTReader.cpp | 2 +- clang/lib/Serialization/ASTWriter.cpp | 2 +- .../StaticAnalyzer/Core/TextDiagnostics.cpp | 1 - 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 +- 28 files changed, 544 insertions(+), 223 deletions(-) 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 d5eca083eb6512..552dd36b6900bf 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); + const 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 d4c0478c63a5cf..c45d8dc3aa6ce6 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 a8b6cc8dd0a46f..4491be9aa0362b 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 dd13b1a9e5613d..84e8fec342829c 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 4ecfdf0184ab40..a812bac0338aa7 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(nullptr, nullptr, + new clang::IgnoringDiagConsumer); + + using Diag = clang::Diagnostic; + { + auto D = DiagEngine.Report(diag::warn_unreachable); + EXPECT_TRUE(isDiagnosticSuppressed( + Diag{&DiagEngine}, 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}, Conf.Diagnostics.Suppress, LangOptions())); + } + { + auto D = DiagEngine.Report(diag::warn_unused_variable); + EXPECT_TRUE(isDiagnosticSuppressed( + Diag{&DiagEngine}, Conf.Diagnostics.Suppress, LangOptions())); + } + { + auto D = DiagEngine.Report(diag::err_typecheck_bool_condition); + EXPECT_TRUE(isDiagnosticSuppressed( + Diag{&DiagEngine}, Conf.Diagnostics.Suppress, LangOptions())); + } + { + auto D = DiagEngine.Report(diag::err_unexpected_friend); + EXPECT_TRUE(isDiagnosticSuppressed( + Diag{&DiagEngine}, Conf.Diagnostics.Suppress, LangOptions())); + } + { + auto D = DiagEngine.Report(diag::warn_alloca); + EXPECT_TRUE(isDiagnosticSuppressed( + Diag{&DiagEngine}, 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 9a7b163b2c6da8..70fad60d4edbb5 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -3358,18 +3358,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 0c7836c2ea569c..54b69e98540239 100644 --- a/clang/include/clang/Basic/Diagnostic.h +++ b/clang/include/clang/Basic/Diagnostic.h @@ -336,10 +336,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 = @@ -870,6 +872,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 LLVM + // [[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 14be326f7515f8..839f8dee3ca89f 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 8b976bdac6dc51..2402996ece5c94 100644 --- a/clang/include/clang/Basic/DiagnosticIDs.h +++ b/clang/include/clang/Basic/DiagnosticIDs.h @@ -14,6 +14,7 @@ #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" @@ -82,7 +83,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. @@ -179,13 +180,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(); @@ -200,7 +284,34 @@ 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}; + case DiagnosticIDs::Level::Note: + return {diag::Severity::Fatal, std::string(Message), CLASS_NOTE, + /*ShowInSystemHeader*/ true}; + case DiagnosticIDs::Level::Remark: + return {diag::Severity::Remark, std::string(Message), CLASS_REMARK, + /*ShowInSystemHeader*/ true}; + case DiagnosticIDs::Level::Warning: + return {diag::Severity::Warning, std::string(Message), CLASS_WARNING, + /*ShowInSystemHeader*/ true}; + case DiagnosticIDs::Level::Error: + return {diag::Severity::Error, std::string(Message), CLASS_ERROR, + /*ShowInSystemHeader*/ true}; + case DiagnosticIDs::Level::Fatal: + return {diag::Severity::Fatal, std::string(Message), CLASS_ERROR, + /*ShowInSystemHeader*/ true}; + } + }()); + } //===--------------------------------------------------------------------===// // Diagnostic classification and reporting interfaces. @@ -212,35 +323,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 @@ -250,19 +362,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. @@ -363,6 +478,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 efdc058edca56d..74835830ceb9f5 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -2927,9 +2927,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 66776daa5e1493..ecff80a5090630 100644 --- a/clang/lib/Basic/Diagnostic.cpp +++ b/clang/lib/Basic/Diagnostic.cpp @@ -138,7 +138,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()); } } @@ -166,8 +166,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; } @@ -309,7 +312,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; @@ -353,9 +357,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"); @@ -407,6 +409,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); @@ -429,6 +433,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. @@ -460,6 +465,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. @@ -492,7 +498,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 cd42573968b212..cae6642bd4bd3e 100644 --- a/clang/lib/Basic/DiagnosticIDs.cpp +++ b/clang/lib/Basic/DiagnosticIDs.cpp @@ -102,13 +102,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 { @@ -269,11 +268,60 @@ CATEGORY(INSTALLAPI, REFACTORING) 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) { @@ -286,6 +334,18 @@ 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); + } +} + /// getCategoryNumberForDiag - Return the category number that a specified /// DiagID belongs to, or 0 if no category. unsigned DiagnosticIDs::getCategoryNumberForDiag(unsigned DiagID) { @@ -343,61 +403,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 //===----------------------------------------------------------------------===// @@ -412,38 +417,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 = @@ -451,10 +450,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; } @@ -464,7 +460,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) { @@ -489,13 +485,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)); } @@ -509,7 +499,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. @@ -517,7 +508,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()) @@ -525,14 +516,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; @@ -550,10 +542,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; } @@ -575,9 +569,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 @@ -597,6 +592,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 @@ -642,7 +646,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; @@ -673,7 +682,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) @@ -692,9 +702,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; } @@ -702,12 +717,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) @@ -730,7 +782,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) { @@ -843,14 +895,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 469d1c22633aa3..4e963af837f01f 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 0887b5a504f05a..d1db31763e5009 100644 --- a/clang/lib/Frontend/SerializedDiagnosticPrinter.cpp +++ b/clang/lib/Frontend/SerializedDiagnosticPrinter.cpp @@ -202,7 +202,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); @@ -536,11 +536,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); } @@ -655,7 +657,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 b2fb762537573e..c2fea3d03f0c0f 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 46ddd360870b4f..d567de703903c9 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -1683,7 +1683,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(); } @@ -1697,7 +1697,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 ec37c0df56c671..fbb3de4b3e4165 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 72d82b424c26c8..fb2c2fba0e4628 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -852,22 +852,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 a155bb2fd3ba2a..d3e009a658f0e8 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -7307,8 +7307,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). @@ -7322,11 +7324,31 @@ 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; + } + }; + 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 bb311e38409280..e97a7d768b931b 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -284,7 +284,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 e5a1e20a265616..4fae6ff02ea9a3 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -6641,7 +6641,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 c6289493fce1de..008bf571f847dc 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -3219,7 +3219,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 71268af22e2424..7cdd545e61b328 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/Sema/diagnose_if.c b/clang/test/Sema/diagnose_if.c index 4df39916c031e3..e9b8497d5ca4e5 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 00000000000000..a39c0c0c33c9ef --- /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 a71f6e3a66c8eb..9f9647126dd8a0 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 66a295db054c35..caf67223921d4b 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 03018229549bd4..6fb3050f5f8445 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());