llvm.org GIT mirror llvm / 56fec39
Extend SpecialCaseList to allow users to blame matches on entries in the file. Summary: Extends SCL functionality to allow users to find the line number in the file the SCL is built from through SpecialCaseList::inSectionBlame(...). Also removes the need to compile the SCL before use. As the matcher now contains a list of regexes to test against instead of a single regex, the regexes can be individually built on each insertion rather than one large compilation at the end of construction. This change also fixes a bug where blank lines would cause the parser to become out-of-sync with the line number. An error on line `k` was being reported as being on line `k - num_blank_lines_before_k`. Note: This change has a cyclical dependency on D39486. Both these changes must be submitted at the same time to avoid a build breakage. Reviewers: vlad.tsyrklevich Reviewed By: vlad.tsyrklevich Subscribers: kcc, pcc, llvm-commits Differential Revision: https://reviews.llvm.org/D39485 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@317617 91177308-0d34-0410-b5e6-96231b3b80d8 Mitch Phillips 1 year, 9 months ago
3 changed file(s) with 91 addition(s) and 69 deletion(s). Raw diff Collapse all Expand all
8888 bool inSection(StringRef Section, StringRef Prefix, StringRef Query,
8989 StringRef Category = StringRef()) const;
9090
91 /// Returns the line number corresponding to the special case list entry if
92 /// the special case list contains a line
93 /// \code
94 /// @Prefix:=@Category
95 /// \endcode
96 /// where @Query satisfies wildcard expression in a given @Section.
97 /// Returns zero if there is no blacklist entry corresponding to this
98 /// expression.
99 unsigned inSectionBlame(StringRef Section, StringRef Prefix, StringRef Query,
100 StringRef Category = StringRef()) const;
101
91102 protected:
92103 // Implementations of the create*() functions that can also be used by derived
93104 // classes.
95106 std::string &Error);
96107 bool createInternal(const MemoryBuffer *MB, std::string &Error);
97108
109 SpecialCaseList() = default;
98110 SpecialCaseList(SpecialCaseList const &) = delete;
99111 SpecialCaseList &operator=(SpecialCaseList const &) = delete;
100112
101113 /// Represents a set of regular expressions. Regular expressions which are
102 /// "literal" (i.e. no regex metacharacters) are stored in Strings, while all
103 /// others are represented as a single pipe-separated regex in RegEx. The
104 /// reason for doing so is efficiency; StringSet is much faster at matching
114 /// "literal" (i.e. no regex metacharacters) are stored in Strings. The
115 /// reason for doing so is efficiency; StringMap is much faster at matching
105116 /// literal strings than Regex.
106117 class Matcher {
107118 public:
108 bool insert(std::string Regexp, std::string &REError);
109 void compile();
110 bool match(StringRef Query) const;
119 bool insert(std::string Regexp, unsigned LineNumber, std::string &REError);
120 // Returns the line number in the source file that this query matches to.
121 // Returns zero if no match is found.
122 unsigned match(StringRef Query) const;
111123
112124 private:
113 StringSet<> Strings;
125 StringMap> Strings;
114126 TrigramIndex Trigrams;
115 std::unique_ptr RegEx;
116 std::string UncompiledRegEx;
127 std::vector, unsigned>> RegExes;
117128 };
118129
119130 using SectionEntries = StringMap>;
126137 };
127138
128139 std::vector
Sections;
129 bool IsCompiled;
130140
131 SpecialCaseList();
132141 /// Parses just-constructed SpecialCaseList entries from a memory buffer.
133142 bool parse(const MemoryBuffer *MB, StringMap &SectionsMap,
134143 std::string &Error);
135 /// compile() should be called once, after parsing all the memory buffers.
136 void compile();
137144
138145 // Helper method for derived classes to search by Prefix, Query, and Category
139146 // once they have already resolved a section entry.
140 bool inSection(const SectionEntries &Entries, StringRef Prefix,
141 StringRef Query, StringRef Category) const;
147 unsigned inSectionBlame(const SectionEntries &Entries, StringRef Prefix,
148 StringRef Query, StringRef Category) const;
142149 };
143150
144151 } // namespace llvm
2626 namespace llvm {
2727
2828 bool SpecialCaseList::Matcher::insert(std::string Regexp,
29 unsigned LineNumber,
2930 std::string &REError) {
3031 if (Regexp.empty()) {
3132 REError = "Supplied regexp was blank";
3334 }
3435
3536 if (Regex::isLiteralERE(Regexp)) {
36 Strings.insert(Regexp);
37 Strings[Regexp] = LineNumber;
3738 return true;
3839 }
3940 Trigrams.insert(Regexp);
4445 Regexp.replace(pos, strlen("*"), ".*");
4546 }
4647
48 Regexp = (Twine("^(") + StringRef(Regexp) + ")$").str();
49
4750 // Check that the regexp is valid.
4851 Regex CheckRE(Regexp);
4952 if (!CheckRE.isValid(REError))
5053 return false;
5154
52 if (!UncompiledRegEx.empty())
53 UncompiledRegEx += "|";
54 UncompiledRegEx += "^(" + Regexp + ")$";
55 return true;
56 }
57
58 void SpecialCaseList::Matcher::compile() {
59 if (!UncompiledRegEx.empty()) {
60 RegEx.reset(new Regex(UncompiledRegEx));
61 UncompiledRegEx.clear();
62 }
63 }
64
65 bool SpecialCaseList::Matcher::match(StringRef Query) const {
66 if (Strings.count(Query))
67 return true;
55 RegExes.emplace_back(
56 std::make_pair(make_unique(std::move(CheckRE)), LineNumber));
57 return true;
58 }
59
60 unsigned SpecialCaseList::Matcher::match(StringRef Query) const {
61 auto It = Strings.find(Query);
62 if (It != Strings.end())
63 return It->second;
6864 if (Trigrams.isDefinitelyOut(Query))
6965 return false;
70 return RegEx && RegEx->match(Query);
71 }
72
73 SpecialCaseList::SpecialCaseList() : Sections(), IsCompiled(false) {}
66 for (auto& RegExKV : RegExes)
67 if (RegExKV.first->match(Query))
68 return RegExKV.second;
69 return 0;
70 }
7471
7572 std::unique_ptr
7673 SpecialCaseList::create(const std::vector &Paths,
113110 return false;
114111 }
115112 }
116 compile();
117113 return true;
118114 }
119115
122118 StringMap Sections;
123119 if (!parse(MB, Sections, Error))
124120 return false;
125 compile();
126121 return true;
127122 }
128123
131126 std::string &Error) {
132127 // Iterate through each line in the blacklist file.
133128 SmallVector Lines;
134 SplitString(MB->getBuffer(), Lines, "\n\r");
135
136 int LineNo = 1;
129 MB->getBuffer().split(Lines, '\n');
130
131 unsigned LineNo = 1;
137132 StringRef Section = "*";
133
138134 for (auto I = Lines.begin(), E = Lines.end(); I != E; ++I, ++LineNo) {
135 *I = I->trim();
139136 // Ignore empty lines and lines starting with "#"
140137 if (I->empty() || I->startswith("#"))
141138 continue;
180177 if (SectionsMap.find(Section) == SectionsMap.end()) {
181178 std::unique_ptr M = make_unique();
182179 std::string REError;
183 if (!M->insert(Section, REError)) {
180 if (!M->insert(Section, LineNo, REError)) {
184181 Error = (Twine("malformed section ") + Section + ": '" + REError).str();
185182 return false;
186183 }
187 M->compile();
188184
189185 SectionsMap[Section] = Sections.size();
190186 Sections.emplace_back(std::move(M));
192188
193189 auto &Entry = Sections[SectionsMap[Section]].Entries[Prefix][Category];
194190 std::string REError;
195 if (!Entry.insert(std::move(Regexp), REError)) {
191 if (!Entry.insert(std::move(Regexp), LineNo, REError)) {
196192 Error = (Twine("malformed regex in line ") + Twine(LineNo) + ": '" +
197193 SplitLine.second + "': " + REError).str();
198194 return false;
201197 return true;
202198 }
203199
204 void SpecialCaseList::compile() {
205 assert(!IsCompiled && "compile() should only be called once");
206 // Iterate through every section compiling regular expressions for every query
207 // and creating Section entries.
208 for (auto &Section : Sections)
209 for (auto &Prefix : Section.Entries)
210 for (auto &Category : Prefix.getValue())
211 Category.getValue().compile();
212
213 IsCompiled = true;
214 }
215
216200 SpecialCaseList::~SpecialCaseList() {}
217201
218202 bool SpecialCaseList::inSection(StringRef Section, StringRef Prefix,
219203 StringRef Query, StringRef Category) const {
220 assert(IsCompiled && "SpecialCaseList::compile() was not called!");
221
204 return inSectionBlame(Section, Prefix, Query, Category);
205 }
206
207 unsigned SpecialCaseList::inSectionBlame(StringRef Section, StringRef Prefix,
208 StringRef Query,
209 StringRef Category) const {
222210 for (auto &SectionIter : Sections)
223 if (SectionIter.SectionMatcher->match(Section) &&
224 inSection(SectionIter.Entries, Prefix, Query, Category))
225 return true;
226
227 return false;
228 }
229
230 bool SpecialCaseList::inSection(const SectionEntries &Entries, StringRef Prefix,
231 StringRef Query, StringRef Category) const {
211 if (SectionIter.SectionMatcher->match(Section)) {
212 unsigned Blame =
213 inSectionBlame(SectionIter.Entries, Prefix, Query, Category);
214 if (Blame)
215 return Blame;
216 }
217 return 0;
218 }
219
220 unsigned SpecialCaseList::inSectionBlame(const SectionEntries &Entries,
221 StringRef Prefix, StringRef Query,
222 StringRef Category) const {
232223 SectionEntries::const_iterator I = Entries.find(Prefix);
233 if (I == Entries.end()) return false;
224 if (I == Entries.end()) return 0;
234225 StringMap::const_iterator II = I->second.find(Category);
235 if (II == I->second.end()) return false;
226 if (II == I->second.end()) return 0;
236227
237228 return II->getValue().match(Query);
238229 }
5757 EXPECT_FALSE(SCL->inSection("", "src", "hi"));
5858 EXPECT_FALSE(SCL->inSection("", "fun", "hello"));
5959 EXPECT_FALSE(SCL->inSection("", "src", "hello", "category"));
60
61 EXPECT_EQ(3u, SCL->inSectionBlame("", "src", "hello"));
62 EXPECT_EQ(4u, SCL->inSectionBlame("", "src", "bye"));
63 EXPECT_EQ(5u, SCL->inSectionBlame("", "src", "hi", "category"));
64 EXPECT_EQ(6u, SCL->inSectionBlame("", "src", "zzzz", "category"));
65 EXPECT_EQ(0u, SCL->inSectionBlame("", "src", "hi"));
66 EXPECT_EQ(0u, SCL->inSectionBlame("", "fun", "hello"));
67 EXPECT_EQ(0u, SCL->inSectionBlame("", "src", "hello", "category"));
68 }
69
70 TEST_F(SpecialCaseListTest, CorrectErrorLineNumberWithBlankLine) {
71 std::string Error;
72 EXPECT_EQ(nullptr, makeSpecialCaseList("# This is a comment.\n"
73 "\n"
74 "[not valid\n",
75 Error));
76 EXPECT_TRUE(
77 ((StringRef)Error).startswith("malformed section header on line 3:"));
78
79 EXPECT_EQ(nullptr, makeSpecialCaseList("\n\n\n"
80 "[not valid\n",
81 Error));
82 EXPECT_TRUE(
83 ((StringRef)Error).startswith("malformed section header on line 4:"));
6084 }
6185
6286 TEST_F(SpecialCaseListTest, SectionRegexErrorHandling) {