From 493a5902c3b68a5bc895889290be87e8217b639e Mon Sep 17 00:00:00 2001 From: Vitaly Buka 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 { /// 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::create(const llvm::MemoryBuffer &Input, std::string &Err) { auto WarningSuppressionList = std::make_unique(); - 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 #include #include -#include #include namespace llvm { @@ -118,119 +110,45 @@ class SpecialCaseList { // classes. LLVM_ABI bool createInternal(const std::vector &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 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 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 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 Globs; - - RadixTree, - RadixTree, - SmallVector>> - PrefixSuffixToGlob; - - RadixTree, - SmallVector> - 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 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 M; - bool RemoveDotSlash; - }; - - using SectionEntries = StringMap>; + // 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 Impl; }; - ArrayRef sections() const { return Sections; } + const std::vector
§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 -#include +#include "llvm/Support/raw_ostream.h" #include #include #include #include #include +#include +#include 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 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 Globs; + + mutable RadixTree, + RadixTree, + SmallVector>> + PrefixSuffixToGlob; + + mutable RadixTree, + SmallVector> + 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 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 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 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(); @@ -161,21 +226,40 @@ SpecialCaseList::Matcher::Matcher(bool UseGlobs, bool RemoveDotSlash) M.emplace(); } -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 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>; + + 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 @@ -215,7 +299,7 @@ bool SpecialCaseList::createInternal(const std::vector &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 &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::sections() const { + return Sections; +} + Expected 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 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(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