Reapply "[clang] Introduce diagnostics suppression mappings (#112517)"
This reverts commit 5f140ba54794fe6ca379362b133eb27780e363d7.
This commit is contained in:
parent
42da81582e
commit
5845688e91
@ -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;
|
||||
|
||||
|
@ -872,6 +872,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
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
@ -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,34 @@ with its corresponding `Wno-` option.
|
||||
Note that when combined with :option:`-w` (which disables all warnings),
|
||||
disabling all warnings wins.
|
||||
|
||||
.. _warning_suppression_mappings:
|
||||
|
||||
Controlling Diagnostics via Suppression Mappings
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Warning suppression mappings enable users to suppress Clang's diagnostics at a
|
||||
per-file granularity. This allows enforcing diagnostics in specific parts of the
|
||||
project even if there are violations in some headers.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ cat mappings.txt
|
||||
[unused]
|
||||
src:foo/*
|
||||
|
||||
$ clang --warning-suppression-mappings=mapping.txt -Wunused foo/bar.cc
|
||||
# This compilation won't emit any unused findings for sources under foo/
|
||||
# directory. But it'll still complain for all the other sources, e.g:
|
||||
$ cat foo/bar.cc
|
||||
#include "dir/include.h" // Clang flags unused declarations here.
|
||||
#include "foo/include.h" // but unused warnings under this source is omitted.
|
||||
#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
|
||||
functionality.
|
||||
|
||||
Controlling Static Analyzer Diagnostics
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
97
clang/docs/WarningSuppressionMappings.rst
Normal file
97
clang/docs/WarningSuppressionMappings.rst
Normal file
@ -0,0 +1,97 @@
|
||||
============================
|
||||
Warning suppression mappings
|
||||
============================
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
|
||||
Introduction
|
||||
============
|
||||
|
||||
Warning suppression mappings enable users to suppress Clang's diagnostics at a
|
||||
per-file granularity. This allows enforcing diagnostics in specific parts of the
|
||||
project even if there are violations in some headers.
|
||||
|
||||
Goal and usage
|
||||
==============
|
||||
|
||||
Clang allows diagnostics to be configured at a translation-unit granularity.
|
||||
If a ``foo.cpp`` is compiled with ``-Wfoo``, all transitively included headers
|
||||
also need to be clean. Hence, turning on new warnings in large codebases
|
||||
requires cleaning up all the existing warnings. This might not be possible when
|
||||
some dependencies aren't in the project owner's control or because new
|
||||
violations are creeping up quicker than the clean up.
|
||||
|
||||
Warning suppression mappings aim to alleviate some of these concerns by making
|
||||
diagnostic configuration granularity finer, at a source file level.
|
||||
|
||||
To achieve this, user can create a file that lists which :doc:`diagnostic
|
||||
groups <DiagnosticsReference>` 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.
|
||||
`Controlling diagnostics pragmas
|
||||
<https://clang.llvm.org/docs/UsersManual.html#controlling-diagnostics-via-pragmas>`_
|
||||
take precedence over suppression mappings. Ensuring code author's explicit
|
||||
intent is always preserved.
|
||||
|
||||
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 the same format as
|
||||
:doc:`SanitizerSpecialCaseList`.
|
||||
|
||||
Sections describe which diagnostic group's behaviour to change, e.g.
|
||||
``[unused]``. When a diagnostic is matched by multiple sections, the latest
|
||||
section takes precedence.
|
||||
|
||||
Afterwards in each section, users can have multiple entities that match source
|
||||
files based on the globs. These entities look like ``src:*/my/dir/*``.
|
||||
Users can also use the ``emit`` category to exclude a subdirectory from
|
||||
suppression.
|
||||
Source files are matched against these globs either:
|
||||
|
||||
- as paths relative to the current working directory
|
||||
- as absolute paths.
|
||||
|
||||
When a source file matches multiple globs in a section, the longest one takes
|
||||
precedence.
|
||||
|
||||
.. 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/*
|
@ -22,6 +22,7 @@ Using Clang as a Compiler
|
||||
ClangCommandLineReference
|
||||
AttributeReference
|
||||
DiagnosticsReference
|
||||
WarningSuppressionMappings
|
||||
CrossCompilation
|
||||
ClangStaticAnalyzer
|
||||
ThreadSafetyAnalysis
|
||||
|
@ -20,9 +20,9 @@
|
||||
#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 <cassert>
|
||||
@ -40,6 +40,10 @@
|
||||
namespace llvm {
|
||||
class Error;
|
||||
class raw_ostream;
|
||||
class MemoryBuffer;
|
||||
namespace vfs {
|
||||
class FileSystem;
|
||||
} // namespace vfs
|
||||
} // namespace llvm
|
||||
|
||||
namespace clang {
|
||||
@ -555,6 +559,10 @@ private:
|
||||
void *ArgToStringCookie = nullptr;
|
||||
ArgToStringFnTy ArgToStringFn;
|
||||
|
||||
/// Whether the diagnostic should be suppressed in FilePath.
|
||||
llvm::unique_function<bool(diag::kind, StringRef /*FilePath*/) const>
|
||||
DiagSuppressionMapping;
|
||||
|
||||
public:
|
||||
explicit DiagnosticsEngine(IntrusiveRefCntPtr<DiagnosticIDs> Diags,
|
||||
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts,
|
||||
@ -946,6 +954,26 @@ public:
|
||||
return (Level)Diags->getDiagnosticLevel(DiagID, Loc, *this);
|
||||
}
|
||||
|
||||
/// Diagnostic suppression mappings can be used to suppress specific
|
||||
/// diagnostics in specific files.
|
||||
/// Mapping file is expected to be a special case list with sections denoting
|
||||
/// diagnostic groups and `src` entries for globs to suppress. `emit` category
|
||||
/// can be used to disable suppression. Longest glob that matches a filepath
|
||||
/// takes precedence. 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. 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 &Input);
|
||||
bool isSuppressedViaMapping(diag::kind DiagId, StringRef FilePath) const;
|
||||
|
||||
/// Issue the message to the client.
|
||||
///
|
||||
/// This actually returns an instance of DiagnosticBuilder which emits the
|
||||
@ -1759,7 +1787,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
|
||||
|
||||
|
@ -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">;
|
||||
}
|
||||
|
@ -108,6 +108,9 @@ public:
|
||||
/// The file to serialize diagnostics to (non-appending).
|
||||
std::string DiagnosticSerializationFile;
|
||||
|
||||
/// 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
|
||||
/// prefixes removed.
|
||||
std::vector<std::string> Warnings;
|
||||
|
@ -965,6 +965,10 @@ def V : JoinedOrSeparate<["-"], "V">, Flags<[NoXarchOption, Unsupported]>;
|
||||
def Wa_COMMA : CommaJoined<["-"], "Wa,">,
|
||||
HelpText<"Pass the comma separated arguments in <arg> to the assembler">,
|
||||
MetaVarName<"<arg>">;
|
||||
def warning_suppression_mappings_EQ : Joined<["--"],
|
||||
"warning-suppression-mappings=">, Group<Diag_Group>,
|
||||
HelpText<"File containing diagnostic suppresion mappings. See user manual "
|
||||
"for file format.">, Visibility<[ClangOption, CC1Option]>;
|
||||
def Wall : Flag<["-"], "Wall">, Group<W_Group>, Flags<[HelpHidden]>,
|
||||
Visibility<[ClangOption, CC1Option, FlangOption]>;
|
||||
def WCL4 : Flag<["-"], "WCL4">, Group<W_Group>, Flags<[HelpHidden]>,
|
||||
|
@ -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 @@ public:
|
||||
/// 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.
|
||||
///
|
||||
|
@ -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"
|
||||
@ -21,13 +23,19 @@
|
||||
#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"
|
||||
#include "llvm/ADT/StringMap.h"
|
||||
#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"
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
@ -35,6 +43,7 @@
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
@ -477,6 +486,138 @@ void DiagnosticsEngine::setSeverityForAll(diag::Flavor Flavor,
|
||||
setSeverity(Diag, Map, Loc);
|
||||
}
|
||||
|
||||
namespace {
|
||||
// FIXME: We should isolate the parser from SpecialCaseList and just use it
|
||||
// here.
|
||||
class WarningsSpecialCaseList : public llvm::SpecialCaseList {
|
||||
public:
|
||||
static std::unique_ptr<WarningsSpecialCaseList>
|
||||
create(const llvm::MemoryBuffer &Input, std::string &Err);
|
||||
|
||||
// Section names refer to diagnostic groups, which cover multiple individual
|
||||
// diagnostics. Expand diagnostic groups here to individual diagnostics.
|
||||
// A diagnostic can have multiple diagnostic groups associated with it, we let
|
||||
// the last section take precedence in such cases.
|
||||
void processSections(DiagnosticsEngine &Diags);
|
||||
|
||||
bool isDiagSuppressed(diag::kind DiagId, StringRef FilePath) const;
|
||||
|
||||
private:
|
||||
// Find the longest glob pattern that matches FilePath amongst
|
||||
// CategoriesToMatchers, return true iff the match exists and belongs to a
|
||||
// positive category.
|
||||
bool globsMatches(const llvm::StringMap<Matcher> &CategoriesToMatchers,
|
||||
StringRef FilePath) const;
|
||||
|
||||
llvm::DenseMap<diag::kind, const Section *> DiagToSection;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
std::unique_ptr<WarningsSpecialCaseList>
|
||||
WarningsSpecialCaseList::create(const llvm::MemoryBuffer &Input,
|
||||
std::string &Err) {
|
||||
auto WarningSuppressionList = std::make_unique<WarningsSpecialCaseList>();
|
||||
if (!WarningSuppressionList->createInternal(&Input, Err))
|
||||
return nullptr;
|
||||
return WarningSuppressionList;
|
||||
}
|
||||
|
||||
void WarningsSpecialCaseList::processSections(DiagnosticsEngine &Diags) {
|
||||
// Drop the default section introduced by special case list, we only support
|
||||
// exact diagnostic group names.
|
||||
// FIXME: We should make this configurable in the parser instead.
|
||||
Sections.erase("*");
|
||||
// Make sure we iterate sections by their line numbers.
|
||||
std::vector<std::pair<unsigned, const llvm::StringMapEntry<Section> *>>
|
||||
LineAndSectionEntry;
|
||||
LineAndSectionEntry.reserve(Sections.size());
|
||||
for (const auto &Entry : Sections) {
|
||||
StringRef DiagName = Entry.getKey();
|
||||
// Each section has a matcher with that section's name, attached to that
|
||||
// line.
|
||||
const auto &DiagSectionMatcher = Entry.getValue().SectionMatcher;
|
||||
unsigned DiagLine = DiagSectionMatcher->Globs.at(DiagName).second;
|
||||
LineAndSectionEntry.emplace_back(DiagLine, &Entry);
|
||||
}
|
||||
llvm::sort(LineAndSectionEntry);
|
||||
static constexpr auto WarningFlavor = clang::diag::Flavor::WarningOrError;
|
||||
for (const auto &[_, SectionEntry] : LineAndSectionEntry) {
|
||||
SmallVector<diag::kind> GroupDiags;
|
||||
StringRef DiagGroup = SectionEntry->getKey();
|
||||
if (Diags.getDiagnosticIDs()->getDiagnosticsInGroup(
|
||||
WarningFlavor, DiagGroup, GroupDiags)) {
|
||||
StringRef Suggestion =
|
||||
DiagnosticIDs::getNearestOption(WarningFlavor, DiagGroup);
|
||||
Diags.Report(diag::warn_unknown_diag_option)
|
||||
<< static_cast<unsigned>(WarningFlavor) << DiagGroup
|
||||
<< !Suggestion.empty() << Suggestion;
|
||||
continue;
|
||||
}
|
||||
for (diag::kind Diag : GroupDiags)
|
||||
// We're intentionally overwriting any previous mappings here to make sure
|
||||
// latest one takes precedence.
|
||||
DiagToSection[Diag] = &SectionEntry->getValue();
|
||||
}
|
||||
}
|
||||
|
||||
void DiagnosticsEngine::setDiagSuppressionMapping(llvm::MemoryBuffer &Input) {
|
||||
std::string Error;
|
||||
auto WarningSuppressionList = WarningsSpecialCaseList::create(Input, Error);
|
||||
if (!WarningSuppressionList) {
|
||||
// FIXME: Use a `%select` statement instead of printing `Error` as-is. This
|
||||
// should help localization.
|
||||
Report(diag::err_drv_malformed_warning_suppression_mapping)
|
||||
<< Input.getBufferIdentifier() << Error;
|
||||
return;
|
||||
}
|
||||
WarningSuppressionList->processSections(*this);
|
||||
DiagSuppressionMapping =
|
||||
[WarningSuppressionList(std::move(WarningSuppressionList))](
|
||||
diag::kind DiagId, StringRef Path) {
|
||||
return WarningSuppressionList->isDiagSuppressed(DiagId, Path);
|
||||
};
|
||||
}
|
||||
|
||||
bool WarningsSpecialCaseList::isDiagSuppressed(diag::kind DiagId,
|
||||
StringRef FilePath) const {
|
||||
const Section *DiagSection = DiagToSection.lookup(DiagId);
|
||||
if (!DiagSection)
|
||||
return false;
|
||||
const SectionEntries &EntityTypeToCategories = DiagSection->Entries;
|
||||
auto SrcEntriesIt = EntityTypeToCategories.find("src");
|
||||
if (SrcEntriesIt == EntityTypeToCategories.end())
|
||||
return false;
|
||||
const llvm::StringMap<llvm::SpecialCaseList::Matcher> &CategoriesToMatchers =
|
||||
SrcEntriesIt->getValue();
|
||||
return globsMatches(CategoriesToMatchers, FilePath);
|
||||
}
|
||||
|
||||
bool WarningsSpecialCaseList::globsMatches(
|
||||
const llvm::StringMap<Matcher> &CategoriesToMatchers,
|
||||
StringRef FilePath) const {
|
||||
StringRef LongestMatch;
|
||||
bool LongestIsPositive = false;
|
||||
for (const auto &Entry : CategoriesToMatchers) {
|
||||
StringRef Category = Entry.getKey();
|
||||
const llvm::SpecialCaseList::Matcher &Matcher = Entry.getValue();
|
||||
bool IsPositive = Category != "emit";
|
||||
for (const auto &[Pattern, Glob] : Matcher.Globs) {
|
||||
if (Pattern.size() < LongestMatch.size())
|
||||
continue;
|
||||
if (!Glob.first.match(FilePath))
|
||||
continue;
|
||||
LongestMatch = Pattern;
|
||||
LongestIsPositive = IsPositive;
|
||||
}
|
||||
}
|
||||
return LongestIsPositive;
|
||||
}
|
||||
|
||||
bool DiagnosticsEngine::isSuppressedViaMapping(diag::kind DiagId,
|
||||
StringRef FilePath) const {
|
||||
return DiagSuppressionMapping && DiagSuppressionMapping(DiagId, FilePath);
|
||||
}
|
||||
|
||||
void DiagnosticsEngine::Report(const StoredDiagnostic &storedDiag) {
|
||||
DiagnosticStorage DiagStorage;
|
||||
DiagStorage.DiagRanges.append(storedDiag.range_begin(),
|
||||
|
@ -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;
|
||||
@ -599,6 +600,17 @@ DiagnosticIDs::getDiagnosticSeverity(unsigned DiagID, SourceLocation Loc,
|
||||
SM.isInSystemMacro(Loc))
|
||||
return diag::Severity::Ignored;
|
||||
|
||||
// Clang-diagnostics pragmas always take precedence over suppression mapping.
|
||||
if (!Mapping.isPragma() && Diag.DiagSuppressionMapping) {
|
||||
// We also use presumed locations here to improve reproducibility for
|
||||
// preprocessed inputs.
|
||||
if (PresumedLoc PLoc = SM.getPresumedLoc(Loc);
|
||||
PLoc.isValid() && Diag.isSuppressedViaMapping(
|
||||
DiagID, llvm::sys::path::remove_leading_dotslash(
|
||||
PLoc.getFilename())))
|
||||
return diag::Severity::Ignored;
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
|
@ -23,10 +23,12 @@
|
||||
// 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 <algorithm>
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/Support/VirtualFileSystem.h"
|
||||
#include <cstring>
|
||||
#include <utility>
|
||||
using namespace clang;
|
||||
|
||||
// EmitUnknownDiagWarning - Emit a warning and typo hint for unknown warning
|
||||
@ -43,6 +45,7 @@ static void EmitUnknownDiagWarning(DiagnosticsEngine &Diags,
|
||||
|
||||
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 +73,16 @@ void clang::ProcessWarningOptions(DiagnosticsEngine &Diags,
|
||||
else
|
||||
Diags.setExtensionHandlingBehavior(diag::Severity::Ignored);
|
||||
|
||||
if (!Opts.DiagnosticSuppressionMappingsFile.empty()) {
|
||||
if (auto FileContents =
|
||||
VFS.getBufferForFile(Opts.DiagnosticSuppressionMappingsFile)) {
|
||||
Diags.setDiagSuppressionMapping(**FileContents);
|
||||
} else if (ReportDiags) {
|
||||
Diags.Report(diag::err_drv_no_such_file)
|
||||
<< Opts.DiagnosticSuppressionMappingsFile;
|
||||
}
|
||||
}
|
||||
|
||||
SmallVector<diag::kind, 10> _Diags;
|
||||
const IntrusiveRefCntPtr< DiagnosticIDs > DiagIDs =
|
||||
Diags.getDiagnosticIDs();
|
||||
|
@ -4514,6 +4514,8 @@ static void RenderDiagnosticsOptions(const Driver &D, const ArgList &Args,
|
||||
|
||||
Args.addOptOutFlag(CmdArgs, options::OPT_fspell_checking,
|
||||
options::OPT_fno_spell_checking);
|
||||
|
||||
Args.addLastArg(CmdArgs, options::OPT_warning_suppression_mappings_EQ);
|
||||
}
|
||||
|
||||
DwarfFissionKind tools::getDebugFissionKind(const Driver &D,
|
||||
|
@ -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,8 @@ 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 +1711,8 @@ bool ASTUnit::LoadFromCompilerInvocation(
|
||||
OverrideMainBuffer =
|
||||
getMainBufferWithPrecompiledPreamble(PCHContainerOps, *Invocation, VFS);
|
||||
getDiagnostics().Reset();
|
||||
ProcessWarningOptions(getDiagnostics(), Invocation->getDiagnosticOpts());
|
||||
ProcessWarningOptions(getDiagnostics(), Invocation->getDiagnosticOpts(),
|
||||
*VFS);
|
||||
}
|
||||
|
||||
SimpleTimer ParsingTimer(WantTiming);
|
||||
@ -1902,7 +1905,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 +2245,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()) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -2521,6 +2521,11 @@ void CompilerInvocationBase::GenerateDiagnosticArgs(
|
||||
|
||||
Consumer(StringRef("-R") + Remark);
|
||||
}
|
||||
|
||||
if (!Opts.DiagnosticSuppressionMappingsFile.empty()) {
|
||||
GenerateArg(Consumer, OPT_warning_suppression_mappings_EQ,
|
||||
Opts.DiagnosticSuppressionMappingsFile);
|
||||
}
|
||||
}
|
||||
|
||||
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.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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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");
|
||||
|
13
clang/test/Misc/Inputs/suppression-mapping.txt
Normal file
13
clang/test/Misc/Inputs/suppression-mapping.txt
Normal file
@ -0,0 +1,13 @@
|
||||
# Suppress unused warnings in all files, apart from the ones under `foo/`.
|
||||
[unused]
|
||||
src:*
|
||||
src:*foo/*=emit
|
||||
|
||||
# This should take precedence over `unused` group, as it's mentioned later.
|
||||
[unused-variable]
|
||||
# We don't suppress unused-variable warnings in "any" file.
|
||||
|
||||
# Some warning groups can have strange spellings.
|
||||
[format=2]
|
||||
src:*
|
||||
src:*foo/*=emit
|
16
clang/test/Misc/warning-suppression-mappings-pragmas.cpp
Normal file
16
clang/test/Misc/warning-suppression-mappings-pragmas.cpp
Normal file
@ -0,0 +1,16 @@
|
||||
// Check that clang-diagnostic pragmas take precedence over suppression mapping.
|
||||
// RUN: %clang -cc1 -verify -Wformat=2 --warning-suppression-mappings=%S/Inputs/suppression-mapping.txt -fsyntax-only %s
|
||||
|
||||
__attribute__((__format__ (__printf__, 1, 2)))
|
||||
void format_err(const char* const pString, ...);
|
||||
|
||||
void foo() {
|
||||
const char *x;
|
||||
format_err(x); // Warning suppressed here.
|
||||
// check that pragmas take precedence
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic warning "-Wformat=2"
|
||||
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}}
|
||||
#pragma clang diagnostic pop
|
||||
}
|
31
clang/test/Misc/warning-suppression-mappings.cpp
Normal file
31
clang/test/Misc/warning-suppression-mappings.cpp
Normal file
@ -0,0 +1,31 @@
|
||||
// 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 -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 -Wformat=2 -Wunused --warning-suppression-mappings=%S/Inputs/suppression-mapping.txt -fsyntax-only %s
|
||||
|
||||
__attribute__((__format__ (__printf__, 1, 2)))
|
||||
void format_err(const char* const pString, ...);
|
||||
|
||||
namespace {
|
||||
void foo() {
|
||||
const char *x;
|
||||
format_err(x); // Warning suppressed here.
|
||||
const char *y; // expected-warning{{unused variable 'y'}}
|
||||
}
|
||||
}
|
||||
|
||||
#line 42 "foo/bar.h"
|
||||
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}}
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -10,8 +10,18 @@
|
||||
#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/ArrayRef.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;
|
||||
@ -28,6 +38,9 @@ void clang::DiagnosticsTestHelper(DiagnosticsEngine &diag) {
|
||||
}
|
||||
|
||||
namespace {
|
||||
using testing::AllOf;
|
||||
using testing::ElementsAre;
|
||||
using testing::IsEmpty;
|
||||
|
||||
// Check that DiagnosticErrorTrap works with SuppressAllDiagnostics.
|
||||
TEST(DiagnosticTest, suppressAndTrap) {
|
||||
@ -167,4 +180,160 @@ 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};
|
||||
|
||||
llvm::ArrayRef<StoredDiagnostic> diags() {
|
||||
return 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;
|
||||
}
|
||||
MATCHER(IsError, "has error severity") {
|
||||
return arg.getLevel() == DiagnosticsEngine::Level::Error;
|
||||
}
|
||||
|
||||
TEST_F(SuppressionMappingTest, MissingMappingFile) {
|
||||
Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";
|
||||
clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);
|
||||
EXPECT_THAT(diags(), ElementsAre(AllOf(
|
||||
WithMessage("no such file or directory: 'foo.txt'"),
|
||||
IsError())));
|
||||
}
|
||||
|
||||
TEST_F(SuppressionMappingTest, MalformedFile) {
|
||||
Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";
|
||||
FS->addFile("foo.txt", /*ModificationTime=*/{},
|
||||
llvm::MemoryBuffer::getMemBuffer("asdf", "foo.txt"));
|
||||
clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);
|
||||
EXPECT_THAT(diags(),
|
||||
ElementsAre(AllOf(
|
||||
WithMessage("failed to process suppression mapping file "
|
||||
"'foo.txt': malformed line 1: 'asdf'"),
|
||||
IsError())));
|
||||
}
|
||||
|
||||
TEST_F(SuppressionMappingTest, UnknownDiagName) {
|
||||
Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";
|
||||
FS->addFile("foo.txt", /*ModificationTime=*/{},
|
||||
llvm::MemoryBuffer::getMemBuffer("[non-existing-warning]"));
|
||||
clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);
|
||||
EXPECT_THAT(diags(), ElementsAre(WithMessage(
|
||||
"unknown warning option 'non-existing-warning'")));
|
||||
}
|
||||
|
||||
TEST_F(SuppressionMappingTest, SuppressesGroup) {
|
||||
llvm::StringLiteral SuppressionMappingFile = R"(
|
||||
[unused]
|
||||
src:*)";
|
||||
Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";
|
||||
FS->addFile("foo.txt", /*ModificationTime=*/{},
|
||||
llvm::MemoryBuffer::getMemBuffer(SuppressionMappingFile));
|
||||
clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);
|
||||
EXPECT_THAT(diags(), IsEmpty());
|
||||
|
||||
EXPECT_TRUE(
|
||||
Diags.isSuppressedViaMapping(diag::warn_unused_function, "foo.cpp"));
|
||||
EXPECT_FALSE(Diags.isSuppressedViaMapping(diag::warn_deprecated, "foo.cpp"));
|
||||
}
|
||||
|
||||
TEST_F(SuppressionMappingTest, EmitCategoryIsExcluded) {
|
||||
llvm::StringLiteral SuppressionMappingFile = R"(
|
||||
[unused]
|
||||
src:*
|
||||
src:*foo.cpp=emit)";
|
||||
Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";
|
||||
FS->addFile("foo.txt", /*ModificationTime=*/{},
|
||||
llvm::MemoryBuffer::getMemBuffer(SuppressionMappingFile));
|
||||
clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);
|
||||
EXPECT_THAT(diags(), 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"(
|
||||
[unused]
|
||||
src:*clang/*
|
||||
src:*clang/lib/Sema/*=emit
|
||||
src:*clang/lib/Sema/foo*)";
|
||||
Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";
|
||||
FS->addFile("foo.txt", /*ModificationTime=*/{},
|
||||
llvm::MemoryBuffer::getMemBuffer(SuppressionMappingFile));
|
||||
clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);
|
||||
EXPECT_THAT(diags(), 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"(
|
||||
[unused]
|
||||
src:*clang/*)";
|
||||
Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";
|
||||
Diags.getDiagnosticOptions().Warnings = {"unused"};
|
||||
FS->addFile("foo.txt", /*ModificationTime=*/{},
|
||||
llvm::MemoryBuffer::getMemBuffer(SuppressionMappingFile));
|
||||
clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);
|
||||
ASSERT_THAT(diags(), 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
|
||||
|
@ -1046,4 +1046,15 @@ 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().DiagnosticSuppressionMappingsFile,
|
||||
"foo.txt");
|
||||
|
||||
Invocation.generateCC1CommandLine(GeneratedArgs, *this);
|
||||
EXPECT_THAT(GeneratedArgs, Contains(StrEq(Args[0])));
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
@ -122,7 +122,6 @@ protected:
|
||||
// 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;
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user