2626#include " llvm/Support/MemoryBuffer.h"
2727#include " llvm/Support/Regex.h"
2828#include " llvm/Support/VirtualFileSystem.h"
29- #include " llvm/Support/raw_ostream.h"
29+ #include " llvm/Support/WithColor.h"
30+ #include < cassert>
3031#include < memory>
32+ #include < mutex>
3133#include < stdio.h>
3234#include < string>
3335#include < system_error>
@@ -44,6 +46,7 @@ class RegexMatcher {
4446public:
4547 Error insert (StringRef Pattern, unsigned LineNumber);
4648 unsigned match (StringRef Query) const ;
49+ StringRef findRule (unsigned LineNo) const ;
4750
4851private:
4952 struct Reg {
@@ -61,6 +64,7 @@ class GlobMatcher {
6164public:
6265 Error insert (StringRef Pattern, unsigned LineNumber);
6366 unsigned match (StringRef Query) const ;
67+ StringRef findRule (unsigned LineNo) const ;
6468
6569private:
6670 struct Glob {
@@ -90,15 +94,25 @@ class GlobMatcher {
9094// / Represents a set of patterns and their line numbers
9195class Matcher {
9296public:
93- Matcher (bool UseGlobs, bool RemoveDotSlash);
97+ enum class QueryOptions {
98+ kUnchanged ,
99+ kNoSlashDot ,
100+ kNoSlashDotWithFallback ,
101+ };
102+
103+ Matcher (bool UseGlobs, QueryOptions QOpts = QueryOptions::kUnchanged );
94104
95105 Error insert (StringRef Pattern, unsigned LineNumber);
96106 unsigned match (StringRef Query) const ;
97107
98108 bool matchAny (StringRef Query) const { return match (Query); }
99109
110+ private:
111+ unsigned matchInternal (StringRef Query) const ;
112+ StringRef findRule (unsigned LineNo) const ;
113+
100114 std::variant<RegexMatcher, GlobMatcher> M;
101- bool RemoveDotSlash ;
115+ QueryOptions Options ;
102116};
103117
104118Error RegexMatcher::insert (StringRef Pattern, unsigned LineNumber) {
@@ -132,6 +146,14 @@ unsigned RegexMatcher::match(StringRef Query) const {
132146 return 0 ;
133147}
134148
149+ StringRef RegexMatcher::findRule (unsigned LineNo) const {
150+ for (const auto &R : RegExes)
151+ if (R.LineNo == LineNo)
152+ return R.Name ;
153+ assert (!" `findRule` should be called only with correct `LineNo`" );
154+ return {};
155+ }
156+
135157Error GlobMatcher::insert (StringRef Pattern, unsigned LineNumber) {
136158 if (Pattern.empty ())
137159 return createStringError (errc::invalid_argument, " Supplied glob was blank" );
@@ -218,8 +240,15 @@ unsigned GlobMatcher::match(StringRef Query) const {
218240 return Best < 0 ? 0 : Globs[Best].LineNo ;
219241}
220242
221- Matcher::Matcher (bool UseGlobs, bool RemoveDotSlash)
222- : RemoveDotSlash(RemoveDotSlash) {
243+ StringRef GlobMatcher::findRule (unsigned LineNo) const {
244+ for (const auto &G : Globs)
245+ if (G.LineNo == LineNo)
246+ return G.Name ;
247+ assert (!" `findRule` should be called only with correct `LineNo`" );
248+ return {};
249+ }
250+
251+ Matcher::Matcher (bool UseGlobs, QueryOptions QOpts) : Options(QOpts) {
223252 if (UseGlobs)
224253 M.emplace <GlobMatcher>();
225254 else
@@ -231,10 +260,42 @@ Error Matcher::insert(StringRef Pattern, unsigned LineNumber) {
231260}
232261
233262unsigned Matcher::match (StringRef Query) const {
234- if (RemoveDotSlash)
235- Query = llvm::sys::path::remove_leading_dotslash (Query);
263+ switch (Options) {
264+ case QueryOptions::kUnchanged :
265+ return matchInternal (Query);
266+ case QueryOptions::kNoSlashDot :
267+ return matchInternal (llvm::sys::path::remove_leading_dotslash (Query));
268+ case QueryOptions::kNoSlashDotWithFallback :
269+ break ;
270+ }
271+
272+ StringRef FixedQuery = llvm::sys::path::remove_leading_dotslash (Query);
273+ unsigned FixedMatched = matchInternal (FixedQuery);
274+ if (FixedQuery == Query)
275+ return FixedMatched;
276+
277+ unsigned OriginalMatch = matchInternal (Query);
278+ if (OriginalMatch > FixedMatched) {
279+ static std::once_flag Warned;
280+ std::call_once (Warned, [&]() {
281+ WithColor::warning () << " Deprecated behaviour: '"
282+ << findRule (OriginalMatch) << " ' matches '" << Query
283+ << " ', but it should match '" << FixedQuery
284+ << " '.\n " ;
285+ });
286+ }
287+ return std::max (OriginalMatch, FixedMatched);
288+ }
289+
290+ unsigned Matcher::matchInternal (StringRef Query) const {
236291 return std::visit ([&](auto &V) -> unsigned { return V.match (Query); }, M);
237292}
293+
294+ StringRef Matcher::findRule (unsigned LineNo) const {
295+ return std::visit ([&](auto &V) -> StringRef { return V.findRule (LineNo); },
296+ M);
297+ }
298+
238299} // namespace
239300
240301class SpecialCaseList ::Section::SectionImpl {
@@ -243,8 +304,7 @@ class SpecialCaseList::Section::SectionImpl {
243304
244305 using SectionEntries = StringMap<StringMap<Matcher>>;
245306
246- explicit SectionImpl (bool UseGlobs)
247- : SectionMatcher(UseGlobs, /* RemoveDotSlash=*/ false ) {}
307+ explicit SectionImpl (bool UseGlobs) : SectionMatcher(UseGlobs) {}
248308
249309 Matcher SectionMatcher;
250310 SectionEntries Entries;
@@ -335,8 +395,6 @@ bool SpecialCaseList::parse(unsigned FileIdx, const MemoryBuffer *MB,
335395 // https://discourse.llvm.org/t/use-glob-instead-of-regex-for-specialcaselists/71666
336396 bool UseGlobs = Version > 1 ;
337397
338- bool RemoveDotSlash = Version > 2 ;
339-
340398 auto ErrOrSection = addSection (" *" , FileIdx, 1 , true );
341399 if (auto Err = ErrOrSection.takeError ()) {
342400 Error = toString (std::move (Err));
@@ -382,10 +440,17 @@ bool SpecialCaseList::parse(unsigned FileIdx, const MemoryBuffer *MB,
382440 return false ;
383441 }
384442
443+ Matcher::QueryOptions QOpts = Matcher::QueryOptions::kUnchanged ;
444+ if (llvm::is_contained (PathPrefixes, Prefix)) {
445+ if (Version == 3 )
446+ QOpts = Matcher::QueryOptions::kNoSlashDotWithFallback ;
447+ else if (Version > 4 )
448+ QOpts = Matcher::QueryOptions::kNoSlashDot ;
449+ }
450+
385451 auto [Pattern, Category] = Postfix.split (" =" );
386- auto [It, _] = CurrentImpl->Entries [Prefix].try_emplace (
387- Category, UseGlobs,
388- RemoveDotSlash && llvm::is_contained (PathPrefixes, Prefix));
452+ auto [It, _] =
453+ CurrentImpl->Entries [Prefix].try_emplace (Category, UseGlobs, QOpts);
389454 Pattern = Pattern.copy (StrAlloc);
390455 if (auto Err = It->second .insert (Pattern, LineNo)) {
391456 Error =
0 commit comments