35#include <system_error>
47 Error insert(StringRef Pattern,
unsigned LineNumber);
48 unsigned match(StringRef Query)
const;
49 StringRef findRule(
unsigned LineNo)
const;
53 Reg(StringRef Name,
unsigned LineNo, Regex &&Rg)
54 : Name(Name), LineNo(LineNo), Rg(std::
move(Rg)) {}
60 std::vector<Reg> RegExes;
65 Error insert(StringRef Pattern,
unsigned LineNumber);
66 unsigned match(StringRef Query)
const;
67 StringRef findRule(
unsigned LineNo)
const;
71 Glob(StringRef Name,
unsigned LineNo, GlobPattern &&Pattern)
72 : Name(Name), LineNo(LineNo), Pattern(std::
move(Pattern)) {}
78 void LazyInit()
const;
80 std::vector<GlobMatcher::Glob> Globs;
82 mutable RadixTree<iterator_range<StringRef::const_iterator>,
83 RadixTree<iterator_range<StringRef::const_reverse_iterator>,
87 mutable RadixTree<iterator_range<StringRef::const_iterator>,
91 mutable bool Initialized =
false;
96 bool RemoveDotSlash =
false;
97 bool WarnDotSlashMatch =
false;
103 explicit Matcher(QueryOptions QOpts);
105 Error insert(StringRef Pattern,
unsigned LineNumber);
106 unsigned match(StringRef Query)
const;
108 bool matchAny(StringRef Query)
const {
return match(Query); }
111 unsigned matchInternal(StringRef Query)
const;
112 StringRef findRule(
unsigned LineNo)
const;
114 std::variant<RegexMatcher, GlobMatcher> M;
115 QueryOptions Options;
116 mutable std::once_flag Warned;
122 "Supplied regex was blank");
125 auto Regexp = Pattern.str();
126 for (
size_t pos = 0; (pos = Regexp.find(
'*', pos)) != std::string::npos;
127 pos += strlen(
".*")) {
128 Regexp.replace(pos, strlen(
"*"),
".*");
131 Regexp = (Twine(
"^(") + StringRef(Regexp) +
")$").str();
134 Regex CheckRE(Regexp);
136 if (!CheckRE.isValid(REError))
139 RegExes.emplace_back(Pattern, LineNumber, std::move(CheckRE));
140 return Error::success();
143unsigned RegexMatcher::match(StringRef Query)
const {
144 for (
const auto &R :
reverse(RegExes))
145 if (
R.Rg.match(Query))
150StringRef RegexMatcher::findRule(
unsigned LineNo)
const {
151 for (
const auto &R : RegExes)
152 if (
R.LineNo == LineNo)
158Error GlobMatcher::insert(StringRef Pattern,
unsigned LineNumber) {
162 auto Res = GlobPattern::create(Pattern, 1024);
163 if (
auto Err = Res.takeError())
165 Globs.emplace_back(Pattern, LineNumber, std::move(Res.get()));
166 return Error::success();
169void GlobMatcher::LazyInit()
const {
173 for (
const auto &[Idx,
G] :
enumerate(Globs)) {
174 StringRef
Prefix =
G.Pattern.prefix();
175 StringRef Suffix =
G.Pattern.suffix();
177 if (Suffix.empty() &&
Prefix.empty()) {
180 StringRef Substr =
G.Pattern.longest_substr();
181 if (!Substr.empty()) {
184 auto &
V = SubstrToGlob.emplace(Substr).first->second;
190 auto &SToGlob = PrefixSuffixToGlob.emplace(Prefix).first->second;
191 auto &
V = SToGlob.emplace(
reverse(Suffix)).first->second;
196unsigned GlobMatcher::match(StringRef Query)
const {
200 if (!PrefixSuffixToGlob.empty()) {
201 for (
const auto &[
_, SToGlob] : PrefixSuffixToGlob.find_prefixes(Query)) {
202 for (
const auto &[
_, V] : SToGlob.find_prefixes(
reverse(Query))) {
206 const GlobMatcher::Glob &
G = Globs[Idx];
207 if (
G.Pattern.match(Query)) {
220 if (!SubstrToGlob.empty()) {
223 for (StringRef Q = Query; !Q.empty(); Q = Q.drop_front()) {
224 for (
const auto &[
_, V] : SubstrToGlob.find_prefixes(Q)) {
228 const GlobMatcher::Glob &
G = Globs[Idx];
229 if (
G.Pattern.match(Query)) {
241 return Best < 0 ? 0 : Globs[Best].LineNo;
244StringRef GlobMatcher::findRule(
unsigned LineNo)
const {
245 for (
const auto &
G : Globs)
246 if (
G.LineNo == LineNo)
252Matcher::Matcher(QueryOptions QOpts) :
Options(QOpts) {
254 M.emplace<GlobMatcher>();
256 M.emplace<RegexMatcher>();
259Error Matcher::insert(StringRef Pattern,
unsigned LineNumber) {
260 return std::visit([&](
auto &V) {
return V.insert(Pattern, LineNumber); }, M);
275unsigned Matcher::match(StringRef Query)
const {
277 return matchInternal(Query);
279 if (!
Options.WarnDotSlashMatch)
283 unsigned FixedMatched = matchInternal(FixedQuery);
284 if (FixedQuery == Query)
287 unsigned OriginalMatch = matchInternal(Query);
288 if (OriginalMatch > FixedMatched) {
289 std::call_once(Warned, [&]() {
290 WithColor::warning() <<
"Deprecated behaviour: pattern '"
291 << findRule(OriginalMatch) <<
"' matches '" << Query
292 <<
"', update it to match '" << FixedQuery
293 <<
"' instead (further warnings suppressed).\n";
296 return std::max(OriginalMatch, FixedMatched);
299unsigned Matcher::matchInternal(StringRef Query)
const {
300 return std::visit([&](
auto &V) ->
unsigned {
return V.match(Query); }, M);
303StringRef Matcher::findRule(
unsigned LineNo)
const {
304 return std::visit([&](
auto &V) -> StringRef {
return V.findRule(LineNo); },
322std::unique_ptr<SpecialCaseList>
326 if (SCL->createInternal(Paths, FS,
Error))
332 std::string &
Error) {
334 if (SCL->createInternal(MB,
Error))
339std::unique_ptr<SpecialCaseList>
350 for (
size_t i = 0; i < Paths.size(); ++i) {
351 const auto &Path = Paths[i];
354 if (std::error_code EC = FileOrErr.
getError()) {
355 Error = (
Twine(
"can't open file '") + Path +
"': " + EC.message()).str();
358 std::string ParseError;
359 if (!
parse(i, FileOrErr.
get().get(), ParseError)) {
360 Error = (
Twine(
"error parsing file '") + Path +
"': " + ParseError).str();
368 std::string &
Error) {
375SpecialCaseList::addSection(
StringRef SectionStr,
unsigned FileNo,
376 unsigned LineNo,
bool UseGlobs) {
377 SectionStr = SectionStr.
copy(StrAlloc);
378 Sections.emplace_back(SectionStr, FileNo, UseGlobs);
379 auto &Section = Sections.back();
381 if (
auto Err = Section.Impl->SectionMatcher.insert(SectionStr, LineNo)) {
383 "malformed section at line " +
Twine(LineNo) +
391bool SpecialCaseList::parse(
unsigned FileIdx,
const MemoryBuffer *MB,
392 std::string &Error) {
393 unsigned long long Version = 2;
395 StringRef Header = MB->getBuffer();
396 if (Header.consume_front(
"#!special-case-list-v"))
399 auto MinVersion = [&](
unsigned V) {
return Version >= V; };
406 bool UseGlobs = MinVersion(2);
407 bool RemoveDotSlash = MinVersion(3);
408 bool WarnDotSlash = MinVersion(4) && !MinVersion(5);
410 auto ErrOrSection =
addSection(
"*", FileIdx, 1,
true);
411 if (
auto Err = ErrOrSection.takeError()) {
415 Section::SectionImpl *CurrentImpl = ErrOrSection.get()->Impl.get();
419 constexpr StringRef PathPrefixes[] = {
"src",
"!src",
"mainfile",
"source"};
421 for (line_iterator LineIt(*MB,
true,
'#');
422 !LineIt.is_at_eof(); LineIt++) {
423 unsigned LineNo = LineIt.line_number();
424 StringRef Line = LineIt->trim();
429 if (Line.starts_with(
"[")) {
430 if (!Line.ends_with(
"]")) {
432 (
"malformed section header on line " + Twine(LineNo) +
": " + Line)
438 addSection(Line.drop_front().drop_back(), FileIdx, LineNo, UseGlobs);
439 if (
auto Err = ErrOrSection.takeError()) {
443 CurrentImpl = ErrOrSection.get()->Impl.get();
448 auto [
Prefix, Postfix] = Line.split(
":");
449 if (Postfix.empty()) {
451 Error = (
"malformed line " + Twine(LineNo) +
": '" + Line +
"'").str();
456 QOpts.UseGlobs = UseGlobs;
458 QOpts.RemoveDotSlash = RemoveDotSlash;
459 QOpts.WarnDotSlashMatch = WarnDotSlash;
462 auto [Pattern, Category] = Postfix.split(
"=");
463 auto [It,
_] = CurrentImpl->Entries[
Prefix].try_emplace(Category, QOpts);
464 Pattern = Pattern.copy(StrAlloc);
465 if (
auto Err = It->second.insert(Pattern, LineNo)) {
467 (Twine(
"malformed ") + (UseGlobs ?
"glob" :
"regex") +
" in line " +
468 Twine(LineNo) +
": '" + Pattern +
"': " +
toString(std::move(Err)))
477SpecialCaseList::~SpecialCaseList() =
default;
485std::pair<unsigned, unsigned>
488 for (
const auto &S :
reverse(Sections)) {
489 if (S.Impl->SectionMatcher.matchAny(
Section)) {
490 unsigned Blame = S.getLastMatch(Prefix, Query, Category);
492 return {S.FileIdx, Blame};
500 : Name(Str), FileIdx(FileIdx),
502 QueryOptions{UseGlobs,
false})) {}
509 return Impl->SectionMatcher.matchAny(Name);
519 if (
II ==
I->second.end())
528 if (
const Matcher *M = Impl->findMatcher(Prefix, Category))
529 return M->match(Query);
534 return Impl->Entries.contains(Prefix);
This file defines the StringMap class.
#define LLVM_LIKELY(EXPR)
static llvm::Error parse(GsymDataExtractor &Data, uint64_t BaseAddr, LineEntryCallback const &Callback)
static const char * toString(MIToken::TokenKind TokenKind)
static Error addSection(const NewSectionInfo &NewSection, Object &Obj)
uint64_t IntrinsicInst * II
This file defines the SmallVector class.
Defines the virtual file system interface vfs::FileSystem.
Represents either an error or a value T.
std::error_code getError() const
Lightweight error class with error context and mandatory checking.
Tagged union holding either a T or a Error.
This interface provides simple read-only access to a block of memory, and provides simple methods for...
StringMap< StringMap< Matcher > > SectionEntries
const Matcher * findMatcher(StringRef Prefix, StringRef Category) const
SectionImpl(QueryOptions QOpts)
LLVM_ABI Section(StringRef Name, unsigned FileIdx, bool UseGlobs)
LLVM_ABI bool hasPrefix(StringRef Prefix) const
Returns true if the section has any entries for the given prefix.
LLVM_ABI unsigned getLastMatch(StringRef Prefix, StringRef Query, StringRef Category) const
LLVM_ABI bool matchName(StringRef Name) const
static constexpr std::pair< unsigned, unsigned > NotFound
LLVM_ABI std::pair< unsigned, unsigned > inSectionBlame(StringRef Section, StringRef Prefix, StringRef Query, StringRef Category=StringRef()) const
Returns the file index and the line number <FileIdx, LineNo> corresponding to the special case list e...
LLVM_ABI bool createInternal(const std::vector< std::string > &Paths, vfs::FileSystem &VFS, std::string &Error)
static LLVM_ABI std::unique_ptr< SpecialCaseList > createOrDie(const std::vector< std::string > &Paths, llvm::vfs::FileSystem &FS)
Parses the special case list entries from files.
static LLVM_ABI std::unique_ptr< SpecialCaseList > create(const std::vector< std::string > &Paths, llvm::vfs::FileSystem &FS, std::string &Error)
Parses the special case list entries from files.
SpecialCaseList()=default
LLVM_ABI bool inSection(StringRef Section, StringRef Prefix, StringRef Query, StringRef Category=StringRef()) const
Returns true, if special case list contains a line.
StringMap - This is an unconventional map that is specialized for handling keys that are "strings",...
StringMapIterBase< StringMap< Matcher >, true > const_iterator
Represent a constant reference to a string, i.e.
StringRef copy(Allocator &A) const
Twine - A lightweight data structure for efficiently representing the concatenation of temporary valu...
The virtual file system interface.
llvm::ErrorOr< std::unique_ptr< llvm::MemoryBuffer > > getBufferForFile(const Twine &Name, int64_t FileSize=-1, bool RequiresNullTerminator=true, bool IsVolatile=false, bool IsText=true)
This is a convenience method that opens a file, gets its content and then closes the file.
This provides a very simple, boring adaptor for a begin and end iterator into a range type.
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
bool match(Val *V, const Pattern &P)
LLVM_ABI StringRef remove_leading_dotslash(StringRef path LLVM_LIFETIME_BOUND, Style style=Style::native)
Remove redundant leading "./" pieces and consecutive separators.
This is an optimization pass for GlobalISel generic memory operations.
auto enumerate(FirstRange &&First, RestRanges &&...Rest)
Given two or more input ranges, returns a new range whose values are tuples (A, B,...
Error createStringError(std::error_code EC, char const *Fmt, const Ts &... Vals)
Create formatted StringError object.
LLVM_ABI bool consumeUnsignedInteger(StringRef &Str, unsigned Radix, unsigned long long &Result)
auto reverse(ContainerTy &&C)
LLVM_ABI void report_fatal_error(Error Err, bool gen_crash_diag=true)
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
OutputIt move(R &&Range, OutputIt Out)
Provide wrappers to std::move which take ranges instead of having to pass begin/end explicitly.
bool is_contained(R &&Range, const E &Element)
Returns true if Element is found in Range.
Implement std::hash so that hash_code can be used in STL containers.