Skip to content

Commit 41e3919

Browse files
authored
[clang] Introduce diagnostics suppression mappings (#112517)
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.
1 parent e385e0d commit 41e3919

28 files changed

+641
-33
lines changed

clang-tools-extra/clang-tidy/ExpandModularHeadersPPCallbacks.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,8 @@ ExpandModularHeadersPPCallbacks::ExpandModularHeadersPPCallbacks(
8181
Diags.setSourceManager(&Sources);
8282
// FIXME: Investigate whatever is there better way to initialize DiagEngine
8383
// or whatever DiagEngine can be shared by multiple preprocessors
84-
ProcessWarningOptions(Diags, Compiler.getDiagnosticOpts());
84+
ProcessWarningOptions(Diags, Compiler.getDiagnosticOpts(),
85+
Compiler.getVirtualFileSystem());
8586

8687
LangOpts.Modules = false;
8788

clang/docs/ReleaseNotes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -869,6 +869,9 @@ New features
869869
attribute, the compiler can generate warnings about the use of any language features, or calls to
870870
other functions, which may block.
871871

872+
- Introduced ``-warning-suppression-mappings`` flag to control diagnostic
873+
suppressions per file. See `documentation <https://clang.llvm.org/docs/WarningSuppressionMappings.html>_` for details.
874+
872875
Crash and bug fixes
873876
^^^^^^^^^^^^^^^^^^^
874877

clang/docs/UsersManual.rst

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,10 @@ Options to Control Error and Warning Messages
151151
instantiation backtrace for a single warning or error. The default is 10, and
152152
the limit can be disabled with `-ftemplate-backtrace-limit=0`.
153153

154+
.. option:: --warning-suppression-mappings=foo.txt
155+
156+
:ref:`Suppress certain diagnostics for certain files. <warning_suppression_mappings>`
157+
154158
.. _cl_diag_formatting:
155159

156160
Formatting of Diagnostics
@@ -1315,6 +1319,34 @@ with its corresponding `Wno-` option.
13151319
Note that when combined with :option:`-w` (which disables all warnings),
13161320
disabling all warnings wins.
13171321

1322+
.. _warning_suppression_mappings:
1323+
1324+
Controlling Diagnostics via Suppression Mappings
1325+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1326+
1327+
Warning suppression mappings enable users to suppress Clang's diagnostics in a
1328+
per-file granular manner. Enabling enforcement of diagnostics in specific parts
1329+
of the project, even if there are violations in some headers.
1330+
1331+
.. code-block:: console
1332+
1333+
$ cat mappings.txt
1334+
[unused]
1335+
src:foo/*
1336+
1337+
$ clang --warning-suppression-mappings=mapping.txt -Wunused foo/bar.cc
1338+
# This compilation won't emit any unused findings for sources under foo/
1339+
# directory. But it'll still complain for all the other sources, e.g:
1340+
$ cat foo/bar.cc
1341+
#include "dir/include.h" // Clang flags unused declarations here.
1342+
#include "foo/include.h" // but unused warnings under this source is omitted.
1343+
#include "next_to_bar_cc.h" // as are unused warnings from this header file.
1344+
// Further, unused warnings in the remainder of bar.cc are also omitted.
1345+
1346+
1347+
See :doc:`WarningSuppressionMappings` for details about the file format and
1348+
functionality.
1349+
13181350
Controlling Static Analyzer Diagnostics
13191351
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
13201352

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
============================
2+
Warning suppression mappings
3+
============================
4+
5+
.. contents::
6+
:local:
7+
8+
Introduction
9+
============
10+
11+
Warning suppression mappings enable users to suppress Clang's diagnostics in a
12+
per-file granular manner. Enabling enforcement of diagnostics in specific parts
13+
of the project, even if there are violations in some headers.
14+
15+
Goal and usage
16+
==============
17+
18+
Clang allows diagnostics to be configured at a translation-unit granularity.
19+
If a ``foo.cpp`` is compiled with ``-Wfoo``, all transitively included headers
20+
also need to be clean. Hence turning on new warnings in large codebases requires
21+
cleaning up all the existing warnings. This might not be possible when some
22+
dependencies aren't in the project owner's control or because new violations are
23+
creeping up quicker than the clean up.
24+
25+
Warning suppression mappings aim to alleviate some of these concerns by making
26+
diagnostic configuration granularity finer, at a source file level.
27+
28+
To achieve this, user can create a file that lists which :doc:`diagnostic
29+
groups <DiagnosticsReference>` to suppress in which files or paths, and pass it
30+
as a command line argument to Clang with the ``--warning-suppression-mappings``
31+
flag.
32+
33+
Note that this mechanism won't enable any diagnostics on its own. Users should
34+
still turn on warnings in their compilations with explicit ``-Wfoo`` flags.
35+
`Controlling diagnostics pragmas
36+
<https://clang.llvm.org/docs/UsersManual.html#controlling-diagnostics-via-pragmas>`_
37+
take precedence over suppression mappings. Ensuring code author's explicit
38+
intent is always preserved.
39+
40+
Example
41+
=======
42+
43+
.. code-block:: bash
44+
45+
$ cat my/user/code.cpp
46+
#include <foo/bar.h>
47+
namespace { void unused_func1(); }
48+
49+
$ cat foo/bar.h
50+
namespace { void unused_func2(); }
51+
52+
$ cat suppression_mappings.txt
53+
# Suppress -Wunused warnings in all files, apart from the ones under `foo/`.
54+
[unused]
55+
src:*
56+
src:*foo/*=emit
57+
$ clang -Wunused --warning-suppression-mappings=suppression_mappings.txt my/user/code.cpp
58+
# prints warning: unused function 'unused_func2', but no warnings for `unused_func1`.
59+
60+
Format
61+
======
62+
63+
Warning suppression mappings uses the same format as
64+
:doc:`SanitizerSpecialCaseList`.
65+
66+
Sections describe which diagnostic group's behaviour to change, e.g.
67+
``[unused]``. When a diagnostic is matched by multiple sections, the latest
68+
section takes precedence.
69+
70+
Afterwards in each section, users can have multiple entities that match source
71+
files based on the globs. These entities look like ``src:*/my/dir/*``.
72+
Users can also use the ``emit`` category to exclude a subdirectory from
73+
suppression.
74+
Source files are matched against these globs either:
75+
76+
- as paths relative to the current working directory
77+
- as absolute paths.
78+
79+
When a source file matches multiple globs in a section, the longest one takes
80+
precedence.
81+
82+
.. code-block:: bash
83+
84+
# Lines starting with # are ignored.
85+
# Configure suppression globs for `-Wunused` warnings
86+
[unused]
87+
# Suppress on all files by default.
88+
src:*
89+
# But enforce for all the sources under foo/.
90+
src:*foo/*=emit
91+
92+
# unused-function warnings are a subgroup of `-Wunused`. So this section
93+
# takes precedence over the previous one for unused-function warnings, but
94+
# not for unused-variable warnings.
95+
[unused-function]
96+
# Only suppress for sources under bar/.
97+
src:*bar/*

clang/docs/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ Using Clang as a Compiler
2222
ClangCommandLineReference
2323
AttributeReference
2424
DiagnosticsReference
25+
WarningSuppressionMappings
2526
CrossCompilation
2627
ClangStaticAnalyzer
2728
ThreadSafetyAnalysis

clang/include/clang/Basic/Diagnostic.h

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "clang/Basic/Specifiers.h"
2121
#include "llvm/ADT/ArrayRef.h"
2222
#include "llvm/ADT/DenseMap.h"
23+
#include "llvm/ADT/FunctionExtras.h"
2324
#include "llvm/ADT/IntrusiveRefCntPtr.h"
2425
#include "llvm/ADT/SmallVector.h"
2526
#include "llvm/ADT/StringRef.h"
@@ -40,6 +41,10 @@
4041
namespace llvm {
4142
class Error;
4243
class raw_ostream;
44+
class MemoryBuffer;
45+
namespace vfs {
46+
class FileSystem;
47+
} // namespace vfs
4348
} // namespace llvm
4449

4550
namespace clang {
@@ -555,6 +560,10 @@ class DiagnosticsEngine : public RefCountedBase<DiagnosticsEngine> {
555560
void *ArgToStringCookie = nullptr;
556561
ArgToStringFnTy ArgToStringFn;
557562

563+
/// Whether the diagnostic should be suppressed in FilePath.
564+
llvm::unique_function<bool(diag::kind, llvm::StringRef /*FilePath*/) const>
565+
DiagSuppressionMapping;
566+
558567
public:
559568
explicit DiagnosticsEngine(IntrusiveRefCntPtr<DiagnosticIDs> Diags,
560569
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts,
@@ -946,6 +955,27 @@ class DiagnosticsEngine : public RefCountedBase<DiagnosticsEngine> {
946955
return (Level)Diags->getDiagnosticLevel(DiagID, Loc, *this);
947956
}
948957

958+
/// Diagnostic suppression mappings can be used to suppress specific
959+
/// diagnostics in specific files.
960+
/// Mapping file is expected to be a special case list with sections denoting
961+
/// diagnostic groups and `src` entries for globs to suppress. `emit` category
962+
/// can be used to disable suppression. Longest glob that matches a filepath
963+
/// takes precedence. For example:
964+
/// [unused]
965+
/// src:clang/*
966+
/// src:clang/foo/*=emit
967+
/// src:clang/foo/bar/*
968+
///
969+
/// Such a mappings file suppress all diagnostics produced by -Wunused in all
970+
/// sources under `clang/` directory apart from `clang/foo/`. Diagnostics
971+
/// under `clang/foo/bar/` will also be suppressed. Note that the FilePath is
972+
/// matched against the globs as-is.
973+
/// These take presumed locations into account, and can still be overriden by
974+
/// clang-diagnostics pragmas.
975+
void setDiagSuppressionMapping(llvm::MemoryBuffer &Input);
976+
bool isSuppressedViaMapping(diag::kind DiagId,
977+
llvm::StringRef FilePath) const;
978+
949979
/// Issue the message to the client.
950980
///
951981
/// This actually returns an instance of DiagnosticBuilder which emits the
@@ -1759,7 +1789,7 @@ const char ToggleHighlight = 127;
17591789
/// warning options specified on the command line.
17601790
void ProcessWarningOptions(DiagnosticsEngine &Diags,
17611791
const DiagnosticOptions &Opts,
1762-
bool ReportDiags = true);
1792+
llvm::vfs::FileSystem &VFS, bool ReportDiags = true);
17631793
void EscapeStringForDiagnostic(StringRef Str, SmallVectorImpl<char> &OutStr);
17641794
} // namespace clang
17651795

