[clang] [clang-tools-extra] [llvm] [clang] Introduce diagnostics suppression mappings (PR #112517)

Boaz Brickner via cfe-commits cfe-commits at lists.llvm.org
Thu Nov 7 01:00:16 PST 2024


================
@@ -477,6 +486,109 @@ void DiagnosticsEngine::setSeverityForAll(diag::Flavor Flavor,
       setSeverity(Diag, Map, Loc);
 }
 
+namespace {
+class WarningsSpecialCaseList : public llvm::SpecialCaseList {
+public:
+  static std::unique_ptr<WarningsSpecialCaseList>
+  create(const llvm::MemoryBuffer &MB, std::string &Err) {
+    auto SCL = std::make_unique<WarningsSpecialCaseList>();
+    if (!SCL->createInternal(&MB, Err))
+      return nullptr;
+    return SCL;
+  }
+
+  // Section names refer to diagnostic groups, which cover multiple individual
+  // diagnostics. Expand diagnostic groups here to individual diagnostics.
+  // A diagnostic can have multiple diagnostic groups associated with it, we let
+  // the last section take precedence in such cases.
+  void processSections(DiagnosticsEngine &Diags) {
+    // Drop the default section introduced by special case list, we only support
+    // exact diagnostic group names.
+    Sections.erase("*");
+    // Make sure we iterate sections by their line numbers.
+    std::vector<std::pair<unsigned, const llvm::StringMapEntry<Section> *>>
+        LineAndSectionEntry;
+    LineAndSectionEntry.reserve(Sections.size());
+    for (const auto &Entry : Sections) {
+      LineAndSectionEntry.emplace_back(
+          Entry.second.SectionMatcher->Globs.at(Entry.first()).second, &Entry);
+    }
+    llvm::sort(LineAndSectionEntry);
+    static constexpr auto kFlavor = clang::diag::Flavor::WarningOrError;
+    for (const auto &[_, SectionEntry] : LineAndSectionEntry) {
+      SmallVector<diag::kind, 256> GroupDiags;
+      llvm::StringRef DiagGroup = SectionEntry->getKey();
+      if (Diags.getDiagnosticIDs()->getDiagnosticsInGroup(kFlavor, DiagGroup,
+                                                          GroupDiags)) {
+        StringRef Suggestion =
+            DiagnosticIDs::getNearestOption(kFlavor, DiagGroup);
+        Diags.Report(diag::warn_unknown_diag_option)
+            << static_cast<unsigned>(kFlavor) << DiagGroup
+            << !Suggestion.empty() << Suggestion;
+        continue;
+      }
+      for (diag::kind D : GroupDiags)
+        DiagToSection[D] = &SectionEntry->getValue();
+    }
+  }
+
+  bool isDiagSuppressed(diag::kind DiagId, llvm::StringRef FilePath) const {
+    auto Section = DiagToSection.find(DiagId);
+    if (Section == DiagToSection.end())
+      return false;
+    auto &DiagEntries = Section->second->Entries;
+    auto SrcEntries = DiagEntries.find("src");
+    if (SrcEntries == DiagEntries.end())
+      return false;
+    return globsMatches(SrcEntries->second, FilePath);
+  }
+
+private:
+  // Find the longest glob pattern that matches FilePath amongst
+  // CategoriesToMatchers, return true iff the match exists and belongs to a
+  // positive category.
+  bool globsMatches(llvm::StringMap<Matcher> CategoriesToMatchers,
+                    llvm::StringRef FilePath) const {
+    llvm::StringRef LongestMatch;
+    bool LongestIsPositive = false;
+    for (const auto &[Category, Matcher] : CategoriesToMatchers) {
+      bool IsPositive = Category != "emit";
+      for (const auto &[Pattern, Glob] : Matcher.Globs) {
----------------
bricknerb wrote:

Sure, I didn't mean to suggest we should do it for other SpecialCaseList users.
We can limit sorting to this use case.

It's not just implementation, it's also behavior.
Two similar mapping files in two different use cases would match different patterns.
I wonder if it is required to have a different matching behavior or we could match by order so that users would just put their patterns in the order they want them to be matched.
If in other use cases the matching is by order, and we could have a pattern wider than another (foo/* wider than foo/bar/*), why is this use case different?

https://github.com/llvm/llvm-project/pull/112517


More information about the cfe-commits mailing list