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

2024-11-13 Thread Aaron Ballman via cfe-commits


@@ -1315,6 +1319,34 @@ with its corresponding `Wno-` option.
 Note that when combined with :option:`-w` (which disables all warnings),
 disabling all warnings wins.
 
+.. _warning_suppression_mappings:
+
+Controlling Diagnostics via Suppression Mappings
+
+
+Warning suppression mappings enable users to suppress Clang's diagnostics in a
+per-file granular manner. Enabling enforcement of diagnostics in specific parts
+of the project, even if there are violations in some headers.

AaronBallman wrote:

That's true, but I think we want to guide users towards fixing diagnostics 
rather than suppressing them. Users will use any suppression mechanisms we 
provide regardless of how we document it, so I don't think it's harmful to 
imply this is more about cases where you can't modify the code. But I also 
don't feel strongly. WDYT?

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-13 Thread kadir çetinkaya via cfe-commits


@@ -1315,6 +1319,34 @@ with its corresponding `Wno-` option.
 Note that when combined with :option:`-w` (which disables all warnings),
 disabling all warnings wins.
 
+.. _warning_suppression_mappings:
+
+Controlling Diagnostics via Suppression Mappings
+
+
+Warning suppression mappings enable users to suppress Clang's diagnostics in a
+per-file granular manner. Enabling enforcement of diagnostics in specific parts

kadircet wrote:

> Add comma: per-file, granular manner

"per-file" was meant to describe "granular". rewrote as `per-file granularity` 
instead.

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-13 Thread kadir çetinkaya via cfe-commits


@@ -23,10 +23,15 @@
 // simpler because a remark can't be promoted to an error.
 #include "clang/Basic/AllDiagnostics.h"
 #include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/DiagnosticDriver.h"
+#include "clang/Basic/DiagnosticIDs.h"
 #include "clang/Basic/DiagnosticOptions.h"
-#include 
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/SpecialCaseList.h"
+#include "llvm/Support/VirtualFileSystem.h"
 #include 
-#include 
+#include 
+#include 

kadircet wrote:

oops, I had more implementation here initially.

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-13 Thread kadir çetinkaya via cfe-commits


@@ -477,6 +486,136 @@ void DiagnosticsEngine::setSeverityForAll(diag::Flavor 
Flavor,
   setSeverity(Diag, Map, Loc);
 }
 