clang/include/clang/Basic/DiagnosticDriverKinds.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -834,4 +834,7 @@ def err_drv_triple_version_invalid : Error<
834834

835835
def warn_missing_include_dirs : Warning<
836836
"no such include directory: '%0'">, InGroup<MissingIncludeDirs>, DefaultIgnore;
837+
838+
def err_drv_malformed_warning_suppression_mapping : Error<
839+
"failed to process suppression mapping file '%0': %1">;
837840
}

clang/include/clang/Basic/DiagnosticOptions.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,9 @@ class DiagnosticOptions : public RefCountedBase<DiagnosticOptions>{
108108
/// The file to serialize diagnostics to (non-appending).
109109
std::string DiagnosticSerializationFile;
110110

111+
/// Path for the file that defines diagnostic suppression mappings.
112+
std::string DiagnosticSuppressionMappingsFile;
113+
111114
/// The list of -W... options used to alter the diagnostic mappings, with the
112115
/// prefixes removed.
113116
std::vector<std::string> Warnings;

clang/include/clang/Driver/Options.td

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9019,3 +9019,8 @@ def wasm_opt : Flag<["--"], "wasm-opt">,
90199019
Group<m_Group>,
90209020
HelpText<"Enable the wasm-opt optimizer (default)">,
90219021
MarshallingInfoNegativeFlag<LangOpts<"NoWasmOpt">>;
9022+
9023+
def warning_suppression_mappings_EQ : Joined<["--"],
9024+
"warning-suppression-mappings=">, Group<Diag_Group>,
9025+
HelpText<"File containing diagnostic suppresion mappings. See user manual "
9026+
"for file format.">, Visibility<[ClangOption, CC1Option]>;

clang/include/clang/Frontend/CompilerInstance.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "llvm/ADT/StringRef.h"
2525
#include "llvm/Support/BuryPointer.h"
2626
#include "llvm/Support/FileSystem.h"
27+
#include "llvm/Support/VirtualFileSystem.h"
2728
#include <cassert>
2829
#include <list>
2930
#include <memory>
@@ -701,11 +702,10 @@ class CompilerInstance : public ModuleLoader {
701702
/// used by some diagnostics printers (for logging purposes only).
702703
///
703704
/// \return The new object on success, or null on failure.
704-
static IntrusiveRefCntPtr<DiagnosticsEngine>
705-
createDiagnostics(DiagnosticOptions *Opts,
706-
DiagnosticConsumer *Client = nullptr,
707-
bool ShouldOwnClient = true,
708-
const CodeGenOptions *CodeGenOpts = nullptr);
705+
static IntrusiveRefCntPtr<DiagnosticsEngine> createDiagnostics(
706+
DiagnosticOptions *Opts, DiagnosticConsumer *Client = nullptr,
707+
bool ShouldOwnClient = true, const CodeGenOptions *CodeGenOpts = nullptr,
708+
IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS = nullptr);
709709

710710
/// Create the file manager and replace any existing one with it.
711711
///

0 commit comments

Comments
 (0)