[clang] [clang-tools-extra] [llvm] [clang] Introduce diagnostics suppression mappings (PR #112517)
kadir çetinkaya via cfe-commits
cfe-commits at lists.llvm.org
Mon Nov 4 06:08:50 PST 2024
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 <kadircet at google.com>
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 <cassert>
#include <cstdint>
#include <limits>
@@ -555,6 +557,10 @@ class DiagnosticsEngine : public RefCountedBase<DiagnosticsEngine> {
void *ArgToStringCookie = nullptr;
ArgToStringFnTy ArgToStringFn;
+ /// Whether the diagnostic should be suppressed in FilePath.
+ llvm::unique_function<bool(diag::kind, llvm::StringRef /*FilePath*/) const>
+ DiagSuppressionMapping;
+
public:
explicit DiagnosticsEngine(IntrusiveRefCntPtr<DiagnosticIDs> Diags,
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts,
@@ -946,6 +952,13 @@ class DiagnosticsEngine : public RefCountedBase<DiagnosticsEngine> {
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(DiagSuppressionMapping) Mapping);
+ bool isSuppressedViaMapping(diag::kind D, llvm::StringRef FilePath) const;
+
/// Issue the message to the client.
///
/// This actually returns an instance of DiagnosticBuilder which emits the
@@ -1759,7 +1772,7 @@ const char ToggleHighlight = 127;
/// warning options specified on the command line.
void ProcessWarningOptions(DiagnosticsEngine &Diags,
const DiagnosticOptions &Opts,
- bool ReportDiags = true);
+ llvm::vfs::FileSystem &VFS, bool ReportDiags = true);
void EscapeStringForDiagnostic(StringRef Str, SmallVectorImpl<char> &OutStr);
} // namespace clang
diff --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td b/clang/include/clang/Basic/DiagnosticDriverKinds.td
index cdfdaa01fb121d..26743c10c55779 100644
--- a/clang/include/clang/Basic/DiagnosticDriverKinds.td
+++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td
@@ -834,4 +834,7 @@ def err_drv_triple_version_invalid : Error<
def warn_missing_include_dirs : Warning<
"no such include directory: '%0'">, InGroup<MissingIncludeDirs>, DefaultIgnore;
+
+def err_drv_malformed_warning_suppression_mapping : Error<
+ "failed to process suppression mapping file '%0' : %1">;
}
diff --git a/clang/include/clang/Basic/DiagnosticOptions.h b/clang/include/clang/Basic/DiagnosticOptions.h
index 30141c2b8f4475..0770805624cec8 100644
--- a/clang/include/clang/Basic/DiagnosticOptions.h
+++ b/clang/include/clang/Basic/DiagnosticOptions.h
@@ -108,6 +108,9 @@ class DiagnosticOptions : public RefCountedBase<DiagnosticOptions>{
/// The file to serialize diagnostics to (non-appending).
std::string DiagnosticSerializationFile;
+ /// File that defines suppression mappings.
+ std::string SuppressionMappingsFile;
+
/// The list of -W... options used to alter the diagnostic mappings, with the
/// prefixes removed.
std::vector<std::string> Warnings;
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 805b79491e6ea4..83a7bb4d2e09d6 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -9017,3 +9017,8 @@ def wasm_opt : Flag<["--"], "wasm-opt">,
Group<m_Group>,
HelpText<"Enable the wasm-opt optimizer (default)">,
MarshallingInfoNegativeFlag<LangOpts<"NoWasmOpt">>;
+
+def warning_suppression_mappings_EQ : Joined<["--"],
+ "warning-suppression-mappings=">, Group<Diag_Group>,
+ HelpText<"File containing list of sources to suppress diagnostics for. See XXX for format">,
+ Visibility<[ClangOption, CC1Option]>;
diff --git a/clang/include/clang/Frontend/CompilerInstance.h b/clang/include/clang/Frontend/CompilerInstance.h
index 3464654284f199..338eb3c6bd028d 100644
--- a/clang/include/clang/Frontend/CompilerInstance.h
+++ b/clang/include/clang/Frontend/CompilerInstance.h
@@ -24,6 +24,7 @@
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/BuryPointer.h"
#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/VirtualFileSystem.h"
#include <cassert>
#include <list>
#include <memory>
@@ -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<DiagnosticsEngine>
- createDiagnostics(DiagnosticOptions *Opts,
- DiagnosticConsumer *Client = nullptr,
- bool ShouldOwnClient = true,
- const CodeGenOptions *CodeGenOpts = nullptr);
+ static IntrusiveRefCntPtr<DiagnosticsEngine> createDiagnostics(
+ DiagnosticOptions *Opts, DiagnosticConsumer *Client = nullptr,
+ bool ShouldOwnClient = true, const CodeGenOptions *CodeGenOpts = nullptr,
+ IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS = nullptr);
/// Create the file manager and replace any existing one with it.
///
diff --git a/clang/lib/Basic/Diagnostic.cpp b/clang/lib/Basic/Diagnostic.cpp
index e23362fc7af00d..e536effd25072f 100644
--- a/clang/lib/Basic/Diagnostic.cpp
+++ b/clang/lib/Basic/Diagnostic.cpp
@@ -21,6 +21,7 @@
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/Specifiers.h"
#include "clang/Basic/TokenKinds.h"
+#include "llvm/ADT/IntrusiveRefCntPtr.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringExtras.h"
@@ -28,6 +29,7 @@
#include "llvm/Support/ConvertUTF.h"
#include "llvm/Support/CrashRecoveryContext.h"
#include "llvm/Support/Unicode.h"
+#include "llvm/Support/VirtualFileSystem.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <cassert>
@@ -477,6 +479,16 @@ void DiagnosticsEngine::setSeverityForAll(diag::Flavor Flavor,
setSeverity(Diag, Map, Loc);
}
+void DiagnosticsEngine::setDiagSuppressionMapping(
+ decltype(DiagSuppressionMapping) Mapping) {
+ DiagSuppressionMapping = std::move(Mapping);
+}
+
+bool DiagnosticsEngine::isSuppressedViaMapping(diag::kind D,
+ llvm::StringRef FilePath) const {
+ return DiagSuppressionMapping && DiagSuppressionMapping(D, FilePath);
+}
+
void DiagnosticsEngine::Report(const StoredDiagnostic &storedDiag) {
DiagnosticStorage DiagStorage;
DiagStorage.DiagRanges.append(storedDiag.range_begin(),
diff --git a/clang/lib/Basic/DiagnosticIDs.cpp b/clang/lib/Basic/DiagnosticIDs.cpp
index fc8bd29faa080f..463b081ee80b38 100644
--- a/clang/lib/Basic/DiagnosticIDs.cpp
+++ b/clang/lib/Basic/DiagnosticIDs.cpp
@@ -599,6 +599,18 @@ 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
+ // mappings.
+ // We also use presumed locations here to improve reproducibility for
+ // preprocessed inputs.
+ if (!Mapping.isPragma()) {
+ if (auto PLoc = SM.getPresumedLoc(Loc);
+ PLoc.isValid() &&
+ Diag.isSuppressedViaMapping(DiagID, PLoc.getFilename()))
+ return diag::Severity::Ignored;
+ }
+
return Result;
}
diff --git a/clang/lib/Basic/Warnings.cpp b/clang/lib/Basic/Warnings.cpp
index 5a5ac555633886..1047980b84d7db 100644
--- a/clang/lib/Basic/Warnings.cpp
+++ b/clang/lib/Basic/Warnings.cpp
@@ -23,10 +23,21 @@
// 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 "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringMapEntry.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/SpecialCaseList.h"
+#include "llvm/Support/VirtualFileSystem.h"
#include <algorithm>
#include <cstring>
+#include <memory>
#include <utility>
+#include <vector>
using namespace clang;
// EmitUnknownDiagWarning - Emit a warning and typo hint for unknown warning
@@ -41,8 +52,96 @@ static void EmitUnknownDiagWarning(DiagnosticsEngine &Diags,
<< (Prefix.str() += std::string(Suggestion));
}
+namespace {
+class WarningsSpecialCaseList : public llvm::SpecialCaseList {
+public:
+ static std::unique_ptr<WarningsSpecialCaseList>
+ create(const llvm::MemoryBuffer &MB, std::string &Err) {
+ auto SCL = std::make_unique<WarningsSpecialCaseList>();
+ if (SCL->createInternal(&MB, Err))
+ return SCL;
+ return nullptr;
+ }
+
+ // Section names refer to diagnostic groups, which cover multiple individual
+ // diagnostics. Expand diagnostic groups here to individual diagnostics.
+ // A diagnostic can have multiple diagnostic groups associated with it, we let
+ // the last section take precedence in such cases.
+ void processSections(DiagnosticsEngine &Diags) {
+ // Drop the default section introduced by special case list, we only support
+ // exact diagnostic group names.
+ Sections.erase("*");
+ // Make sure we iterate sections by their line numbers.
+ std::vector<std::pair<unsigned, const llvm::StringMapEntry<Section> *>>
+ LineAndSection;
+ for (const auto &Entry : Sections) {
+ LineAndSection.emplace_back(
+ Entry.second.SectionMatcher->Globs.at(Entry.first()).second, &Entry);
+ }
+ llvm::sort(LineAndSection);
+ for (const auto &[_, Entry] : LineAndSection) {
+ SmallVector<diag::kind, 256> GroupDiags;
+ if (Diags.getDiagnosticIDs()->getDiagnosticsInGroup(
+ clang::diag::Flavor::WarningOrError, Entry->first(),
+ GroupDiags)) {
+ EmitUnknownDiagWarning(Diags, clang::diag::Flavor::WarningOrError, "",
+ Entry->first());
+ continue;
+ }
+ for (auto D : GroupDiags)
+ DiagToSection[D] = &Entry->getValue();
+ }
+ }
+
+ bool isDiagSuppressed(diag::kind D, llvm::StringRef FilePath) const {
+ auto Section = DiagToSection.find(D);
+ if (Section == DiagToSection.end())
+ return false;
+ auto SrcEntries = Section->second->Entries.find("src");
+ if(SrcEntries == Section->second->Entries.end())
+ return false;
+ // Find the longest glob pattern that matches FilePath. A positive match
+ // implies D should be suppressed for FilePath.
+ llvm::StringRef LongestMatch;
+ bool LongestWasNegative;
+ for (const auto &CatIt : SrcEntries->second) {
+ bool IsNegative = CatIt.first() == "emit";
+ for (const auto &GlobIt : CatIt.second.Globs) {
+ if (GlobIt.getKeyLength() < LongestMatch.size())
+ continue;
+ if (!GlobIt.second.first.match(FilePath))
+ continue;
+ LongestMatch = GlobIt.getKey();
+ LongestWasNegative = IsNegative;
+ }
+ }
+ return !LongestMatch.empty() && !LongestWasNegative;
+ }
+
+private:
+ llvm::DenseMap<diag::kind, const Section*> DiagToSection;
+};
+
+void parseSuppressionMappings(const llvm::MemoryBuffer &MB,
+ DiagnosticsEngine &Diags) {
+ std::string Err;
+ auto SCL = WarningsSpecialCaseList::create(MB, Err);
+ if (!SCL) {
+ Diags.Report(diag::err_drv_malformed_warning_suppression_mapping)
+ << MB.getBufferIdentifier() << Err;
+ return;
+ }
+ SCL->processSections(Diags);
+ Diags.setDiagSuppressionMapping(
+ [SCL(std::move(SCL))](diag::kind K, llvm::StringRef Path) {
+ return SCL->isDiagSuppressed(K, Path);
+ });
+}
+} // namespace
+
void clang::ProcessWarningOptions(DiagnosticsEngine &Diags,
const DiagnosticOptions &Opts,
+ llvm::vfs::FileSystem& VFS,
bool ReportDiags) {
Diags.setSuppressSystemWarnings(true); // Default to -Wno-system-headers
Diags.setIgnoreAllWarnings(Opts.IgnoreWarnings);
@@ -70,6 +169,14 @@ void clang::ProcessWarningOptions(DiagnosticsEngine &Diags,
else
Diags.setExtensionHandlingBehavior(diag::Severity::Ignored);
+ if (!Opts.SuppressionMappingsFile.empty()) {
+ if (auto Buf = VFS.getBufferForFile(Opts.SuppressionMappingsFile)) {
+ parseSuppressionMappings(**Buf, Diags);
+ } else if (ReportDiags) {
+ Diags.Report(diag::err_drv_no_such_file) << Opts.SuppressionMappingsFile;
+ }
+ }
+
SmallVector<diag::kind, 10> _Diags;
const IntrusiveRefCntPtr< DiagnosticIDs > DiagIDs =
Diags.getDiagnosticIDs();
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index dca8d3fd7b3eaf..b7768c1cd00d24 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -4514,6 +4514,13 @@ static void RenderDiagnosticsOptions(const Driver &D, const ArgList &Args,
Args.addOptOutFlag(CmdArgs, options::OPT_fspell_checking,
options::OPT_fno_spell_checking);
+
+ if (const Arg *A =
+ Args.getLastArg(options::OPT_warning_suppression_mappings_EQ)) {
+ if (!D.getVFS().exists(A->getValue()))
+ D.Diag(clang::diag::err_drv_no_such_file) << A->getValue();
+ CmdArgs.push_back(Args.MakeArgString(A->getSpelling() + A->getValue()));
+ }
}
DwarfFissionKind tools::getDebugFissionKind(const Driver &D,
diff --git a/clang/lib/Frontend/ASTUnit.cpp b/clang/lib/Frontend/ASTUnit.cpp
index 4aec928f9eb0a5..8b3a1cd4183f41 100644
--- a/clang/lib/Frontend/ASTUnit.cpp
+++ b/clang/lib/Frontend/ASTUnit.cpp
@@ -1367,7 +1367,7 @@ ASTUnit::getMainBufferWithPrecompiledPreamble(
// after parsing the preamble.
getDiagnostics().Reset();
ProcessWarningOptions(getDiagnostics(),
- PreambleInvocationIn.getDiagnosticOpts());
+ PreambleInvocationIn.getDiagnosticOpts(), *VFS);
getDiagnostics().setNumWarnings(NumWarningsInPreamble);
PreambleRebuildCountdown = 1;
@@ -1593,7 +1593,8 @@ ASTUnit *ASTUnit::LoadFromCompilerInvocationAction(
// We'll manage file buffers ourselves.
CI->getPreprocessorOpts().RetainRemappedFileBuffers = true;
CI->getFrontendOpts().DisableFree = false;
- ProcessWarningOptions(AST->getDiagnostics(), CI->getDiagnosticOpts());
+ ProcessWarningOptions(AST->getDiagnostics(), CI->getDiagnosticOpts(),
+ AST->getFileManager().getVirtualFileSystem());
// Create the compiler instance to use for building the AST.
std::unique_ptr<CompilerInstance> Clang(
@@ -1701,7 +1702,7 @@ bool ASTUnit::LoadFromCompilerInvocation(
Invocation->getPreprocessorOpts().RetainRemappedFileBuffers = true;
Invocation->getFrontendOpts().DisableFree = false;
getDiagnostics().Reset();
- ProcessWarningOptions(getDiagnostics(), Invocation->getDiagnosticOpts());
+ ProcessWarningOptions(getDiagnostics(), Invocation->getDiagnosticOpts(), *VFS);
std::unique_ptr<llvm::MemoryBuffer> OverrideMainBuffer;
if (PrecompilePreambleAfterNParses > 0) {
@@ -1709,7 +1710,7 @@ bool ASTUnit::LoadFromCompilerInvocation(
OverrideMainBuffer =
getMainBufferWithPrecompiledPreamble(PCHContainerOps, *Invocation, VFS);
getDiagnostics().Reset();
- ProcessWarningOptions(getDiagnostics(), Invocation->getDiagnosticOpts());
+ ProcessWarningOptions(getDiagnostics(), Invocation->getDiagnosticOpts(), *VFS);
}
SimpleTimer ParsingTimer(WantTiming);
@@ -1902,7 +1903,8 @@ bool ASTUnit::Reparse(std::shared_ptr<PCHContainerOperations> PCHContainerOps,
// Clear out the diagnostics state.
FileMgr.reset();
getDiagnostics().Reset();
- ProcessWarningOptions(getDiagnostics(), Invocation->getDiagnosticOpts());
+ ProcessWarningOptions(getDiagnostics(), Invocation->getDiagnosticOpts(),
+ *VFS);
if (OverrideMainBuffer)
getDiagnostics().setNumWarnings(NumWarningsInPreamble);
@@ -2241,7 +2243,8 @@ void ASTUnit::CodeComplete(
CaptureDroppedDiagnostics Capture(CaptureDiagsKind::All,
Clang->getDiagnostics(),
&StoredDiagnostics, nullptr);
- ProcessWarningOptions(Diag, Inv.getDiagnosticOpts());
+ ProcessWarningOptions(Diag, Inv.getDiagnosticOpts(),
+ FileMgr.getVirtualFileSystem());
// Create the target instance.
if (!Clang->createTarget()) {
diff --git a/clang/lib/Frontend/CompilerInstance.cpp b/clang/lib/Frontend/CompilerInstance.cpp
index 240305b33824b8..ecc6782c7cb4fb 100644
--- a/clang/lib/Frontend/CompilerInstance.cpp
+++ b/clang/lib/Frontend/CompilerInstance.cpp
@@ -39,6 +39,7 @@
#include "clang/Serialization/ASTReader.h"
#include "clang/Serialization/GlobalModuleIndex.h"
#include "clang/Serialization/InMemoryModuleCache.h"
+#include "llvm/ADT/IntrusiveRefCntPtr.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/ScopeExit.h"
#include "llvm/ADT/Statistic.h"
@@ -54,6 +55,7 @@
#include "llvm/Support/Signals.h"
#include "llvm/Support/TimeProfiler.h"
#include "llvm/Support/Timer.h"
+#include "llvm/Support/VirtualFileSystem.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/TargetParser/Host.h"
#include <optional>
@@ -332,19 +334,22 @@ static void SetupSerializedDiagnostics(DiagnosticOptions *DiagOpts,
void CompilerInstance::createDiagnostics(DiagnosticConsumer *Client,
bool ShouldOwnClient) {
- Diagnostics = createDiagnostics(&getDiagnosticOpts(), Client,
- ShouldOwnClient, &getCodeGenOpts());
+ Diagnostics = createDiagnostics(
+ &getDiagnosticOpts(), Client, ShouldOwnClient, &getCodeGenOpts(),
+ FileMgr ? FileMgr->getVirtualFileSystemPtr() : nullptr);
}
-IntrusiveRefCntPtr<DiagnosticsEngine>
-CompilerInstance::createDiagnostics(DiagnosticOptions *Opts,
- DiagnosticConsumer *Client,
- bool ShouldOwnClient,
- const CodeGenOptions *CodeGenOpts) {
+IntrusiveRefCntPtr<DiagnosticsEngine> CompilerInstance::createDiagnostics(
+ DiagnosticOptions *Opts, DiagnosticConsumer *Client, bool ShouldOwnClient,
+ const CodeGenOptions *CodeGenOpts,
+ llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS) {
IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
IntrusiveRefCntPtr<DiagnosticsEngine>
Diags(new DiagnosticsEngine(DiagID, Opts));
+ if (!VFS)
+ VFS = llvm::vfs::getRealFileSystem();
+
// Create the diagnostic client for reporting errors or for
// implementing -verify.
if (Client) {
@@ -367,7 +372,7 @@ CompilerInstance::createDiagnostics(DiagnosticOptions *Opts,
Opts->DiagnosticSerializationFile);
// Configure our handling of diagnostics.
- ProcessWarningOptions(*Diags, *Opts);
+ ProcessWarningOptions(*Diags, *Opts, *VFS);
return Diags;
}
diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp
index b5fd35aaa1e841..e26a1300fc8720 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -2521,6 +2521,11 @@ void CompilerInvocationBase::GenerateDiagnosticArgs(
Consumer(StringRef("-R") + Remark);
}
+
+ if (!Opts.SuppressionMappingsFile.empty()) {
+ GenerateArg(Consumer, OPT_warning_suppression_mappings_EQ,
+ Opts.SuppressionMappingsFile);
+ }
}
std::unique_ptr<DiagnosticOptions>
@@ -2597,6 +2602,9 @@ bool clang::ParseDiagnosticArgs(DiagnosticOptions &Opts, ArgList &Args,
Opts.TabStop = DiagnosticOptions::DefaultTabStop;
}
+ if (const Arg *A = Args.getLastArg(OPT_warning_suppression_mappings_EQ))
+ Opts.SuppressionMappingsFile = A->getValue();
+
addDiagnosticArgs(Args, OPT_W_Group, OPT_W_value_Group, Opts.Warnings);
addDiagnosticArgs(Args, OPT_R_Group, OPT_R_value_Group, Opts.Remarks);
diff --git a/clang/lib/Frontend/PrecompiledPreamble.cpp b/clang/lib/Frontend/PrecompiledPreamble.cpp
index cab5838fceb24d..d1bac09830b04b 100644
--- a/clang/lib/Frontend/PrecompiledPreamble.cpp
+++ b/clang/lib/Frontend/PrecompiledPreamble.cpp
@@ -479,7 +479,7 @@ llvm::ErrorOr<PrecompiledPreamble> PrecompiledPreamble::Build(
// Clear out old caches and data.
Diagnostics.Reset();
- ProcessWarningOptions(Diagnostics, Clang->getDiagnosticOpts());
+ ProcessWarningOptions(Diagnostics, Clang->getDiagnosticOpts(), *VFS);
VFS =
createVFSFromCompilerInvocation(Clang->getInvocation(), Diagnostics, VFS);
diff --git a/clang/lib/Interpreter/CodeCompletion.cpp b/clang/lib/Interpreter/CodeCompletion.cpp
index 791426807cb91d..bbc8830d76bc00 100644
--- a/clang/lib/Interpreter/CodeCompletion.cpp
+++ b/clang/lib/Interpreter/CodeCompletion.cpp
@@ -380,8 +380,8 @@ void ReplCodeCompleter::codeComplete(CompilerInstance *InterpCI,
AU->CodeComplete(CodeCompletionFileName, 1, Col, RemappedFiles, false, false,
false, consumer,
std::make_shared<clang::PCHContainerOperations>(), *diag,
- InterpCI->getLangOpts(), InterpCI->getSourceManager(),
- InterpCI->getFileManager(), sd, tb, std::move(Act));
+ InterpCI->getLangOpts(), AU->getSourceManager(),
+ AU->getFileManager(), sd, tb, std::move(Act));
}
} // namespace clang
diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp
index 004a584ff77b40..c0b18fb50135dc 100644
--- a/clang/lib/Serialization/ASTReader.cpp
+++ b/clang/lib/Serialization/ASTReader.cpp
@@ -603,7 +603,9 @@ bool PCHValidator::ReadDiagnosticOptions(
new DiagnosticsEngine(DiagIDs, DiagOpts.get()));
// This should never fail, because we would have processed these options
// before writing them to an ASTFile.
- ProcessWarningOptions(*Diags, *DiagOpts, /*Report*/false);
+ ProcessWarningOptions(*Diags, *DiagOpts,
+ PP.getFileManager().getVirtualFileSystem(),
+ /*Report*/ false);
ModuleManager &ModuleMgr = Reader.getModuleManager();
assert(ModuleMgr.size() >= 1 && "what ASTFile is this then");
diff --git a/clang/test/Misc/Inputs/suppression-mapping.txt b/clang/test/Misc/Inputs/suppression-mapping.txt
new file mode 100644
index 00000000000000..d1fd0e89256d3b
--- /dev/null
+++ b/clang/test/Misc/Inputs/suppression-mapping.txt
@@ -0,0 +1,4 @@
+# Suppress unused warnings in all files, apart from the ones under `foo/`.
+[unused]
+src:*
+src:*foo/*=emit
diff --git a/clang/test/Misc/warning-suppression-mappings.cpp b/clang/test/Misc/warning-suppression-mappings.cpp
new file mode 100644
index 00000000000000..0626d7279e804a
--- /dev/null
+++ b/clang/test/Misc/warning-suppression-mappings.cpp
@@ -0,0 +1,16 @@
+// Check for warnings
+// RUN: not %clang --warning-suppression-mappings=foo.txt -fsyntax-only %s 2>&1 | FileCheck -check-prefix MISSING_MAPPING %s
+// RUN: not %clang -cc1 --warning-suppression-mappings=foo.txt -fsyntax-only %s 2>&1 | FileCheck -check-prefix MISSING_MAPPING %s
+// MISSING_MAPPING: error: no such file or directory: 'foo.txt'
+
+// Check that it's no-op when diagnostics aren't enabled.
+// RUN: %clang -cc1 -Werror --warning-suppression-mappings=%S/Inputs/suppression-mapping.txt -fsyntax-only %s 2>&1 | FileCheck -check-prefix WARNINGS_DISABLED --allow-empty %s
+// WARNINGS_DISABLED-NOT: warning:
+// WARNINGS_DISABLED-NOT: error:
+
+// RUN: %clang -cc1 -verify -Wunused --warning-suppression-mappings=%S/Inputs/suppression-mapping.txt -fsyntax-only %s
+
+namespace { void foo(); }
+
+#line 42 "foo/bar.h"
+namespace { void bar(); } // expected-warning{{unused function 'bar'}}
diff --git a/clang/tools/driver/cc1gen_reproducer_main.cpp b/clang/tools/driver/cc1gen_reproducer_main.cpp
index be081cac8c03b1..df59b53f9ef186 100644
--- a/clang/tools/driver/cc1gen_reproducer_main.cpp
+++ b/clang/tools/driver/cc1gen_reproducer_main.cpp
@@ -121,9 +121,10 @@ generateReproducerForInvocationArguments(ArrayRef<const char *> Argv,
IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
DiagnosticsEngine Diags(DiagID, &*DiagOpts, new IgnoringDiagConsumer());
- ProcessWarningOptions(Diags, *DiagOpts, /*ReportDiags=*/false);
- Driver TheDriver(ToolContext.Path, llvm::sys::getDefaultTargetTriple(),
- Diags);
+ auto VFS = llvm::vfs::getRealFileSystem();
+ ProcessWarningOptions(Diags, *DiagOpts, *VFS, /*ReportDiags=*/false);
+ Driver TheDriver(ToolContext.Path, llvm::sys::getDefaultTargetTriple(), Diags,
+ /*Title=*/"clang LLVM compiler", VFS);
TheDriver.setTargetAndMode(TargetAndMode);
if (ToolContext.NeedsPrependArg)
TheDriver.setPrependArg(ToolContext.PrependArg);
diff --git a/clang/tools/driver/driver.cpp b/clang/tools/driver/driver.cpp
index 686eaea0aa7c83..12038de476ace1 100644
--- a/clang/tools/driver/driver.cpp
+++ b/clang/tools/driver/driver.cpp
@@ -47,6 +47,7 @@
#include "llvm/Support/StringSaver.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/Timer.h"
+#include "llvm/Support/VirtualFileSystem.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/TargetParser/Host.h"
#include <memory>
@@ -334,9 +335,11 @@ int clang_main(int Argc, char **Argv, const llvm::ToolContext &ToolContext) {
Diags.takeClient(), std::move(SerializedConsumer)));
}
- ProcessWarningOptions(Diags, *DiagOpts, /*ReportDiags=*/false);
+ auto VFS = llvm::vfs::getRealFileSystem();
+ ProcessWarningOptions(Diags, *DiagOpts, *VFS, /*ReportDiags=*/false);
- Driver TheDriver(Path, llvm::sys::getDefaultTargetTriple(), Diags);
+ Driver TheDriver(Path, llvm::sys::getDefaultTargetTriple(), Diags,
+ /*Title=*/"clang LLVM compiler", VFS);
auto TargetAndMode = ToolChain::getTargetAndModeFromProgramName(ProgName);
TheDriver.setTargetAndMode(TargetAndMode);
// If -canonical-prefixes is set, GetExecutablePath will have resolved Path
diff --git a/clang/unittests/Basic/DiagnosticTest.cpp b/clang/unittests/Basic/DiagnosticTest.cpp
index d8d23e3b670097..141cec442dee92 100644
--- a/clang/unittests/Basic/DiagnosticTest.cpp
+++ b/clang/unittests/Basic/DiagnosticTest.cpp
@@ -10,8 +10,17 @@
#include "clang/Basic/DiagnosticError.h"
#include "clang/Basic/DiagnosticIDs.h"
#include "clang/Basic/DiagnosticLex.h"
+#include "clang/Basic/DiagnosticSema.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Basic/SourceManager.h"
+#include "llvm/ADT/IntrusiveRefCntPtr.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/VirtualFileSystem.h"
+#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include <optional>
+#include <vector>
using namespace llvm;
using namespace clang;
@@ -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<llvm::vfs::InMemoryFileSystem> FS =
+ llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
+ DiagnosticsEngine Diags{new DiagnosticIDs(), new DiagnosticOptions};
+
+ std::vector<StoredDiagnostic> takeDiags() {
+ return std::move(CaptureConsumer.StoredDiags);
+ }
+
+private:
+ class CaptureDiagnosticConsumer : public DiagnosticConsumer {
+ public:
+ std::vector<StoredDiagnostic> 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().SuppressionMappingsFile = "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().SuppressionMappingsFile = "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().SuppressionMappingsFile = "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().SuppressionMappingsFile = "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().SuppressionMappingsFile = "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().SuppressionMappingsFile = "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().SuppressionMappingsFile = "foo.txt";
+ Diags.getDiagnosticOptions().Warnings = {"unused"};
+ FS->addFile("foo.txt", /*ModificationTime=*/{},
+ llvm::MemoryBuffer::getMemBuffer(SuppressionMappingFile));
+ clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);
+ EXPECT_THAT(takeDiags(), testing::IsEmpty());
+
+ FileManager FM({}, FS);
+ SourceManager SM(Diags, FM);
+
+ auto ClangID =
+ SM.createFileID(llvm::MemoryBuffer::getMemBuffer("", "clang/foo.h"));
+ auto NonClangID =
+ SM.createFileID(llvm::MemoryBuffer::getMemBuffer("", "llvm/foo.h"));
+ auto PresumedClangID =
+ SM.createFileID(llvm::MemoryBuffer::getMemBuffer("", "llvm/foo2.h"));
+ // Add a line directive to point into clang/foo.h
+ SM.AddLineNote(SM.getLocForStartOfFile(PresumedClangID), 42,
+ SM.getLineTableFilenameID("clang/foo.h"), false, false,
+ clang::SrcMgr::C_User);
+
+ EXPECT_TRUE(Diags.isIgnored(diag::warn_unused_function,
+ SM.getLocForStartOfFile(ClangID)));
+ EXPECT_FALSE(Diags.isIgnored(diag::warn_unused_function,
+ SM.getLocForStartOfFile(NonClangID)));
+ EXPECT_TRUE(Diags.isIgnored(diag::warn_unused_function,
+ SM.getLocForStartOfFile(PresumedClangID)));
+
+ // Pretend we have a clang-diagnostic pragma to enforce the warning. Make sure
+ // suppressing mapping doesn't take over.
+ Diags.setSeverity(diag::warn_unused_function, diag::Severity::Error,
+ SM.getLocForStartOfFile(ClangID));
+ EXPECT_FALSE(Diags.isIgnored(diag::warn_unused_function,
+ SM.getLocForStartOfFile(ClangID)));
}
+} // namespace
diff --git a/clang/unittests/Frontend/CompilerInvocationTest.cpp b/clang/unittests/Frontend/CompilerInvocationTest.cpp
index 7912253b761e9b..1cf2bbb6576621 100644
--- a/clang/unittests/Frontend/CompilerInvocationTest.cpp
+++ b/clang/unittests/Frontend/CompilerInvocationTest.cpp
@@ -1046,4 +1046,14 @@ TEST_F(CommandLineTest, PluginArgsRoundTripDeterminism) {
ASSERT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
}
+
+TEST_F(CommandLineTest, WarningSuppressionMappings) {
+ const char *Args[] = {"--warning-suppression-mappings=foo.txt"};
+
+ EXPECT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
+ EXPECT_EQ(Invocation.getDiagnosticOpts().SuppressionMappingsFile, "foo.txt");
+
+ Invocation.generateCC1CommandLine(GeneratedArgs, *this);
+ EXPECT_THAT(GeneratedArgs, Contains(StrEq(Args[0])));
+}
} // anonymous namespace
diff --git a/llvm/include/llvm/Support/SpecialCaseList.h b/llvm/include/llvm/Support/SpecialCaseList.h
index 6dc1a29c5a281d..30e3fc644bbc33 100644
--- a/llvm/include/llvm/Support/SpecialCaseList.h
+++ b/llvm/include/llvm/Support/SpecialCaseList.h
@@ -122,7 +122,6 @@ class SpecialCaseList {
// Returns zero if no match is found.
unsigned match(StringRef Query) const;
- private:
StringMap<std::pair<GlobPattern, unsigned>> Globs;
std::vector<std::pair<std::unique_ptr<Regex>, unsigned>> RegExes;
};
>From aa47fe45530d7b149375c52a63543630aed34ae9 Mon Sep 17 00:00:00 2001
From: Kadir Cetinkaya <kadircet at google.com>
Date: Thu, 17 Oct 2024 10:40:14 +0200
Subject: [PATCH 2/7] clang-format
---
clang/lib/Basic/Warnings.cpp | 6 +++---
clang/lib/Frontend/ASTUnit.cpp | 6 ++++--
clang/unittests/Basic/DiagnosticTest.cpp | 2 +-
3 files changed, 8 insertions(+), 6 deletions(-)
diff --git a/clang/lib/Basic/Warnings.cpp b/clang/lib/Basic/Warnings.cpp
index 1047980b84d7db..c074d719a77234 100644
--- a/clang/lib/Basic/Warnings.cpp
+++ b/clang/lib/Basic/Warnings.cpp
@@ -98,7 +98,7 @@ class WarningsSpecialCaseList : public llvm::SpecialCaseList {
if (Section == DiagToSection.end())
return false;
auto SrcEntries = Section->second->Entries.find("src");
- if(SrcEntries == Section->second->Entries.end())
+ if (SrcEntries == Section->second->Entries.end())
return false;
// Find the longest glob pattern that matches FilePath. A positive match
// implies D should be suppressed for FilePath.
@@ -119,7 +119,7 @@ class WarningsSpecialCaseList : public llvm::SpecialCaseList {
}
private:
- llvm::DenseMap<diag::kind, const Section*> DiagToSection;
+ llvm::DenseMap<diag::kind, const Section *> DiagToSection;
};
void parseSuppressionMappings(const llvm::MemoryBuffer &MB,
@@ -141,7 +141,7 @@ void parseSuppressionMappings(const llvm::MemoryBuffer &MB,
void clang::ProcessWarningOptions(DiagnosticsEngine &Diags,
const DiagnosticOptions &Opts,
- llvm::vfs::FileSystem& VFS,
+ llvm::vfs::FileSystem &VFS,
bool ReportDiags) {
Diags.setSuppressSystemWarnings(true); // Default to -Wno-system-headers
Diags.setIgnoreAllWarnings(Opts.IgnoreWarnings);
diff --git a/clang/lib/Frontend/ASTUnit.cpp b/clang/lib/Frontend/ASTUnit.cpp
index 8b3a1cd4183f41..4c29b2a40f73fe 100644
--- a/clang/lib/Frontend/ASTUnit.cpp
+++ b/clang/lib/Frontend/ASTUnit.cpp
@@ -1702,7 +1702,8 @@ bool ASTUnit::LoadFromCompilerInvocation(
Invocation->getPreprocessorOpts().RetainRemappedFileBuffers = true;
Invocation->getFrontendOpts().DisableFree = false;
getDiagnostics().Reset();
- ProcessWarningOptions(getDiagnostics(), Invocation->getDiagnosticOpts(), *VFS);
+ ProcessWarningOptions(getDiagnostics(), Invocation->getDiagnosticOpts(),
+ *VFS);
std::unique_ptr<llvm::MemoryBuffer> OverrideMainBuffer;
if (PrecompilePreambleAfterNParses > 0) {
@@ -1710,7 +1711,8 @@ bool ASTUnit::LoadFromCompilerInvocation(
OverrideMainBuffer =
getMainBufferWithPrecompiledPreamble(PCHContainerOps, *Invocation, VFS);
getDiagnostics().Reset();
- ProcessWarningOptions(getDiagnostics(), Invocation->getDiagnosticOpts(), *VFS);
+ ProcessWarningOptions(getDiagnostics(), Invocation->getDiagnosticOpts(),
+ *VFS);
}
SimpleTimer ParsingTimer(WantTiming);
diff --git a/clang/unittests/Basic/DiagnosticTest.cpp b/clang/unittests/Basic/DiagnosticTest.cpp
index 141cec442dee92..387ae5da14aff0 100644
--- a/clang/unittests/Basic/DiagnosticTest.cpp
+++ b/clang/unittests/Basic/DiagnosticTest.cpp
@@ -195,7 +195,7 @@ class SuppressionMappingTest : public testing::Test {
private:
class CaptureDiagnosticConsumer : public DiagnosticConsumer {
public:
- std::vector<StoredDiagnostic> StoredDiags;
+ std::vector<StoredDiagnostic> StoredDiags;
void HandleDiagnostic(DiagnosticsEngine::Level level,
const Diagnostic &Info) override {
>From 98acb77acb153cef36641a832146b8d54f0b7cf5 Mon Sep 17 00:00:00 2001
From: Kadir Cetinkaya <kadircet at google.com>
Date: Tue, 22 Oct 2024 10:31:43 +0200
Subject: [PATCH 3/7] Rename diag-option && move parsing to DiagnosticsEngine
---
clang/include/clang/Basic/Diagnostic.h | 16 ++-
clang/include/clang/Basic/DiagnosticOptions.h | 4 +-
clang/lib/Basic/Diagnostic.cpp | 96 ++++++++++++++++-
clang/lib/Basic/Warnings.cpp | 102 ++----------------
clang/lib/Frontend/CompilerInvocation.cpp | 6 +-
clang/unittests/Basic/DiagnosticTest.cpp | 14 +--
.../Frontend/CompilerInvocationTest.cpp | 3 +-
7 files changed, 127 insertions(+), 114 deletions(-)
diff --git a/clang/include/clang/Basic/Diagnostic.h b/clang/include/clang/Basic/Diagnostic.h
index 79b7545e92d6da..d3706460a62a0f 100644
--- a/clang/include/clang/Basic/Diagnostic.h
+++ b/clang/include/clang/Basic/Diagnostic.h
@@ -26,6 +26,7 @@
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/iterator_range.h"
#include "llvm/Support/Compiler.h"
+#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/VirtualFileSystem.h"
#include <cassert>
#include <cstdint>
@@ -953,10 +954,21 @@ class DiagnosticsEngine : public RefCountedBase<DiagnosticsEngine> {
}
/// Diagnostic suppression mappings can be used to ignore diagnostics based on
- /// the file they occur in.
+ /// the file they occur in. 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:
+ /// [unused]
+ /// src:*clang/*
+ /// src:*clang/foo/*=emit
+ /// src:*clang/foo/bar/*
+ ///
+ /// Such a mappings file suppress all diagnostics produced by -Wunused in all
+ /// sources under `clang/` directory apart from `clang/foo/`. Diagnostics
+ /// under `clang/foo/bar/` will also be suppressed.
/// These take presumed locations into account, and can still be overriden by
/// clang-diagnostics pragmas.
- void setDiagSuppressionMapping(decltype(DiagSuppressionMapping) Mapping);
+ void setDiagSuppressionMapping(llvm::MemoryBuffer &MB);
bool isSuppressedViaMapping(diag::kind D, llvm::StringRef FilePath) const;
/// Issue the message to the client.
diff --git a/clang/include/clang/Basic/DiagnosticOptions.h b/clang/include/clang/Basic/DiagnosticOptions.h
index 0770805624cec8..53ffb265cdc870 100644
--- a/clang/include/clang/Basic/DiagnosticOptions.h
+++ b/clang/include/clang/Basic/DiagnosticOptions.h
@@ -108,8 +108,8 @@ class DiagnosticOptions : public RefCountedBase<DiagnosticOptions>{
/// The file to serialize diagnostics to (non-appending).
std::string DiagnosticSerializationFile;
- /// File that defines suppression mappings.
- std::string SuppressionMappingsFile;
+ /// File that defines diagnostic suppression mappings.
+ std::string DiagnosticSuppressionMappingsFile;
/// The list of -W... options used to alter the diagnostic mappings, with the
/// prefixes removed.
diff --git a/clang/lib/Basic/Diagnostic.cpp b/clang/lib/Basic/Diagnostic.cpp
index e536effd25072f..beea68c003e2fb 100644
--- a/clang/lib/Basic/Diagnostic.cpp
+++ b/clang/lib/Basic/Diagnostic.cpp
@@ -12,7 +12,9 @@
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/CharInfo.h"
+#include "clang/Basic/DiagnosticDriver.h"
#include "clang/Basic/DiagnosticError.h"
+#include "clang/Basic/DiagnosticFrontend.h"
#include "clang/Basic/DiagnosticIDs.h"
#include "clang/Basic/DiagnosticOptions.h"
#include "clang/Basic/IdentifierTable.h"
@@ -28,6 +30,9 @@
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/ConvertUTF.h"
#include "llvm/Support/CrashRecoveryContext.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/SpecialCaseList.h"
#include "llvm/Support/Unicode.h"
#include "llvm/Support/VirtualFileSystem.h"
#include "llvm/Support/raw_ostream.h"
@@ -37,6 +42,7 @@
#include <cstdint>
#include <cstring>
#include <limits>
+#include <memory>
#include <string>
#include <utility>
#include <vector>
@@ -479,9 +485,93 @@ void DiagnosticsEngine::setSeverityForAll(diag::Flavor Flavor,
setSeverity(Diag, Map, Loc);
}
-void DiagnosticsEngine::setDiagSuppressionMapping(
- decltype(DiagSuppressionMapping) Mapping) {
- DiagSuppressionMapping = std::move(Mapping);
+namespace {
+class WarningsSpecialCaseList : public llvm::SpecialCaseList {
+public:
+ static std::unique_ptr<WarningsSpecialCaseList>
+ create(const llvm::MemoryBuffer &MB, std::string &Err) {
+ auto SCL = std::make_unique<WarningsSpecialCaseList>();
+ if (SCL->createInternal(&MB, Err))
+ return SCL;
+ return nullptr;
+ }
+
+ // Section names refer to diagnostic groups, which cover multiple individual
+ // diagnostics. Expand diagnostic groups here to individual diagnostics.
+ // A diagnostic can have multiple diagnostic groups associated with it, we let
+ // the last section take precedence in such cases.
+ void processSections(DiagnosticsEngine &Diags) {
+ // Drop the default section introduced by special case list, we only support
+ // exact diagnostic group names.
+ Sections.erase("*");
+ // Make sure we iterate sections by their line numbers.
+ std::vector<std::pair<unsigned, const llvm::StringMapEntry<Section> *>>
+ LineAndSection;
+ for (const auto &Entry : Sections) {
+ LineAndSection.emplace_back(
+ Entry.second.SectionMatcher->Globs.at(Entry.first()).second, &Entry);
+ }
+ llvm::sort(LineAndSection);
+ static constexpr auto kFlavor = clang::diag::Flavor::WarningOrError;
+ for (const auto &[_, Entry] : LineAndSection) {
+ SmallVector<diag::kind, 256> GroupDiags;
+ if (Diags.getDiagnosticIDs()->getDiagnosticsInGroup(
+ kFlavor, Entry->first(), GroupDiags)) {
+ StringRef Suggestion =
+ DiagnosticIDs::getNearestOption(kFlavor, Entry->first());
+ Diags.Report(diag::warn_unknown_diag_option)
+ << static_cast<unsigned>(kFlavor) << Entry->first()
+ << !Suggestion.empty() << Suggestion;
+ continue;
+ }
+ for (auto D : GroupDiags)
+ DiagToSection[D] = &Entry->getValue();
+ }
+ }
+
+ bool isDiagSuppressed(diag::kind D, llvm::StringRef FilePath) const {
+ auto Section = DiagToSection.find(D);
+ if (Section == DiagToSection.end())
+ return false;
+ auto SrcEntries = Section->second->Entries.find("src");
+ if (SrcEntries == Section->second->Entries.end())
+ return false;
+ // Find the longest glob pattern that matches FilePath. A positive match
+ // implies D should be suppressed for FilePath.
+ llvm::StringRef LongestMatch;
+ bool LongestWasNegative;
+ for (const auto &CatIt : SrcEntries->second) {
+ bool IsNegative = CatIt.first() == "emit";
+ for (const auto &GlobIt : CatIt.second.Globs) {
+ if (GlobIt.getKeyLength() < LongestMatch.size())
+ continue;
+ if (!GlobIt.second.first.match(FilePath))
+ continue;
+ LongestMatch = GlobIt.getKey();
+ LongestWasNegative = IsNegative;
+ }
+ }
+ return !LongestMatch.empty() && !LongestWasNegative;
+ }
+
+private:
+ llvm::DenseMap<diag::kind, const Section *> DiagToSection;
+};
+} // namespace
+
+void DiagnosticsEngine::setDiagSuppressionMapping(llvm::MemoryBuffer &MB) {
+ std::string Err;
+ auto SCL = WarningsSpecialCaseList::create(MB, Err);
+ if (!SCL) {
+ Report(diag::err_drv_malformed_warning_suppression_mapping)
+ << MB.getBufferIdentifier() << Err;
+ return;
+ }
+ SCL->processSections(*this);
+ DiagSuppressionMapping = [SCL(std::move(SCL))](diag::kind K,
+ llvm::StringRef Path) {
+ return SCL->isDiagSuppressed(K, Path);
+ };
}
bool DiagnosticsEngine::isSuppressedViaMapping(diag::kind D,
diff --git a/clang/lib/Basic/Warnings.cpp b/clang/lib/Basic/Warnings.cpp
index c074d719a77234..bcad90abf1c21b 100644
--- a/clang/lib/Basic/Warnings.cpp
+++ b/clang/lib/Basic/Warnings.cpp
@@ -26,17 +26,11 @@
#include "clang/Basic/DiagnosticDriver.h"
#include "clang/Basic/DiagnosticIDs.h"
#include "clang/Basic/DiagnosticOptions.h"
-#include "llvm/ADT/DenseMap.h"
-#include "llvm/ADT/STLExtras.h"
-#include "llvm/ADT/StringMapEntry.h"
#include "llvm/ADT/StringRef.h"
-#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/SpecialCaseList.h"
#include "llvm/Support/VirtualFileSystem.h"
-#include <algorithm>
#include <cstring>
#include <memory>
-#include <utility>
#include <vector>
using namespace clang;
@@ -52,92 +46,6 @@ static void EmitUnknownDiagWarning(DiagnosticsEngine &Diags,
<< (Prefix.str() += std::string(Suggestion));
}
-namespace {
-class WarningsSpecialCaseList : public llvm::SpecialCaseList {
-public:
- static std::unique_ptr<WarningsSpecialCaseList>
- create(const llvm::MemoryBuffer &MB, std::string &Err) {
- auto SCL = std::make_unique<WarningsSpecialCaseList>();
- if (SCL->createInternal(&MB, Err))
- return SCL;
- return nullptr;
- }
-
- // Section names refer to diagnostic groups, which cover multiple individual
- // diagnostics. Expand diagnostic groups here to individual diagnostics.
- // A diagnostic can have multiple diagnostic groups associated with it, we let
- // the last section take precedence in such cases.
- void processSections(DiagnosticsEngine &Diags) {
- // Drop the default section introduced by special case list, we only support
- // exact diagnostic group names.
- Sections.erase("*");
- // Make sure we iterate sections by their line numbers.
- std::vector<std::pair<unsigned, const llvm::StringMapEntry<Section> *>>
- LineAndSection;
- for (const auto &Entry : Sections) {
- LineAndSection.emplace_back(
- Entry.second.SectionMatcher->Globs.at(Entry.first()).second, &Entry);
- }
- llvm::sort(LineAndSection);
- for (const auto &[_, Entry] : LineAndSection) {
- SmallVector<diag::kind, 256> GroupDiags;
- if (Diags.getDiagnosticIDs()->getDiagnosticsInGroup(
- clang::diag::Flavor::WarningOrError, Entry->first(),
- GroupDiags)) {
- EmitUnknownDiagWarning(Diags, clang::diag::Flavor::WarningOrError, "",
- Entry->first());
- continue;
- }
- for (auto D : GroupDiags)
- DiagToSection[D] = &Entry->getValue();
- }
- }
-
- bool isDiagSuppressed(diag::kind D, llvm::StringRef FilePath) const {
- auto Section = DiagToSection.find(D);
- if (Section == DiagToSection.end())
- return false;
- auto SrcEntries = Section->second->Entries.find("src");
- if (SrcEntries == Section->second->Entries.end())
- return false;
- // Find the longest glob pattern that matches FilePath. A positive match
- // implies D should be suppressed for FilePath.
- llvm::StringRef LongestMatch;
- bool LongestWasNegative;
- for (const auto &CatIt : SrcEntries->second) {
- bool IsNegative = CatIt.first() == "emit";
- for (const auto &GlobIt : CatIt.second.Globs) {
- if (GlobIt.getKeyLength() < LongestMatch.size())
- continue;
- if (!GlobIt.second.first.match(FilePath))
- continue;
- LongestMatch = GlobIt.getKey();
- LongestWasNegative = IsNegative;
- }
- }
- return !LongestMatch.empty() && !LongestWasNegative;
- }
-
-private:
- llvm::DenseMap<diag::kind, const Section *> DiagToSection;
-};
-
-void parseSuppressionMappings(const llvm::MemoryBuffer &MB,
- DiagnosticsEngine &Diags) {
- std::string Err;
- auto SCL = WarningsSpecialCaseList::create(MB, Err);
- if (!SCL) {
- Diags.Report(diag::err_drv_malformed_warning_suppression_mapping)
- << MB.getBufferIdentifier() << Err;
- return;
- }
- SCL->processSections(Diags);
- Diags.setDiagSuppressionMapping(
- [SCL(std::move(SCL))](diag::kind K, llvm::StringRef Path) {
- return SCL->isDiagSuppressed(K, Path);
- });
-}
-} // namespace
void clang::ProcessWarningOptions(DiagnosticsEngine &Diags,
const DiagnosticOptions &Opts,
@@ -169,11 +77,13 @@ void clang::ProcessWarningOptions(DiagnosticsEngine &Diags,
else
Diags.setExtensionHandlingBehavior(diag::Severity::Ignored);
- if (!Opts.SuppressionMappingsFile.empty()) {
- if (auto Buf = VFS.getBufferForFile(Opts.SuppressionMappingsFile)) {
- parseSuppressionMappings(**Buf, Diags);
+ if (!Opts.DiagnosticSuppressionMappingsFile.empty()) {
+ if (auto Buf =
+ VFS.getBufferForFile(Opts.DiagnosticSuppressionMappingsFile)) {
+ Diags.setDiagSuppressionMapping(**Buf);
} else if (ReportDiags) {
- Diags.Report(diag::err_drv_no_such_file) << Opts.SuppressionMappingsFile;
+ Diags.Report(diag::err_drv_no_such_file)
+ << Opts.DiagnosticSuppressionMappingsFile;
}
}
diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp
index e26a1300fc8720..e3145dcacf58d1 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -2522,9 +2522,9 @@ void CompilerInvocationBase::GenerateDiagnosticArgs(
Consumer(StringRef("-R") + Remark);
}
- if (!Opts.SuppressionMappingsFile.empty()) {
+ if (!Opts.DiagnosticSuppressionMappingsFile.empty()) {
GenerateArg(Consumer, OPT_warning_suppression_mappings_EQ,
- Opts.SuppressionMappingsFile);
+ Opts.DiagnosticSuppressionMappingsFile);
}
}
@@ -2603,7 +2603,7 @@ bool clang::ParseDiagnosticArgs(DiagnosticOptions &Opts, ArgList &Args,
}
if (const Arg *A = Args.getLastArg(OPT_warning_suppression_mappings_EQ))
- Opts.SuppressionMappingsFile = A->getValue();
+ Opts.DiagnosticSuppressionMappingsFile = A->getValue();
addDiagnosticArgs(Args, OPT_W_Group, OPT_W_value_Group, Opts.Warnings);
addDiagnosticArgs(Args, OPT_R_Group, OPT_R_value_Group, Opts.Remarks);
diff --git a/clang/unittests/Basic/DiagnosticTest.cpp b/clang/unittests/Basic/DiagnosticTest.cpp
index 387ae5da14aff0..62996fee4bb778 100644
--- a/clang/unittests/Basic/DiagnosticTest.cpp
+++ b/clang/unittests/Basic/DiagnosticTest.cpp
@@ -210,14 +210,14 @@ MATCHER_P(WithMessage, Msg, "has diagnostic message") {
}
TEST_F(SuppressionMappingTest, MissingMappingFile) {
- Diags.getDiagnosticOptions().SuppressionMappingsFile = "foo.txt";
+ 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().SuppressionMappingsFile = "foo.txt";
+ Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";
FS->addFile("foo.txt", /*ModificationTime=*/{},
llvm::MemoryBuffer::getMemBuffer("asdf", "foo.txt"));
clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);
@@ -227,7 +227,7 @@ TEST_F(SuppressionMappingTest, MalformedFile) {
}
TEST_F(SuppressionMappingTest, UnknownDiagName) {
- Diags.getDiagnosticOptions().SuppressionMappingsFile = "foo.txt";
+ Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";
FS->addFile("foo.txt", /*ModificationTime=*/{},
llvm::MemoryBuffer::getMemBuffer("[non-existing-warning]"));
clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);
@@ -241,7 +241,7 @@ TEST_F(SuppressionMappingTest, SuppressesGroup) {
[unused]
src:*
)txt";
- Diags.getDiagnosticOptions().SuppressionMappingsFile = "foo.txt";
+ Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";
FS->addFile("foo.txt", /*ModificationTime=*/{},
llvm::MemoryBuffer::getMemBuffer(SuppressionMappingFile));
clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);
@@ -258,7 +258,7 @@ TEST_F(SuppressionMappingTest, ExclusionsWork) {
src:*
src:*foo.cpp=emit
)txt";
- Diags.getDiagnosticOptions().SuppressionMappingsFile = "foo.txt";
+ Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";
FS->addFile("foo.txt", /*ModificationTime=*/{},
llvm::MemoryBuffer::getMemBuffer(SuppressionMappingFile));
clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);
@@ -277,7 +277,7 @@ TEST_F(SuppressionMappingTest, LongestMatchWins) {
src:*clang/lib/Sema/*=emit
src:*clang/lib/Sema/foo*
)txt";
- Diags.getDiagnosticOptions().SuppressionMappingsFile = "foo.txt";
+ Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";
FS->addFile("foo.txt", /*ModificationTime=*/{},
llvm::MemoryBuffer::getMemBuffer(SuppressionMappingFile));
clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);
@@ -296,7 +296,7 @@ TEST_F(SuppressionMappingTest, IsIgnored) {
[unused]
src:*clang/*
)txt";
- Diags.getDiagnosticOptions().SuppressionMappingsFile = "foo.txt";
+ Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";
Diags.getDiagnosticOptions().Warnings = {"unused"};
FS->addFile("foo.txt", /*ModificationTime=*/{},
llvm::MemoryBuffer::getMemBuffer(SuppressionMappingFile));
diff --git a/clang/unittests/Frontend/CompilerInvocationTest.cpp b/clang/unittests/Frontend/CompilerInvocationTest.cpp
index 1cf2bbb6576621..45478de5e6f7c8 100644
--- a/clang/unittests/Frontend/CompilerInvocationTest.cpp
+++ b/clang/unittests/Frontend/CompilerInvocationTest.cpp
@@ -1051,7 +1051,8 @@ TEST_F(CommandLineTest, WarningSuppressionMappings) {
const char *Args[] = {"--warning-suppression-mappings=foo.txt"};
EXPECT_TRUE(CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags));
- EXPECT_EQ(Invocation.getDiagnosticOpts().SuppressionMappingsFile, "foo.txt");
+ EXPECT_EQ(Invocation.getDiagnosticOpts().DiagnosticSuppressionMappingsFile,
+ "foo.txt");
Invocation.generateCC1CommandLine(GeneratedArgs, *this);
EXPECT_THAT(GeneratedArgs, Contains(StrEq(Args[0])));
>From d7d792b3224df3f11640ade439d85c6d0256ce9f Mon Sep 17 00:00:00 2001
From: Kadir Cetinkaya <kadircet at google.com>
Date: Mon, 28 Oct 2024 12:18:52 +0100
Subject: [PATCH 4/7] Add docs and address comments
---
clang/docs/ReleaseNotes.rst | 3 +
clang/docs/WarningSuppressionMappings.rst | 92 +++++++++++++++++++
clang/include/clang/Basic/Diagnostic.h | 11 ++-
clang/include/clang/Basic/DiagnosticOptions.h | 2 +-
clang/include/clang/Driver/Options.td | 2 +-
clang/lib/Basic/Diagnostic.cpp | 64 +++++++------
6 files changed, 140 insertions(+), 34 deletions(-)
create mode 100644 clang/docs/WarningSuppressionMappings.rst
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 1372e49dfac03c..70a59328c139c4 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -801,6 +801,9 @@ New features
attribute, the compiler can generate warnings about the use of any language features, or calls to
other functions, which may block.
+- Introduced ``-warning-suppression-mappings`` flag to control diagnostic
+ suppressions per file. See `documentation <https://clang.llvm.org/docs/WarningSuppressionMappings.html>_` for details.
+
Crash and bug fixes
^^^^^^^^^^^^^^^^^^^
diff --git a/clang/docs/WarningSuppressionMappings.rst b/clang/docs/WarningSuppressionMappings.rst
new file mode 100644
index 00000000000000..0cab48b8972538
--- /dev/null
+++ b/clang/docs/WarningSuppressionMappings.rst
@@ -0,0 +1,92 @@
+============================
+Warning suppression mappings
+============================
+
+.. contents::
+ :local:
+
+Introduction
+============
+
+Warning suppression mappings enables 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 dependencies or other parts of
+the codebase.
+
+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 at large codebases is quite difficult
+today:
+- It requires cleaning up all the existing warnings, which might not be possible
+ when some dependencies aren't in project owner's control.
+- Preventing backsliding in the meanwhile as the diagnostic can't be enforced at
+ all until codebase is cleaned up.
+
+Warning suppression mappings aims to alleviate some of these concerns by making
+diagnostic configuration granularity finer, at a source file level.
+
+To achieve this, user may create a file listing which diagnostic groups to
+suppress in which files, and pass it as a command line argument to clang with
+``--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 <foo/bar.h>
+ 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 a format similar to
+: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 ``emit`` category to exclude a subdirectory from suppression.
+Source files are matched against these globs either as paths relative to current
+working directory, or as absolute paths.
+When a source file matches multiple globs, the longest one takes precendence.
+
+.. code-block:: bash
+
+ # Lines starting with # are ignored.
+ # Configure suppression globs for `-Wunused` warnings
+ [unused]
+ # Suppress on all files by default.
+ src:*
+ # But enforce for all the sources under foo/.
+ src:*foo/*=emit
+
+ # unused-function warnings are a subgroup of `-Wunused`. So this section
+ # takes precedence over the previous one for unused-function warnings, but
+ # not for unused-variable warnings.
+ [unused-function]
+ # Only suppress for sources under bar/.
+ src:*bar/*
diff --git a/clang/include/clang/Basic/Diagnostic.h b/clang/include/clang/Basic/Diagnostic.h
index d3706460a62a0f..ed3a652be97f15 100644
--- a/clang/include/clang/Basic/Diagnostic.h
+++ b/clang/include/clang/Basic/Diagnostic.h
@@ -953,11 +953,12 @@ class DiagnosticsEngine : public RefCountedBase<DiagnosticsEngine> {
return (Level)Diags->getDiagnosticLevel(DiagID, Loc, *this);
}
- /// Diagnostic suppression mappings can be used to ignore diagnostics based on
- /// the file they occur in. 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:
+ /// 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:
/// [unused]
/// src:*clang/*
/// src:*clang/foo/*=emit
diff --git a/clang/include/clang/Basic/DiagnosticOptions.h b/clang/include/clang/Basic/DiagnosticOptions.h
index 53ffb265cdc870..e2faf3d0df95cb 100644
--- a/clang/include/clang/Basic/DiagnosticOptions.h
+++ b/clang/include/clang/Basic/DiagnosticOptions.h
@@ -108,7 +108,7 @@ class DiagnosticOptions : public RefCountedBase<DiagnosticOptions>{
/// The file to serialize diagnostics to (non-appending).
std::string DiagnosticSerializationFile;
- /// File that defines diagnostic suppression mappings.
+ /// Path for the file that defines diagnostic suppression mappings.
std::string DiagnosticSuppressionMappingsFile;
/// The list of -W... options used to alter the diagnostic mappings, with the
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 83a7bb4d2e09d6..7e4e4c7a76a07e 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -9020,5 +9020,5 @@ def wasm_opt : Flag<["--"], "wasm-opt">,
def warning_suppression_mappings_EQ : Joined<["--"],
"warning-suppression-mappings=">, Group<Diag_Group>,
- HelpText<"File containing list of sources to suppress diagnostics for. See XXX for format">,
+ HelpText<"File containing list of sources to suppress diagnostics for.">,
Visibility<[ClangOption, CC1Option]>;
diff --git a/clang/lib/Basic/Diagnostic.cpp b/clang/lib/Basic/Diagnostic.cpp
index beea68c003e2fb..677f2bcf69b43a 100644
--- a/clang/lib/Basic/Diagnostic.cpp
+++ b/clang/lib/Basic/Diagnostic.cpp
@@ -27,6 +27,7 @@
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/ConvertUTF.h"
#include "llvm/Support/CrashRecoveryContext.h"
@@ -491,9 +492,9 @@ class WarningsSpecialCaseList : public llvm::SpecialCaseList {
static std::unique_ptr<WarningsSpecialCaseList>
create(const llvm::MemoryBuffer &MB, std::string &Err) {
auto SCL = std::make_unique<WarningsSpecialCaseList>();
- if (SCL->createInternal(&MB, Err))
- return SCL;
- return nullptr;
+ if (!SCL->createInternal(&MB, Err))
+ return nullptr;
+ return SCL;
}
// Section names refer to diagnostic groups, which cover multiple individual
@@ -506,26 +507,28 @@ class WarningsSpecialCaseList : public llvm::SpecialCaseList {
Sections.erase("*");
// Make sure we iterate sections by their line numbers.
std::vector<std::pair<unsigned, const llvm::StringMapEntry<Section> *>>
- LineAndSection;
+ LineAndSectionEntry;
+ LineAndSectionEntry.reserve(Sections.size());
for (const auto &Entry : Sections) {
- LineAndSection.emplace_back(
+ LineAndSectionEntry.emplace_back(
Entry.second.SectionMatcher->Globs.at(Entry.first()).second, &Entry);
}
- llvm::sort(LineAndSection);
+ llvm::sort(LineAndSectionEntry);
static constexpr auto kFlavor = clang::diag::Flavor::WarningOrError;
- for (const auto &[_, Entry] : LineAndSection) {
+ for (const auto &[_, SectionEntry] : LineAndSectionEntry) {
SmallVector<diag::kind, 256> GroupDiags;
- if (Diags.getDiagnosticIDs()->getDiagnosticsInGroup(
- kFlavor, Entry->first(), GroupDiags)) {
+ llvm::StringRef DiagGroup = SectionEntry->getKey();
+ if (Diags.getDiagnosticIDs()->getDiagnosticsInGroup(kFlavor, DiagGroup,
+ GroupDiags)) {
StringRef Suggestion =
- DiagnosticIDs::getNearestOption(kFlavor, Entry->first());
+ DiagnosticIDs::getNearestOption(kFlavor, DiagGroup);
Diags.Report(diag::warn_unknown_diag_option)
- << static_cast<unsigned>(kFlavor) << Entry->first()
+ << static_cast<unsigned>(kFlavor) << DiagGroup
<< !Suggestion.empty() << Suggestion;
continue;
}
- for (auto D : GroupDiags)
- DiagToSection[D] = &Entry->getValue();
+ for (diag::kind D : GroupDiags)
+ DiagToSection[D] = &SectionEntry->getValue();
}
}
@@ -533,28 +536,35 @@ class WarningsSpecialCaseList : public llvm::SpecialCaseList {
auto Section = DiagToSection.find(D);
if (Section == DiagToSection.end())
return false;
- auto SrcEntries = Section->second->Entries.find("src");
- if (SrcEntries == Section->second->Entries.end())
+ auto &DiagEntries = Section->second->Entries;
+ auto SrcEntries = DiagEntries.find("src");
+ if (SrcEntries == DiagEntries.end())
return false;
- // Find the longest glob pattern that matches FilePath. A positive match
- // implies D should be suppressed for FilePath.
+ return globsMatches(SrcEntries->second, FilePath);
+ }
+
+private:
+ // Find the longest glob pattern that matches FilePath amongst
+ // CategoriesToMatchers, return true iff the match exists and belongs to a
+ // positive category.
+ bool globsMatches(llvm::StringMap<Matcher> CategoriesToMatchers,
+ llvm::StringRef FilePath) const {
llvm::StringRef LongestMatch;
- bool LongestWasNegative;
- for (const auto &CatIt : SrcEntries->second) {
- bool IsNegative = CatIt.first() == "emit";
- for (const auto &GlobIt : CatIt.second.Globs) {
- if (GlobIt.getKeyLength() < LongestMatch.size())
+ bool LongestIsPositive = false;
+ for (const auto &[Category, Matcher] : CategoriesToMatchers) {
+ bool IsPositive = Category != "emit";
+ for (const auto &[Pattern, Glob] : Matcher.Globs) {
+ if (Pattern.size() < LongestMatch.size())
continue;
- if (!GlobIt.second.first.match(FilePath))
+ if (!Glob.first.match(FilePath))
continue;
- LongestMatch = GlobIt.getKey();
- LongestWasNegative = IsNegative;
+ LongestMatch = Pattern;
+ LongestIsPositive = IsPositive;
}
}
- return !LongestMatch.empty() && !LongestWasNegative;
+ return LongestIsPositive;
}
-private:
llvm::DenseMap<diag::kind, const Section *> DiagToSection;
};
} // namespace
>From 60105195020a97fa33da1c5c207a7171f24a7b74 Mon Sep 17 00:00:00 2001
From: Kadir Cetinkaya <kadircet at google.com>
Date: Tue, 29 Oct 2024 15:08:37 +0100
Subject: [PATCH 5/7] update usersmanual && some renaming
---
clang/docs/UsersManual.rst | 27 ++++++++++++++++++++++++++
clang/include/clang/Basic/Diagnostic.h | 12 +++++++-----
clang/include/clang/Driver/Options.td | 4 ++--
clang/lib/Basic/Diagnostic.cpp | 12 ++++++------
clang/lib/Basic/DiagnosticIDs.cpp | 6 ++++--
clang/lib/Basic/Warnings.cpp | 1 -
6 files changed, 46 insertions(+), 16 deletions(-)
diff --git a/clang/docs/UsersManual.rst b/clang/docs/UsersManual.rst
index 4f03388bc87bd0..d3a79191a96ca5 100644
--- a/clang/docs/UsersManual.rst
+++ b/clang/docs/UsersManual.rst
@@ -151,6 +151,10 @@ Options to Control Error and Warning Messages
instantiation backtrace for a single warning or error. The default is 10, and
the limit can be disabled with `-ftemplate-backtrace-limit=0`.
+.. option:: --warning-suppression-mappings=foo.txt
+
+ :ref:`Suppress certain diagnostics for certain files. <warning_suppression_mappings>`
+
.. _cl_diag_formatting:
Formatting of Diagnostics
@@ -1315,6 +1319,29 @@ 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 enables 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 dependencies or other parts of
+the codebase.
+
+.. 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.
+
+See :doc:`WarningSuppressionMappings` for details about the file format and
+functionality.
+
Controlling Static Analyzer Diagnostics
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/include/clang/Basic/Diagnostic.h b/clang/include/clang/Basic/Diagnostic.h
index ed3a652be97f15..9584ca8d795d2f 100644
--- a/clang/include/clang/Basic/Diagnostic.h
+++ b/clang/include/clang/Basic/Diagnostic.h
@@ -960,17 +960,19 @@ class DiagnosticsEngine : public RefCountedBase<DiagnosticsEngine> {
/// can be used to disable suppression. Longest glob that matches a filepath
/// takes precendence. For example:
/// [unused]
- /// src:*clang/*
- /// src:*clang/foo/*=emit
- /// src:*clang/foo/bar/*
+ /// src:clang/*
+ /// src:clang/foo/*=emit
+ /// src:clang/foo/bar/*
///
/// Such a mappings file suppress all diagnostics produced by -Wunused in all
/// sources under `clang/` directory apart from `clang/foo/`. Diagnostics
- /// under `clang/foo/bar/` will also be suppressed.
+ /// under `clang/foo/bar/` will also be suppressed. Note that the FilePath is
+ /// matched against the globs as-is.
/// These take presumed locations into account, and can still be overriden by
/// clang-diagnostics pragmas.
void setDiagSuppressionMapping(llvm::MemoryBuffer &MB);
- bool isSuppressedViaMapping(diag::kind D, llvm::StringRef FilePath) const;
+ bool isSuppressedViaMapping(diag::kind DiagId,
+ llvm::StringRef FilePath) const;
/// Issue the message to the client.
///
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 7e4e4c7a76a07e..ab536465bb5549 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -9020,5 +9020,5 @@ def wasm_opt : Flag<["--"], "wasm-opt">,
def warning_suppression_mappings_EQ : Joined<["--"],
"warning-suppression-mappings=">, Group<Diag_Group>,
- HelpText<"File containing list of sources to suppress diagnostics for.">,
- Visibility<[ClangOption, CC1Option]>;
+ HelpText<"File containing diagnostic suppresion mappings. See user manual for"
+ " file format.">, Visibility<[ClangOption, CC1Option]>;
diff --git a/clang/lib/Basic/Diagnostic.cpp b/clang/lib/Basic/Diagnostic.cpp
index 677f2bcf69b43a..4a020b006f0aae 100644
--- a/clang/lib/Basic/Diagnostic.cpp
+++ b/clang/lib/Basic/Diagnostic.cpp
@@ -532,8 +532,8 @@ class WarningsSpecialCaseList : public llvm::SpecialCaseList {
}
}
- bool isDiagSuppressed(diag::kind D, llvm::StringRef FilePath) const {
- auto Section = DiagToSection.find(D);
+ 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;
@@ -578,15 +578,15 @@ void DiagnosticsEngine::setDiagSuppressionMapping(llvm::MemoryBuffer &MB) {
return;
}
SCL->processSections(*this);
- DiagSuppressionMapping = [SCL(std::move(SCL))](diag::kind K,
+ DiagSuppressionMapping = [SCL(std::move(SCL))](diag::kind DiagId,
llvm::StringRef Path) {
- return SCL->isDiagSuppressed(K, Path);
+ return SCL->isDiagSuppressed(DiagId, Path);
};
}
-bool DiagnosticsEngine::isSuppressedViaMapping(diag::kind D,
+bool DiagnosticsEngine::isSuppressedViaMapping(diag::kind DiagId,
llvm::StringRef FilePath) const {
- return DiagSuppressionMapping && DiagSuppressionMapping(D, FilePath);
+ return DiagSuppressionMapping && DiagSuppressionMapping(DiagId, FilePath);
}
void DiagnosticsEngine::Report(const StoredDiagnostic &storedDiag) {
diff --git a/clang/lib/Basic/DiagnosticIDs.cpp b/clang/lib/Basic/DiagnosticIDs.cpp
index 463b081ee80b38..042a55654c06d5 100644
--- a/clang/lib/Basic/DiagnosticIDs.cpp
+++ b/clang/lib/Basic/DiagnosticIDs.cpp
@@ -17,6 +17,7 @@
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/Path.h"
#include <map>
#include <optional>
using namespace clang;
@@ -606,8 +607,9 @@ DiagnosticIDs::getDiagnosticSeverity(unsigned DiagID, SourceLocation Loc,
// preprocessed inputs.
if (!Mapping.isPragma()) {
if (auto PLoc = SM.getPresumedLoc(Loc);
- PLoc.isValid() &&
- Diag.isSuppressedViaMapping(DiagID, PLoc.getFilename()))
+ PLoc.isValid() && Diag.isSuppressedViaMapping(
+ DiagID, llvm::sys::path::remove_leading_dotslash(
+ PLoc.getFilename())))
return diag::Severity::Ignored;
}
diff --git a/clang/lib/Basic/Warnings.cpp b/clang/lib/Basic/Warnings.cpp
index bcad90abf1c21b..7b5d68b9f0eb96 100644
--- a/clang/lib/Basic/Warnings.cpp
+++ b/clang/lib/Basic/Warnings.cpp
@@ -46,7 +46,6 @@ static void EmitUnknownDiagWarning(DiagnosticsEngine &Diags,
<< (Prefix.str() += std::string(Suggestion));
}
-
void clang::ProcessWarningOptions(DiagnosticsEngine &Diags,
const DiagnosticOptions &Opts,
llvm::vfs::FileSystem &VFS,
>From 2c6be5467c97ec69e073bacabae3ca56716431a1 Mon Sep 17 00:00:00 2001
From: Kadir Cetinkaya <kadircet at google.com>
Date: Thu, 31 Oct 2024 15:37:36 +0100
Subject: [PATCH 6/7] Address comments
- Add more names to intermediate variables.
- Rewording in docs
---
clang/docs/UsersManual.rst | 11 ++--
clang/docs/WarningSuppressionMappings.rst | 36 +++++------
clang/include/clang/Basic/Diagnostic.h | 8 ++-
.../clang/Basic/DiagnosticDriverKinds.td | 2 +-
clang/lib/Basic/Diagnostic.cpp | 63 +++++++++++--------
clang/lib/Basic/DiagnosticIDs.cpp | 2 +-
clang/lib/Driver/ToolChains/Clang.cpp | 8 +--
.../test/Misc/Inputs/suppression-mapping.txt | 5 ++
.../Misc/warning-suppression-mappings.cpp | 22 +++++--
clang/unittests/Basic/DiagnosticTest.cpp | 2 +-
10 files changed, 93 insertions(+), 66 deletions(-)
diff --git a/clang/docs/UsersManual.rst b/clang/docs/UsersManual.rst
index d3a79191a96ca5..4bf56c0c332790 100644
--- a/clang/docs/UsersManual.rst
+++ b/clang/docs/UsersManual.rst
@@ -1324,10 +1324,9 @@ disabling all warnings wins.
Controlling Diagnostics via Suppression Mappings
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-Warning suppression mappings enables users to suppress clang's diagnostics in a
+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 dependencies or other parts of
-the codebase.
+of the project, even if there are violations in some headers.
.. code-block:: console
@@ -1337,7 +1336,11 @@ the codebase.
$ 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.
+ # 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.
+
See :doc:`WarningSuppressionMappings` for details about the file format and
functionality.
diff --git a/clang/docs/WarningSuppressionMappings.rst b/clang/docs/WarningSuppressionMappings.rst
index 0cab48b8972538..540821ce08f923 100644
--- a/clang/docs/WarningSuppressionMappings.rst
+++ b/clang/docs/WarningSuppressionMappings.rst
@@ -8,29 +8,26 @@ Warning suppression mappings
Introduction
============
-Warning suppression mappings enables users to suppress clang's diagnostics in a
+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 dependencies or other parts of
-the codebase.
+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 at large codebases is quite difficult
-today:
-- It requires cleaning up all the existing warnings, which might not be possible
- when some dependencies aren't in project owner's control.
-- Preventing backsliding in the meanwhile as the diagnostic can't be enforced at
- all until codebase is cleaned up.
-
-Warning suppression mappings aims to alleviate some of these concerns by making
+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 may create a file listing which diagnostic groups to
-suppress in which files, and pass it as a command line argument to clang with
-``--warning-suppression-mappings`` flag.
+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.
@@ -58,7 +55,7 @@ Example
Format
======
-Warning suppression mappings uses a format similar to
+Warning suppression mappings uses the same format as
:doc:`SanitizerSpecialCaseList`.
Users can mention sections to describe which diagnostic group behaviours to
@@ -69,9 +66,10 @@ 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 ``emit`` category to exclude a subdirectory from suppression.
-Source files are matched against these globs either as paths relative to current
-working directory, or as absolute paths.
+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.
.. code-block:: bash
diff --git a/clang/include/clang/Basic/Diagnostic.h b/clang/include/clang/Basic/Diagnostic.h
index 9584ca8d795d2f..2861e354657438 100644
--- a/clang/include/clang/Basic/Diagnostic.h
+++ b/clang/include/clang/Basic/Diagnostic.h
@@ -26,8 +26,6 @@
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/iterator_range.h"
#include "llvm/Support/Compiler.h"
-#include "llvm/Support/MemoryBuffer.h"
-#include "llvm/Support/VirtualFileSystem.h"
#include <cassert>
#include <cstdint>
#include <limits>
@@ -43,6 +41,10 @@
namespace llvm {
class Error;
class raw_ostream;
+class MemoryBuffer;
+namespace vfs {
+class FileSystem;
+} // namespace vfs
} // namespace llvm
namespace clang {
@@ -970,7 +972,7 @@ class DiagnosticsEngine : public RefCountedBase<DiagnosticsEngine> {
/// matched against the globs as-is.
/// These take presumed locations into account, and can still be overriden by
/// clang-diagnostics pragmas.
- void setDiagSuppressionMapping(llvm::MemoryBuffer &MB);
+ void setDiagSuppressionMapping(llvm::MemoryBuffer &Input);
bool isSuppressedViaMapping(diag::kind DiagId,
llvm::StringRef FilePath) const;
diff --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td b/clang/include/clang/Basic/DiagnosticDriverKinds.td
index 26743c10c55779..76fdbdbfb01d94 100644
--- a/clang/include/clang/Basic/DiagnosticDriverKinds.td
+++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td
@@ -836,5 +836,5 @@ def warn_missing_include_dirs : Warning<
"no such include directory: '%0'">, InGroup<MissingIncludeDirs>, DefaultIgnore;
def err_drv_malformed_warning_suppression_mapping : Error<
- "failed to process suppression mapping file '%0' : %1">;
+ "failed to process suppression mapping file '%0': %1">;
}
diff --git a/clang/lib/Basic/Diagnostic.cpp b/clang/lib/Basic/Diagnostic.cpp
index 4a020b006f0aae..22a925f9cbdaae 100644
--- a/clang/lib/Basic/Diagnostic.cpp
+++ b/clang/lib/Basic/Diagnostic.cpp
@@ -510,48 +510,56 @@ class WarningsSpecialCaseList : public llvm::SpecialCaseList {
LineAndSectionEntry;
LineAndSectionEntry.reserve(Sections.size());
for (const auto &Entry : Sections) {
- LineAndSectionEntry.emplace_back(
- Entry.second.SectionMatcher->Globs.at(Entry.first()).second, &Entry);
+ llvm::StringRef DiagName = Entry.first();
+ // Each section has a matcher with that section's name, attached to that
+ // line.
+ const auto &DiagSection = Entry.second.SectionMatcher;
+ unsigned DiagLine = DiagSection->Globs.at(DiagName).second;
+ LineAndSectionEntry.emplace_back(DiagLine, &Entry);
}
llvm::sort(LineAndSectionEntry);
- static constexpr auto kFlavor = clang::diag::Flavor::WarningOrError;
+ static constexpr auto WarningFlavor = clang::diag::Flavor::WarningOrError;
for (const auto &[_, SectionEntry] : LineAndSectionEntry) {
- SmallVector<diag::kind, 256> GroupDiags;
+ SmallVector<diag::kind> GroupDiags;
llvm::StringRef DiagGroup = SectionEntry->getKey();
- if (Diags.getDiagnosticIDs()->getDiagnosticsInGroup(kFlavor, DiagGroup,
- GroupDiags)) {
+ if (Diags.getDiagnosticIDs()->getDiagnosticsInGroup(
+ WarningFlavor, DiagGroup, GroupDiags)) {
StringRef Suggestion =
- DiagnosticIDs::getNearestOption(kFlavor, DiagGroup);
+ DiagnosticIDs::getNearestOption(WarningFlavor, DiagGroup);
Diags.Report(diag::warn_unknown_diag_option)
- << static_cast<unsigned>(kFlavor) << DiagGroup
+ << static_cast<unsigned>(WarningFlavor) << DiagGroup
<< !Suggestion.empty() << Suggestion;
continue;
}
- for (diag::kind D : GroupDiags)
- DiagToSection[D] = &SectionEntry->getValue();
+ for (diag::kind Diag : GroupDiags)
+ DiagToSection[Diag] = &SectionEntry->getValue();
}
}
bool isDiagSuppressed(diag::kind DiagId, llvm::StringRef FilePath) const {
- auto Section = DiagToSection.find(DiagId);
- if (Section == DiagToSection.end())
+ const Section *DiagSection = DiagToSection.lookup(DiagId);
+ if (!DiagSection)
return false;
- auto &DiagEntries = Section->second->Entries;
- auto SrcEntries = DiagEntries.find("src");
- if (SrcEntries == DiagEntries.end())
+ const SectionEntries &EntityTypeToCategories = DiagSection->Entries;
+ auto SrcEntriesIt = EntityTypeToCategories.find("src");
+ if (SrcEntriesIt == EntityTypeToCategories.end())
return false;
- return globsMatches(SrcEntries->second, FilePath);
+ const llvm::StringMap<llvm::SpecialCaseList::Matcher>
+ &CategoriesToMatchers = SrcEntriesIt->second;
+ return globsMatches(CategoriesToMatchers, FilePath);
}
private:
// Find the longest glob pattern that matches FilePath amongst
// CategoriesToMatchers, return true iff the match exists and belongs to a
// positive category.
- bool globsMatches(llvm::StringMap<Matcher> CategoriesToMatchers,
+ bool globsMatches(const llvm::StringMap<Matcher> &CategoriesToMatchers,
llvm::StringRef FilePath) const {
llvm::StringRef LongestMatch;
bool LongestIsPositive = false;
- for (const auto &[Category, Matcher] : CategoriesToMatchers) {
+ for (const auto &Entry : CategoriesToMatchers) {
+ llvm::StringRef Category = Entry.first();
+ const auto &Matcher = Entry.second;
bool IsPositive = Category != "emit";
for (const auto &[Pattern, Glob] : Matcher.Globs) {
if (Pattern.size() < LongestMatch.size())
@@ -569,19 +577,20 @@ class WarningsSpecialCaseList : public llvm::SpecialCaseList {
};
} // namespace
-void DiagnosticsEngine::setDiagSuppressionMapping(llvm::MemoryBuffer &MB) {
+void DiagnosticsEngine::setDiagSuppressionMapping(llvm::MemoryBuffer &Input) {
std::string Err;
- auto SCL = WarningsSpecialCaseList::create(MB, Err);
- if (!SCL) {
+ auto WarningSuppressionList = WarningsSpecialCaseList::create(Input, Err);
+ if (!WarningSuppressionList) {
Report(diag::err_drv_malformed_warning_suppression_mapping)
- << MB.getBufferIdentifier() << Err;
+ << Input.getBufferIdentifier() << Err;
return;
}
- SCL->processSections(*this);
- DiagSuppressionMapping = [SCL(std::move(SCL))](diag::kind DiagId,
- llvm::StringRef Path) {
- return SCL->isDiagSuppressed(DiagId, Path);
- };
+ WarningSuppressionList->processSections(*this);
+ DiagSuppressionMapping =
+ [WarningSuppressionList(std::move(WarningSuppressionList))](
+ diag::kind DiagId, llvm::StringRef Path) {
+ return WarningSuppressionList->isDiagSuppressed(DiagId, Path);
+ };
}
bool DiagnosticsEngine::isSuppressedViaMapping(diag::kind DiagId,
diff --git a/clang/lib/Basic/DiagnosticIDs.cpp b/clang/lib/Basic/DiagnosticIDs.cpp
index 042a55654c06d5..b88d6ffdc3410e 100644
--- a/clang/lib/Basic/DiagnosticIDs.cpp
+++ b/clang/lib/Basic/DiagnosticIDs.cpp
@@ -606,7 +606,7 @@ DiagnosticIDs::getDiagnosticSeverity(unsigned DiagID, SourceLocation Loc,
// We also use presumed locations here to improve reproducibility for
// preprocessed inputs.
if (!Mapping.isPragma()) {
- if (auto PLoc = SM.getPresumedLoc(Loc);
+ if (PresumedLoc PLoc = SM.getPresumedLoc(Loc);
PLoc.isValid() && Diag.isSuppressedViaMapping(
DiagID, llvm::sys::path::remove_leading_dotslash(
PLoc.getFilename())))
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index b7768c1cd00d24..7c33cb93ee1ec3 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -44,6 +44,7 @@
#include "llvm/ADT/ScopeExit.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringRef.h"
#include "llvm/BinaryFormat/Magic.h"
#include "llvm/Config/llvm-config.h"
#include "llvm/Frontend/Debug/Options.h"
@@ -4515,12 +4516,7 @@ static void RenderDiagnosticsOptions(const Driver &D, const ArgList &Args,
Args.addOptOutFlag(CmdArgs, options::OPT_fspell_checking,
options::OPT_fno_spell_checking);
- if (const Arg *A =
- Args.getLastArg(options::OPT_warning_suppression_mappings_EQ)) {
- if (!D.getVFS().exists(A->getValue()))
- D.Diag(clang::diag::err_drv_no_such_file) << A->getValue();
- CmdArgs.push_back(Args.MakeArgString(A->getSpelling() + A->getValue()));
- }
+ Args.addLastArg(CmdArgs, options::OPT_warning_suppression_mappings_EQ);
}
DwarfFissionKind tools::getDebugFissionKind(const Driver &D,
diff --git a/clang/test/Misc/Inputs/suppression-mapping.txt b/clang/test/Misc/Inputs/suppression-mapping.txt
index d1fd0e89256d3b..1784e82cf8f7ac 100644
--- a/clang/test/Misc/Inputs/suppression-mapping.txt
+++ b/clang/test/Misc/Inputs/suppression-mapping.txt
@@ -2,3 +2,8 @@
[unused]
src:*
src:*foo/*=emit
+
+# Some warning groups can have strange spellings.
+[format=2]
+src:*
+src:*foo/*=emit
diff --git a/clang/test/Misc/warning-suppression-mappings.cpp b/clang/test/Misc/warning-suppression-mappings.cpp
index 0626d7279e804a..fe930df70fd9ba 100644
--- a/clang/test/Misc/warning-suppression-mappings.cpp
+++ b/clang/test/Misc/warning-suppression-mappings.cpp
@@ -4,13 +4,27 @@
// MISSING_MAPPING: error: no such file or directory: 'foo.txt'
// Check that it's no-op when diagnostics aren't enabled.
-// RUN: %clang -cc1 -Werror --warning-suppression-mappings=%S/Inputs/suppression-mapping.txt -fsyntax-only %s 2>&1 | FileCheck -check-prefix WARNINGS_DISABLED --allow-empty %s
+// RUN: %clang -cc1 -Wno-everything -Werror --warning-suppression-mappings=%S/Inputs/suppression-mapping.txt -fsyntax-only %s 2>&1 | FileCheck -check-prefix WARNINGS_DISABLED --allow-empty %s
// WARNINGS_DISABLED-NOT: warning:
// WARNINGS_DISABLED-NOT: error:
-// RUN: %clang -cc1 -verify -Wunused --warning-suppression-mappings=%S/Inputs/suppression-mapping.txt -fsyntax-only %s
+// RUN: %clang -cc1 -verify -Wformat=2 -Wunused --warning-suppression-mappings=%S/Inputs/suppression-mapping.txt -fsyntax-only %s
-namespace { void foo(); }
+__attribute__((__format__ (__printf__, 1, 2)))
+void format_err(const char* const pString, ...);
+
+namespace {
+void foo() {
+ const char *x;
+ format_err(x); // Warning suppressed here.
+}
+}
#line 42 "foo/bar.h"
-namespace { void bar(); } // expected-warning{{unused function 'bar'}}
+namespace {
+void bar() { // expected-warning{{unused function 'bar'}}
+ const char *x;
+ format_err(x); // expected-warning{{format string is not a string literal (potentially insecure)}} \
+ // expected-note{{treat the string as an argument to avoid this}}
+}
+}
diff --git a/clang/unittests/Basic/DiagnosticTest.cpp b/clang/unittests/Basic/DiagnosticTest.cpp
index 62996fee4bb778..3285b29555f801 100644
--- a/clang/unittests/Basic/DiagnosticTest.cpp
+++ b/clang/unittests/Basic/DiagnosticTest.cpp
@@ -223,7 +223,7 @@ TEST_F(SuppressionMappingTest, MalformedFile) {
clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);
EXPECT_THAT(takeDiags(), testing::ElementsAre(WithMessage(
"failed to process suppression mapping file "
- "'foo.txt' : malformed line 1: 'asdf'")));
+ "'foo.txt': malformed line 1: 'asdf'")));
}
TEST_F(SuppressionMappingTest, UnknownDiagName) {
>From 038afe21f01713c55c1f9d0ab40248fecfdcc3a7 Mon Sep 17 00:00:00 2001
From: Kadir Cetinkaya <kadircet at google.com>
Date: Mon, 4 Nov 2024 15:08:29 +0100
Subject: [PATCH 7/7] Add fixme and update usersmanual
---
clang/docs/UsersManual.rst | 4 +++-
clang/lib/Basic/Diagnostic.cpp | 2 ++
2 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/clang/docs/UsersManual.rst b/clang/docs/UsersManual.rst
index 4bf56c0c332790..f484c5aa8ca49c 100644
--- a/clang/docs/UsersManual.rst
+++ b/clang/docs/UsersManual.rst
@@ -1338,8 +1338,10 @@ of the project, even if there are violations in some headers.
# 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 "dir/include.h" // Clang flags unused declarations here.
#include "foo/include.h" // but unused warnings under this source is 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.
See :doc:`WarningSuppressionMappings` for details about the file format and
diff --git a/clang/lib/Basic/Diagnostic.cpp b/clang/lib/Basic/Diagnostic.cpp
index 22a925f9cbdaae..a763d9502072f1 100644
--- a/clang/lib/Basic/Diagnostic.cpp
+++ b/clang/lib/Basic/Diagnostic.cpp
@@ -487,6 +487,8 @@ void DiagnosticsEngine::setSeverityForAll(diag::Flavor Flavor,
}
namespace {
+// FIXME: We should isolate the parser from SpecialCaseList and just use it
+// here.
class WarningsSpecialCaseList : public llvm::SpecialCaseList {
public:
static std::unique_ptr<WarningsSpecialCaseList>
More information about the cfe-commits
mailing list