[clang] [llvm] [NFC][SpecialCaseList] Precommit Version 4 tests (PR #167282)
Vitaly Buka via llvm-commits
llvm-commits at lists.llvm.org
Mon Nov 10 10:56:45 PST 2025
https://github.com/vitalybuka updated https://github.com/llvm/llvm-project/pull/167282
>From 493a5902c3b68a5bc895889290be87e8217b639e Mon Sep 17 00:00:00 2001
From: Vitaly Buka <vitalybuka at google.com>
Date: Sun, 9 Nov 2025 23:20:17 -0800
Subject: [PATCH] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20change?=
=?UTF-8?q?s=20to=20main=20this=20commit=20is=20based=20on?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Created using spr 1.3.7
[skip ci]
---
clang/docs/ReleaseNotes.rst | 2 +
clang/docs/WarningSuppressionMappings.rst | 4 +-
clang/include/clang/Basic/Diagnostic.h | 2 +-
clang/lib/Basic/Diagnostic.cpp | 16 +-
clang/lib/Basic/ProfileList.cpp | 2 +-
clang/lib/Basic/SanitizerSpecialCaseList.cpp | 4 +-
clang/unittests/Basic/DiagnosticTest.cpp | 8 +-
llvm/include/llvm/Support/SpecialCaseList.h | 124 ++-------
llvm/lib/Support/SpecialCaseList.cpp | 265 ++++++++++++-------
9 files changed, 210 insertions(+), 217 deletions(-)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 6b396e7ba63f3..146825ce43191 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -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.
diff --git a/clang/docs/WarningSuppressionMappings.rst b/clang/docs/WarningSuppressionMappings.rst
index d96341ac6e563..d8af856f64ef0 100644
--- a/clang/docs/WarningSuppressionMappings.rst
+++ b/clang/docs/WarningSuppressionMappings.rst
@@ -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.
@@ -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
diff --git a/clang/include/clang/Basic/Diagnostic.h b/clang/include/clang/Basic/Diagnostic.h
index e540040ddc524..c6e931d0c9517 100644
--- a/clang/include/clang/Basic/Diagnostic.h
+++ b/clang/include/clang/Basic/Diagnostic.h
@@ -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/*
diff --git a/clang/lib/Basic/Diagnostic.cpp b/clang/lib/Basic/Diagnostic.cpp
index 2dec26ecacf26..4802478c379bb 100644
--- a/clang/lib/Basic/Diagnostic.cpp
+++ b/clang/lib/Basic/Diagnostic.cpp
@@ -525,8 +525,7 @@ 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;
}
@@ -534,7 +533,7 @@ WarningsSpecialCaseList::create(const llvm::MemoryBuffer &Input,
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.
@@ -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,
diff --git a/clang/lib/Basic/ProfileList.cpp b/clang/lib/Basic/ProfileList.cpp
index 9cb118893a0d9..8727057eb78d1 100644
--- a/clang/lib/Basic/ProfileList.cpp
+++ b/clang/lib/Basic/ProfileList.cpp
@@ -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;
}
diff --git a/clang/lib/Basic/SanitizerSpecialCaseList.cpp b/clang/lib/Basic/SanitizerSpecialCaseList.cpp
index 56f551628cf89..928c086898097 100644
--- a/clang/lib/Basic/SanitizerSpecialCaseList.cpp
+++ b/clang/lib/Basic/SanitizerSpecialCaseList.cpp
@@ -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)
@@ -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;
diff --git a/clang/unittests/Basic/DiagnosticTest.cpp b/clang/unittests/Basic/DiagnosticTest.cpp
index de090864e5095..5492146f40fa9 100644
--- a/clang/unittests/Basic/DiagnosticTest.cpp
+++ b/clang/unittests/Basic/DiagnosticTest.cpp
@@ -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/*
@@ -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) {
diff --git a/llvm/include/llvm/Support/SpecialCaseList.h b/llvm/include/llvm/Support/SpecialCaseList.h
index cb8e568de02e0..9d542c4ef5055 100644
--- a/llvm/include/llvm/Support/SpecialCaseList.h
+++ b/llvm/include/llvm/Support/SpecialCaseList.h
@@ -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 {
@@ -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> §ions() const;
private:
BumpPtrAllocator StrAlloc;
@@ -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
diff --git a/llvm/lib/Support/SpecialCaseList.cpp b/llvm/lib/Support/SpecialCaseList.cpp
index 246d90cce3a43..79032cbb07f3f 100644
--- a/llvm/lib/Support/SpecialCaseList.cpp
+++ b/llvm/lib/Support/SpecialCaseList.cpp
@@ -14,24 +14,94 @@
//===----------------------------------------------------------------------===//
#include "llvm/Support/SpecialCaseList.h"
+#include "llvm/ADT/RadixTree.h"
#include "llvm/ADT/STLExtras.h"
-#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/iterator_range.h"
+#include "llvm/Support/Compiler.h"
+#include "llvm/Support/GlobPattern.h"
#include "llvm/Support/LineIterator.h"
#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Regex.h"
#include "llvm/Support/VirtualFileSystem.h"
-#include <algorithm>
-#include <limits>
+#include "llvm/Support/raw_ostream.h"
#include <memory>
#include <stdio.h>
#include <string>
#include <system_error>
#include <utility>
+#include <variant>
+#include <vector>
namespace llvm {
-Error SpecialCaseList::RegexMatcher::insert(StringRef Pattern,
- unsigned LineNumber) {
+namespace {
+
+// Lagacy v1 matcher.
+class RegexMatcher {
+public:
+ Error insert(StringRef Pattern, unsigned LineNumber);
+ unsigned match(StringRef Query) const;
+
+private:
+ 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:
+ Error insert(StringRef Pattern, unsigned LineNumber);
+ unsigned match(StringRef Query) const;
+
+private:
+ struct Glob {
+ Glob(StringRef Name, unsigned LineNo, GlobPattern &&Pattern)
+ : Name(Name), LineNo(LineNo), Pattern(std::move(Pattern)) {}
+ StringRef Name;
+ unsigned LineNo;
+ GlobPattern Pattern;
+ };
+
+ void LazyInit() const;
+
+ std::vector<GlobMatcher::Glob> Globs;
+
+ mutable RadixTree<iterator_range<StringRef::const_iterator>,
+ RadixTree<iterator_range<StringRef::const_reverse_iterator>,
+ SmallVector<int, 1>>>
+ PrefixSuffixToGlob;
+
+ mutable RadixTree<iterator_range<StringRef::const_iterator>,
+ SmallVector<int, 1>>
+ SubstrToGlob;
+
+ mutable bool Initialized = false;
+};
+
+/// Represents a set of patterns and their line numbers
+class Matcher {
+public:
+ Matcher(bool UseGlobs, bool RemoveDotSlash);
+
+ Error insert(StringRef Pattern, unsigned LineNumber);
+ unsigned match(StringRef Query) const;
+
+ bool matchAny(StringRef Query) const { return match(Query); }
+
+ std::variant<RegexMatcher, GlobMatcher> M;
+ bool RemoveDotSlash;
+};
+
+Error RegexMatcher::insert(StringRef Pattern, unsigned LineNumber) {
if (Pattern.empty())
return createStringError(errc::invalid_argument,
"Supplied regex was blank");
@@ -55,24 +125,14 @@ Error SpecialCaseList::RegexMatcher::insert(StringRef Pattern,
return Error::success();
}
-void SpecialCaseList::RegexMatcher::preprocess(bool BySize) {
- if (BySize) {
- llvm::stable_sort(RegExes, [](const Reg &A, const Reg &B) {
- return A.Name.size() < B.Name.size();
- });
- }
-}
-
-void SpecialCaseList::RegexMatcher::match(
- StringRef Query,
- llvm::function_ref<void(StringRef Rule, unsigned LineNo)> Cb) const {
+unsigned RegexMatcher::match(StringRef Query) const {
for (const auto &R : reverse(RegExes))
if (R.Rg.match(Query))
- return Cb(R.Name, R.LineNo);
+ return R.LineNo;
+ return 0;
}
-Error SpecialCaseList::GlobMatcher::insert(StringRef Pattern,
- unsigned LineNumber) {
+Error GlobMatcher::insert(StringRef Pattern, unsigned LineNumber) {
if (Pattern.empty())
return createStringError(errc::invalid_argument, "Supplied glob was blank");
@@ -83,14 +143,11 @@ Error SpecialCaseList::GlobMatcher::insert(StringRef Pattern,
return Error::success();
}
-void SpecialCaseList::GlobMatcher::preprocess(bool BySize) {
- if (BySize) {
- llvm::stable_sort(Globs, [](const Glob &A, const Glob &B) {
- return A.Name.size() < B.Name.size();
- });
- }
-
- for (const auto &G : reverse(Globs)) {
+void GlobMatcher::LazyInit() const {
+ if (LLVM_LIKELY(Initialized))
+ return;
+ Initialized = true;
+ for (const auto &[Idx, G] : enumerate(Globs)) {
StringRef Prefix = G.Pattern.prefix();
StringRef Suffix = G.Pattern.suffix();
@@ -102,26 +159,30 @@ void SpecialCaseList::GlobMatcher::preprocess(bool BySize) {
// But only if substring is not empty. Searching this tree is more
// expensive.
auto &V = SubstrToGlob.emplace(Substr).first->second;
- V.emplace_back(&G);
+ V.emplace_back(Idx);
continue;
}
}
auto &SToGlob = PrefixSuffixToGlob.emplace(Prefix).first->second;
auto &V = SToGlob.emplace(reverse(Suffix)).first->second;
- V.emplace_back(&G);
+ V.emplace_back(Idx);
}
}
-void SpecialCaseList::GlobMatcher::match(
- StringRef Query,
- llvm::function_ref<void(StringRef Rule, unsigned LineNo)> Cb) const {
+unsigned GlobMatcher::match(StringRef Query) const {
+ LazyInit();
+
+ int Best = -1;
if (!PrefixSuffixToGlob.empty()) {
for (const auto &[_, SToGlob] : PrefixSuffixToGlob.find_prefixes(Query)) {
for (const auto &[_, V] : SToGlob.find_prefixes(reverse(Query))) {
- for (const auto *G : V) {
- if (G->Pattern.match(Query)) {
- Cb(G->Name, G->LineNo);
+ for (int Idx : reverse(V)) {
+ if (Best > Idx)
+ break;
+ const GlobMatcher::Glob &G = Globs[Idx];
+ if (G.Pattern.match(Query)) {
+ Best = Idx;
// As soon as we find a match in the vector, we can break for this
// vector, since the globs are already sorted by priority within the
// prefix group. However, we continue searching other prefix groups
@@ -138,9 +199,12 @@ void SpecialCaseList::GlobMatcher::match(
// possibilities. In most cases search will fail on first characters.
for (StringRef Q = Query; !Q.empty(); Q = Q.drop_front()) {
for (const auto &[_, V] : SubstrToGlob.find_prefixes(Q)) {
- for (const auto *G : V) {
- if (G->Pattern.match(Query)) {
- Cb(G->Name, G->LineNo);
+ for (int Idx : reverse(V)) {
+ if (Best > Idx)
+ break;
+ const GlobMatcher::Glob &G = Globs[Idx];
+ if (G.Pattern.match(Query)) {
+ Best = Idx;
// As soon as we find a match in the vector, we can break for this
// vector, since the globs are already sorted by priority within the
// prefix group. However, we continue searching other prefix groups
@@ -151,9 +215,10 @@ void SpecialCaseList::GlobMatcher::match(
}
}
}
+ return Best < 0 ? 0 : Globs[Best].LineNo;
}
-SpecialCaseList::Matcher::Matcher(bool UseGlobs, bool RemoveDotSlash)
+Matcher::Matcher(bool UseGlobs, bool RemoveDotSlash)
: RemoveDotSlash(RemoveDotSlash) {
if (UseGlobs)
M.emplace<GlobMatcher>();
@@ -161,21 +226,40 @@ SpecialCaseList::Matcher::Matcher(bool UseGlobs, bool RemoveDotSlash)
M.emplace<RegexMatcher>();
}
-Error SpecialCaseList::Matcher::insert(StringRef Pattern, unsigned LineNumber) {
+Error Matcher::insert(StringRef Pattern, unsigned LineNumber) {
return std::visit([&](auto &V) { return V.insert(Pattern, LineNumber); }, M);
}
-void SpecialCaseList::Matcher::preprocess(bool BySize) {
- return std::visit([&](auto &V) { return V.preprocess(BySize); }, M);
-}
-
-void SpecialCaseList::Matcher::match(
- StringRef Query,
- llvm::function_ref<void(StringRef Rule, unsigned LineNo)> Cb) const {
+unsigned Matcher::match(StringRef Query) const {
if (RemoveDotSlash)
Query = llvm::sys::path::remove_leading_dotslash(Query);
- return std::visit([&](auto &V) { return V.match(Query, Cb); }, M);
+ return std::visit([&](auto &V) -> unsigned { return V.match(Query); }, M);
}
+} // namespace
+
+class SpecialCaseList::Section::SectionImpl {
+ friend class SpecialCaseList;
+ const Matcher *findMatcher(StringRef Prefix, StringRef Category) const;
+
+public:
+ using SectionEntries = StringMap<StringMap<Matcher>>;
+
+ SectionImpl(StringRef Str, bool UseGlobs)
+ : SectionMatcher(UseGlobs, /*RemoveDotSlash=*/false) {}
+
+ Matcher SectionMatcher;
+ SectionEntries Entries;
+
+ // 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.
+ 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.
+ StringRef getLongestMatch(StringRef Prefix, StringRef Query,
+ StringRef Category) const;
+};
// TODO: Refactor this to return Expected<...>
std::unique_ptr<SpecialCaseList>
@@ -215,7 +299,7 @@ bool SpecialCaseList::createInternal(const std::vector<std::string> &Paths,
return false;
}
std::string ParseError;
- if (!parse(i, FileOrErr.get().get(), ParseError, /*OrderBySize=*/false)) {
+ if (!parse(i, FileOrErr.get().get(), ParseError)) {
Error = (Twine("error parsing file '") + Path + "': " + ParseError).str();
return false;
}
@@ -223,21 +307,25 @@ bool SpecialCaseList::createInternal(const std::vector<std::string> &Paths,
return true;
}
-bool SpecialCaseList::createInternal(const MemoryBuffer *MB, std::string &Error,
- bool OrderBySize) {
- if (!parse(0, MB, Error, OrderBySize))
+bool SpecialCaseList::createInternal(const MemoryBuffer *MB,
+ std::string &Error) {
+ if (!parse(0, MB, Error))
return false;
return true;
}
+const std::vector<SpecialCaseList::Section> &SpecialCaseList::sections() const {
+ return Sections;
+}
+
Expected<SpecialCaseList::Section *>
SpecialCaseList::addSection(StringRef SectionStr, unsigned FileNo,
unsigned LineNo, bool UseGlobs) {
+ SectionStr = SectionStr.copy(StrAlloc);
Sections.emplace_back(SectionStr, FileNo, UseGlobs);
auto &Section = Sections.back();
- SectionStr = SectionStr.copy(StrAlloc);
- if (auto Err = Section.SectionMatcher.insert(SectionStr, LineNo)) {
+ if (auto Err = Section.Impl->SectionMatcher.insert(SectionStr, LineNo)) {
return createStringError(errc::invalid_argument,
"malformed section at line " + Twine(LineNo) +
": '" + SectionStr +
@@ -248,7 +336,7 @@ SpecialCaseList::addSection(StringRef SectionStr, unsigned FileNo,
}
bool SpecialCaseList::parse(unsigned FileIdx, const MemoryBuffer *MB,
- std::string &Error, bool OrderBySize) {
+ std::string &Error) {
unsigned long long Version = 2;
StringRef Header = MB->getBuffer();
@@ -264,11 +352,12 @@ bool SpecialCaseList::parse(unsigned FileIdx, const MemoryBuffer *MB,
bool RemoveDotSlash = Version > 2;
- Section *CurrentSection;
- if (auto Err = addSection("*", FileIdx, 1, true).moveInto(CurrentSection)) {
+ auto ErrOrSection = addSection("*", FileIdx, 1, true);
+ if (auto Err = ErrOrSection.takeError()) {
Error = toString(std::move(Err));
return false;
}
+ Section::SectionImpl *CurrentImpl = ErrOrSection.get()->Impl.get();
// This is the current list of prefixes for all existing users matching file
// path. We may need parametrization in constructor in future.
@@ -290,12 +379,13 @@ bool SpecialCaseList::parse(unsigned FileIdx, const MemoryBuffer *MB,
return false;
}
- if (auto Err = addSection(Line.drop_front().drop_back(), FileIdx, LineNo,
- UseGlobs)
- .moveInto(CurrentSection)) {
+ auto ErrOrSection =
+ addSection(Line.drop_front().drop_back(), FileIdx, LineNo, UseGlobs);
+ if (auto Err = ErrOrSection.takeError()) {
Error = toString(std::move(Err));
return false;
}
+ CurrentImpl = ErrOrSection.get()->Impl.get();
continue;
}
@@ -308,7 +398,7 @@ bool SpecialCaseList::parse(unsigned FileIdx, const MemoryBuffer *MB,
}
auto [Pattern, Category] = Postfix.split("=");
- auto [It, _] = CurrentSection->Entries[Prefix].try_emplace(
+ auto [It, _] = CurrentImpl->Entries[Prefix].try_emplace(
Category, UseGlobs,
RemoveDotSlash && llvm::is_contained(PathPrefixes, Prefix));
Pattern = Pattern.copy(StrAlloc);
@@ -321,9 +411,6 @@ bool SpecialCaseList::parse(unsigned FileIdx, const MemoryBuffer *MB,
}
}
- for (Section &S : Sections)
- S.preprocess(OrderBySize);
-
return true;
}
@@ -339,7 +426,7 @@ std::pair<unsigned, unsigned>
SpecialCaseList::inSectionBlame(StringRef Section, StringRef Prefix,
StringRef Query, StringRef Category) const {
for (const auto &S : reverse(Sections)) {
- if (S.SectionMatcher.matchAny(Section)) {
+ if (S.Impl->SectionMatcher.matchAny(Section)) {
unsigned Blame = S.getLastMatch(Prefix, Query, Category);
if (Blame)
return {S.FileIdx, Blame};
@@ -348,9 +435,21 @@ SpecialCaseList::inSectionBlame(StringRef Section, StringRef Prefix,
return NotFound;
}
-const SpecialCaseList::Matcher *
-SpecialCaseList::Section::findMatcher(StringRef Prefix,
- StringRef Category) const {
+SpecialCaseList::Section::Section(StringRef Str, unsigned FileIdx,
+ bool UseGlobs)
+ : Name(Str), FileIdx(FileIdx),
+ Impl(std::make_unique<SectionImpl>(Str, UseGlobs)) {}
+
+SpecialCaseList::Section::Section(Section &&) = default;
+SpecialCaseList::Section::~Section() = default;
+
+bool SpecialCaseList::Section::matchName(StringRef Name) const {
+ return Impl->SectionMatcher.matchAny(Name);
+}
+
+const Matcher *
+SpecialCaseList::Section::SectionImpl::findMatcher(StringRef Prefix,
+ StringRef Category) const {
SectionEntries::const_iterator I = Entries.find(Prefix);
if (I == Entries.end())
return nullptr;
@@ -361,36 +460,16 @@ SpecialCaseList::Section::findMatcher(StringRef Prefix,
return &II->second;
}
-LLVM_ABI void SpecialCaseList::Section::preprocess(bool OrderBySize) {
- SectionMatcher.preprocess(false);
- for (auto &[K1, E] : Entries)
- for (auto &[K2, M] : E)
- M.preprocess(OrderBySize);
-}
-
unsigned SpecialCaseList::Section::getLastMatch(StringRef Prefix,
StringRef Query,
StringRef Category) const {
- unsigned LastLine = 0;
- if (const Matcher *M = findMatcher(Prefix, Category)) {
- M->match(Query, [&](StringRef, unsigned LineNo) {
- LastLine = std::max(LastLine, LineNo);
- });
- }
- return LastLine;
+ if (const Matcher *M = Impl->findMatcher(Prefix, Category))
+ return M->match(Query);
+ return 0;
}
-StringRef SpecialCaseList::Section::getLongestMatch(StringRef Prefix,
- StringRef Query,
- StringRef Category) const {
- StringRef LongestRule;
- if (const Matcher *M = findMatcher(Prefix, Category)) {
- M->match(Query, [&](StringRef Rule, unsigned) {
- if (LongestRule.size() < Rule.size())
- LongestRule = Rule;
- });
- }
- return LongestRule;
+bool SpecialCaseList::Section::hasPrefix(StringRef Prefix) const {
+ return Impl->Entries.find(Prefix) != Impl->Entries.end();
}
} // namespace llvm
More information about the llvm-commits
mailing list