+namespace {
+// FIXME: We should isolate the parser from SpecialCaseList and just use it
+// here.
+class WarningsSpecialCaseList : public llvm::SpecialCaseList {

kadircet wrote:

As you well know these discrepancies are coming from 
https://github.com/llvm/llvm-project/blob/main/clang/include/clang/Basic/LLVM.h.
 It's really hard to know/remember what's available there and what isn't. 
Moreover the header only forward-declares, so at usage side one actually needs 
to include the actual header from `llvm/...`.

Hence can't do this for `SpecialCaseList` but doing it for others that I can. 
But I think in the long run it'd be interesting to see if people still find 
this intermediate header useful or not.

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-13 Thread kadir çetinkaya via cfe-commits

https://github.com/kadircet edited 
https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-13 Thread kadir çetinkaya via cfe-commits


@@ -1315,6 +1319,34 @@ with its corresponding `Wno-` option.
 Note that when combined with :option:`-w` (which disables all warnings),
 disabling all warnings wins.
 
+.. _warning_suppression_mappings:
+
+Controlling Diagnostics via Suppression Mappings
+
+
+Warning suppression mappings enable users to suppress Clang's diagnostics in a
+per-file granular manner. Enabling enforcement of diagnostics in specific parts
+of the project, even if there are violations in some headers.

kadircet wrote:

> Reword: some headers -> some headers which cannot be modified.

But this covers headers even inside the project (i.e. the incremental cleanup 
of a new warning). So I feel like `which cannot be modified.` adds a 
non-existing limitation.

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-13 Thread kadir çetinkaya via cfe-commits


@@ -9017,3 +9017,8 @@ def wasm_opt : Flag<["--"], "wasm-opt">,
   Group,
   HelpText<"Enable the wasm-opt optimizer (default)">,
   MarshallingInfoNegativeFlag>;
+
+def warning_suppression_mappings_EQ : Joined<["--"],

kadircet wrote:

> This should move elsewhere in Options.td, it's currently listed under 
> clang-dxc options.

Oh didn't notice we actually had "lexical" sections in the doc (AFAICT it 
doesn't have any semantic affect afterwards, flag is still only visible for cc1 
and clang-driver).

> Also, should this be exposed in clang-cl as well? What about flang? CC 
> @zmodem @rnk @MaskRay @jansvoboda11 for opinions

Going to carry this discussion into RFC to make sure it doesn't get lost. 
https://discourse.llvm.org/t/rfc-add-support-for-controlling-diagnostics-severities-at-file-level-granularity-through-command-line/81292/21?u=kadircet

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-12 Thread Nikita Popov via cfe-commits

nikic wrote:

> so 
> [12e3ed8](https://github.com/llvm/llvm-project/commit/12e3ed8de8c6063b15916b3faf67c8c9cd17df1f)
>  should resolve both lldb buildbot breakage and compile time regression in 
> clang.

Thank you, I can confirm that this fixes the compile-time regression (or at 
least most of it).

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-12 Thread kadir çetinkaya via cfe-commits

kadircet wrote:

so 
https://github.com/llvm/llvm-project/commit/12e3ed8de8c6063b15916b3faf67c8c9cd17df1f
 should resolve both lldb buildbot breakage and compile time regression in 
clang.

that being said, I don't think 
https://github.com/llvm/llvm-project/commit/12e3ed8de8c6063b15916b3faf67c8c9cd17df1f
is the right fix for lldb breakage. I think this change reveals an actual bug 
in lldb's handling of modules, but I can't repro locally. I hit a similar issue 
with clang-repl during development, you can find the relevant fix in 
https://github.com/llvm/llvm-project/pull/112517/files#diff-23c4b4ecf706e1f0b594e365095a343594e3978b47991718782e1f76064f241a.

Basically I think LLDB is doing something similar, it's probably 
loading/building modules with one SourceManager and then it's trying to consume 
them using another. @Michael137, If you can provide more detailed instructions 
about how to set up a similar testing environment in linux (I couldn't get the 
std-modules bit to work), or if you can provide a detailed stack trace from 
assertion failure, I can try to dig deeper.

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-12 Thread Michael Buch via cfe-commits

Michael137 wrote:

Hmm this one's a bit strange. Looks like the issue is in:
```
// Clang-diagnostics pragmas always take precedence over suppression mapping.
if (!Mapping.isPragma()) {   
  // We also use presumed locations here to improve reproducibility for  
  // preprocessed inputs.
  if (PresumedLoc PLoc = SM.getPresumedLoc(Loc); 
  PLoc.isValid() && Diag.isSuppressedViaMapping( 
DiagID, llvm::sys::path::remove_leading_dotslash(
PLoc.getFilename(
return diag::Severity::Ignored;  
}
```
The assert triggers inside `getPresumedLoc`. So doesn't look like it's an 
inherent issue with the patch. But it seems to be breaking assumptions that 
LLDB is making about these source locations

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-12 Thread Michael Buch via cfe-commits

Michael137 wrote:

> > FYI, looks like this is causing following LLDB tests to fail: 
> > https://green.lab.llvm.org/job/llvm.org/view/LLDB/job/as-lldb-cmake/15106/execution/node/97/log/
> > ```
> > Unresolved Tests (17):
> >   lldb-api :: 
> > commands/expression/import-std-module/array/TestArrayFromStdModule.py
> >   lldb-api :: 
> > commands/expression/import-std-module/deque-basic/TestDequeFromStdModule.py
> >   lldb-api :: 
> > commands/expression/import-std-module/deque-dbg-info-content/TestDbgInfoContentDequeFromStdModule.py
> >   lldb-api :: 
> > commands/expression/import-std-module/forward_list-dbg-info-content/TestDbgInfoContentForwardListFromStdModule.py
> >   lldb-api :: 
> > commands/expression/import-std-module/forward_list/TestForwardListFromStdModule.py
> >   lldb-api :: 
> > commands/expression/import-std-module/iterator/TestIteratorFromStdModule.py
> >   lldb-api :: 
> > commands/expression/import-std-module/list-dbg-info-content/TestDbgInfoContentListFromStdModule.py
> >   lldb-api :: 
> > commands/expression/import-std-module/list/TestListFromStdModule.py
> >   lldb-api :: 
> > commands/expression/import-std-module/non-module-type-separation/TestNonModuleTypeSeparation.py
> >   lldb-api :: 
> > commands/expression/import-std-module/retry-with-std-module/TestRetryWithStdModule.py
> >   lldb-api :: 
> > commands/expression/import-std-module/shared_ptr-dbg-info-content/TestSharedPtrDbgInfoContentFromStdModule.py
> >   lldb-api :: 
> > commands/expression/import-std-module/shared_ptr/TestSharedPtrFromStdModule.py
> >   lldb-api :: 
> > commands/expression/import-std-module/unique_ptr-dbg-info-content/TestUniquePtrDbgInfoContent.py
> >   lldb-api :: 
> > commands/expression/import-std-module/unique_ptr/TestUniquePtrFromStdModule.py
> >   lldb-api :: 
> > commands/expression/import-std-module/vector-of-vectors/TestVectorOfVectorsFromStdModule.py
> >   lldb-api :: 
> > commands/expression/import-std-module/weak_ptr-dbg-info-content/TestDbgInfoContentWeakPtrFromStdModule.py
> >   lldb-api :: 
> > commands/expression/import-std-module/weak_ptr/TestWeakPtrFromStdModule.py
> > ```
> > 
> > 
> > 
> >   
> > 
> > 
> >   
> > 
> > 
> > 
> >   
> > Example assertion:
> > ```
> > Assertion failed: (0 && "Invalid SLocOffset or bad function choice"), 
> > function getFileIDLoaded, file SourceManager.cpp, line 867.
> > PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ 
> > and include the crash backtrace.
> > ``
> > ```
> 
> FWIW, I am still trying to reproduce these failures. It seems to be hard on a 
> linux machine as tests are all disabled with a link to 
> https://discourse.llvm.org/t/lldb-test-failures-on-linux/80095.

AFAIK only a few of these are skipped on Linux. E.g., 
`TestIteratorFromStdModule.py` should still reproduce. Happy to help you 
reproduce/narrow this down

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-12 Thread Nikita Popov via cfe-commits

nikic wrote:

Though looking at that file list in more details, this also affects files 
outside clang, so I think it might be adding some compile-time overhead that is 
only visible on specific workloads. E.g. 
`LLVMSupport.dir/UnicodeNameToCodepointGenerated.cpp` has a 4.5% increase.

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-12 Thread Nikita Popov via cfe-commits

nikic wrote:

It looks like this increases time to build clang by 0.5% (see affected files 
here: 
https://llvm-compile-time-tracker.com/compare_clang.php?from=e385e0d3e71e17da0b2023f480259c95923707bd&to=41e3919ded78d8870f7c95e9181c7f7e29aa3cc4&stat=instructions%3Au&sortBy=interestingness)

This usually indicates that you are including something expensive in a core 
header -- it would be good to check whether it can be avoided.

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-12 Thread kadir çetinkaya via cfe-commits

kadircet wrote:

> FYI, looks like this is causing following LLDB tests to fail: 
> https://green.lab.llvm.org/job/llvm.org/view/LLDB/job/as-lldb-cmake/15106/execution/node/97/log/
> 
> ```
> Unresolved Tests (17):
>   lldb-api :: 
> commands/expression/import-std-module/array/TestArrayFromStdModule.py
>   lldb-api :: 
> commands/expression/import-std-module/deque-basic/TestDequeFromStdModule.py
>   lldb-api :: 
> commands/expression/import-std-module/deque-dbg-info-content/TestDbgInfoContentDequeFromStdModule.py
>   lldb-api :: 
> commands/expression/import-std-module/forward_list-dbg-info-content/TestDbgInfoContentForwardListFromStdModule.py
>   lldb-api :: 
> commands/expression/import-std-module/forward_list/TestForwardListFromStdModule.py
>   lldb-api :: 
> commands/expression/import-std-module/iterator/TestIteratorFromStdModule.py
>   lldb-api :: 
> commands/expression/import-std-module/list-dbg-info-content/TestDbgInfoContentListFromStdModule.py
>   lldb-api :: 
> commands/expression/import-std-module/list/TestListFromStdModule.py
>   lldb-api :: 
> commands/expression/import-std-module/non-module-type-separation/TestNonModuleTypeSeparation.py
>   lldb-api :: 
> commands/expression/import-std-module/retry-with-std-module/TestRetryWithStdModule.py
>   lldb-api :: 
> commands/expression/import-std-module/shared_ptr-dbg-info-content/TestSharedPtrDbgInfoContentFromStdModule.py
>   lldb-api :: 
> commands/expression/import-std-module/shared_ptr/TestSharedPtrFromStdModule.py
>   lldb-api :: 
> commands/expression/import-std-module/unique_ptr-dbg-info-content/TestUniquePtrDbgInfoContent.py
>   lldb-api :: 
> commands/expression/import-std-module/unique_ptr/TestUniquePtrFromStdModule.py
>   lldb-api :: 
> commands/expression/import-std-module/vector-of-vectors/TestVectorOfVectorsFromStdModule.py
>   lldb-api :: 
> commands/expression/import-std-module/weak_ptr-dbg-info-content/TestDbgInfoContentWeakPtrFromStdModule.py
>   lldb-api :: 
> commands/expression/import-std-module/weak_ptr/TestWeakPtrFromStdModule.py
> ```
> 
> Example assertion:
> 
> ```
> Assertion failed: (0 && "Invalid SLocOffset or bad function choice"), 
> function getFileIDLoaded, file SourceManager.cpp, line 867.
> PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ 
> and include the crash backtrace.
> ``
> ```

FWIW, I am still trying to reproduce these failures. It seems to be hard on a 
linux machine as tests are all disabled with a link to 
https://discourse.llvm.org/t/lldb-test-failures-on-linux/80095.

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-12 Thread Aaron Ballman via cfe-commits


@@ -23,10 +23,15 @@
 // simpler because a remark can't be promoted to an error.
 #include "clang/Basic/AllDiagnostics.h"
 #include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/DiagnosticDriver.h"
+#include "clang/Basic/DiagnosticIDs.h"
 #include "clang/Basic/DiagnosticOptions.h"
-#include 
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/SpecialCaseList.h"
+#include "llvm/Support/VirtualFileSystem.h"
 #include 
-#include 
+#include 
+#include 

AaronBallman wrote:

Why are these being added?

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-12 Thread Aaron Ballman via cfe-commits


@@ -0,0 +1,97 @@
+
+Warning suppression mappings
+
+
+.. contents::
+   :local:
+
+Introduction
+
+
+Warning suppression mappings enable users to suppress Clang's diagnostics in a
+per-file granular manner. Enabling enforcement of diagnostics in specific parts
+of the project, even if there are violations in some headers.
+
+Goal and usage
+==
+
+Clang allows diagnostics to be configured at a translation-unit granularity.
+If a ``foo.cpp`` is compiled with ``-Wfoo``, all transitively included headers
+also need to be clean. Hence turning on new warnings in large codebases 
requires

AaronBallman wrote:

Add comma: Hence, turning on

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-12 Thread Aaron Ballman via cfe-commits


@@ -477,6 +486,136 @@ void DiagnosticsEngine::setSeverityForAll(diag::Flavor 
Flavor,
   setSeverity(Diag, Map, Loc);
 }
 
+namespace {
+// FIXME: We should isolate the parser from SpecialCaseList and just use it
+// here.
+class WarningsSpecialCaseList : public llvm::SpecialCaseList {
+public:
+  static std::unique_ptr
+  create(const llvm::MemoryBuffer &Input, std::string &Err);
+
+  // 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);
+
+  bool isDiagSuppressed(diag::kind DiagId, llvm::StringRef FilePath) const;
+
+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(const llvm::StringMap &CategoriesToMatchers,
+llvm::StringRef FilePath) const;
+
+  llvm::DenseMap DiagToSection;
+};
+} // namespace
+
+std::unique_ptr
+WarningsSpecialCaseList::create(const llvm::MemoryBuffer &Input,
+std::string &Err) {
+  auto WarningSuppressionList = std::make_unique();
+  if (!WarningSuppressionList->createInternal(&Input, Err))
+return nullptr;
+  return WarningSuppressionList;
+}
+
+void WarningsSpecialCaseList::processSections(DiagnosticsEngine &Diags) {
+  // Drop the default section introduced by special case list, we only support
+  // exact diagnostic group names.
+  // FIXME: We should make this configurable in the parser instead.
+  Sections.erase("*");
+  // Make sure we iterate sections by their line numbers.
+  std::vector *>>
+  LineAndSectionEntry;
+  LineAndSectionEntry.reserve(Sections.size());
+  for (const auto &Entry : Sections) {
+llvm::StringRef DiagName = Entry.getKey();
+// Each section has a matcher with that section's name, attached to that
+// line.
+const auto &DiagSectionMatcher = Entry.getValue().SectionMatcher;
+unsigned DiagLine = DiagSectionMatcher->Globs.at(DiagName).second;
+LineAndSectionEntry.emplace_back(DiagLine, &Entry);
+  }
+  llvm::sort(LineAndSectionEntry);
+  static constexpr auto WarningFlavor = clang::diag::Flavor::WarningOrError;
+  for (const auto &[_, SectionEntry] : LineAndSectionEntry) {
+SmallVector GroupDiags;
+llvm::StringRef DiagGroup = SectionEntry->getKey();
+if (Diags.getDiagnosticIDs()->getDiagnosticsInGroup(
+WarningFlavor, DiagGroup, GroupDiags)) {
+  StringRef Suggestion =
+  DiagnosticIDs::getNearestOption(WarningFlavor, DiagGroup);
+  Diags.Report(diag::warn_unknown_diag_option)
+  << static_cast(WarningFlavor) << DiagGroup
+  << !Suggestion.empty() << Suggestion;
+  continue;
+}
+for (diag::kind Diag : GroupDiags)
+  // We're intentionally overwriting any previous mappings here to make 
sure
+  // latest one takes precedence.
+  DiagToSection[Diag] = &SectionEntry->getValue();
+  }
+}
+
+void DiagnosticsEngine::setDiagSuppressionMapping(llvm::MemoryBuffer &Input) {
+  std::string Error;
+  auto WarningSuppressionList = WarningsSpecialCaseList::create(Input, Error);
+  if (!WarningSuppressionList) {
+Report(diag::err_drv_malformed_warning_suppression_mapping)

AaronBallman wrote:

Can you add a FIXME comment here: it would be better to not use `Error` here as 
a string but instead use `%select` in the diagnostic message; combining 
arbitrary strings makes it harder when folks are localizing diagnostics.

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-12 Thread Aaron Ballman via cfe-commits


@@ -44,6 +44,7 @@
 #include "llvm/ADT/ScopeExit.h"
 #include "llvm/ADT/SmallSet.h"
 #include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringRef.h"

AaronBallman wrote:

Why is this being added?

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-12 Thread Aaron Ballman via cfe-commits


@@ -1315,6 +1319,34 @@ with its corresponding `Wno-` option.
 Note that when combined with :option:`-w` (which disables all warnings),
 disabling all warnings wins.
 
+.. _warning_suppression_mappings:
+
+Controlling Diagnostics via Suppression Mappings
+
+
+Warning suppression mappings enable users to suppress Clang's diagnostics in a
+per-file granular manner. Enabling enforcement of diagnostics in specific parts

AaronBallman wrote:

Add comma: per-file, granular manner
Reword: Enabling enforcement of -> This allows enforcing

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-12 Thread Aaron Ballman via cfe-commits


@@ -555,6 +560,10 @@ class DiagnosticsEngine : public 
RefCountedBase {
   void *ArgToStringCookie = nullptr;
   ArgToStringFnTy ArgToStringFn;
 
+  /// Whether the diagnostic should be suppressed in FilePath.
+  llvm::unique_function

AaronBallman wrote:

You can drop the `llvm::` from things in this file as the prevailing style 
leaves them off already.

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-12 Thread Aaron Ballman via cfe-commits


@@ -477,6 +486,136 @@ void DiagnosticsEngine::setSeverityForAll(diag::Flavor 
Flavor,
   setSeverity(Diag, Map, Loc);
 }
 
+namespace {
+// FIXME: We should isolate the parser from SpecialCaseList and just use it
+// here.
+class WarningsSpecialCaseList : public llvm::SpecialCaseList {

AaronBallman wrote:

Prevailing style in this file also drops the `llvm::` prefixes. Probably worth 
taking a pass over the whole PR to verify you're following the local styles.

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-12 Thread Aaron Ballman via cfe-commits


@@ -1315,6 +1319,34 @@ with its corresponding `Wno-` option.
 Note that when combined with :option:`-w` (which disables all warnings),
 disabling all warnings wins.
 
+.. _warning_suppression_mappings:
+
+Controlling Diagnostics via Suppression Mappings
+
+
+Warning suppression mappings enable users to suppress Clang's diagnostics in a
+per-file granular manner. Enabling enforcement of diagnostics in specific parts
+of the project, even if there are violations in some headers.

AaronBallman wrote:

Remove comma: project even if there
Reword: some headers -> some headers which cannot be modified.

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-12 Thread Aaron Ballman via cfe-commits


@@ -0,0 +1,97 @@
+
+Warning suppression mappings
+
+
+.. contents::
+   :local:
+
+Introduction
+
+
+Warning suppression mappings enable users to suppress Clang's diagnostics in a
+per-file granular manner. Enabling enforcement of diagnostics in specific parts
+of the project, even if there are violations in some headers.

AaronBallman wrote:

Same suggestions here as above.

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-12 Thread Aaron Ballman via cfe-commits

https://github.com/AaronBallman commented:

> @AaronBallman merging this to main now. PLMK if you have any concerns, I am 
> happy to address them post-submit (or revert if need be)

Sorry, there was a US holiday on Monday and I was mostly out on Friday, so 
that's why there was a delay in review. In the future, I would recommend 
waiting before landing significant changes like these unless the review is 
accepted by a maintainer or someone known to be familiar with that area of the 
code. That reduces pressure on reviewers and allows folks to be sick/take 
vacations/etc without things landing out from under them. As it turns out, I 
can no longer leave suggested changes on the PR because it was already merged 
(thanks, GitHub), so my review comments will be somewhat awkward.

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-12 Thread Aaron Ballman via cfe-commits

https://github.com/AaronBallman edited 
https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-12 Thread Aaron Ballman via cfe-commits


@@ -9017,3 +9017,8 @@ def wasm_opt : Flag<["--"], "wasm-opt">,
   Group,
   HelpText<"Enable the wasm-opt optimizer (default)">,
   MarshallingInfoNegativeFlag>;
+
+def warning_suppression_mappings_EQ : Joined<["--"],

AaronBallman wrote:

This should move elsewhere in Options.td, it's currently listed under 
`clang-dxc` options.

Also, should this be exposed in clang-cl as well? What about flang? CC @zmodem 
@rnk @MaskRay @jansvoboda11 for opinions

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-12 Thread Michael Buch via cfe-commits

Michael137 wrote:

FYI, looks like this is causing following LLDB builds to fail: 
https://green.lab.llvm.org/job/llvm.org/view/LLDB/job/as-lldb-cmake/15106/execution/node/97/log/
```
Unresolved Tests (17):
  lldb-api :: 
commands/expression/import-std-module/array/TestArrayFromStdModule.py
  lldb-api :: 
commands/expression/import-std-module/deque-basic/TestDequeFromStdModule.py
  lldb-api :: 
commands/expression/import-std-module/deque-dbg-info-content/TestDbgInfoContentDequeFromStdModule.py
  lldb-api :: 
commands/expression/import-std-module/forward_list-dbg-info-content/TestDbgInfoContentForwardListFromStdModule.py
  lldb-api :: 
commands/expression/import-std-module/forward_list/TestForwardListFromStdModule.py
  lldb-api :: 
commands/expression/import-std-module/iterator/TestIteratorFromStdModule.py
  lldb-api :: 
commands/expression/import-std-module/list-dbg-info-content/TestDbgInfoContentListFromStdModule.py
  lldb-api :: 
commands/expression/import-std-module/list/TestListFromStdModule.py
  lldb-api :: 
commands/expression/import-std-module/non-module-type-separation/TestNonModuleTypeSeparation.py
  lldb-api :: 
commands/expression/import-std-module/retry-with-std-module/TestRetryWithStdModule.py
  lldb-api :: 
commands/expression/import-std-module/shared_ptr-dbg-info-content/TestSharedPtrDbgInfoContentFromStdModule.py
  lldb-api :: 
commands/expression/import-std-module/shared_ptr/TestSharedPtrFromStdModule.py
  lldb-api :: 
commands/expression/import-std-module/unique_ptr-dbg-info-content/TestUniquePtrDbgInfoContent.py
  lldb-api :: 
commands/expression/import-std-module/unique_ptr/TestUniquePtrFromStdModule.py
  lldb-api :: 
commands/expression/import-std-module/vector-of-vectors/TestVectorOfVectorsFromStdModule.py
  lldb-api :: 
commands/expression/import-std-module/weak_ptr-dbg-info-content/TestDbgInfoContentWeakPtrFromStdModule.py
  lldb-api :: 
commands/expression/import-std-module/weak_ptr/TestWeakPtrFromStdModule.py
```

Example assertion:
```
Assertion failed: (0 && "Invalid SLocOffset or bad function choice"), function 
getFileIDLoaded, file SourceManager.cpp, line 867.
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and 
include the crash backtrace.
``

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-12 Thread kadir çetinkaya via cfe-commits

kadircet wrote:

@AaronBallman merging this to main now. PLMK if you have any concerns, I am 
happy to address them post-submit (or revert if need be)

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-12 Thread kadir çetinkaya via cfe-commits

https://github.com/kadircet closed 
https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-08 Thread kadir çetinkaya via cfe-commits

kadircet wrote:

thanks for the review!

@AaronBallman i'd like to land this early next week and start 
testing/polishing. It'd be great if you can give some explicit LGTM.

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-08 Thread Boaz Brickner via cfe-commits


@@ -477,6 +486,136 @@ void DiagnosticsEngine::setSeverityForAll(diag::Flavor 
Flavor,
   setSeverity(Diag, Map, Loc);
 }
 
+namespace {
+// FIXME: We should isolate the parser from SpecialCaseList and just use it
+// here.
+class WarningsSpecialCaseList : public llvm::SpecialCaseList {
+public:
+  static std::unique_ptr
+  create(const llvm::MemoryBuffer &Input, std::string &Err);
+
+  // 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);
+
+  bool isDiagSuppressed(diag::kind DiagId, llvm::StringRef FilePath) const;
+
+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(const llvm::StringMap &CategoriesToMatchers,
+llvm::StringRef FilePath) const;
+
+  llvm::DenseMap DiagToSection;
+};
+} // namespace
+
+std::unique_ptr
+WarningsSpecialCaseList::create(const llvm::MemoryBuffer &Input,
+std::string &Err) {
+  auto WarningSuppressionList = std::make_unique();
+  if (!WarningSuppressionList->createInternal(&Input, Err))
+return nullptr;
+  return WarningSuppressionList;
+}
+
+void WarningsSpecialCaseList::processSections(DiagnosticsEngine &Diags) {
+  // Drop the default section introduced by special case list, we only support
+  // exact diagnostic group names.
+  // FIXME: We should make this configurable in the parser instead.
+  Sections.erase("*");
+  // Make sure we iterate sections by their line numbers.
+  std::vector *>>
+  LineAndSectionEntry;
+  LineAndSectionEntry.reserve(Sections.size());
+  for (const auto &Entry : Sections) {
+llvm::StringRef DiagName = Entry.first();
+// Each section has a matcher with that section's name, attached to that
+// line.
+const auto &DiagSectionMatcher = Entry.second.SectionMatcher;
+unsigned DiagLine = DiagSectionMatcher->Globs.at(DiagName).second;
+LineAndSectionEntry.emplace_back(DiagLine, &Entry);
+  }
+  llvm::sort(LineAndSectionEntry);
+  static constexpr auto WarningFlavor = clang::diag::Flavor::WarningOrError;
+  for (const auto &[_, SectionEntry] : LineAndSectionEntry) {
+SmallVector GroupDiags;
+llvm::StringRef DiagGroup = SectionEntry->getKey();
+if (Diags.getDiagnosticIDs()->getDiagnosticsInGroup(
+WarningFlavor, DiagGroup, GroupDiags)) {
+  StringRef Suggestion =
+  DiagnosticIDs::getNearestOption(WarningFlavor, DiagGroup);
+  Diags.Report(diag::warn_unknown_diag_option)
+  << static_cast(WarningFlavor) << DiagGroup
+  << !Suggestion.empty() << Suggestion;
+  continue;
+}
+for (diag::kind Diag : GroupDiags)
+  // We're intentionally overwriting any previous mappings here to make 
sure
+  // latest one takes precedence.
+  DiagToSection[Diag] = &SectionEntry->getValue();
+  }
+}
+
+void DiagnosticsEngine::setDiagSuppressionMapping(llvm::MemoryBuffer &Input) {
+  std::string Error;
+  auto WarningSuppressionList = WarningsSpecialCaseList::create(Input, Error);
+  if (!WarningSuppressionList) {
+Report(diag::err_drv_malformed_warning_suppression_mapping)
+<< Input.getBufferIdentifier() << Error;
+return;
+  }
+  WarningSuppressionList->processSections(*this);
+  DiagSuppressionMapping =
+  [WarningSuppressionList(std::move(WarningSuppressionList))](
+  diag::kind DiagId, llvm::StringRef Path) {
+return WarningSuppressionList->isDiagSuppressed(DiagId, Path);
+  };
+}
+
+bool WarningsSpecialCaseList::isDiagSuppressed(diag::kind DiagId,
+   llvm::StringRef FilePath) const 
{
+  const Section *DiagSection = DiagToSection.lookup(DiagId);
+  if (!DiagSection)
+return false;
+  const SectionEntries &EntityTypeToCategories = DiagSection->Entries;
+  auto SrcEntriesIt = EntityTypeToCategories.find("src");
+  if (SrcEntriesIt == EntityTypeToCategories.end())
+return false;
+  const llvm::StringMap &CategoriesToMatchers =
+  SrcEntriesIt->second;

bricknerb wrote:

I suggest using getValue() and getKey() instead of second and first throughout 
the code when possible, since it's more readable.

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-08 Thread Boaz Brickner via cfe-commits


@@ -167,4 +179,155 @@ TEST(DiagnosticTest, storedDiagEmptyWarning) {
   // Make sure an empty warning can round-trip with \c StoredDiagnostic.
   Diags.Report(CaptureConsumer.StoredDiags.front());
 }
+
+class SuppressionMappingTest : public testing::Test {
+public:
+  SuppressionMappingTest() {
+Diags.setClient(&CaptureConsumer, /*ShouldOwnClient=*/false);
+  }
+
+protected:
+  llvm::IntrusiveRefCntPtr FS =
+  llvm::makeIntrusiveRefCnt();
+  DiagnosticsEngine Diags{new DiagnosticIDs(), new DiagnosticOptions};
+
+  llvm::ArrayRef takeDiags() {

bricknerb wrote:

Rename to getDiags()?
Take suggests they're no longer there.

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-08 Thread Boaz Brickner via cfe-commits


@@ -167,4 +176,159 @@ TEST(DiagnosticTest, storedDiagEmptyWarning) {
   // Make sure an empty warning can round-trip with \c StoredDiagnostic.
   Diags.Report(CaptureConsumer.StoredDiags.front());
 }
+
+class SuppressionMappingTest : public testing::Test {
+public:
+  SuppressionMappingTest() {
+Diags.setClient(&CaptureConsumer, /*ShouldOwnClient=*/false);
+  }
+
+protected:
+  llvm::IntrusiveRefCntPtr FS =
+  llvm::makeIntrusiveRefCnt();
+  DiagnosticsEngine Diags{new DiagnosticIDs(), new DiagnosticOptions};
+
+  std::vector takeDiags() {
+return std::move(CaptureConsumer.StoredDiags);
+  }
+
+private:
+  class CaptureDiagnosticConsumer : public DiagnosticConsumer {
+  public:
+std::vector StoredDiags;
+
+void HandleDiagnostic(DiagnosticsEngine::Level level,
+  const Diagnostic &Info) override {
+  StoredDiags.push_back(StoredDiagnostic(level, Info));
+}
+  };
+  CaptureDiagnosticConsumer CaptureConsumer;
+};
+
+MATCHER_P(WithMessage, Msg, "has diagnostic message") {
+  return arg.getMessage() == Msg;
+}
+
+TEST_F(SuppressionMappingTest, MissingMappingFile) {
+  Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";
+  clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);
+  EXPECT_THAT(takeDiags(), testing::ElementsAre(WithMessage(
+   "no such file or directory: 'foo.txt'")));
+}
+
+TEST_F(SuppressionMappingTest, MalformedFile) {
+  Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";
+  FS->addFile("foo.txt", /*ModificationTime=*/{},
+  llvm::MemoryBuffer::getMemBuffer("asdf", "foo.txt"));
+  clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);
+  EXPECT_THAT(takeDiags(), testing::ElementsAre(WithMessage(
+   "failed to process suppression mapping file "
+   "'foo.txt': malformed line 1: 'asdf'")));
+}
+
+TEST_F(SuppressionMappingTest, UnknownDiagName) {
+  Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";
+  FS->addFile("foo.txt", /*ModificationTime=*/{},
+  llvm::MemoryBuffer::getMemBuffer("[non-existing-warning]"));
+  clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);
+  EXPECT_THAT(takeDiags(),
+  testing::ElementsAre(WithMessage(
+  "unknown warning option 'non-existing-warning'")));
+}
+
+TEST_F(SuppressionMappingTest, SuppressesGroup) {
+  llvm::StringLiteral SuppressionMappingFile = R"txt(
+  [unused]
+  src:*
+  )txt";
+  Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";
+  FS->addFile("foo.txt", /*ModificationTime=*/{},
+  llvm::MemoryBuffer::getMemBuffer(SuppressionMappingFile));
+  clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);
+  EXPECT_THAT(takeDiags(), testing::IsEmpty());
+
+  EXPECT_TRUE(
+  Diags.isSuppressedViaMapping(diag::warn_unused_function, "foo.cpp"));
+  EXPECT_FALSE(Diags.isSuppressedViaMapping(diag::warn_deprecated, "foo.cpp"));
+}
+
+TEST_F(SuppressionMappingTest, ExclusionsWork) {
+  llvm::StringLiteral SuppressionMappingFile = R"txt(
+  [unused]
+  src:*
+  src:*foo.cpp=emit
+  )txt";
+  Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";
+  FS->addFile("foo.txt", /*ModificationTime=*/{},
+  llvm::MemoryBuffer::getMemBuffer(SuppressionMappingFile));
+  clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);
+  EXPECT_THAT(takeDiags(), testing::IsEmpty());
+
+  EXPECT_TRUE(
+  Diags.isSuppressedViaMapping(diag::warn_unused_function, "bar.cpp"));
+  EXPECT_FALSE(
+  Diags.isSuppressedViaMapping(diag::warn_unused_function, "foo.cpp"));
+}
+
+TEST_F(SuppressionMappingTest, LongestMatchWins) {
+  llvm::StringLiteral SuppressionMappingFile = R"txt(
+  [unused]
+  src:*clang/*
+  src:*clang/lib/Sema/*=emit
+  src:*clang/lib/Sema/foo*
+  )txt";
+  Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";
+  FS->addFile("foo.txt", /*ModificationTime=*/{},
+  llvm::MemoryBuffer::getMemBuffer(SuppressionMappingFile));
+  clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);
+  EXPECT_THAT(takeDiags(), testing::IsEmpty());
+
+  EXPECT_TRUE(Diags.isSuppressedViaMapping(diag::warn_unused_function,
+   "clang/lib/Basic/foo.h"));
+  EXPECT_FALSE(Diags.isSuppressedViaMapping(diag::warn_unused_function,
+"clang/lib/Sema/bar.h"));
+  EXPECT_TRUE(Diags.isSuppressedViaMapping(diag::warn_unused_function,
+   "clang/lib/Sema/foo.h"));
+}
+
+TEST_F(SuppressionMappingTest, IsIgnored) {
+  llvm::StringLiteral SuppressionMappingFile = R"txt(
+  [unused]
+  src:*clang/*
+  )txt";
+  Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";
+  Diags.getDiagnost

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

2024-11-08 Thread Boaz Brickner via cfe-commits


@@ -167,4 +176,159 @@ TEST(DiagnosticTest, storedDiagEmptyWarning) {
   // Make sure an empty warning can round-trip with \c StoredDiagnostic.
   Diags.Report(CaptureConsumer.StoredDiags.front());
 }
+
+class SuppressionMappingTest : public testing::Test {
+public:
+  SuppressionMappingTest() {
+Diags.setClient(&CaptureConsumer, /*ShouldOwnClient=*/false);
+  }
+
+protected:
+  llvm::IntrusiveRefCntPtr FS =
+  llvm::makeIntrusiveRefCnt();
+  DiagnosticsEngine Diags{new DiagnosticIDs(), new DiagnosticOptions};
+
+  std::vector takeDiags() {
+return std::move(CaptureConsumer.StoredDiags);
+  }
+
+private:
+  class CaptureDiagnosticConsumer : public DiagnosticConsumer {
+  public:
+std::vector StoredDiags;
+
+void HandleDiagnostic(DiagnosticsEngine::Level level,
+  const Diagnostic &Info) override {
+  StoredDiags.push_back(StoredDiagnostic(level, Info));
+}
+  };
+  CaptureDiagnosticConsumer CaptureConsumer;
+};
+
+MATCHER_P(WithMessage, Msg, "has diagnostic message") {
+  return arg.getMessage() == Msg;
+}
+
+TEST_F(SuppressionMappingTest, MissingMappingFile) {
+  Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";
+  clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);
+  EXPECT_THAT(takeDiags(), testing::ElementsAre(WithMessage(
+   "no such file or directory: 'foo.txt'")));

bricknerb wrote:

We might care whether we propagate this as an error, warning etc (I guess we 
could change that).
In another case we use `WithMessage()` below we test 
`rr_drv_malformed_warning_suppression_mapping`, which I guess we should care 
about more than just the text.
For consistency and making sure we have coverage, I suggest checking all the 
interesting fields, and not just the text.

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-08 Thread Boaz Brickner via cfe-commits


@@ -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
+  create(const llvm::MemoryBuffer &MB, std::string &Err) {
+auto SCL = std::make_unique();
+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 *>>
+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 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(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 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:

Performance isn't my main concern here, just API consistency.
Given that there are no exclusions in other use cases, I think no inconsistency 
can happen, so we should be good.

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-08 Thread Boaz Brickner via cfe-commits

https://github.com/bricknerb approved this pull request.


https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-08 Thread Boaz Brickner via cfe-commits


@@ -0,0 +1,90 @@
+
+Warning suppression mappings
+
+
+.. contents::
+   :local:
+
+Introduction
+
+
+Warning suppression mappings enable users to suppress Clang's diagnostics in a
+per-file granular manner. Enabling enforcement of diagnostics in specific parts
+of the project, even if there are violations in some headers.
+
+Goal and usage
+==
+
+Clang allows diagnostics to be configured at a translation-unit granularity.
+If a ``foo.cpp`` is compiled with ``-Wfoo``, all transitively included headers
+also need to be clean. Hence turning on new warnings in large codebases can be
+difficult today. Since it requires cleaning up all the existing warnings,
+which might not be possible when some dependencies aren't in the project 
owner's
+control or because new violations are creeping up quicker than the clean up.
+
+Warning suppression mappings aim to alleviate some of these concerns by making
+diagnostic configuration granularity finer, at a source file level.
+
+To achieve this, user can create a file that lists which diagnostic groups to
+suppress in which files or paths, and pass it as a command line argument to
+Clang with the ``--warning-suppression-mappings`` flag.
+
+Note that this mechanism won't enable any diagnostics on its own. Users should
+still turn on warnings in their compilations with explicit ``-Wfoo`` flags.
+
+Example
+===
+
+.. code-block:: bash
+
+  $ cat my/user/code.cpp
+  #include 
+  namespace { void unused_func1(); }
+
+  $ cat foo/bar.h
+  namespace { void unused_func2(); }
+
+  $ cat suppression_mappings.txt
+  # Suppress -Wunused warnings in all files, apart from the ones under `foo/`.
+  [unused]
+  src:*
+  src:*foo/*=emit
+  $ clang -Wunused --warning-suppression-mappings=suppression_mappings.txt 
my/user/code.cpp
+  # prints warning: unused function 'unused_func2', but no warnings for 
`unused_func1`.
+
+Format
+==
+
+Warning suppression mappings uses the same format as
+:doc:`SanitizerSpecialCaseList`.
+
+Users can mention sections to describe which diagnostic group behaviours to
+change. Sections are denoted as ``[unused]`` in this format. Each section name
+must match a diagnostic group.
+When a diagnostic is matched by multiple groups, the latest one takes
+precendence.
+
+Afterwards in each section, users can have multiple entities that match source
+files based on the globs. These entities look like ``src:*/my/dir/*``.
+Users can also use the ``emit`` category to exclude a subdirectory from
+suppression.
+Source files are matched against these globs either as paths relative to th
+current working directory, or as absolute paths.

bricknerb wrote:

Careful reading it again this is ok.
If you want to emphasize it better, you can make it a list and/or emphasize the 
either.
... against these globs **either**:
* as ...
* as ...

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-08 Thread Boaz Brickner via cfe-commits


@@ -477,6 +486,136 @@ void DiagnosticsEngine::setSeverityForAll(diag::Flavor 
Flavor,
   setSeverity(Diag, Map, Loc);
 }
 
+namespace {
+// FIXME: We should isolate the parser from SpecialCaseList and just use it
+// here.
+class WarningsSpecialCaseList : public llvm::SpecialCaseList {
+public:
+  static std::unique_ptr
+  create(const llvm::MemoryBuffer &Input, std::string &Err);
+
+  // 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);
+
+  bool isDiagSuppressed(diag::kind DiagId, llvm::StringRef FilePath) const;
+
+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(const llvm::StringMap &CategoriesToMatchers,
+llvm::StringRef FilePath) const;
+
+  llvm::DenseMap DiagToSection;
+};
+} // namespace
+
+std::unique_ptr
+WarningsSpecialCaseList::create(const llvm::MemoryBuffer &Input,
+std::string &Err) {
+  auto WarningSuppressionList = std::make_unique();
+  if (!WarningSuppressionList->createInternal(&Input, Err))
+return nullptr;
+  return WarningSuppressionList;
+}
+
+void WarningsSpecialCaseList::processSections(DiagnosticsEngine &Diags) {
+  // Drop the default section introduced by special case list, we only support
+  // exact diagnostic group names.
+  // FIXME: We should make this configurable in the parser instead.
+  Sections.erase("*");
+  // Make sure we iterate sections by their line numbers.
+  std::vector *>>
+  LineAndSectionEntry;
+  LineAndSectionEntry.reserve(Sections.size());
+  for (const auto &Entry : Sections) {
+llvm::StringRef DiagName = Entry.first();
+// Each section has a matcher with that section's name, attached to that
+// line.
+const auto &DiagSectionMatcher = Entry.second.SectionMatcher;
+unsigned DiagLine = DiagSectionMatcher->Globs.at(DiagName).second;
+LineAndSectionEntry.emplace_back(DiagLine, &Entry);
+  }
+  llvm::sort(LineAndSectionEntry);
+  static constexpr auto WarningFlavor = clang::diag::Flavor::WarningOrError;
+  for (const auto &[_, SectionEntry] : LineAndSectionEntry) {
+SmallVector GroupDiags;
+llvm::StringRef DiagGroup = SectionEntry->getKey();
+if (Diags.getDiagnosticIDs()->getDiagnosticsInGroup(
+WarningFlavor, DiagGroup, GroupDiags)) {
+  StringRef Suggestion =
+  DiagnosticIDs::getNearestOption(WarningFlavor, DiagGroup);
+  Diags.Report(diag::warn_unknown_diag_option)
+  << static_cast(WarningFlavor) << DiagGroup
+  << !Suggestion.empty() << Suggestion;
+  continue;
+}
+for (diag::kind Diag : GroupDiags)
+  // We're intentionally overwriting any previous mappings here to make 
sure
+  // latest one takes precedence.
+  DiagToSection[Diag] = &SectionEntry->getValue();
+  }
+}
+
+void DiagnosticsEngine::setDiagSuppressionMapping(llvm::MemoryBuffer &Input) {
+  std::string Err;
+  auto WarningSuppressionList = WarningsSpecialCaseList::create(Input, Err);
+  if (!WarningSuppressionList) {
+Report(diag::err_drv_malformed_warning_suppression_mapping)
+<< Input.getBufferIdentifier() << Err;
+return;
+  }
+  WarningSuppressionList->processSections(*this);
+  DiagSuppressionMapping =
+  [WarningSuppressionList(std::move(WarningSuppressionList))](
+  diag::kind DiagId, llvm::StringRef Path) {
+return WarningSuppressionList->isDiagSuppressed(DiagId, Path);
+  };
+}
+
+bool WarningsSpecialCaseList::isDiagSuppressed(diag::kind DiagId,
+   llvm::StringRef FilePath) const 
{
+  const Section *DiagSection = DiagToSection.lookup(DiagId);
+  if (!DiagSection)
+return false;
+  const SectionEntries &EntityTypeToCategories = DiagSection->Entries;
+  auto SrcEntriesIt = EntityTypeToCategories.find("src");
+  if (SrcEntriesIt == EntityTypeToCategories.end())
+return false;
+  const llvm::StringMap &CategoriesToMatchers =
+  SrcEntriesIt->second;

bricknerb wrote:

Thanks, makes sense!
Now I feel like adding this functionality :).

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-08 Thread Boaz Brickner via cfe-commits




bricknerb wrote:

Unit tests cover the code and Clang integration tests are useful to test a 
feature end-to-end.
The compiler logic that handles the code until it reaches this point might 
break the assumptions we have in the specific code (especially if we think 
about future changes to the logic).

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-07 Thread kadir çetinkaya via cfe-commits


@@ -167,4 +176,159 @@ TEST(DiagnosticTest, storedDiagEmptyWarning) {
   // Make sure an empty warning can round-trip with \c StoredDiagnostic.
   Diags.Report(CaptureConsumer.StoredDiags.front());
 }
+
+class SuppressionMappingTest : public testing::Test {
+public:
+  SuppressionMappingTest() {
+Diags.setClient(&CaptureConsumer, /*ShouldOwnClient=*/false);
+  }
+
+protected:
+  llvm::IntrusiveRefCntPtr FS =
+  llvm::makeIntrusiveRefCnt();
+  DiagnosticsEngine Diags{new DiagnosticIDs(), new DiagnosticOptions};
+
+  std::vector takeDiags() {
+return std::move(CaptureConsumer.StoredDiags);
+  }
+
+private:
+  class CaptureDiagnosticConsumer : public DiagnosticConsumer {
+  public:
+std::vector StoredDiags;
+
+void HandleDiagnostic(DiagnosticsEngine::Level level,
+  const Diagnostic &Info) override {
+  StoredDiags.push_back(StoredDiagnostic(level, Info));
+}
+  };
+  CaptureDiagnosticConsumer CaptureConsumer;
+};
+
+MATCHER_P(WithMessage, Msg, "has diagnostic message") {
+  return arg.getMessage() == Msg;
+}
+
+TEST_F(SuppressionMappingTest, MissingMappingFile) {
+  Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";
+  clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);
+  EXPECT_THAT(takeDiags(), testing::ElementsAre(WithMessage(
+   "no such file or directory: 'foo.txt'")));
+}
+
+TEST_F(SuppressionMappingTest, MalformedFile) {
+  Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";
+  FS->addFile("foo.txt", /*ModificationTime=*/{},
+  llvm::MemoryBuffer::getMemBuffer("asdf", "foo.txt"));
+  clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);
+  EXPECT_THAT(takeDiags(), testing::ElementsAre(WithMessage(
+   "failed to process suppression mapping file "
+   "'foo.txt': malformed line 1: 'asdf'")));
+}
+
+TEST_F(SuppressionMappingTest, UnknownDiagName) {
+  Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";
+  FS->addFile("foo.txt", /*ModificationTime=*/{},
+  llvm::MemoryBuffer::getMemBuffer("[non-existing-warning]"));
+  clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);
+  EXPECT_THAT(takeDiags(),
+  testing::ElementsAre(WithMessage(
+  "unknown warning option 'non-existing-warning'")));
+}
+
+TEST_F(SuppressionMappingTest, SuppressesGroup) {
+  llvm::StringLiteral SuppressionMappingFile = R"txt(
+  [unused]
+  src:*
+  )txt";
+  Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";
+  FS->addFile("foo.txt", /*ModificationTime=*/{},
+  llvm::MemoryBuffer::getMemBuffer(SuppressionMappingFile));
+  clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);
+  EXPECT_THAT(takeDiags(), testing::IsEmpty());
+
+  EXPECT_TRUE(
+  Diags.isSuppressedViaMapping(diag::warn_unused_function, "foo.cpp"));
+  EXPECT_FALSE(Diags.isSuppressedViaMapping(diag::warn_deprecated, "foo.cpp"));
+}
+
+TEST_F(SuppressionMappingTest, ExclusionsWork) {
+  llvm::StringLiteral SuppressionMappingFile = R"txt(
+  [unused]
+  src:*
+  src:*foo.cpp=emit
+  )txt";
+  Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";
+  FS->addFile("foo.txt", /*ModificationTime=*/{},
+  llvm::MemoryBuffer::getMemBuffer(SuppressionMappingFile));
+  clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);
+  EXPECT_THAT(takeDiags(), testing::IsEmpty());
+
+  EXPECT_TRUE(
+  Diags.isSuppressedViaMapping(diag::warn_unused_function, "bar.cpp"));
+  EXPECT_FALSE(
+  Diags.isSuppressedViaMapping(diag::warn_unused_function, "foo.cpp"));
+}
+
+TEST_F(SuppressionMappingTest, LongestMatchWins) {
+  llvm::StringLiteral SuppressionMappingFile = R"txt(
+  [unused]
+  src:*clang/*
+  src:*clang/lib/Sema/*=emit
+  src:*clang/lib/Sema/foo*
+  )txt";
+  Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";
+  FS->addFile("foo.txt", /*ModificationTime=*/{},
+  llvm::MemoryBuffer::getMemBuffer(SuppressionMappingFile));
+  clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);
+  EXPECT_THAT(takeDiags(), testing::IsEmpty());
+
+  EXPECT_TRUE(Diags.isSuppressedViaMapping(diag::warn_unused_function,
+   "clang/lib/Basic/foo.h"));
+  EXPECT_FALSE(Diags.isSuppressedViaMapping(diag::warn_unused_function,
+"clang/lib/Sema/bar.h"));
+  EXPECT_TRUE(Diags.isSuppressedViaMapping(diag::warn_unused_function,
+   "clang/lib/Sema/foo.h"));
+}
+
+TEST_F(SuppressionMappingTest, IsIgnored) {
+  llvm::StringLiteral SuppressionMappingFile = R"txt(
+  [unused]
+  src:*clang/*
+  )txt";
+  Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";
+  Diags.getDiagnost

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

2024-11-07 Thread kadir çetinkaya via cfe-commits

https://github.com/kadircet commented:

> Note that there are a few open comments from previous iterations.

I tried to go over them, but github UI was ... suboptimal. So PLMK if you got 
some that aren't addressed in this revision as well.

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-07 Thread kadir çetinkaya via cfe-commits


@@ -701,11 +702,10 @@ class CompilerInstance : public ModuleLoader {
   /// used by some diagnostics printers (for logging purposes only).
   ///
   /// \return The new object on success, or null on failure.
-  static IntrusiveRefCntPtr
-  createDiagnostics(DiagnosticOptions *Opts,
-DiagnosticConsumer *Client = nullptr,
-bool ShouldOwnClient = true,
-const CodeGenOptions *CodeGenOpts = nullptr);
+  static IntrusiveRefCntPtr createDiagnostics(
+  DiagnosticOptions *Opts, DiagnosticConsumer *Client = nullptr,
+  bool ShouldOwnClient = true, const CodeGenOptions *CodeGenOpts = nullptr,
+  IntrusiveRefCntPtr VFS = nullptr);

kadircet wrote:

i got that coming up in a future change, pending this one to land. 

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-07 Thread kadir çetinkaya via cfe-commits


@@ -0,0 +1,90 @@
+
+Warning suppression mappings
+
+
+.. contents::
+   :local:
+
+Introduction
+
+
+Warning suppression mappings enable users to suppress Clang's diagnostics in a
+per-file granular manner. Enabling enforcement of diagnostics in specific parts
+of the project, even if there are violations in some headers.
+
+Goal and usage
+==
+
+Clang allows diagnostics to be configured at a translation-unit granularity.
+If a ``foo.cpp`` is compiled with ``-Wfoo``, all transitively included headers
+also need to be clean. Hence turning on new warnings in large codebases can be
+difficult today. Since it requires cleaning up all the existing warnings,
+which might not be possible when some dependencies aren't in the project 
owner's
+control or because new violations are creeping up quicker than the clean up.
+
+Warning suppression mappings aim to alleviate some of these concerns by making
+diagnostic configuration granularity finer, at a source file level.
+
+To achieve this, user can create a file that lists which diagnostic groups to
+suppress in which files or paths, and pass it as a command line argument to
+Clang with the ``--warning-suppression-mappings`` flag.
+
+Note that this mechanism won't enable any diagnostics on its own. Users should
+still turn on warnings in their compilations with explicit ``-Wfoo`` flags.
+
+Example
+===
+
+.. code-block:: bash
+
+  $ cat my/user/code.cpp
+  #include 
+  namespace { void unused_func1(); }
+
+  $ cat foo/bar.h
+  namespace { void unused_func2(); }
+
+  $ cat suppression_mappings.txt
+  # Suppress -Wunused warnings in all files, apart from the ones under `foo/`.
+  [unused]
+  src:*
+  src:*foo/*=emit
+  $ clang -Wunused --warning-suppression-mappings=suppression_mappings.txt 
my/user/code.cpp
+  # prints warning: unused function 'unused_func2', but no warnings for 
`unused_func1`.
+
+Format
+==
+
+Warning suppression mappings uses the same format as
+:doc:`SanitizerSpecialCaseList`.
+
+Users can mention sections to describe which diagnostic group behaviours to
+change. Sections are denoted as ``[unused]`` in this format. Each section name
+must match a diagnostic group.
+When a diagnostic is matched by multiple groups, the latest one takes
+precendence.
+
+Afterwards in each section, users can have multiple entities that match source
+files based on the globs. These entities look like ``src:*/my/dir/*``.
+Users can also use the ``emit`` category to exclude a subdirectory from
+suppression.
+Source files are matched against these globs either as paths relative to th
+current working directory, or as absolute paths.
+When a source file matches multiple globs, the longest one takes precendence.

kadircet wrote:

longest glob in the latest matching section. mentioned that matching still 
happens within a single section.

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-07 Thread kadir çetinkaya via cfe-commits


@@ -477,6 +486,136 @@ void DiagnosticsEngine::setSeverityForAll(diag::Flavor 
Flavor,
   setSeverity(Diag, Map, Loc);
 }
 
+namespace {
+// FIXME: We should isolate the parser from SpecialCaseList and just use it
+// here.
+class WarningsSpecialCaseList : public llvm::SpecialCaseList {
+public:
+  static std::unique_ptr
+  create(const llvm::MemoryBuffer &Input, std::string &Err);
+
+  // 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);
+
+  bool isDiagSuppressed(diag::kind DiagId, llvm::StringRef FilePath) const;
+
+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(const llvm::StringMap &CategoriesToMatchers,
+llvm::StringRef FilePath) const;
+
+  llvm::DenseMap DiagToSection;
+};
+} // namespace
+
+std::unique_ptr
+WarningsSpecialCaseList::create(const llvm::MemoryBuffer &Input,
+std::string &Err) {
+  auto WarningSuppressionList = std::make_unique();
+  if (!WarningSuppressionList->createInternal(&Input, Err))
+return nullptr;
+  return WarningSuppressionList;
+}
+
+void WarningsSpecialCaseList::processSections(DiagnosticsEngine &Diags) {
+  // Drop the default section introduced by special case list, we only support
+  // exact diagnostic group names.
+  // FIXME: We should make this configurable in the parser instead.
+  Sections.erase("*");
+  // Make sure we iterate sections by their line numbers.
+  std::vector *>>
+  LineAndSectionEntry;
+  LineAndSectionEntry.reserve(Sections.size());
+  for (const auto &Entry : Sections) {
+llvm::StringRef DiagName = Entry.first();
+// Each section has a matcher with that section's name, attached to that
+// line.
+const auto &DiagSectionMatcher = Entry.second.SectionMatcher;
+unsigned DiagLine = DiagSectionMatcher->Globs.at(DiagName).second;
+LineAndSectionEntry.emplace_back(DiagLine, &Entry);
+  }
+  llvm::sort(LineAndSectionEntry);
+  static constexpr auto WarningFlavor = clang::diag::Flavor::WarningOrError;
+  for (const auto &[_, SectionEntry] : LineAndSectionEntry) {
+SmallVector GroupDiags;
+llvm::StringRef DiagGroup = SectionEntry->getKey();
+if (Diags.getDiagnosticIDs()->getDiagnosticsInGroup(
+WarningFlavor, DiagGroup, GroupDiags)) {
+  StringRef Suggestion =
+  DiagnosticIDs::getNearestOption(WarningFlavor, DiagGroup);
+  Diags.Report(diag::warn_unknown_diag_option)
+  << static_cast(WarningFlavor) << DiagGroup
+  << !Suggestion.empty() << Suggestion;
+  continue;
+}
+for (diag::kind Diag : GroupDiags)
+  // We're intentionally overwriting any previous mappings here to make 
sure
+  // latest one takes precedence.
+  DiagToSection[Diag] = &SectionEntry->getValue();
+  }
+}
+
+void DiagnosticsEngine::setDiagSuppressionMapping(llvm::MemoryBuffer &Input) {
+  std::string Err;
+  auto WarningSuppressionList = WarningsSpecialCaseList::create(Input, Err);
+  if (!WarningSuppressionList) {
+Report(diag::err_drv_malformed_warning_suppression_mapping)
+<< Input.getBufferIdentifier() << Err;
+return;
+  }
+  WarningSuppressionList->processSections(*this);
+  DiagSuppressionMapping =
+  [WarningSuppressionList(std::move(WarningSuppressionList))](
+  diag::kind DiagId, llvm::StringRef Path) {
+return WarningSuppressionList->isDiagSuppressed(DiagId, Path);
+  };
+}
+
+bool WarningsSpecialCaseList::isDiagSuppressed(diag::kind DiagId,
+   llvm::StringRef FilePath) const 
{
+  const Section *DiagSection = DiagToSection.lookup(DiagId);
+  if (!DiagSection)
+return false;
+  const SectionEntries &EntityTypeToCategories = DiagSection->Entries;
+  auto SrcEntriesIt = EntityTypeToCategories.find("src");
+  if (SrcEntriesIt == EntityTypeToCategories.end())
+return false;
+  const llvm::StringMap &CategoriesToMatchers =
+  SrcEntriesIt->second;

kadircet wrote:

lookup returns by value (because it needs to return a default-constructed 
object when key doesn't exist), hence can't bind it to a reference.

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-07 Thread kadir çetinkaya via cfe-commits

https://github.com/kadircet edited 
https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-07 Thread kadir çetinkaya via cfe-commits


@@ -0,0 +1,90 @@
+
+Warning suppression mappings
+
+
+.. contents::
+   :local:
+
+Introduction
+
+
+Warning suppression mappings enable users to suppress Clang's diagnostics in a
+per-file granular manner. Enabling enforcement of diagnostics in specific parts
+of the project, even if there are violations in some headers.
+
+Goal and usage
+==
+
+Clang allows diagnostics to be configured at a translation-unit granularity.
+If a ``foo.cpp`` is compiled with ``-Wfoo``, all transitively included headers
+also need to be clean. Hence turning on new warnings in large codebases can be
+difficult today. Since it requires cleaning up all the existing warnings,
+which might not be possible when some dependencies aren't in the project 
owner's
+control or because new violations are creeping up quicker than the clean up.
+
+Warning suppression mappings aim to alleviate some of these concerns by making
+diagnostic configuration granularity finer, at a source file level.
+
+To achieve this, user can create a file that lists which diagnostic groups to
+suppress in which files or paths, and pass it as a command line argument to
+Clang with the ``--warning-suppression-mappings`` flag.
+
+Note that this mechanism won't enable any diagnostics on its own. Users should
+still turn on warnings in their compilations with explicit ``-Wfoo`` flags.
+
+Example
+===
+
+.. code-block:: bash
+
+  $ cat my/user/code.cpp
+  #include 
+  namespace { void unused_func1(); }
+
+  $ cat foo/bar.h
+  namespace { void unused_func2(); }
+
+  $ cat suppression_mappings.txt
+  # Suppress -Wunused warnings in all files, apart from the ones under `foo/`.
+  [unused]
+  src:*
+  src:*foo/*=emit
+  $ clang -Wunused --warning-suppression-mappings=suppression_mappings.txt 
my/user/code.cpp
+  # prints warning: unused function 'unused_func2', but no warnings for 
`unused_func1`.
+
+Format
+==
+
+Warning suppression mappings uses the same format as
+:doc:`SanitizerSpecialCaseList`.
+
+Users can mention sections to describe which diagnostic group behaviours to
+change. Sections are denoted as ``[unused]`` in this format. Each section name
+must match a diagnostic group.
+When a diagnostic is matched by multiple groups, the latest one takes

kadircet wrote:

but diagnostics usually belong to multiple groups. i think the poor choice was 
saying `groups` here rather than `sections`. reworded, ptal.

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-07 Thread kadir çetinkaya via cfe-commits




kadircet wrote:

sorry i am struggling to see the value in an integration test while we can (and 
do) test it in a unittest. can you elaborate ?

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-07 Thread kadir çetinkaya via cfe-commits




kadircet wrote:

sorry missed that, added docs to clang root tree

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-07 Thread kadir çetinkaya via cfe-commits


@@ -0,0 +1,90 @@
+
+Warning suppression mappings
+
+
+.. contents::
+   :local:
+
+Introduction
+
+
+Warning suppression mappings enable users to suppress Clang's diagnostics in a
+per-file granular manner. Enabling enforcement of diagnostics in specific parts
+of the project, even if there are violations in some headers.
+
+Goal and usage
+==
+
+Clang allows diagnostics to be configured at a translation-unit granularity.
+If a ``foo.cpp`` is compiled with ``-Wfoo``, all transitively included headers
+also need to be clean. Hence turning on new warnings in large codebases can be
+difficult today. Since it requires cleaning up all the existing warnings,
+which might not be possible when some dependencies aren't in the project 
owner's
+control or because new violations are creeping up quicker than the clean up.
+
+Warning suppression mappings aim to alleviate some of these concerns by making
+diagnostic configuration granularity finer, at a source file level.
+
+To achieve this, user can create a file that lists which diagnostic groups to
+suppress in which files or paths, and pass it as a command line argument to
+Clang with the ``--warning-suppression-mappings`` flag.
+
+Note that this mechanism won't enable any diagnostics on its own. Users should
+still turn on warnings in their compilations with explicit ``-Wfoo`` flags.
+
+Example
+===
+
+.. code-block:: bash
+
+  $ cat my/user/code.cpp
+  #include 
+  namespace { void unused_func1(); }
+
+  $ cat foo/bar.h
+  namespace { void unused_func2(); }
+
+  $ cat suppression_mappings.txt
+  # Suppress -Wunused warnings in all files, apart from the ones under `foo/`.
+  [unused]
+  src:*
+  src:*foo/*=emit
+  $ clang -Wunused --warning-suppression-mappings=suppression_mappings.txt 
my/user/code.cpp
+  # prints warning: unused function 'unused_func2', but no warnings for 
`unused_func1`.
+
+Format
+==
+
+Warning suppression mappings uses the same format as
+:doc:`SanitizerSpecialCaseList`.
+
+Users can mention sections to describe which diagnostic group behaviours to
+change. Sections are denoted as ``[unused]`` in this format. Each section name
+must match a diagnostic group.
+When a diagnostic is matched by multiple groups, the latest one takes
+precendence.
+
+Afterwards in each section, users can have multiple entities that match source
+files based on the globs. These entities look like ``src:*/my/dir/*``.
+Users can also use the ``emit`` category to exclude a subdirectory from

kadircet wrote:

it really is called a category though, 
https://clang.llvm.org/docs/SanitizerSpecialCaseList.html#:~:text=a%20tool%2Dspecific-,category,-%2C%20e.g.

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-07 Thread kadir çetinkaya via cfe-commits


@@ -167,4 +176,159 @@ TEST(DiagnosticTest, storedDiagEmptyWarning) {
   // Make sure an empty warning can round-trip with \c StoredDiagnostic.
   Diags.Report(CaptureConsumer.StoredDiags.front());
 }
+
+class SuppressionMappingTest : public testing::Test {
+public:
+  SuppressionMappingTest() {
+Diags.setClient(&CaptureConsumer, /*ShouldOwnClient=*/false);
+  }
+
+protected:
+  llvm::IntrusiveRefCntPtr FS =
+  llvm::makeIntrusiveRefCnt();
+  DiagnosticsEngine Diags{new DiagnosticIDs(), new DiagnosticOptions};
+
+  std::vector takeDiags() {
+return std::move(CaptureConsumer.StoredDiags);
+  }
+
+private:
+  class CaptureDiagnosticConsumer : public DiagnosticConsumer {
+  public:
+std::vector StoredDiags;
+
+void HandleDiagnostic(DiagnosticsEngine::Level level,
+  const Diagnostic &Info) override {
+  StoredDiags.push_back(StoredDiagnostic(level, Info));
+}
+  };
+  CaptureDiagnosticConsumer CaptureConsumer;
+};
+
+MATCHER_P(WithMessage, Msg, "has diagnostic message") {
+  return arg.getMessage() == Msg;
+}
+
+TEST_F(SuppressionMappingTest, MissingMappingFile) {
+  Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";
+  clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);
+  EXPECT_THAT(takeDiags(), testing::ElementsAre(WithMessage(
+   "no such file or directory: 'foo.txt'")));
+}
+
+TEST_F(SuppressionMappingTest, MalformedFile) {
+  Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";
+  FS->addFile("foo.txt", /*ModificationTime=*/{},
+  llvm::MemoryBuffer::getMemBuffer("asdf", "foo.txt"));
+  clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);
+  EXPECT_THAT(takeDiags(), testing::ElementsAre(WithMessage(
+   "failed to process suppression mapping file "
+   "'foo.txt': malformed line 1: 'asdf'")));
+}
+
+TEST_F(SuppressionMappingTest, UnknownDiagName) {
+  Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";
+  FS->addFile("foo.txt", /*ModificationTime=*/{},
+  llvm::MemoryBuffer::getMemBuffer("[non-existing-warning]"));
+  clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);
+  EXPECT_THAT(takeDiags(),
+  testing::ElementsAre(WithMessage(
+  "unknown warning option 'non-existing-warning'")));
+}
+
+TEST_F(SuppressionMappingTest, SuppressesGroup) {
+  llvm::StringLiteral SuppressionMappingFile = R"txt(
+  [unused]
+  src:*
+  )txt";
+  Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";
+  FS->addFile("foo.txt", /*ModificationTime=*/{},
+  llvm::MemoryBuffer::getMemBuffer(SuppressionMappingFile));
+  clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);
+  EXPECT_THAT(takeDiags(), testing::IsEmpty());
+
+  EXPECT_TRUE(
+  Diags.isSuppressedViaMapping(diag::warn_unused_function, "foo.cpp"));
+  EXPECT_FALSE(Diags.isSuppressedViaMapping(diag::warn_deprecated, "foo.cpp"));
+}
+
+TEST_F(SuppressionMappingTest, ExclusionsWork) {
+  llvm::StringLiteral SuppressionMappingFile = R"txt(
+  [unused]
+  src:*
+  src:*foo.cpp=emit
+  )txt";
+  Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";
+  FS->addFile("foo.txt", /*ModificationTime=*/{},
+  llvm::MemoryBuffer::getMemBuffer(SuppressionMappingFile));
+  clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);
+  EXPECT_THAT(takeDiags(), testing::IsEmpty());
+
+  EXPECT_TRUE(
+  Diags.isSuppressedViaMapping(diag::warn_unused_function, "bar.cpp"));
+  EXPECT_FALSE(
+  Diags.isSuppressedViaMapping(diag::warn_unused_function, "foo.cpp"));
+}
+
+TEST_F(SuppressionMappingTest, LongestMatchWins) {
+  llvm::StringLiteral SuppressionMappingFile = R"txt(
+  [unused]
+  src:*clang/*
+  src:*clang/lib/Sema/*=emit
+  src:*clang/lib/Sema/foo*
+  )txt";
+  Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";
+  FS->addFile("foo.txt", /*ModificationTime=*/{},
+  llvm::MemoryBuffer::getMemBuffer(SuppressionMappingFile));
+  clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);
+  EXPECT_THAT(takeDiags(), testing::IsEmpty());
+
+  EXPECT_TRUE(Diags.isSuppressedViaMapping(diag::warn_unused_function,
+   "clang/lib/Basic/foo.h"));
+  EXPECT_FALSE(Diags.isSuppressedViaMapping(diag::warn_unused_function,
+"clang/lib/Sema/bar.h"));
+  EXPECT_TRUE(Diags.isSuppressedViaMapping(diag::warn_unused_function,
+   "clang/lib/Sema/foo.h"));
+}
+
+TEST_F(SuppressionMappingTest, IsIgnored) {

kadircet wrote:

i modified `DiagnosticIDs::getDiagnosticSeverity` and it is an implementation 
detail of the public `isIgnored` API. hence yes, this patch changes the 
observ

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

2024-11-07 Thread kadir çetinkaya via cfe-commits


@@ -0,0 +1,90 @@
+
+Warning suppression mappings
+
+
+.. contents::
+   :local:
+
+Introduction
+
+
+Warning suppression mappings enable users to suppress Clang's diagnostics in a
+per-file granular manner. Enabling enforcement of diagnostics in specific parts
+of the project, even if there are violations in some headers.
+
+Goal and usage
+==
+
+Clang allows diagnostics to be configured at a translation-unit granularity.
+If a ``foo.cpp`` is compiled with ``-Wfoo``, all transitively included headers
+also need to be clean. Hence turning on new warnings in large codebases can be
+difficult today. Since it requires cleaning up all the existing warnings,
+which might not be possible when some dependencies aren't in the project 
owner's
+control or because new violations are creeping up quicker than the clean up.
+
+Warning suppression mappings aim to alleviate some of these concerns by making
+diagnostic configuration granularity finer, at a source file level.
+
+To achieve this, user can create a file that lists which diagnostic groups to
+suppress in which files or paths, and pass it as a command line argument to
+Clang with the ``--warning-suppression-mappings`` flag.
+
+Note that this mechanism won't enable any diagnostics on its own. Users should
+still turn on warnings in their compilations with explicit ``-Wfoo`` flags.
+
+Example
+===
+
+.. code-block:: bash
+
+  $ cat my/user/code.cpp
+  #include 
+  namespace { void unused_func1(); }
+
+  $ cat foo/bar.h
+  namespace { void unused_func2(); }
+
+  $ cat suppression_mappings.txt
+  # Suppress -Wunused warnings in all files, apart from the ones under `foo/`.
+  [unused]
+  src:*
+  src:*foo/*=emit
+  $ clang -Wunused --warning-suppression-mappings=suppression_mappings.txt 
my/user/code.cpp
+  # prints warning: unused function 'unused_func2', but no warnings for 
`unused_func1`.
+
+Format
+==
+
+Warning suppression mappings uses the same format as
+:doc:`SanitizerSpecialCaseList`.
+
+Users can mention sections to describe which diagnostic group behaviours to
+change. Sections are denoted as ``[unused]`` in this format. Each section name
+must match a diagnostic group.
+When a diagnostic is matched by multiple groups, the latest one takes
+precendence.
+
+Afterwards in each section, users can have multiple entities that match source
+files based on the globs. These entities look like ``src:*/my/dir/*``.
+Users can also use the ``emit`` category to exclude a subdirectory from
+suppression.
+Source files are matched against these globs either as paths relative to th
+current working directory, or as absolute paths.

kadircet wrote:

it's the latter. i was trying to emphasize that with `either/or`, do you have 
any recommendations for making it more concrete?

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-07 Thread kadir çetinkaya via cfe-commits


@@ -167,4 +176,159 @@ TEST(DiagnosticTest, storedDiagEmptyWarning) {
   // Make sure an empty warning can round-trip with \c StoredDiagnostic.
   Diags.Report(CaptureConsumer.StoredDiags.front());
 }
+
+class SuppressionMappingTest : public testing::Test {
+public:
+  SuppressionMappingTest() {
+Diags.setClient(&CaptureConsumer, /*ShouldOwnClient=*/false);
+  }
+
+protected:
+  llvm::IntrusiveRefCntPtr FS =
+  llvm::makeIntrusiveRefCnt();
+  DiagnosticsEngine Diags{new DiagnosticIDs(), new DiagnosticOptions};
+
+  std::vector takeDiags() {
+return std::move(CaptureConsumer.StoredDiags);
+  }
+
+private:
+  class CaptureDiagnosticConsumer : public DiagnosticConsumer {
+  public:
+std::vector StoredDiags;
+
+void HandleDiagnostic(DiagnosticsEngine::Level level,
+  const Diagnostic &Info) override {
+  StoredDiags.push_back(StoredDiagnostic(level, Info));
+}
+  };
+  CaptureDiagnosticConsumer CaptureConsumer;
+};
+
+MATCHER_P(WithMessage, Msg, "has diagnostic message") {
+  return arg.getMessage() == Msg;
+}
+
+TEST_F(SuppressionMappingTest, MissingMappingFile) {
+  Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";
+  clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);
+  EXPECT_THAT(takeDiags(), testing::ElementsAre(WithMessage(
+   "no such file or directory: 'foo.txt'")));

kadircet wrote:

the only interesting bit in the logic is propagation of input filename in the 
warning message. i don't see the value in testing rest of the shared diagnostic 
infrastructure. happy to test that if you feel strongly about this.

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-07 Thread kadir çetinkaya via cfe-commits


@@ -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
+  create(const llvm::MemoryBuffer &MB, std::string &Err) {
+auto SCL = std::make_unique();
+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 *>>
+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 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(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 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) {

kadircet wrote:

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

I am skeptical of that being a net-positive even for 
warning-suppression-mappings. In the common path, compiler doesn't emit 
warnings. Hence most of the compilations will just end up sorting this list, 
and never query it afterwards.

> 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.

I'd rather give users less ways to misconfigure this infrastructure. but by the 
nature of the current behavior, users can get the exact same end result by just 
ensuring a subdirectory is mentioned before its parents in the list, eg:
```
foo/bar/*=emit
foo/*
```

> 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?

other use cases only allow inclusion, they don't allow exclusions.

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-07 Thread Boaz Brickner via cfe-commits


@@ -167,4 +176,159 @@ TEST(DiagnosticTest, storedDiagEmptyWarning) {
   // Make sure an empty warning can round-trip with \c StoredDiagnostic.
   Diags.Report(CaptureConsumer.StoredDiags.front());
 }
+
+class SuppressionMappingTest : public testing::Test {
+public:
+  SuppressionMappingTest() {
+Diags.setClient(&CaptureConsumer, /*ShouldOwnClient=*/false);
+  }
+
+protected:
+  llvm::IntrusiveRefCntPtr FS =
+  llvm::makeIntrusiveRefCnt();
+  DiagnosticsEngine Diags{new DiagnosticIDs(), new DiagnosticOptions};
+
+  std::vector takeDiags() {
+return std::move(CaptureConsumer.StoredDiags);
+  }
+
+private:
+  class CaptureDiagnosticConsumer : public DiagnosticConsumer {
+  public:
+std::vector StoredDiags;
+
+void HandleDiagnostic(DiagnosticsEngine::Level level,
+  const Diagnostic &Info) override {
+  StoredDiags.push_back(StoredDiagnostic(level, Info));
+}
+  };
+  CaptureDiagnosticConsumer CaptureConsumer;
+};
+
+MATCHER_P(WithMessage, Msg, "has diagnostic message") {
+  return arg.getMessage() == Msg;
+}
+
+TEST_F(SuppressionMappingTest, MissingMappingFile) {
+  Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";
+  clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);
+  EXPECT_THAT(takeDiags(), testing::ElementsAre(WithMessage(
+   "no such file or directory: 'foo.txt'")));
+}
+
+TEST_F(SuppressionMappingTest, MalformedFile) {
+  Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";
+  FS->addFile("foo.txt", /*ModificationTime=*/{},
+  llvm::MemoryBuffer::getMemBuffer("asdf", "foo.txt"));
+  clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);
+  EXPECT_THAT(takeDiags(), testing::ElementsAre(WithMessage(
+   "failed to process suppression mapping file "
+   "'foo.txt': malformed line 1: 'asdf'")));
+}
+
+TEST_F(SuppressionMappingTest, UnknownDiagName) {
+  Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";
+  FS->addFile("foo.txt", /*ModificationTime=*/{},
+  llvm::MemoryBuffer::getMemBuffer("[non-existing-warning]"));
+  clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);
+  EXPECT_THAT(takeDiags(),
+  testing::ElementsAre(WithMessage(
+  "unknown warning option 'non-existing-warning'")));
+}
+
+TEST_F(SuppressionMappingTest, SuppressesGroup) {
+  llvm::StringLiteral SuppressionMappingFile = R"txt(

bricknerb wrote:

Nit: Consider removing the "txt" as it has no effect here.
Seems like other use cases of R"()" in llvm don't set this.

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-07 Thread Boaz Brickner via cfe-commits


@@ -167,4 +176,159 @@ TEST(DiagnosticTest, storedDiagEmptyWarning) {
   // Make sure an empty warning can round-trip with \c StoredDiagnostic.
   Diags.Report(CaptureConsumer.StoredDiags.front());
 }
+
+class SuppressionMappingTest : public testing::Test {
+public:
+  SuppressionMappingTest() {
+Diags.setClient(&CaptureConsumer, /*ShouldOwnClient=*/false);
+  }
+
+protected:
+  llvm::IntrusiveRefCntPtr FS =
+  llvm::makeIntrusiveRefCnt();
+  DiagnosticsEngine Diags{new DiagnosticIDs(), new DiagnosticOptions};
+
+  std::vector takeDiags() {
+return std::move(CaptureConsumer.StoredDiags);
+  }
+
+private:
+  class CaptureDiagnosticConsumer : public DiagnosticConsumer {
+  public:
+std::vector StoredDiags;
+
+void HandleDiagnostic(DiagnosticsEngine::Level level,
+  const Diagnostic &Info) override {
+  StoredDiags.push_back(StoredDiagnostic(level, Info));
+}
+  };
+  CaptureDiagnosticConsumer CaptureConsumer;
+};
+
+MATCHER_P(WithMessage, Msg, "has diagnostic message") {
+  return arg.getMessage() == Msg;
+}
+
+TEST_F(SuppressionMappingTest, MissingMappingFile) {
+  Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";
+  clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);
+  EXPECT_THAT(takeDiags(), testing::ElementsAre(WithMessage(
+   "no such file or directory: 'foo.txt'")));
+}
+
+TEST_F(SuppressionMappingTest, MalformedFile) {
+  Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";
+  FS->addFile("foo.txt", /*ModificationTime=*/{},
+  llvm::MemoryBuffer::getMemBuffer("asdf", "foo.txt"));
+  clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);
+  EXPECT_THAT(takeDiags(), testing::ElementsAre(WithMessage(
+   "failed to process suppression mapping file "
+   "'foo.txt': malformed line 1: 'asdf'")));
+}
+
+TEST_F(SuppressionMappingTest, UnknownDiagName) {
+  Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";
+  FS->addFile("foo.txt", /*ModificationTime=*/{},
+  llvm::MemoryBuffer::getMemBuffer("[non-existing-warning]"));
+  clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);
+  EXPECT_THAT(takeDiags(),
+  testing::ElementsAre(WithMessage(
+  "unknown warning option 'non-existing-warning'")));
+}
+
+TEST_F(SuppressionMappingTest, SuppressesGroup) {
+  llvm::StringLiteral SuppressionMappingFile = R"txt(
+  [unused]
+  src:*
+  )txt";
+  Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";
+  FS->addFile("foo.txt", /*ModificationTime=*/{},
+  llvm::MemoryBuffer::getMemBuffer(SuppressionMappingFile));
+  clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);
+  EXPECT_THAT(takeDiags(), testing::IsEmpty());
+
+  EXPECT_TRUE(
+  Diags.isSuppressedViaMapping(diag::warn_unused_function, "foo.cpp"));
+  EXPECT_FALSE(Diags.isSuppressedViaMapping(diag::warn_deprecated, "foo.cpp"));
+}
+
+TEST_F(SuppressionMappingTest, ExclusionsWork) {
+  llvm::StringLiteral SuppressionMappingFile = R"txt(
+  [unused]
+  src:*
+  src:*foo.cpp=emit
+  )txt";
+  Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";
+  FS->addFile("foo.txt", /*ModificationTime=*/{},
+  llvm::MemoryBuffer::getMemBuffer(SuppressionMappingFile));
+  clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);
+  EXPECT_THAT(takeDiags(), testing::IsEmpty());
+
+  EXPECT_TRUE(
+  Diags.isSuppressedViaMapping(diag::warn_unused_function, "bar.cpp"));
+  EXPECT_FALSE(
+  Diags.isSuppressedViaMapping(diag::warn_unused_function, "foo.cpp"));
+}
+
+TEST_F(SuppressionMappingTest, LongestMatchWins) {
+  llvm::StringLiteral SuppressionMappingFile = R"txt(
+  [unused]
+  src:*clang/*
+  src:*clang/lib/Sema/*=emit
+  src:*clang/lib/Sema/foo*
+  )txt";
+  Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";
+  FS->addFile("foo.txt", /*ModificationTime=*/{},
+  llvm::MemoryBuffer::getMemBuffer(SuppressionMappingFile));
+  clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);
+  EXPECT_THAT(takeDiags(), testing::IsEmpty());
+
+  EXPECT_TRUE(Diags.isSuppressedViaMapping(diag::warn_unused_function,
+   "clang/lib/Basic/foo.h"));
+  EXPECT_FALSE(Diags.isSuppressedViaMapping(diag::warn_unused_function,
+"clang/lib/Sema/bar.h"));
+  EXPECT_TRUE(Diags.isSuppressedViaMapping(diag::warn_unused_function,
+   "clang/lib/Sema/foo.h"));
+}
+
+TEST_F(SuppressionMappingTest, IsIgnored) {
+  llvm::StringLiteral SuppressionMappingFile = R"txt(
+  [unused]
+  src:*clang/*
+  )txt";
+  Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";
+  Diags.getDiagnost

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

2024-11-07 Thread Boaz Brickner via cfe-commits


@@ -167,4 +176,159 @@ TEST(DiagnosticTest, storedDiagEmptyWarning) {
   // Make sure an empty warning can round-trip with \c StoredDiagnostic.
   Diags.Report(CaptureConsumer.StoredDiags.front());
 }
+
+class SuppressionMappingTest : public testing::Test {
+public:
+  SuppressionMappingTest() {
+Diags.setClient(&CaptureConsumer, /*ShouldOwnClient=*/false);
+  }
+
+protected:
+  llvm::IntrusiveRefCntPtr FS =
+  llvm::makeIntrusiveRefCnt();
+  DiagnosticsEngine Diags{new DiagnosticIDs(), new DiagnosticOptions};
+
+  std::vector takeDiags() {

bricknerb wrote:

Why move instead of returning const& ?

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-07 Thread Boaz Brickner via cfe-commits


@@ -167,4 +176,159 @@ TEST(DiagnosticTest, storedDiagEmptyWarning) {
   // Make sure an empty warning can round-trip with \c StoredDiagnostic.
   Diags.Report(CaptureConsumer.StoredDiags.front());
 }
+
+class SuppressionMappingTest : public testing::Test {
+public:
+  SuppressionMappingTest() {
+Diags.setClient(&CaptureConsumer, /*ShouldOwnClient=*/false);
+  }
+
+protected:
+  llvm::IntrusiveRefCntPtr FS =
+  llvm::makeIntrusiveRefCnt();
+  DiagnosticsEngine Diags{new DiagnosticIDs(), new DiagnosticOptions};
+
+  std::vector takeDiags() {
+return std::move(CaptureConsumer.StoredDiags);
+  }
+
+private:
+  class CaptureDiagnosticConsumer : public DiagnosticConsumer {
+  public:
+std::vector StoredDiags;
+
+void HandleDiagnostic(DiagnosticsEngine::Level level,
+  const Diagnostic &Info) override {
+  StoredDiags.push_back(StoredDiagnostic(level, Info));
+}
+  };
+  CaptureDiagnosticConsumer CaptureConsumer;
+};
+
+MATCHER_P(WithMessage, Msg, "has diagnostic message") {
+  return arg.getMessage() == Msg;
+}
+
+TEST_F(SuppressionMappingTest, MissingMappingFile) {
+  Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";
+  clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);
+  EXPECT_THAT(takeDiags(), testing::ElementsAre(WithMessage(
+   "no such file or directory: 'foo.txt'")));

bricknerb wrote:

Why do we only test the message and not also level and possibly other fields?

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-07 Thread Boaz Brickner via cfe-commits


@@ -167,4 +176,159 @@ TEST(DiagnosticTest, storedDiagEmptyWarning) {
   // Make sure an empty warning can round-trip with \c StoredDiagnostic.
   Diags.Report(CaptureConsumer.StoredDiags.front());
 }
+
+class SuppressionMappingTest : public testing::Test {
+public:
+  SuppressionMappingTest() {
+Diags.setClient(&CaptureConsumer, /*ShouldOwnClient=*/false);
+  }
+
+protected:
+  llvm::IntrusiveRefCntPtr FS =
+  llvm::makeIntrusiveRefCnt();
+  DiagnosticsEngine Diags{new DiagnosticIDs(), new DiagnosticOptions};
+
+  std::vector takeDiags() {
+return std::move(CaptureConsumer.StoredDiags);
+  }
+
+private:
+  class CaptureDiagnosticConsumer : public DiagnosticConsumer {
+  public:
+std::vector StoredDiags;
+
+void HandleDiagnostic(DiagnosticsEngine::Level level,
+  const Diagnostic &Info) override {
+  StoredDiags.push_back(StoredDiagnostic(level, Info));
+}
+  };
+  CaptureDiagnosticConsumer CaptureConsumer;
+};
+
+MATCHER_P(WithMessage, Msg, "has diagnostic message") {
+  return arg.getMessage() == Msg;
+}
+
+TEST_F(SuppressionMappingTest, MissingMappingFile) {
+  Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";
+  clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);
+  EXPECT_THAT(takeDiags(), testing::ElementsAre(WithMessage(
+   "no such file or directory: 'foo.txt'")));
+}
+
+TEST_F(SuppressionMappingTest, MalformedFile) {
+  Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";
+  FS->addFile("foo.txt", /*ModificationTime=*/{},
+  llvm::MemoryBuffer::getMemBuffer("asdf", "foo.txt"));
+  clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);
+  EXPECT_THAT(takeDiags(), testing::ElementsAre(WithMessage(
+   "failed to process suppression mapping file "
+   "'foo.txt': malformed line 1: 'asdf'")));
+}
+
+TEST_F(SuppressionMappingTest, UnknownDiagName) {
+  Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";
+  FS->addFile("foo.txt", /*ModificationTime=*/{},
+  llvm::MemoryBuffer::getMemBuffer("[non-existing-warning]"));
+  clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);
+  EXPECT_THAT(takeDiags(),
+  testing::ElementsAre(WithMessage(
+  "unknown warning option 'non-existing-warning'")));
+}
+
+TEST_F(SuppressionMappingTest, SuppressesGroup) {
+  llvm::StringLiteral SuppressionMappingFile = R"txt(
+  [unused]
+  src:*
+  )txt";
+  Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";
+  FS->addFile("foo.txt", /*ModificationTime=*/{},
+  llvm::MemoryBuffer::getMemBuffer(SuppressionMappingFile));
+  clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);
+  EXPECT_THAT(takeDiags(), testing::IsEmpty());
+
+  EXPECT_TRUE(
+  Diags.isSuppressedViaMapping(diag::warn_unused_function, "foo.cpp"));
+  EXPECT_FALSE(Diags.isSuppressedViaMapping(diag::warn_deprecated, "foo.cpp"));
+}
+
+TEST_F(SuppressionMappingTest, ExclusionsWork) {
+  llvm::StringLiteral SuppressionMappingFile = R"txt(
+  [unused]
+  src:*
+  src:*foo.cpp=emit
+  )txt";
+  Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";
+  FS->addFile("foo.txt", /*ModificationTime=*/{},
+  llvm::MemoryBuffer::getMemBuffer(SuppressionMappingFile));
+  clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);
+  EXPECT_THAT(takeDiags(), testing::IsEmpty());
+
+  EXPECT_TRUE(
+  Diags.isSuppressedViaMapping(diag::warn_unused_function, "bar.cpp"));
+  EXPECT_FALSE(
+  Diags.isSuppressedViaMapping(diag::warn_unused_function, "foo.cpp"));
+}
+
+TEST_F(SuppressionMappingTest, LongestMatchWins) {
+  llvm::StringLiteral SuppressionMappingFile = R"txt(
+  [unused]
+  src:*clang/*
+  src:*clang/lib/Sema/*=emit
+  src:*clang/lib/Sema/foo*
+  )txt";
+  Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";
+  FS->addFile("foo.txt", /*ModificationTime=*/{},
+  llvm::MemoryBuffer::getMemBuffer(SuppressionMappingFile));
+  clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);
+  EXPECT_THAT(takeDiags(), testing::IsEmpty());
+
+  EXPECT_TRUE(Diags.isSuppressedViaMapping(diag::warn_unused_function,
+   "clang/lib/Basic/foo.h"));
+  EXPECT_FALSE(Diags.isSuppressedViaMapping(diag::warn_unused_function,
+"clang/lib/Sema/bar.h"));
+  EXPECT_TRUE(Diags.isSuppressedViaMapping(diag::warn_unused_function,
+   "clang/lib/Sema/foo.h"));
+}
+
+TEST_F(SuppressionMappingTest, IsIgnored) {

bricknerb wrote:

Did we change the logic of isIgnored that we need to add specific tests for it 
here?

I think most of the tests should be for isSuppressedViaMapping() and w

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

2024-11-07 Thread Boaz Brickner via cfe-commits


@@ -167,4 +176,159 @@ TEST(DiagnosticTest, storedDiagEmptyWarning) {
   // Make sure an empty warning can round-trip with \c StoredDiagnostic.
   Diags.Report(CaptureConsumer.StoredDiags.front());
 }
+
+class SuppressionMappingTest : public testing::Test {
+public:
+  SuppressionMappingTest() {
+Diags.setClient(&CaptureConsumer, /*ShouldOwnClient=*/false);
+  }
+
+protected:
+  llvm::IntrusiveRefCntPtr FS =
+  llvm::makeIntrusiveRefCnt();
+  DiagnosticsEngine Diags{new DiagnosticIDs(), new DiagnosticOptions};
+
+  std::vector takeDiags() {
+return std::move(CaptureConsumer.StoredDiags);
+  }
+
+private:
+  class CaptureDiagnosticConsumer : public DiagnosticConsumer {
+  public:
+std::vector StoredDiags;
+
+void HandleDiagnostic(DiagnosticsEngine::Level level,
+  const Diagnostic &Info) override {
+  StoredDiags.push_back(StoredDiagnostic(level, Info));
+}
+  };
+  CaptureDiagnosticConsumer CaptureConsumer;
+};
+
+MATCHER_P(WithMessage, Msg, "has diagnostic message") {
+  return arg.getMessage() == Msg;
+}
+
+TEST_F(SuppressionMappingTest, MissingMappingFile) {
+  Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";
+  clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);
+  EXPECT_THAT(takeDiags(), testing::ElementsAre(WithMessage(
+   "no such file or directory: 'foo.txt'")));
+}
+
+TEST_F(SuppressionMappingTest, MalformedFile) {
+  Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";
+  FS->addFile("foo.txt", /*ModificationTime=*/{},
+  llvm::MemoryBuffer::getMemBuffer("asdf", "foo.txt"));
+  clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);
+  EXPECT_THAT(takeDiags(), testing::ElementsAre(WithMessage(
+   "failed to process suppression mapping file "
+   "'foo.txt': malformed line 1: 'asdf'")));
+}
+
+TEST_F(SuppressionMappingTest, UnknownDiagName) {
+  Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";
+  FS->addFile("foo.txt", /*ModificationTime=*/{},
+  llvm::MemoryBuffer::getMemBuffer("[non-existing-warning]"));
+  clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);
+  EXPECT_THAT(takeDiags(),
+  testing::ElementsAre(WithMessage(
+  "unknown warning option 'non-existing-warning'")));
+}
+
+TEST_F(SuppressionMappingTest, SuppressesGroup) {
+  llvm::StringLiteral SuppressionMappingFile = R"txt(
+  [unused]
+  src:*
+  )txt";
+  Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";
+  FS->addFile("foo.txt", /*ModificationTime=*/{},
+  llvm::MemoryBuffer::getMemBuffer(SuppressionMappingFile));
+  clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);
+  EXPECT_THAT(takeDiags(), testing::IsEmpty());
+
+  EXPECT_TRUE(
+  Diags.isSuppressedViaMapping(diag::warn_unused_function, "foo.cpp"));
+  EXPECT_FALSE(Diags.isSuppressedViaMapping(diag::warn_deprecated, "foo.cpp"));
+}
+
+TEST_F(SuppressionMappingTest, ExclusionsWork) {

bricknerb wrote:

What do you mean by "Work"?
Would "Exclusion" be enough? Or "ExclusionSuccess"?

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-07 Thread Boaz Brickner via cfe-commits


@@ -167,4 +176,159 @@ TEST(DiagnosticTest, storedDiagEmptyWarning) {
   // Make sure an empty warning can round-trip with \c StoredDiagnostic.
   Diags.Report(CaptureConsumer.StoredDiags.front());
 }
+
+class SuppressionMappingTest : public testing::Test {
+public:
+  SuppressionMappingTest() {
+Diags.setClient(&CaptureConsumer, /*ShouldOwnClient=*/false);
+  }
+
+protected:
+  llvm::IntrusiveRefCntPtr FS =
+  llvm::makeIntrusiveRefCnt();
+  DiagnosticsEngine Diags{new DiagnosticIDs(), new DiagnosticOptions};
+
+  std::vector takeDiags() {
+return std::move(CaptureConsumer.StoredDiags);
+  }
+
+private:
+  class CaptureDiagnosticConsumer : public DiagnosticConsumer {
+  public:
+std::vector StoredDiags;
+
+void HandleDiagnostic(DiagnosticsEngine::Level level,
+  const Diagnostic &Info) override {
+  StoredDiags.push_back(StoredDiagnostic(level, Info));
+}
+  };
+  CaptureDiagnosticConsumer CaptureConsumer;
+};
+
+MATCHER_P(WithMessage, Msg, "has diagnostic message") {
+  return arg.getMessage() == Msg;
+}
+
+TEST_F(SuppressionMappingTest, MissingMappingFile) {
+  Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";
+  clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);
+  EXPECT_THAT(takeDiags(), testing::ElementsAre(WithMessage(
+   "no such file or directory: 'foo.txt'")));
+}
+
+TEST_F(SuppressionMappingTest, MalformedFile) {
+  Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";
+  FS->addFile("foo.txt", /*ModificationTime=*/{},
+  llvm::MemoryBuffer::getMemBuffer("asdf", "foo.txt"));
+  clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);
+  EXPECT_THAT(takeDiags(), testing::ElementsAre(WithMessage(

bricknerb wrote:

Consider adding using for these:
```
using testing::ElementsAre;
using testing::IsEmpty;
```

It seems like the common practice in Clang's unit tests.

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-07 Thread Boaz Brickner via cfe-commits


@@ -477,6 +486,136 @@ void DiagnosticsEngine::setSeverityForAll(diag::Flavor 
Flavor,
   setSeverity(Diag, Map, Loc);
 }
 
+namespace {
+// FIXME: We should isolate the parser from SpecialCaseList and just use it
+// here.
+class WarningsSpecialCaseList : public llvm::SpecialCaseList {
+public:
+  static std::unique_ptr
+  create(const llvm::MemoryBuffer &Input, std::string &Err);
+
+  // 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);
+
+  bool isDiagSuppressed(diag::kind DiagId, llvm::StringRef FilePath) const;
+
+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(const llvm::StringMap &CategoriesToMatchers,
+llvm::StringRef FilePath) const;
+
+  llvm::DenseMap DiagToSection;
+};
+} // namespace
+
+std::unique_ptr
+WarningsSpecialCaseList::create(const llvm::MemoryBuffer &Input,
+std::string &Err) {
+  auto WarningSuppressionList = std::make_unique();
+  if (!WarningSuppressionList->createInternal(&Input, Err))
+return nullptr;
+  return WarningSuppressionList;
+}
+
+void WarningsSpecialCaseList::processSections(DiagnosticsEngine &Diags) {
+  // Drop the default section introduced by special case list, we only support
+  // exact diagnostic group names.
+  // FIXME: We should make this configurable in the parser instead.
+  Sections.erase("*");
+  // Make sure we iterate sections by their line numbers.
+  std::vector *>>
+  LineAndSectionEntry;
+  LineAndSectionEntry.reserve(Sections.size());
+  for (const auto &Entry : Sections) {
+llvm::StringRef DiagName = Entry.first();
+// Each section has a matcher with that section's name, attached to that
+// line.
+const auto &DiagSectionMatcher = Entry.second.SectionMatcher;
+unsigned DiagLine = DiagSectionMatcher->Globs.at(DiagName).second;
+LineAndSectionEntry.emplace_back(DiagLine, &Entry);
+  }
+  llvm::sort(LineAndSectionEntry);
+  static constexpr auto WarningFlavor = clang::diag::Flavor::WarningOrError;
+  for (const auto &[_, SectionEntry] : LineAndSectionEntry) {
+SmallVector GroupDiags;
+llvm::StringRef DiagGroup = SectionEntry->getKey();
+if (Diags.getDiagnosticIDs()->getDiagnosticsInGroup(
+WarningFlavor, DiagGroup, GroupDiags)) {
+  StringRef Suggestion =
+  DiagnosticIDs::getNearestOption(WarningFlavor, DiagGroup);
+  Diags.Report(diag::warn_unknown_diag_option)
+  << static_cast(WarningFlavor) << DiagGroup
+  << !Suggestion.empty() << Suggestion;
+  continue;
+}
+for (diag::kind Diag : GroupDiags)
+  // We're intentionally overwriting any previous mappings here to make 
sure
+  // latest one takes precedence.
+  DiagToSection[Diag] = &SectionEntry->getValue();
+  }
+}
+
+void DiagnosticsEngine::setDiagSuppressionMapping(llvm::MemoryBuffer &Input) {
+  std::string Err;
+  auto WarningSuppressionList = WarningsSpecialCaseList::create(Input, Err);
+  if (!WarningSuppressionList) {
+Report(diag::err_drv_malformed_warning_suppression_mapping)
+<< Input.getBufferIdentifier() << Err;
+return;
+  }
+  WarningSuppressionList->processSections(*this);
+  DiagSuppressionMapping =
+  [WarningSuppressionList(std::move(WarningSuppressionList))](
+  diag::kind DiagId, llvm::StringRef Path) {
+return WarningSuppressionList->isDiagSuppressed(DiagId, Path);
+  };
+}
+
+bool WarningsSpecialCaseList::isDiagSuppressed(diag::kind DiagId,
+   llvm::StringRef FilePath) const 
{
+  const Section *DiagSection = DiagToSection.lookup(DiagId);
+  if (!DiagSection)
+return false;
+  const SectionEntries &EntityTypeToCategories = DiagSection->Entries;
+  auto SrcEntriesIt = EntityTypeToCategories.find("src");
+  if (SrcEntriesIt == EntityTypeToCategories.end())
+return false;
+  const llvm::StringMap &CategoriesToMatchers =
+  SrcEntriesIt->second;
+  return globsMatches(CategoriesToMatchers, FilePath);
+}
+
+bool WarningsSpecialCaseList::globsMatches(
+const llvm::StringMap &CategoriesToMatchers,
+llvm::StringRef FilePath) const {
+  llvm::StringRef LongestMatch;
+  bool LongestIsPositive = false;
+  for (const auto &Entry : CategoriesToMatchers) {
+llvm::StringRef Category = Entry.first();
+const auto &Matcher = Entry.second;

bricknerb wrote:

Consider avoiding auto here since the type isn't trivial.

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@list

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

2024-11-07 Thread Boaz Brickner via cfe-commits


@@ -477,6 +486,136 @@ void DiagnosticsEngine::setSeverityForAll(diag::Flavor 
Flavor,
   setSeverity(Diag, Map, Loc);
 }
 
+namespace {
+// FIXME: We should isolate the parser from SpecialCaseList and just use it
+// here.
+class WarningsSpecialCaseList : public llvm::SpecialCaseList {
+public:
+  static std::unique_ptr
+  create(const llvm::MemoryBuffer &Input, std::string &Err);
+
+  // 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);
+
+  bool isDiagSuppressed(diag::kind DiagId, llvm::StringRef FilePath) const;
+
+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(const llvm::StringMap &CategoriesToMatchers,
+llvm::StringRef FilePath) const;
+
+  llvm::DenseMap DiagToSection;
+};
+} // namespace
+
+std::unique_ptr
+WarningsSpecialCaseList::create(const llvm::MemoryBuffer &Input,
+std::string &Err) {
+  auto WarningSuppressionList = std::make_unique();
+  if (!WarningSuppressionList->createInternal(&Input, Err))
+return nullptr;
+  return WarningSuppressionList;
+}
+
+void WarningsSpecialCaseList::processSections(DiagnosticsEngine &Diags) {
+  // Drop the default section introduced by special case list, we only support
+  // exact diagnostic group names.
+  // FIXME: We should make this configurable in the parser instead.
+  Sections.erase("*");
+  // Make sure we iterate sections by their line numbers.
+  std::vector *>>
+  LineAndSectionEntry;
+  LineAndSectionEntry.reserve(Sections.size());
+  for (const auto &Entry : Sections) {
+llvm::StringRef DiagName = Entry.first();
+// Each section has a matcher with that section's name, attached to that
+// line.
+const auto &DiagSectionMatcher = Entry.second.SectionMatcher;
+unsigned DiagLine = DiagSectionMatcher->Globs.at(DiagName).second;
+LineAndSectionEntry.emplace_back(DiagLine, &Entry);
+  }
+  llvm::sort(LineAndSectionEntry);
+  static constexpr auto WarningFlavor = clang::diag::Flavor::WarningOrError;
+  for (const auto &[_, SectionEntry] : LineAndSectionEntry) {
+SmallVector GroupDiags;
+llvm::StringRef DiagGroup = SectionEntry->getKey();
+if (Diags.getDiagnosticIDs()->getDiagnosticsInGroup(
+WarningFlavor, DiagGroup, GroupDiags)) {
+  StringRef Suggestion =
+  DiagnosticIDs::getNearestOption(WarningFlavor, DiagGroup);
+  Diags.Report(diag::warn_unknown_diag_option)
+  << static_cast(WarningFlavor) << DiagGroup
+  << !Suggestion.empty() << Suggestion;
+  continue;
+}
+for (diag::kind Diag : GroupDiags)
+  // We're intentionally overwriting any previous mappings here to make 
sure
+  // latest one takes precedence.
+  DiagToSection[Diag] = &SectionEntry->getValue();
+  }
+}
+
+void DiagnosticsEngine::setDiagSuppressionMapping(llvm::MemoryBuffer &Input) {
+  std::string Err;
+  auto WarningSuppressionList = WarningsSpecialCaseList::create(Input, Err);
+  if (!WarningSuppressionList) {
+Report(diag::err_drv_malformed_warning_suppression_mapping)
+<< Input.getBufferIdentifier() << Err;
+return;
+  }
+  WarningSuppressionList->processSections(*this);
+  DiagSuppressionMapping =
+  [WarningSuppressionList(std::move(WarningSuppressionList))](
+  diag::kind DiagId, llvm::StringRef Path) {
+return WarningSuppressionList->isDiagSuppressed(DiagId, Path);
+  };
+}
+
+bool WarningsSpecialCaseList::isDiagSuppressed(diag::kind DiagId,
+   llvm::StringRef FilePath) const 
{
+  const Section *DiagSection = DiagToSection.lookup(DiagId);
+  if (!DiagSection)
+return false;
+  const SectionEntries &EntityTypeToCategories = DiagSection->Entries;
+  auto SrcEntriesIt = EntityTypeToCategories.find("src");
+  if (SrcEntriesIt == EntityTypeToCategories.end())
+return false;
+  const llvm::StringMap &CategoriesToMatchers =
+  SrcEntriesIt->second;

bricknerb wrote:

How about replacing lines 585-589 with:

const llvm::StringMap &CategoriesToMatchers = 
EntityTypeToCategories.lookup("src");

I believe this will just work.

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-07 Thread Boaz Brickner via cfe-commits

https://github.com/bricknerb edited 
https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-07 Thread Boaz Brickner via cfe-commits

https://github.com/bricknerb requested changes to this pull request.

Note that there are a few open comments from previous iterations.

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-07 Thread Boaz Brickner via cfe-commits


@@ -477,6 +486,136 @@ void DiagnosticsEngine::setSeverityForAll(diag::Flavor 
Flavor,
   setSeverity(Diag, Map, Loc);
 }
 
+namespace {
+// FIXME: We should isolate the parser from SpecialCaseList and just use it
+// here.
+class WarningsSpecialCaseList : public llvm::SpecialCaseList {
+public:
+  static std::unique_ptr
+  create(const llvm::MemoryBuffer &Input, std::string &Err);
+
+  // 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);
+
+  bool isDiagSuppressed(diag::kind DiagId, llvm::StringRef FilePath) const;
+
+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(const llvm::StringMap &CategoriesToMatchers,
+llvm::StringRef FilePath) const;
+
+  llvm::DenseMap DiagToSection;
+};
+} // namespace
+
+std::unique_ptr
+WarningsSpecialCaseList::create(const llvm::MemoryBuffer &Input,
+std::string &Err) {
+  auto WarningSuppressionList = std::make_unique();
+  if (!WarningSuppressionList->createInternal(&Input, Err))
+return nullptr;
+  return WarningSuppressionList;
+}
+
+void WarningsSpecialCaseList::processSections(DiagnosticsEngine &Diags) {
+  // Drop the default section introduced by special case list, we only support
+  // exact diagnostic group names.
+  // FIXME: We should make this configurable in the parser instead.
+  Sections.erase("*");
+  // Make sure we iterate sections by their line numbers.
+  std::vector *>>
+  LineAndSectionEntry;
+  LineAndSectionEntry.reserve(Sections.size());
+  for (const auto &Entry : Sections) {
+llvm::StringRef DiagName = Entry.first();
+// Each section has a matcher with that section's name, attached to that
+// line.
+const auto &DiagSectionMatcher = Entry.second.SectionMatcher;
+unsigned DiagLine = DiagSectionMatcher->Globs.at(DiagName).second;
+LineAndSectionEntry.emplace_back(DiagLine, &Entry);
+  }
+  llvm::sort(LineAndSectionEntry);
+  static constexpr auto WarningFlavor = clang::diag::Flavor::WarningOrError;
+  for (const auto &[_, SectionEntry] : LineAndSectionEntry) {
+SmallVector GroupDiags;
+llvm::StringRef DiagGroup = SectionEntry->getKey();
+if (Diags.getDiagnosticIDs()->getDiagnosticsInGroup(
+WarningFlavor, DiagGroup, GroupDiags)) {
+  StringRef Suggestion =
+  DiagnosticIDs::getNearestOption(WarningFlavor, DiagGroup);
+  Diags.Report(diag::warn_unknown_diag_option)
+  << static_cast(WarningFlavor) << DiagGroup
+  << !Suggestion.empty() << Suggestion;
+  continue;
+}
+for (diag::kind Diag : GroupDiags)
+  // We're intentionally overwriting any previous mappings here to make 
sure
+  // latest one takes precedence.
+  DiagToSection[Diag] = &SectionEntry->getValue();
+  }
+}
+
+void DiagnosticsEngine::setDiagSuppressionMapping(llvm::MemoryBuffer &Input) {
+  std::string Err;

bricknerb wrote:

It seems more common to avoid the short name.
```suggestion
  std::string Error;
```

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-07 Thread Boaz Brickner via cfe-commits


@@ -701,11 +702,10 @@ class CompilerInstance : public ModuleLoader {
   /// used by some diagnostics printers (for logging purposes only).
   ///
   /// \return The new object on success, or null on failure.
-  static IntrusiveRefCntPtr
-  createDiagnostics(DiagnosticOptions *Opts,
-DiagnosticConsumer *Client = nullptr,
-bool ShouldOwnClient = true,
-const CodeGenOptions *CodeGenOpts = nullptr);
+  static IntrusiveRefCntPtr createDiagnostics(
+  DiagnosticOptions *Opts, DiagnosticConsumer *Client = nullptr,
+  bool ShouldOwnClient = true, const CodeGenOptions *CodeGenOpts = nullptr,
+  IntrusiveRefCntPtr VFS = nullptr);

bricknerb wrote:

Optional: Given that this already has 5 parameters, consider wrapping the 
params in a struct to make the calls easier.

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-07 Thread Boaz Brickner via cfe-commits

https://github.com/bricknerb edited 
https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-07 Thread Boaz Brickner via cfe-commits

https://github.com/bricknerb edited 
https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-07 Thread Boaz Brickner via cfe-commits


@@ -0,0 +1,90 @@
+
+Warning suppression mappings
+
+
+.. contents::
+   :local:
+
+Introduction
+
+
+Warning suppression mappings enable users to suppress Clang's diagnostics in a
+per-file granular manner. Enabling enforcement of diagnostics in specific parts
+of the project, even if there are violations in some headers.
+
+Goal and usage
+==
+
+Clang allows diagnostics to be configured at a translation-unit granularity.
+If a ``foo.cpp`` is compiled with ``-Wfoo``, all transitively included headers
+also need to be clean. Hence turning on new warnings in large codebases can be
+difficult today. Since it requires cleaning up all the existing warnings,

bricknerb wrote:

RE "today": If this documentation should describe what the feature is and how 
to use, I wouldn't refer to "today" as if we don't have the feature.

Also, instead of "difficult" I would just continue to the next sentence to say 
it requires cleaning ...

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-07 Thread Boaz Brickner via cfe-commits


@@ -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
+  create(const llvm::MemoryBuffer &MB, std::string &Err) {
+auto SCL = std::make_unique();
+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 *>>
+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 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(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 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
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-07 Thread Boaz Brickner via cfe-commits




bricknerb wrote:

Yes, I meant as an integration test.

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-07 Thread Boaz Brickner via cfe-commits


@@ -599,6 +600,19 @@ DiagnosticIDs::getDiagnosticSeverity(unsigned DiagID, 
SourceLocation Loc,
   SM.isInSystemMacro(Loc))
 return diag::Severity::Ignored;
 
+  // Apply suppression mappings if severity wasn't explicitly mapped with a
+  // clang-diagnostics pragma to ensure pragmas always take precedence over

bricknerb wrote:

Carefully reading the comment, it's clear.
We could make it easier perhaps.

// Clang-diagnostics pragmas always take precedence over suppression mapping.

The rest of the information in the comment can be put inside the first if().

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-04 Thread kadir çetinkaya via cfe-commits


@@ -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
+  create(const llvm::MemoryBuffer &MB, std::string &Err) {
+auto SCL = std::make_unique();
+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 *>>
+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 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(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 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) {

kadircet wrote:

> RE: "rest of the speical-case-list actually match entries in line order", 

sorry I think I was confused while writing this one. I was trying to say, rest 
of the SpecialCaseList users can observe which line matched first. Hence 
performing a re-ordering of patterns would result in behavioral changes for 
these users (I can't say if it'll be a regression or not, as I don't fully 
understand their use cases).

> do you mean that the behavior here is significantly different. Would this be 
> confusing?

not sure what you mean here exactly. overall we don't have a different matching 
behavior in a "user-visible" manner. implementation-wise it's slightly 
different than other sanitizer-list matching, as we keep searching for longest 
matching glob, rather than stopping at first match. it's documented as such and 
I think is intuitive for users (that the most specific entry takes precedence).

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-04 Thread kadir çetinkaya via cfe-commits




kadircet wrote:

this is already tested in 
https://github.com/llvm/llvm-project/pull/112517/files#diff-a8d03c054381627f238bdb8f7914daa4abebdae705d313dda026ce36eca0fe2fR327-R332

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-04 Thread kadir çetinkaya via cfe-commits


@@ -477,6 +485,100 @@ void DiagnosticsEngine::setSeverityForAll(diag::Flavor 
Flavor,
   setSeverity(Diag, Map, Loc);
 }
 
+namespace {
+class WarningsSpecialCaseList : public llvm::SpecialCaseList {

kadircet wrote:

also see https://discourse.llvm.org/t/refactoring-llvm-specialcaselist/82931

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-04 Thread kadir çetinkaya via cfe-commits


@@ -70,6 +76,16 @@ void clang::ProcessWarningOptions(DiagnosticsEngine &Diags,
   else
 Diags.setExtensionHandlingBehavior(diag::Severity::Ignored);
 
+  if (!Opts.DiagnosticSuppressionMappingsFile.empty()) {
+if (auto Buf =

kadircet wrote:

sure, i think it's a little bit unfortunate to spend so much effort in a 
variable that's scoped to 2 lines.

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-04 Thread kadir çetinkaya via cfe-commits


@@ -122,7 +122,6 @@ class SpecialCaseList {
 // Returns zero if no match is found.
 unsigned match(StringRef Query) const;
 
-  private:
 StringMap> Globs;

kadircet wrote:

thanks, and I'd like to address these in a follow-up refactoring, sent out 
https://discourse.llvm.org/t/refactoring-llvm-specialcaselist/82931 to see what 
people think about this.

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-04 Thread kadir çetinkaya via cfe-commits


@@ -599,6 +600,19 @@ DiagnosticIDs::getDiagnosticSeverity(unsigned DiagID, 
SourceLocation Loc,
   SM.isInSystemMacro(Loc))
 return diag::Severity::Ignored;
 
+  // Apply suppression mappings if severity wasn't explicitly mapped with a
+  // clang-diagnostics pragma to ensure pragmas always take precedence over

kadircet wrote:

pragma takes precedence and we don't suppress via the mappings. do you think 
the comment needs rewording?

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-04 Thread kadir çetinkaya via cfe-commits


@@ -167,4 +176,159 @@ TEST(DiagnosticTest, storedDiagEmptyWarning) {
   // Make sure an empty warning can round-trip with \c StoredDiagnostic.
   Diags.Report(CaptureConsumer.StoredDiags.front());
 }
+
+class SuppressionMappingTest : public testing::Test {

kadircet wrote:

yes, see 
https://github.com/llvm/llvm-project/pull/112517/files#diff-a8d03c054381627f238bdb8f7914daa4abebdae705d313dda026ce36eca0fe2fR327-R332

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-04 Thread Boaz Brickner via cfe-commits


@@ -0,0 +1,90 @@
+
+Warning suppression mappings
+
+
+.. contents::
+   :local:
+
+Introduction
+
+
+Warning suppression mappings enable users to suppress Clang's diagnostics in a
+per-file granular manner. Enabling enforcement of diagnostics in specific parts
+of the project, even if there are violations in some headers.
+
+Goal and usage
+==
+
+Clang allows diagnostics to be configured at a translation-unit granularity.
+If a ``foo.cpp`` is compiled with ``-Wfoo``, all transitively included headers
+also need to be clean. Hence turning on new warnings in large codebases can be
+difficult today. Since it requires cleaning up all the existing warnings,
+which might not be possible when some dependencies aren't in the project 
owner's
+control or because new violations are creeping up quicker than the clean up.
+
+Warning suppression mappings aim to alleviate some of these concerns by making
+diagnostic configuration granularity finer, at a source file level.
+
+To achieve this, user can create a file that lists which diagnostic groups to
+suppress in which files or paths, and pass it as a command line argument to
+Clang with the ``--warning-suppression-mappings`` flag.
+
+Note that this mechanism won't enable any diagnostics on its own. Users should
+still turn on warnings in their compilations with explicit ``-Wfoo`` flags.
+
+Example
+===
+
+.. code-block:: bash
+
+  $ cat my/user/code.cpp
+  #include 
+  namespace { void unused_func1(); }
+
+  $ cat foo/bar.h
+  namespace { void unused_func2(); }
+
+  $ cat suppression_mappings.txt
+  # Suppress -Wunused warnings in all files, apart from the ones under `foo/`.
+  [unused]
+  src:*
+  src:*foo/*=emit
+  $ clang -Wunused --warning-suppression-mappings=suppression_mappings.txt 
my/user/code.cpp
+  # prints warning: unused function 'unused_func2', but no warnings for 
`unused_func1`.
+
+Format
+==
+
+Warning suppression mappings uses the same format as
+:doc:`SanitizerSpecialCaseList`.
+
+Users can mention sections to describe which diagnostic group behaviours to
+change. Sections are denoted as ``[unused]`` in this format. Each section name
+must match a diagnostic group.
+When a diagnostic is matched by multiple groups, the latest one takes

bricknerb wrote:

After seeing the below text and example, I think it's better to say that a 
diagnostic "belongs to" multiple groups.
When you say "matched", I assumed you mean the diagnostic has matching global 
in multiple group sections.

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-04 Thread Aaron Ballman via cfe-commits


@@ -122,7 +122,6 @@ class SpecialCaseList {
 // Returns zero if no match is found.
 unsigned match(StringRef Query) const;
 
-  private:
 StringMap> Globs;

AaronBallman wrote:

Oh, I missed the inner class context, that makes sense, thanks! It unfortunate 
that we're losing encapsulation with the change, but I can hold my nose.

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-04 Thread Boaz Brickner via cfe-commits


@@ -0,0 +1,90 @@
+
+Warning suppression mappings
+
+
+.. contents::
+   :local:
+
+Introduction
+
+
+Warning suppression mappings enable users to suppress Clang's diagnostics in a
+per-file granular manner. Enabling enforcement of diagnostics in specific parts
+of the project, even if there are violations in some headers.
+
+Goal and usage
+==
+
+Clang allows diagnostics to be configured at a translation-unit granularity.
+If a ``foo.cpp`` is compiled with ``-Wfoo``, all transitively included headers
+also need to be clean. Hence turning on new warnings in large codebases can be
+difficult today. Since it requires cleaning up all the existing warnings,
+which might not be possible when some dependencies aren't in the project 
owner's
+control or because new violations are creeping up quicker than the clean up.
+
+Warning suppression mappings aim to alleviate some of these concerns by making
+diagnostic configuration granularity finer, at a source file level.
+
+To achieve this, user can create a file that lists which diagnostic groups to
+suppress in which files or paths, and pass it as a command line argument to
+Clang with the ``--warning-suppression-mappings`` flag.
+
+Note that this mechanism won't enable any diagnostics on its own. Users should
+still turn on warnings in their compilations with explicit ``-Wfoo`` flags.
+
+Example
+===
+
+.. code-block:: bash
+
+  $ cat my/user/code.cpp
+  #include 
+  namespace { void unused_func1(); }
+
+  $ cat foo/bar.h
+  namespace { void unused_func2(); }
+
+  $ cat suppression_mappings.txt
+  # Suppress -Wunused warnings in all files, apart from the ones under `foo/`.
+  [unused]
+  src:*
+  src:*foo/*=emit
+  $ clang -Wunused --warning-suppression-mappings=suppression_mappings.txt 
my/user/code.cpp
+  # prints warning: unused function 'unused_func2', but no warnings for 
`unused_func1`.
+
+Format
+==
+
+Warning suppression mappings uses the same format as
+:doc:`SanitizerSpecialCaseList`.
+
+Users can mention sections to describe which diagnostic group behaviours to
+change. Sections are denoted as ``[unused]`` in this format. Each section name

bricknerb wrote:

> Sections are denoted as ``[unused]`` in this format.

Isn't "unused" only an example for a diagnostic group?
Did you mean to refer to the specific example?

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-04 Thread Boaz Brickner via cfe-commits




bricknerb wrote:

Consider referring to pragmas.

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-04 Thread Boaz Brickner via cfe-commits


@@ -70,6 +76,16 @@ void clang::ProcessWarningOptions(DiagnosticsEngine &Diags,
   else
 Diags.setExtensionHandlingBehavior(diag::Severity::Ignored);
 
+  if (!Opts.DiagnosticSuppressionMappingsFile.empty()) {
+if (auto Buf =

bricknerb wrote:

[Buf](https://github.com/search?type=code&auto_enroll=true&q=repo%3Allvm%2Fllvm-project+%2Fbuf%5B%5Ef%5D.*%3D.*%5Cn%3F.*getBufferForFile%2F)
 is less common, and more so if we focus on Clang code. IIUC, this is the 
content of the file, FileBuffer or FileContent is more descriptive, and Buffer 
is better than Buf.

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-04 Thread Boaz Brickner via cfe-commits




bricknerb wrote:

Consider also testing the same warning belonging to multiple groups.

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-04 Thread Boaz Brickner via cfe-commits


@@ -0,0 +1,90 @@
+
+Warning suppression mappings
+
+
+.. contents::
+   :local:
+
+Introduction
+
+
+Warning suppression mappings enable users to suppress Clang's diagnostics in a
+per-file granular manner. Enabling enforcement of diagnostics in specific parts
+of the project, even if there are violations in some headers.
+
+Goal and usage
+==
+
+Clang allows diagnostics to be configured at a translation-unit granularity.
+If a ``foo.cpp`` is compiled with ``-Wfoo``, all transitively included headers
+also need to be clean. Hence turning on new warnings in large codebases can be
+difficult today. Since it requires cleaning up all the existing warnings,
+which might not be possible when some dependencies aren't in the project 
owner's
+control or because new violations are creeping up quicker than the clean up.
+
+Warning suppression mappings aim to alleviate some of these concerns by making
+diagnostic configuration granularity finer, at a source file level.
+
+To achieve this, user can create a file that lists which diagnostic groups to
+suppress in which files or paths, and pass it as a command line argument to
+Clang with the ``--warning-suppression-mappings`` flag.
+
+Note that this mechanism won't enable any diagnostics on its own. Users should
+still turn on warnings in their compilations with explicit ``-Wfoo`` flags.
+
+Example
+===
+
+.. code-block:: bash
+
+  $ cat my/user/code.cpp
+  #include 
+  namespace { void unused_func1(); }
+
+  $ cat foo/bar.h
+  namespace { void unused_func2(); }
+
+  $ cat suppression_mappings.txt
+  # Suppress -Wunused warnings in all files, apart from the ones under `foo/`.
+  [unused]
+  src:*
+  src:*foo/*=emit
+  $ clang -Wunused --warning-suppression-mappings=suppression_mappings.txt 
my/user/code.cpp
+  # prints warning: unused function 'unused_func2', but no warnings for 
`unused_func1`.
+
+Format
+==
+
+Warning suppression mappings uses the same format as
+:doc:`SanitizerSpecialCaseList`.
+
+Users can mention sections to describe which diagnostic group behaviours to
+change. Sections are denoted as ``[unused]`` in this format. Each section name
+must match a diagnostic group.
+When a diagnostic is matched by multiple groups, the latest one takes
+precendence.
+
+Afterwards in each section, users can have multiple entities that match source
+files based on the globs. These entities look like ``src:*/my/dir/*``.
+Users can also use the ``emit`` category to exclude a subdirectory from

bricknerb wrote:

"category" here might be confused with a diagnostic category, which is probably 
not what you mean here.

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-04 Thread Boaz Brickner via cfe-commits


@@ -167,4 +176,159 @@ TEST(DiagnosticTest, storedDiagEmptyWarning) {
   // Make sure an empty warning can round-trip with \c StoredDiagnostic.
   Diags.Report(CaptureConsumer.StoredDiags.front());
 }
+
+class SuppressionMappingTest : public testing::Test {

bricknerb wrote:

Do you also test pragmas here?

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-04 Thread Boaz Brickner via cfe-commits


@@ -0,0 +1,90 @@
+
+Warning suppression mappings
+
+
+.. contents::
+   :local:
+
+Introduction
+
+
+Warning suppression mappings enable users to suppress Clang's diagnostics in a
+per-file granular manner. Enabling enforcement of diagnostics in specific parts
+of the project, even if there are violations in some headers.
+
+Goal and usage
+==
+
+Clang allows diagnostics to be configured at a translation-unit granularity.
+If a ``foo.cpp`` is compiled with ``-Wfoo``, all transitively included headers
+also need to be clean. Hence turning on new warnings in large codebases can be
+difficult today. Since it requires cleaning up all the existing warnings,
+which might not be possible when some dependencies aren't in the project 
owner's
+control or because new violations are creeping up quicker than the clean up.
+
+Warning suppression mappings aim to alleviate some of these concerns by making
+diagnostic configuration granularity finer, at a source file level.
+
+To achieve this, user can create a file that lists which diagnostic groups to
+suppress in which files or paths, and pass it as a command line argument to
+Clang with the ``--warning-suppression-mappings`` flag.
+
+Note that this mechanism won't enable any diagnostics on its own. Users should
+still turn on warnings in their compilations with explicit ``-Wfoo`` flags.
+
+Example
+===
+
+.. code-block:: bash
+
+  $ cat my/user/code.cpp
+  #include 
+  namespace { void unused_func1(); }
+
+  $ cat foo/bar.h
+  namespace { void unused_func2(); }
+
+  $ cat suppression_mappings.txt
+  # Suppress -Wunused warnings in all files, apart from the ones under `foo/`.
+  [unused]
+  src:*
+  src:*foo/*=emit
+  $ clang -Wunused --warning-suppression-mappings=suppression_mappings.txt 
my/user/code.cpp
+  # prints warning: unused function 'unused_func2', but no warnings for 
`unused_func1`.
+
+Format
+==
+
+Warning suppression mappings uses the same format as
+:doc:`SanitizerSpecialCaseList`.
+
+Users can mention sections to describe which diagnostic group behaviours to
+change. Sections are denoted as ``[unused]`` in this format. Each section name
+must match a diagnostic group.
+When a diagnostic is matched by multiple groups, the latest one takes
+precendence.
+
+Afterwards in each section, users can have multiple entities that match source
+files based on the globs. These entities look like ``src:*/my/dir/*``.
+Users can also use the ``emit`` category to exclude a subdirectory from
+suppression.
+Source files are matched against these globs either as paths relative to th
+current working directory, or as absolute paths.

bricknerb wrote:

When you say "or" do you we try to match both or do we match in one of these 
methods? I think it's worth to clarify that.

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-04 Thread Boaz Brickner via cfe-commits


@@ -477,6 +486,118 @@ void DiagnosticsEngine::setSeverityForAll(diag::Flavor 
Flavor,
   setSeverity(Diag, Map, Loc);
 }
 
+namespace {
+class WarningsSpecialCaseList : public llvm::SpecialCaseList {
+public:
+  static std::unique_ptr
+  create(const llvm::MemoryBuffer &MB, std::string &Err) {
+auto SCL = std::make_unique();

bricknerb wrote:

Replace SCL with WarningSuppressionList

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-04 Thread Boaz Brickner via cfe-commits


@@ -946,6 +955,27 @@ class DiagnosticsEngine : public 
RefCountedBase {
 return (Level)Diags->getDiagnosticLevel(DiagID, Loc, *this);
   }
 
+  /// Diagnostic suppression mappings can be used to suppress specific
+  /// diagnostics in specific files.
+  /// Mapping file is expected to be a special case list with sections denoting
+  /// diagnostic groups and `src` entries for globs to suppress. `emit` 
category
+  /// can be used to disable suppression. Longest glob that matches a filepath
+  /// takes precendence. For example:

bricknerb wrote:

```suggestion
  /// takes precedence. For example:
```

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-04 Thread Boaz Brickner via cfe-commits


@@ -0,0 +1,90 @@
+
+Warning suppression mappings
+
+
+.. contents::
+   :local:
+
+Introduction
+
+
+Warning suppression mappings enable users to suppress Clang's diagnostics in a
+per-file granular manner. Enabling enforcement of diagnostics in specific parts
+of the project, even if there are violations in some headers.
+
+Goal and usage
+==
+
+Clang allows diagnostics to be configured at a translation-unit granularity.
+If a ``foo.cpp`` is compiled with ``-Wfoo``, all transitively included headers
+also need to be clean. Hence turning on new warnings in large codebases can be
+difficult today. Since it requires cleaning up all the existing warnings,
+which might not be possible when some dependencies aren't in the project 
owner's
+control or because new violations are creeping up quicker than the clean up.
+
+Warning suppression mappings aim to alleviate some of these concerns by making
+diagnostic configuration granularity finer, at a source file level.
+
+To achieve this, user can create a file that lists which diagnostic groups to
+suppress in which files or paths, and pass it as a command line argument to
+Clang with the ``--warning-suppression-mappings`` flag.
+
+Note that this mechanism won't enable any diagnostics on its own. Users should
+still turn on warnings in their compilations with explicit ``-Wfoo`` flags.
+
+Example
+===
+
+.. code-block:: bash
+
+  $ cat my/user/code.cpp
+  #include 
+  namespace { void unused_func1(); }
+
+  $ cat foo/bar.h
+  namespace { void unused_func2(); }
+
+  $ cat suppression_mappings.txt
+  # Suppress -Wunused warnings in all files, apart from the ones under `foo/`.
+  [unused]
+  src:*
+  src:*foo/*=emit
+  $ clang -Wunused --warning-suppression-mappings=suppression_mappings.txt 
my/user/code.cpp
+  # prints warning: unused function 'unused_func2', but no warnings for 
`unused_func1`.
+
+Format
+==
+
+Warning suppression mappings uses the same format as
+:doc:`SanitizerSpecialCaseList`.
+
+Users can mention sections to describe which diagnostic group behaviours to
+change. Sections are denoted as ``[unused]`` in this format. Each section name
+must match a diagnostic group.
+When a diagnostic is matched by multiple groups, the latest one takes
+precendence.
+
+Afterwards in each section, users can have multiple entities that match source
+files based on the globs. These entities look like ``src:*/my/dir/*``.
+Users can also use the ``emit`` category to exclude a subdirectory from
+suppression.
+Source files are matched against these globs either as paths relative to th
+current working directory, or as absolute paths.
+When a source file matches multiple globs, the longest one takes precendence.

bricknerb wrote:

What happens if a warning that belongs to multiple diagnostic groups is in a 
source file that matches multiple globs in multiple diagnostic groups?
Would it be the longest glob in the last group or the longest glob in all 
categories?
I think it's clear from the example that the warning would only trigger the 
section of the latest group, but it might worth clarifying.

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-04 Thread Boaz Brickner via cfe-commits


@@ -0,0 +1,90 @@
+
+Warning suppression mappings
+
+
+.. contents::
+   :local:
+
+Introduction
+
+
+Warning suppression mappings enable users to suppress Clang's diagnostics in a
+per-file granular manner. Enabling enforcement of diagnostics in specific parts
+of the project, even if there are violations in some headers.
+
+Goal and usage
+==
+
+Clang allows diagnostics to be configured at a translation-unit granularity.
+If a ``foo.cpp`` is compiled with ``-Wfoo``, all transitively included headers
+also need to be clean. Hence turning on new warnings in large codebases can be
+difficult today. Since it requires cleaning up all the existing warnings,
+which might not be possible when some dependencies aren't in the project 
owner's
+control or because new violations are creeping up quicker than the clean up.
+
+Warning suppression mappings aim to alleviate some of these concerns by making
+diagnostic configuration granularity finer, at a source file level.
+
+To achieve this, user can create a file that lists which diagnostic groups to
+suppress in which files or paths, and pass it as a command line argument to
+Clang with the ``--warning-suppression-mappings`` flag.
+
+Note that this mechanism won't enable any diagnostics on its own. Users should
+still turn on warnings in their compilations with explicit ``-Wfoo`` flags.
+
+Example
+===
+
+.. code-block:: bash
+
+  $ cat my/user/code.cpp
+  #include 
+  namespace { void unused_func1(); }
+
+  $ cat foo/bar.h
+  namespace { void unused_func2(); }
+
+  $ cat suppression_mappings.txt
+  # Suppress -Wunused warnings in all files, apart from the ones under `foo/`.
+  [unused]
+  src:*
+  src:*foo/*=emit
+  $ clang -Wunused --warning-suppression-mappings=suppression_mappings.txt 
my/user/code.cpp
+  # prints warning: unused function 'unused_func2', but no warnings for 
`unused_func1`.
+
+Format
+==
+
+Warning suppression mappings uses the same format as
+:doc:`SanitizerSpecialCaseList`.
+
+Users can mention sections to describe which diagnostic group behaviours to
+change. Sections are denoted as ``[unused]`` in this format. Each section name
+must match a diagnostic group.
+When a diagnostic is matched by multiple groups, the latest one takes
+precendence.

bricknerb wrote:

precedence

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-04 Thread Boaz Brickner via cfe-commits


@@ -0,0 +1,90 @@
+
+Warning suppression mappings
+
+
+.. contents::
+   :local:
+
+Introduction
+
+
+Warning suppression mappings enable users to suppress Clang's diagnostics in a
+per-file granular manner. Enabling enforcement of diagnostics in specific parts
+of the project, even if there are violations in some headers.
+
+Goal and usage
+==
+
+Clang allows diagnostics to be configured at a translation-unit granularity.
+If a ``foo.cpp`` is compiled with ``-Wfoo``, all transitively included headers
+also need to be clean. Hence turning on new warnings in large codebases can be
+difficult today. Since it requires cleaning up all the existing warnings,
+which might not be possible when some dependencies aren't in the project 
owner's
+control or because new violations are creeping up quicker than the clean up.
+
+Warning suppression mappings aim to alleviate some of these concerns by making
+diagnostic configuration granularity finer, at a source file level.
+
+To achieve this, user can create a file that lists which diagnostic groups to

bricknerb wrote:

Perhaps refer somewhere to what is a "diagnostic group" so a user can easily 
what diagnostic groups are available?

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-04 Thread Boaz Brickner via cfe-commits


@@ -477,6 +486,118 @@ void DiagnosticsEngine::setSeverityForAll(diag::Flavor 
Flavor,
   setSeverity(Diag, Map, Loc);
 }
 
+namespace {
+class WarningsSpecialCaseList : public llvm::SpecialCaseList {
+public:
+  static std::unique_ptr
+  create(const llvm::MemoryBuffer &MB, std::string &Err) {

bricknerb wrote:

Avoid putting the implementation in an anonymous namespace.
Per https://llvm.org/docs/CodingStandards.html#anonymous-namespaces, we should 
only put the class declaration into anonymous namespace.

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-04 Thread Boaz Brickner via cfe-commits


@@ -0,0 +1,90 @@
+
+Warning suppression mappings
+
+
+.. contents::
+   :local:
+
+Introduction
+
+
+Warning suppression mappings enable users to suppress Clang's diagnostics in a
+per-file granular manner. Enabling enforcement of diagnostics in specific parts
+of the project, even if there are violations in some headers.
+
+Goal and usage
+==
+
+Clang allows diagnostics to be configured at a translation-unit granularity.
+If a ``foo.cpp`` is compiled with ``-Wfoo``, all transitively included headers
+also need to be clean. Hence turning on new warnings in large codebases can be
+difficult today. Since it requires cleaning up all the existing warnings,
+which might not be possible when some dependencies aren't in the project 
owner's
+control or because new violations are creeping up quicker than the clean up.
+
+Warning suppression mappings aim to alleviate some of these concerns by making
+diagnostic configuration granularity finer, at a source file level.
+
+To achieve this, user can create a file that lists which diagnostic groups to
+suppress in which files or paths, and pass it as a command line argument to
+Clang with the ``--warning-suppression-mappings`` flag.
+
+Note that this mechanism won't enable any diagnostics on its own. Users should
+still turn on warnings in their compilations with explicit ``-Wfoo`` flags.
+
+Example
+===
+
+.. code-block:: bash
+
+  $ cat my/user/code.cpp
+  #include 
+  namespace { void unused_func1(); }
+
+  $ cat foo/bar.h
+  namespace { void unused_func2(); }
+
+  $ cat suppression_mappings.txt
+  # Suppress -Wunused warnings in all files, apart from the ones under `foo/`.
+  [unused]
+  src:*
+  src:*foo/*=emit
+  $ clang -Wunused --warning-suppression-mappings=suppression_mappings.txt 
my/user/code.cpp
+  # prints warning: unused function 'unused_func2', but no warnings for 
`unused_func1`.
+
+Format
+==
+
+Warning suppression mappings uses the same format as
+:doc:`SanitizerSpecialCaseList`.
+
+Users can mention sections to describe which diagnostic group behaviours to
+change. Sections are denoted as ``[unused]`` in this format. Each section name
+must match a diagnostic group.
+When a diagnostic is matched by multiple groups, the latest one takes
+precendence.
+
+Afterwards in each section, users can have multiple entities that match source
+files based on the globs. These entities look like ``src:*/my/dir/*``.
+Users can also use the ``emit`` category to exclude a subdirectory from
+suppression.
+Source files are matched against these globs either as paths relative to th

bricknerb wrote:

"th" typo.
```suggestion
Source files are matched against these globs either as paths relative to the
```

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-04 Thread Boaz Brickner via cfe-commits




bricknerb wrote:

Given that Test documentation build check failed, it's worth clarifying how the 
documentation was tested and perhaps add a few screenshots.

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-04 Thread kadir çetinkaya via cfe-commits

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

From 20f1c1fea3466de38a04b5486cb05d95dbe3b96c Mon Sep 17 00:00:00 2001
From: Kadir Cetinkaya 
Date: Mon, 14 Oct 2024 11:20:55 +0200
Subject: [PATCH 1/7] [clang] Introduce diagnostics suppression mappings

This implements
https://discourse.llvm.org/t/rfc-add-support-for-controlling-diagnostics-severities-at-file-level-granularity-through-command-line/81292.

Users now can suppress warnings for certain headers by providing a
mapping with globs, a sample file looks like:
```
[unused]
src:*
src:*clang/*=emit
```

This will suppress warnings from `-Wunused` group in all files that
aren't under `clang/` directory. This mapping file can be passed to
clang via `--warning-suppression-mappings=foo.txt`.

At a high level, mapping file is stored in DiagnosticOptions and then
processed with rest of the warning flags when creating a
DiagnosticsEngine. This is a functor that uses SpecialCaseLists
underneath to match against globs coming from the mappings file.

This implies processing warning options now performs IO, relevant
interfaces are updated to take in a VFS, falling back to RealFileSystem
when one is not available.
---
 .../ExpandModularHeadersPPCallbacks.cpp   |   3 +-
 clang/include/clang/Basic/Diagnostic.h|  15 +-
 .../clang/Basic/DiagnosticDriverKinds.td  |   3 +
 clang/include/clang/Basic/DiagnosticOptions.h |   3 +
 clang/include/clang/Driver/Options.td |   5 +
 .../include/clang/Frontend/CompilerInstance.h |  10 +-
 clang/lib/Basic/Diagnostic.cpp|  12 ++
 clang/lib/Basic/DiagnosticIDs.cpp |  12 ++
 clang/lib/Basic/Warnings.cpp  | 107 
 clang/lib/Driver/ToolChains/Clang.cpp |   7 +
 clang/lib/Frontend/ASTUnit.cpp|  15 +-
 clang/lib/Frontend/CompilerInstance.cpp   |  21 ++-
 clang/lib/Frontend/CompilerInvocation.cpp |   8 +
 clang/lib/Frontend/PrecompiledPreamble.cpp|   2 +-
 clang/lib/Interpreter/CodeCompletion.cpp  |   4 +-
 clang/lib/Serialization/ASTReader.cpp |   4 +-
 .../test/Misc/Inputs/suppression-mapping.txt  |   4 +
 .../Misc/warning-suppression-mappings.cpp |  16 ++
 clang/tools/driver/cc1gen_reproducer_main.cpp |   7 +-
 clang/tools/driver/driver.cpp |   7 +-
 clang/unittests/Basic/DiagnosticTest.cpp  | 164 ++
 .../Frontend/CompilerInvocationTest.cpp   |  10 ++
 llvm/include/llvm/Support/SpecialCaseList.h   |   1 -
 23 files changed, 409 insertions(+), 31 deletions(-)
 create mode 100644 clang/test/Misc/Inputs/suppression-mapping.txt
 create mode 100644 clang/test/Misc/warning-suppression-mappings.cpp

diff --git a/clang-tools-extra/clang-tidy/ExpandModularHeadersPPCallbacks.cpp 
b/clang-tools-extra/clang-tidy/ExpandModularHeadersPPCallbacks.cpp
index fef086c5a99d86..4c34f9ea122d9e 100644
--- a/clang-tools-extra/clang-tidy/ExpandModularHeadersPPCallbacks.cpp
+++ b/clang-tools-extra/clang-tidy/ExpandModularHeadersPPCallbacks.cpp
@@ -81,7 +81,8 @@ 
ExpandModularHeadersPPCallbacks::ExpandModularHeadersPPCallbacks(
   Diags.setSourceManager(&Sources);
   // FIXME: Investigate whatever is there better way to initialize DiagEngine
   // or whatever DiagEngine can be shared by multiple preprocessors
-  ProcessWarningOptions(Diags, Compiler.getDiagnosticOpts());
+  ProcessWarningOptions(Diags, Compiler.getDiagnosticOpts(),
+Compiler.getVirtualFileSystem());
 
   LangOpts.Modules = false;
 
diff --git a/clang/include/clang/Basic/Diagnostic.h 
b/clang/include/clang/Basic/Diagnostic.h
index 3b1efdb12824c7..79b7545e92d6da 100644
--- a/clang/include/clang/Basic/Diagnostic.h
+++ b/clang/include/clang/Basic/Diagnostic.h
@@ -20,11 +20,13 @@
 #include "clang/Basic/Specifiers.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/FunctionExtras.h"
 #include "llvm/ADT/IntrusiveRefCntPtr.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/ADT/iterator_range.h"
 #include "llvm/Support/Compiler.h"
+#include "llvm/Support/VirtualFileSystem.h"
 #include 
 #include 
 #include 
@@ -555,6 +557,10 @@ class DiagnosticsEngine : public 
RefCountedBase {
   void *ArgToStringCookie = nullptr;
   ArgToStringFnTy ArgToStringFn;
 
+  /// Whether the diagnostic should be suppressed in FilePath.
+  llvm::unique_function
+  DiagSuppressionMapping;
+
 public:
   explicit DiagnosticsEngine(IntrusiveRefCntPtr Diags,
  IntrusiveRefCntPtr DiagOpts,
@@ -946,6 +952,13 @@ class DiagnosticsEngine : public 
RefCountedBase {
 return (Level)Diags->getDiagnosticLevel(DiagID, Loc, *this);
   }
 
+  /// Diagnostic suppression mappings can be used to ignore diagnostics based 
on
+  /// the file they occur in.
+  /// These take presumed locations into account, and can still be overriden by
+  /// clang-diagnostics pragmas.
+  void setDiagSuppressionMapping(decltype(DiagS

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

2024-11-04 Thread kadir çetinkaya via cfe-commits


@@ -122,7 +122,6 @@ class SpecialCaseList {
 // Returns zero if no match is found.
 unsigned match(StringRef Query) const;
 
-  private:
 StringMap> Globs;

kadircet wrote:

these are members of `SpecialCaseList::Matcher`. `WarningsSpecialCaseList` is 
just inheriting from `SpecialCaseList`. Hence it won't have visibility into 
protected members of a nested class.

e.g. something like:
```cpp
$ cat a.cc
struct Foo {
  class Bar {
   protected:
int x;
  };
};

struct Derived : public Foo {
  void x() {
Bar b;
b.x = 3; // visibility error
  }
};
```

`Derived` can't access `Foo::Bar::x`.

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-04 Thread Aaron Ballman via cfe-commits


@@ -122,7 +122,6 @@ class SpecialCaseList {
 // Returns zero if no match is found.
 unsigned match(StringRef Query) const;
 
-  private:
 StringMap> Globs;

AaronBallman wrote:

The only places I see it used are within `class WarningsSpecialCaseList : 
public llvm::SpecialCaseList` -- what am I missing?

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2024-11-04 Thread Aaron Ballman via cfe-commits


@@ -1315,6 +1319,32 @@ with its corresponding `Wno-` option.
 Note that when combined with :option:`-w` (which disables all warnings),
 disabling all warnings wins.
 
+.. _warning_suppression_mappings:
+
+Controlling Diagnostics via Suppression Mappings
+
+
+Warning suppression mappings enable users to suppress Clang's diagnostics in a
+per-file granular manner. Enabling enforcement of diagnostics in specific parts
+of the project, even if there are violations in some headers.
+
+.. code-block:: console
+
+  $ cat mappings.txt
+  [unused]
+  src:foo/*
+
+  $ clang --warning-suppression-mappings=mapping.txt -Wunused foo/bar.cc
+  # This compilation won't emit any unused findings for sources under foo/
+  # directory. But it'll still complain for all the other sources, e.g:
+  $ cat foo/bar.cc
+  #include "dir/include.h" // clang flags unused declarations here.
+  #include "foo/include.h" // but unused warnings under this source is omitted.
+
+

AaronBallman wrote:

```suggestion
  $ cat foo/bar.cc
  #include "dir/include.h" // Clang flags unused declarations here
  #include "foo/include.h" // but unused warnings from this header are omitted
  #include "next_to_bar_cc.h" // as are unused warnings from this header file.
  // Further, unused warnings in the remainder of bar.cc are also omitted.


```

https://github.com/llvm/llvm-project/pull/112517
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


  1   2   3   >