Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ Potentially Breaking Changes
call the member ``operator delete`` instead of the expected global
delete operator. The old behavior is retained under ``-fclang-abi-compat=21``
flag.
- Clang warning suppressions file, ``--warning-suppression-mappings=``, now will
use the last matching entry instead of the longest one.
- Trailing null statements in GNU statement expressions are no longer
ignored by Clang; they now result in a void type. Clang previously
matched GCC's behavior, which was recently clarified to be incorrect.
Expand Down
4 changes: 2 additions & 2 deletions clang/docs/WarningSuppressionMappings.rst
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ Format
Warning suppression mappings uses the same format as
:doc:`SanitizerSpecialCaseList`.

Sections describe which diagnostic group's behaviour to change, e.g.
Sections describe which diagnostic group's behavior to change, e.g.
``[unused]``. When a diagnostic is matched by multiple sections, the latest
section takes precedence.

Expand All @@ -76,7 +76,7 @@ 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
When a source file matches multiple globs in a section, the last one takes
precedence.

.. code-block:: bash
Expand Down
2 changes: 1 addition & 1 deletion clang/include/clang/Basic/Diagnostic.h
Original file line number Diff line number Diff line change
Expand Up @@ -971,7 +971,7 @@ class DiagnosticsEngine : public RefCountedBase<DiagnosticsEngine> {
/// 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
/// can be used to disable suppression. The last glob that matches a filepath
/// takes precedence. For example:
/// [unused]
/// src:clang/*
Expand Down
16 changes: 6 additions & 10 deletions clang/lib/Basic/Diagnostic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -525,16 +525,15 @@ std::unique_ptr<WarningsSpecialCaseList>
WarningsSpecialCaseList::create(const llvm::MemoryBuffer &Input,
std::string &Err) {
auto WarningSuppressionList = std::make_unique<WarningsSpecialCaseList>();
if (!WarningSuppressionList->createInternal(&Input, Err,
/*OrderBySize=*/true))
if (!WarningSuppressionList->createInternal(&Input, Err))
return nullptr;
return WarningSuppressionList;
}

void WarningsSpecialCaseList::processSections(DiagnosticsEngine &Diags) {
static constexpr auto WarningFlavor = clang::diag::Flavor::WarningOrError;
for (const auto &SectionEntry : sections()) {
StringRef DiagGroup = SectionEntry.SectionStr;
StringRef DiagGroup = SectionEntry.name();
if (DiagGroup == "*") {
// Drop the default section introduced by special case list, we only
// support exact diagnostic group names.
Expand Down Expand Up @@ -588,15 +587,12 @@ bool WarningsSpecialCaseList::isDiagSuppressed(diag::kind DiagId,

StringRef F = llvm::sys::path::remove_leading_dotslash(PLoc.getFilename());

StringRef LongestSup = DiagSection->getLongestMatch("src", F, "");
if (LongestSup.empty())
unsigned LastSup = DiagSection->getLastMatch("src", F, "");
if (LastSup == 0)
return false;

StringRef LongestEmit = DiagSection->getLongestMatch("src", F, "emit");
if (LongestEmit.empty())
return true;

return LongestSup.size() > LongestEmit.size();
unsigned LastEmit = DiagSection->getLastMatch("src", F, "emit");
return LastSup > LastEmit;
}

bool DiagnosticsEngine::isSuppressedViaMapping(diag::kind DiagId,
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Basic/ProfileList.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class ProfileSpecialCaseList : public llvm::SpecialCaseList {

bool hasPrefix(StringRef Prefix) const {
for (const auto &It : sections())
if (It.Entries.count(Prefix) > 0)
if (It.hasPrefix(Prefix))
return true;
return false;
}
Expand Down
4 changes: 2 additions & 2 deletions clang/lib/Basic/SanitizerSpecialCaseList.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ void SanitizerSpecialCaseList::createSanitizerSections() {
SanitizerMask Mask;

#define SANITIZER(NAME, ID) \
if (S.SectionMatcher.matchAny(NAME)) \
if (S.matchName(NAME)) \
Mask |= SanitizerKind::ID;
#define SANITIZER_GROUP(NAME, ID, ALIAS) SANITIZER(NAME, ID)

Expand All @@ -68,7 +68,7 @@ SanitizerSpecialCaseList::inSectionBlame(SanitizerMask Mask, StringRef Prefix,
if (S.Mask & Mask) {
unsigned LineNum = S.S.getLastMatch(Prefix, Query, Category);
if (LineNum > 0)
return {S.S.FileIdx, LineNum};
return {S.S.fileIndex(), LineNum};
}
}
return NotFound;
Expand Down
8 changes: 3 additions & 5 deletions clang/unittests/Basic/DiagnosticTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ TEST_F(SuppressionMappingTest, EmitCategoryIsExcluded) {
locForFile("foo.cpp")));
}

TEST_F(SuppressionMappingTest, LongestMatchWins) {
TEST_F(SuppressionMappingTest, LastMatchWins) {
llvm::StringLiteral SuppressionMappingFile = R"(
[unused]
src:*clang/*
Expand Down Expand Up @@ -327,10 +327,8 @@ TEST_F(SuppressionMappingTest, LongShortMatch) {

EXPECT_TRUE(Diags.isSuppressedViaMapping(diag::warn_unused_function,
locForFile("test/t1.cpp")));

// FIXME: This is confusing.
EXPECT_TRUE(Diags.isSuppressedViaMapping(diag::warn_unused_function,
locForFile("lld/test/t2.cpp")));
EXPECT_FALSE(Diags.isSuppressedViaMapping(diag::warn_unused_function,
locForFile("lld/test/t2.cpp")));
}

TEST_F(SuppressionMappingTest, ShortLongMatch) {
Expand Down
124 changes: 21 additions & 103 deletions llvm/include/llvm/Support/SpecialCaseList.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,11 @@
#ifndef LLVM_SUPPORT_SPECIALCASELIST_H
#define LLVM_SUPPORT_SPECIALCASELIST_H

#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/RadixTree.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/iterator_range.h"
#include "llvm/Support/Allocator.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/GlobPattern.h"
#include "llvm/Support/Regex.h"
#include "llvm/Support/Error.h"
#include <memory>
#include <string>
#include <utility>
#include <variant>
#include <vector>

namespace llvm {
Expand Down Expand Up @@ -118,119 +110,45 @@ class SpecialCaseList {
// classes.
LLVM_ABI bool createInternal(const std::vector<std::string> &Paths,
vfs::FileSystem &VFS, std::string &Error);
LLVM_ABI bool createInternal(const MemoryBuffer *MB, std::string &Error,
bool OrderBySize = false);
LLVM_ABI bool createInternal(const MemoryBuffer *MB, std::string &Error);

SpecialCaseList() = default;
SpecialCaseList(SpecialCaseList const &) = delete;
SpecialCaseList &operator=(SpecialCaseList const &) = delete;

private:
// Lagacy v1 matcher.
class RegexMatcher {
class Section {
public:
LLVM_ABI Error insert(StringRef Pattern, unsigned LineNumber);
LLVM_ABI void preprocess(bool BySize);

LLVM_ABI void
match(StringRef Query,
llvm::function_ref<void(StringRef Rule, unsigned LineNo)> Cb) const;

struct Reg {
Reg(StringRef Name, unsigned LineNo, Regex &&Rg)
: Name(Name), LineNo(LineNo), Rg(std::move(Rg)) {}
StringRef Name;
unsigned LineNo;
Regex Rg;
};

std::vector<Reg> RegExes;
};

class GlobMatcher {
public:
LLVM_ABI Error insert(StringRef Pattern, unsigned LineNumber);
LLVM_ABI void preprocess(bool BySize);

LLVM_ABI void
match(StringRef Query,
llvm::function_ref<void(StringRef Rule, unsigned LineNo)> Cb) const;

struct Glob {
Glob(StringRef Name, unsigned LineNo, GlobPattern &&Pattern)
: Name(Name), LineNo(LineNo), Pattern(std::move(Pattern)) {}
StringRef Name;
unsigned LineNo;
GlobPattern Pattern;
};

std::vector<GlobMatcher::Glob> Globs;

RadixTree<iterator_range<StringRef::const_iterator>,
RadixTree<iterator_range<StringRef::const_reverse_iterator>,
SmallVector<const GlobMatcher::Glob *, 1>>>
PrefixSuffixToGlob;

RadixTree<iterator_range<StringRef::const_iterator>,
SmallVector<const GlobMatcher::Glob *, 1>>
SubstrToGlob;
};

/// Represents a set of patterns and their line numbers
class Matcher {
public:
LLVM_ABI Matcher(bool UseGlobs, bool RemoveDotSlash);

LLVM_ABI Error insert(StringRef Pattern, unsigned LineNumber);
LLVM_ABI void preprocess(bool BySize);

LLVM_ABI void
match(StringRef Query,
llvm::function_ref<void(StringRef Rule, unsigned LineNo)> Cb) const;
LLVM_ABI Section(StringRef Name, unsigned FileIdx, bool UseGlobs);
LLVM_ABI Section(Section &&);
LLVM_ABI ~Section();

LLVM_ABI bool matchAny(StringRef Query) const {
bool R = false;
match(Query, [&](StringRef, unsigned) { R = true; });
return R;
}
// Return name of the section, it's entire string in [].
StringRef name() const { return Name; }

std::variant<RegexMatcher, GlobMatcher> M;
bool RemoveDotSlash;
};

using SectionEntries = StringMap<StringMap<Matcher>>;
// Returns true of string 'Name' matches section name interpreted as a glob.
LLVM_ABI bool matchName(StringRef Name) const;

protected:
struct Section {
Section(StringRef Str, unsigned FileIdx, bool UseGlobs)
: SectionMatcher(UseGlobs, /*RemoveDotSlash=*/false), SectionStr(Str),
FileIdx(FileIdx) {}

Section(Section &&) = default;

Matcher SectionMatcher;
SectionEntries Entries;
std::string SectionStr;
unsigned FileIdx;
// Return sequence number of the file where this section is defined.
unsigned fileIndex() const { return FileIdx; }

// Helper method to search by Prefix, Query, and Category. Returns
// 1-based line number on which rule is defined, or 0 if there is no match.
LLVM_ABI unsigned getLastMatch(StringRef Prefix, StringRef Query,
StringRef Category) const;

// Helper method to search by Prefix, Query, and Category. Returns
// matching rule, or empty string if there is no match.
LLVM_ABI StringRef getLongestMatch(StringRef Prefix, StringRef Query,
StringRef Category) const;
/// Returns true if the section has any entries for the given prefix.
LLVM_ABI bool hasPrefix(StringRef Prefix) const;

private:
friend class SpecialCaseList;
LLVM_ABI void preprocess(bool OrderBySize);
LLVM_ABI const SpecialCaseList::Matcher *
findMatcher(StringRef Prefix, StringRef Category) const;
class SectionImpl;

StringRef Name;
unsigned FileIdx;
std::unique_ptr<SectionImpl> Impl;
};

ArrayRef<const Section> sections() const { return Sections; }
const std::vector<Section> &sections() const;

private:
BumpPtrAllocator StrAlloc;
Expand All @@ -242,7 +160,7 @@ class SpecialCaseList {

/// Parses just-constructed SpecialCaseList entries from a memory buffer.
LLVM_ABI bool parse(unsigned FileIdx, const MemoryBuffer *MB,
std::string &Error, bool OrderBySize);
std::string &Error);
};

} // namespace llvm
Expand Down
Loading