1 | //===- unittest/ASTMatchers/Dynamic/ParserTest.cpp - Parser unit tests -===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===-------------------------------------------------------------------===// |
8 | |
9 | #include "../ASTMatchersTest.h" |
10 | #include "clang/ASTMatchers/Dynamic/Parser.h" |
11 | #include "clang/ASTMatchers/Dynamic/Registry.h" |
12 | #include "gtest/gtest.h" |
13 | #include <optional> |
14 | #include <string> |
15 | #include <vector> |
16 | |
17 | namespace clang { |
18 | namespace ast_matchers { |
19 | namespace dynamic { |
20 | namespace { |
21 | |
22 | class MockSema : public Parser::Sema { |
23 | public: |
24 | ~MockSema() override {} |
25 | |
26 | uint64_t expectMatcher(StringRef MatcherName) { |
27 | // Optimizations on the matcher framework make simple matchers like |
28 | // 'stmt()' to be all the same matcher. |
29 | // Use a more complex expression to prevent that. |
30 | ast_matchers::internal::Matcher<Stmt> M = stmt(stmt(), stmt()); |
31 | ExpectedMatchers.insert(x: std::make_pair(x: std::string(MatcherName), y&: M)); |
32 | return M.getID().second; |
33 | } |
34 | |
35 | bool isBuilderMatcher(MatcherCtor) const override { return false; } |
36 | |
37 | ASTNodeKind nodeMatcherType(MatcherCtor) const override { return {}; } |
38 | |
39 | internal::MatcherDescriptorPtr |
40 | buildMatcherCtor(MatcherCtor, SourceRange NameRange, |
41 | ArrayRef<ParserValue> Args, |
42 | Diagnostics *Error) const override { |
43 | return internal::MatcherDescriptorPtr{nullptr}; |
44 | } |
45 | |
46 | void parse(StringRef Code) { |
47 | Diagnostics Error; |
48 | VariantValue Value; |
49 | Parser::parseExpression(Code, S: this, Value: &Value, Error: &Error); |
50 | Values.push_back(x: Value); |
51 | Errors.push_back(x: Error.toStringFull()); |
52 | } |
53 | |
54 | std::optional<MatcherCtor> lookupMatcherCtor(StringRef MatcherName) override { |
55 | const ExpectedMatchersTy::value_type *Matcher = |
56 | &*ExpectedMatchers.find(x: std::string(MatcherName)); |
57 | return reinterpret_cast<MatcherCtor>(Matcher); |
58 | } |
59 | |
60 | VariantMatcher actOnMatcherExpression(MatcherCtor Ctor, |
61 | SourceRange NameRange, |
62 | StringRef BindID, |
63 | ArrayRef<ParserValue> Args, |
64 | Diagnostics *Error) override { |
65 | const ExpectedMatchersTy::value_type *Matcher = |
66 | reinterpret_cast<const ExpectedMatchersTy::value_type *>(Ctor); |
67 | MatcherInfo ToStore = {.MatcherName: Matcher->first, .NameRange: NameRange, .Args: Args, |
68 | .BoundID: std::string(BindID)}; |
69 | Matchers.push_back(x: ToStore); |
70 | return VariantMatcher::SingleMatcher(Matcher: Matcher->second); |
71 | } |
72 | |
73 | struct MatcherInfo { |
74 | StringRef MatcherName; |
75 | SourceRange NameRange; |
76 | std::vector<ParserValue> Args; |
77 | std::string BoundID; |
78 | }; |
79 | |
80 | std::vector<std::string> Errors; |
81 | std::vector<VariantValue> Values; |
82 | std::vector<MatcherInfo> Matchers; |
83 | typedef std::map<std::string, ast_matchers::internal::Matcher<Stmt> > |
84 | ExpectedMatchersTy; |
85 | ExpectedMatchersTy ExpectedMatchers; |
86 | }; |
87 | |
88 | TEST(ParserTest, ParseBoolean) { |
89 | MockSema Sema; |
90 | Sema.parse(Code: "true" ); |
91 | Sema.parse(Code: "false" ); |
92 | EXPECT_EQ(2U, Sema.Values.size()); |
93 | EXPECT_TRUE(Sema.Values[0].getBoolean()); |
94 | EXPECT_FALSE(Sema.Values[1].getBoolean()); |
95 | } |
96 | |
97 | TEST(ParserTest, ParseDouble) { |
98 | MockSema Sema; |
99 | Sema.parse(Code: "1.0" ); |
100 | Sema.parse(Code: "2.0f" ); |
101 | Sema.parse(Code: "34.56e-78" ); |
102 | Sema.parse(Code: "4.E+6" ); |
103 | Sema.parse(Code: "1" ); |
104 | EXPECT_EQ(5U, Sema.Values.size()); |
105 | EXPECT_EQ(1.0, Sema.Values[0].getDouble()); |
106 | EXPECT_EQ("1:1: Error parsing numeric literal: <2.0f>" , Sema.Errors[1]); |
107 | EXPECT_EQ(34.56e-78, Sema.Values[2].getDouble()); |
108 | EXPECT_EQ(4e+6, Sema.Values[3].getDouble()); |
109 | EXPECT_FALSE(Sema.Values[4].isDouble()); |
110 | } |
111 | |
112 | TEST(ParserTest, ParseUnsigned) { |
113 | MockSema Sema; |
114 | Sema.parse(Code: "0" ); |
115 | Sema.parse(Code: "123" ); |
116 | Sema.parse(Code: "0x1f" ); |
117 | Sema.parse(Code: "12345678901" ); |
118 | Sema.parse(Code: "1a1" ); |
119 | EXPECT_EQ(5U, Sema.Values.size()); |
120 | EXPECT_EQ(0U, Sema.Values[0].getUnsigned()); |
121 | EXPECT_EQ(123U, Sema.Values[1].getUnsigned()); |
122 | EXPECT_EQ(31U, Sema.Values[2].getUnsigned()); |
123 | EXPECT_EQ("1:1: Error parsing numeric literal: <12345678901>" , Sema.Errors[3]); |
124 | EXPECT_EQ("1:1: Error parsing numeric literal: <1a1>" , Sema.Errors[4]); |
125 | } |
126 | |
127 | TEST(ParserTest, ParseString) { |
128 | MockSema Sema; |
129 | Sema.parse(Code: "\"Foo\"" ); |
130 | Sema.parse(Code: "\"\"" ); |
131 | Sema.parse(Code: "\"Baz" ); |
132 | EXPECT_EQ(3ULL, Sema.Values.size()); |
133 | EXPECT_EQ("Foo" , Sema.Values[0].getString()); |
134 | EXPECT_EQ("" , Sema.Values[1].getString()); |
135 | EXPECT_EQ("1:1: Error parsing string token: <\"Baz>" , Sema.Errors[2]); |
136 | } |
137 | |
138 | bool matchesRange(SourceRange Range, unsigned StartLine, |
139 | unsigned EndLine, unsigned StartColumn, unsigned EndColumn) { |
140 | EXPECT_EQ(StartLine, Range.Start.Line); |
141 | EXPECT_EQ(EndLine, Range.End.Line); |
142 | EXPECT_EQ(StartColumn, Range.Start.Column); |
143 | EXPECT_EQ(EndColumn, Range.End.Column); |
144 | return Range.Start.Line == StartLine && Range.End.Line == EndLine && |
145 | Range.Start.Column == StartColumn && Range.End.Column == EndColumn; |
146 | } |
147 | |
148 | std::optional<DynTypedMatcher> getSingleMatcher(const VariantValue &Value) { |
149 | std::optional<DynTypedMatcher> Result = Value.getMatcher().getSingleMatcher(); |
150 | EXPECT_TRUE(Result); |
151 | return Result; |
152 | } |
153 | |
154 | TEST(ParserTest, ParseMatcher) { |
155 | MockSema Sema; |
156 | const uint64_t ExpectedFoo = Sema.expectMatcher(MatcherName: "Foo" ); |
157 | const uint64_t ExpectedBar = Sema.expectMatcher(MatcherName: "Bar" ); |
158 | const uint64_t ExpectedBaz = Sema.expectMatcher(MatcherName: "Baz" ); |
159 | Sema.parse(Code: " Foo ( Bar ( 17), Baz( \n \"B A,Z\") ) .bind( \"Yo!\") " ); |
160 | for (const auto &E : Sema.Errors) { |
161 | EXPECT_EQ("" , E); |
162 | } |
163 | |
164 | EXPECT_NE(ExpectedFoo, ExpectedBar); |
165 | EXPECT_NE(ExpectedFoo, ExpectedBaz); |
166 | EXPECT_NE(ExpectedBar, ExpectedBaz); |
167 | |
168 | EXPECT_EQ(1ULL, Sema.Values.size()); |
169 | EXPECT_EQ(ExpectedFoo, getSingleMatcher(Sema.Values[0])->getID().second); |
170 | |
171 | EXPECT_EQ(3ULL, Sema.Matchers.size()); |
172 | const MockSema::MatcherInfo Bar = Sema.Matchers[0]; |
173 | EXPECT_EQ("Bar" , Bar.MatcherName); |
174 | EXPECT_TRUE(matchesRange(Bar.NameRange, 1, 1, 8, 17)); |
175 | EXPECT_EQ(1ULL, Bar.Args.size()); |
176 | EXPECT_EQ(17U, Bar.Args[0].Value.getUnsigned()); |
177 | |
178 | const MockSema::MatcherInfo Baz = Sema.Matchers[1]; |
179 | EXPECT_EQ("Baz" , Baz.MatcherName); |
180 | EXPECT_TRUE(matchesRange(Baz.NameRange, 1, 2, 19, 10)); |
181 | EXPECT_EQ(1ULL, Baz.Args.size()); |
182 | EXPECT_EQ("B A,Z" , Baz.Args[0].Value.getString()); |
183 | |
184 | const MockSema::MatcherInfo Foo = Sema.Matchers[2]; |
185 | EXPECT_EQ("Foo" , Foo.MatcherName); |
186 | EXPECT_TRUE(matchesRange(Foo.NameRange, 1, 2, 2, 12)); |
187 | EXPECT_EQ(2ULL, Foo.Args.size()); |
188 | EXPECT_EQ(ExpectedBar, getSingleMatcher(Foo.Args[0].Value)->getID().second); |
189 | EXPECT_EQ(ExpectedBaz, getSingleMatcher(Foo.Args[1].Value)->getID().second); |
190 | EXPECT_EQ("Yo!" , Foo.BoundID); |
191 | } |
192 | |
193 | TEST(ParserTest, ParseComment) { |
194 | MockSema Sema; |
195 | Sema.expectMatcher(MatcherName: "Foo" ); |
196 | Sema.parse(Code: " Foo() # Bar() " ); |
197 | for (const auto &E : Sema.Errors) { |
198 | EXPECT_EQ("" , E); |
199 | } |
200 | |
201 | EXPECT_EQ(1ULL, Sema.Matchers.size()); |
202 | |
203 | Sema.parse(Code: "Foo(#) " ); |
204 | |
205 | EXPECT_EQ("1:4: Error parsing matcher. Found end-of-code while looking for ')'." , Sema.Errors[1]); |
206 | } |
207 | |
208 | using ast_matchers::internal::Matcher; |
209 | |
210 | Parser::NamedValueMap getTestNamedValues() { |
211 | Parser::NamedValueMap Values; |
212 | Values["nameX" ] = llvm::StringRef("x" ); |
213 | Values["hasParamA" ] = VariantMatcher::SingleMatcher( |
214 | Matcher: functionDecl(hasParameter(N: 0, InnerMatcher: hasName(Name: "a" )))); |
215 | return Values; |
216 | } |
217 | |
218 | TEST(ParserTest, FullParserTest) { |
219 | Diagnostics Error; |
220 | |
221 | StringRef Code = |
222 | "varDecl(hasInitializer(binaryOperator(hasLHS(integerLiteral())," |
223 | " hasOperatorName(\"+\"))))" ; |
224 | std::optional<DynTypedMatcher> VarDecl( |
225 | Parser::parseMatcherExpression(MatcherCode&: Code, Error: &Error)); |
226 | EXPECT_EQ("" , Error.toStringFull()); |
227 | Matcher<Decl> M = VarDecl->unconditionalConvertTo<Decl>(); |
228 | EXPECT_TRUE(matches("int x = 1 + false;" , M)); |
229 | EXPECT_FALSE(matches("int x = true + 1;" , M)); |
230 | EXPECT_FALSE(matches("int x = 1 - false;" , M)); |
231 | EXPECT_FALSE(matches("int x = true - 1;" , M)); |
232 | |
233 | Code = "implicitCastExpr(hasCastKind(\"CK_IntegralToBoolean\"))" ; |
234 | std::optional<DynTypedMatcher> implicitIntBooleanCast( |
235 | Parser::parseMatcherExpression(MatcherCode&: Code, S: nullptr, NamedValues: nullptr, Error: &Error)); |
236 | EXPECT_EQ("" , Error.toStringFull()); |
237 | Matcher<Stmt> MCastStmt = |
238 | traverse(TK: TK_AsIs, InnerMatcher: implicitIntBooleanCast->unconditionalConvertTo<Stmt>()); |
239 | EXPECT_TRUE(matches("bool X = 1;" , MCastStmt)); |
240 | EXPECT_FALSE(matches("bool X = true;" , MCastStmt)); |
241 | |
242 | Code = "functionDecl(hasParameter(1, hasName(\"x\")))" ; |
243 | std::optional<DynTypedMatcher> HasParameter( |
244 | Parser::parseMatcherExpression(MatcherCode&: Code, Error: &Error)); |
245 | EXPECT_EQ("" , Error.toStringFull()); |
246 | M = HasParameter->unconditionalConvertTo<Decl>(); |
247 | |
248 | EXPECT_TRUE(matches("void f(int a, int x);" , M)); |
249 | EXPECT_FALSE(matches("void f(int x, int a);" , M)); |
250 | |
251 | // Test named values. |
252 | auto NamedValues = getTestNamedValues(); |
253 | |
254 | Code = "functionDecl(hasParamA, hasParameter(1, hasName(nameX)))" ; |
255 | std::optional<DynTypedMatcher> HasParameterWithNamedValues( |
256 | Parser::parseMatcherExpression(MatcherCode&: Code, S: nullptr, NamedValues: &NamedValues, Error: &Error)); |
257 | EXPECT_EQ("" , Error.toStringFull()); |
258 | M = HasParameterWithNamedValues->unconditionalConvertTo<Decl>(); |
259 | |
260 | EXPECT_TRUE(matches("void f(int a, int x);" , M)); |
261 | EXPECT_FALSE(matches("void f(int x, int a);" , M)); |
262 | |
263 | Code = "unaryExprOrTypeTraitExpr(ofKind(\"UETT_SizeOf\"))" ; |
264 | std::optional<DynTypedMatcher> UnaryExprSizeOf( |
265 | Parser::parseMatcherExpression(MatcherCode&: Code, S: nullptr, NamedValues: nullptr, Error: &Error)); |
266 | EXPECT_EQ("" , Error.toStringFull()); |
267 | Matcher<Stmt> MStmt = UnaryExprSizeOf->unconditionalConvertTo<Stmt>(); |
268 | EXPECT_TRUE(matches("unsigned X = sizeof(int);" , MStmt)); |
269 | EXPECT_FALSE(matches("unsigned X = alignof(int);" , MStmt)); |
270 | |
271 | Code = |
272 | R"query(namedDecl(matchesName("^::[ABC]*$", "IgnoreCase | BasicRegex")))query" ; |
273 | std::optional<DynTypedMatcher> MatchesName( |
274 | Parser::parseMatcherExpression(MatcherCode&: Code, S: nullptr, NamedValues: nullptr, Error: &Error)); |
275 | EXPECT_EQ("" , Error.toStringFull()); |
276 | M = MatchesName->unconditionalConvertTo<Decl>(); |
277 | EXPECT_TRUE(matches("unsigned AAACCBB;" , M)); |
278 | EXPECT_TRUE(matches("unsigned aaaccbb;" , M)); |
279 | |
280 | Code = "hasInitializer(\n binaryOperator(hasLHS(\"A\")))" ; |
281 | EXPECT_TRUE(!Parser::parseMatcherExpression(Code, &Error)); |
282 | EXPECT_EQ("1:1: Error parsing argument 1 for matcher hasInitializer.\n" |
283 | "2:5: Error parsing argument 1 for matcher binaryOperator.\n" |
284 | "2:20: Error building matcher hasLHS.\n" |
285 | "2:27: Incorrect type for arg 1. " |
286 | "(Expected = Matcher<Expr>) != (Actual = String)" , |
287 | Error.toStringFull()); |
288 | } |
289 | |
290 | TEST(ParserTest, VariadicMatchTest) { |
291 | Diagnostics Error; |
292 | |
293 | StringRef Code = |
294 | "stmt(objcMessageExpr(hasAnySelector(\"methodA\", \"methodB:\")))" ; |
295 | std::optional<DynTypedMatcher> OM( |
296 | Parser::parseMatcherExpression(MatcherCode&: Code, Error: &Error)); |
297 | EXPECT_EQ("" , Error.toStringFull()); |
298 | auto M = OM->unconditionalConvertTo<Stmt>(); |
299 | EXPECT_TRUE(matchesObjC("@interface I @end " |
300 | "void foo(I* i) { [i methodA]; }" , M)); |
301 | } |
302 | |
303 | std::string ParseWithError(StringRef Code) { |
304 | Diagnostics Error; |
305 | VariantValue Value; |
306 | Parser::parseExpression(Code, Value: &Value, Error: &Error); |
307 | return Error.toStringFull(); |
308 | } |
309 | |
310 | std::string ParseMatcherWithError(StringRef Code) { |
311 | Diagnostics Error; |
312 | Parser::parseMatcherExpression(MatcherCode&: Code, Error: &Error); |
313 | return Error.toStringFull(); |
314 | } |
315 | |
316 | TEST(ParserTest, Errors) { |
317 | EXPECT_EQ( |
318 | "1:5: Error parsing matcher. Found token <123> while looking for '('." , |
319 | ParseWithError("Foo 123" )); |
320 | EXPECT_EQ( |
321 | "1:1: Matcher not found: Foo\n" |
322 | "1:9: Error parsing matcher. Found token <123> while looking for ','." , |
323 | ParseWithError("Foo(\"A\" 123)" )); |
324 | EXPECT_EQ( |
325 | "1:1: Error parsing argument 1 for matcher stmt.\n" |
326 | "1:6: Value not found: someValue" , |
327 | ParseWithError("stmt(someValue)" )); |
328 | EXPECT_EQ( |
329 | "1:1: Matcher not found: Foo\n" |
330 | "1:4: Error parsing matcher. Found end-of-code while looking for ')'." , |
331 | ParseWithError("Foo(" )); |
332 | EXPECT_EQ("1:1: End of code found while looking for token." , |
333 | ParseWithError("" )); |
334 | EXPECT_EQ("Input value is not a matcher expression." , |
335 | ParseMatcherWithError("\"A\"" )); |
336 | EXPECT_EQ("1:1: Matcher not found: Foo\n" |
337 | "1:1: Error parsing argument 1 for matcher Foo.\n" |
338 | "1:5: Invalid token <(> found when looking for a value." , |
339 | ParseWithError("Foo((" )); |
340 | EXPECT_EQ("1:7: Expected end of code." , ParseWithError("expr()a" )); |
341 | EXPECT_EQ("1:11: Period not followed by valid chained call." , |
342 | ParseWithError("isArrow().biind" )); |
343 | EXPECT_EQ("1:15: Malformed bind() expression." , |
344 | ParseWithError("isArrow().bind" )); |
345 | EXPECT_EQ("1:16: Malformed bind() expression." , |
346 | ParseWithError("isArrow().bind(foo" )); |
347 | EXPECT_EQ("1:21: Malformed bind() expression." , |
348 | ParseWithError("isArrow().bind(\"foo\"" )); |
349 | EXPECT_EQ("1:1: Error building matcher isArrow.\n" |
350 | "1:1: Matcher does not support binding." , |
351 | ParseWithError("isArrow().bind(\"foo\")" )); |
352 | EXPECT_EQ("1:1: Error building matcher isArrow.\n" |
353 | "1:11: Matcher does not support with call." , |
354 | ParseWithError("isArrow().with" )); |
355 | EXPECT_EQ( |
356 | "1:22: Error parsing matcher. Found token <EOF> while looking for '('." , |
357 | ParseWithError("mapAnyOf(ifStmt).with" )); |
358 | EXPECT_EQ( |
359 | "1:22: Error parsing matcher. Found end-of-code while looking for ')'." , |
360 | ParseWithError("mapAnyOf(ifStmt).with(" )); |
361 | EXPECT_EQ("1:1: Failed to build matcher: mapAnyOf." , |
362 | ParseWithError("mapAnyOf()" )); |
363 | EXPECT_EQ("1:1: Error parsing argument 1 for matcher mapAnyOf.\n1:1: Failed " |
364 | "to build matcher: mapAnyOf." , |
365 | ParseWithError("mapAnyOf(\"foo\")" )); |
366 | EXPECT_EQ("Input value has unresolved overloaded type: " |
367 | "Matcher<DoStmt|ForStmt|WhileStmt|CXXForRangeStmt|FunctionDecl|" |
368 | "CoroutineBodyStmt>" , |
369 | ParseMatcherWithError("hasBody(stmt())" )); |
370 | EXPECT_EQ( |
371 | "1:1: Error parsing argument 1 for matcher decl.\n" |
372 | "1:6: Error building matcher hasAttr.\n" |
373 | "1:14: Unknown value 'attr::Fnal' for arg 1; did you mean 'attr::Final'" , |
374 | ParseMatcherWithError(R"query(decl(hasAttr("attr::Fnal")))query" )); |
375 | EXPECT_EQ("1:1: Error parsing argument 1 for matcher decl.\n" |
376 | "1:6: Error building matcher hasAttr.\n" |
377 | "1:14: Unknown value 'Final' for arg 1; did you mean 'attr::Final'" , |
378 | ParseMatcherWithError(R"query(decl(hasAttr("Final")))query" )); |
379 | EXPECT_EQ("1:1: Error parsing argument 1 for matcher decl.\n" |
380 | "1:6: Error building matcher hasAttr.\n" |
381 | "1:14: Value not found: unrelated" , |
382 | ParseMatcherWithError(R"query(decl(hasAttr("unrelated")))query" )); |
383 | EXPECT_EQ( |
384 | "1:1: Error parsing argument 1 for matcher namedDecl.\n" |
385 | "1:11: Error building matcher matchesName.\n" |
386 | "1:33: Unknown value 'Ignorecase' for arg 2; did you mean 'IgnoreCase'" , |
387 | ParseMatcherWithError( |
388 | R"query(namedDecl(matchesName("[ABC]*", "Ignorecase")))query" )); |
389 | EXPECT_EQ( |
390 | "1:1: Error parsing argument 1 for matcher namedDecl.\n" |
391 | "1:11: Error building matcher matchesName.\n" |
392 | "1:33: Value not found: IgnoreCase & BasicRegex" , |
393 | ParseMatcherWithError( |
394 | R"query(namedDecl(matchesName("[ABC]*", "IgnoreCase & BasicRegex")))query" )); |
395 | EXPECT_EQ( |
396 | "1:1: Error parsing argument 1 for matcher namedDecl.\n" |
397 | "1:11: Error building matcher matchesName.\n" |
398 | "1:33: Unknown value 'IgnoreCase | Basicregex' for arg 2; did you mean " |
399 | "'IgnoreCase | BasicRegex'" , |
400 | ParseMatcherWithError( |
401 | R"query(namedDecl(matchesName("[ABC]*", "IgnoreCase | Basicregex")))query" )); |
402 | } |
403 | |
404 | TEST(ParserTest, OverloadErrors) { |
405 | EXPECT_EQ("1:1: Error building matcher callee.\n" |
406 | "1:8: Candidate 1: Incorrect type for arg 1. " |
407 | "(Expected = Matcher<Stmt>) != (Actual = String)\n" |
408 | "1:8: Candidate 2: Incorrect type for arg 1. " |
409 | "(Expected = Matcher<Decl>) != (Actual = String)" , |
410 | ParseWithError("callee(\"A\")" )); |
411 | } |
412 | |
413 | TEST(ParserTest, ParseMultiline) { |
414 | StringRef Code; |
415 | |
416 | std::optional<DynTypedMatcher> M; |
417 | { |
418 | Code = R"matcher(varDecl( |
419 | hasName("foo") |
420 | ) |
421 | )matcher" ; |
422 | Diagnostics Error; |
423 | EXPECT_TRUE(Parser::parseMatcherExpression(Code, &Error)); |
424 | } |
425 | |
426 | { |
427 | Code = R"matcher(varDecl( |
428 | # Internal comment |
429 | hasName("foo") # Internal comment |
430 | # Internal comment |
431 | ) |
432 | )matcher" ; |
433 | Diagnostics Error; |
434 | EXPECT_TRUE(Parser::parseMatcherExpression(Code, &Error)); |
435 | } |
436 | |
437 | { |
438 | Code = R"matcher(decl().bind( |
439 | "paramName") |
440 | )matcher" ; |
441 | Diagnostics Error; |
442 | EXPECT_TRUE(Parser::parseMatcherExpression(Code, &Error)); |
443 | } |
444 | |
445 | { |
446 | Code = R"matcher(decl().bind( |
447 | "paramName" |
448 | ) |
449 | )matcher" ; |
450 | Diagnostics Error; |
451 | EXPECT_TRUE(Parser::parseMatcherExpression(Code, &Error).has_value()); |
452 | } |
453 | |
454 | { |
455 | Code = R"matcher(decl(decl() |
456 | , decl()))matcher" ; |
457 | Diagnostics Error; |
458 | EXPECT_TRUE(Parser::parseMatcherExpression(Code, &Error).has_value()); |
459 | } |
460 | |
461 | { |
462 | Code = R"matcher(decl(decl(), |
463 | decl()))matcher" ; |
464 | Diagnostics Error; |
465 | EXPECT_TRUE(Parser::parseMatcherExpression(Code, &Error).has_value()); |
466 | } |
467 | |
468 | { |
469 | Code = "namedDecl(hasName(\"n\"\n))" ; |
470 | Diagnostics Error; |
471 | EXPECT_TRUE(Parser::parseMatcherExpression(Code, &Error).has_value()); |
472 | } |
473 | |
474 | { |
475 | Diagnostics Error; |
476 | |
477 | auto NamedValues = getTestNamedValues(); |
478 | |
479 | Code = R"matcher(hasParamA.bind |
480 | ("paramName") |
481 | )matcher" ; |
482 | M = Parser::parseMatcherExpression(MatcherCode&: Code, S: nullptr, NamedValues: &NamedValues, Error: &Error); |
483 | EXPECT_FALSE(M); |
484 | EXPECT_EQ("1:15: Malformed bind() expression." , Error.toStringFull()); |
485 | } |
486 | |
487 | { |
488 | Diagnostics Error; |
489 | |
490 | auto NamedValues = getTestNamedValues(); |
491 | |
492 | Code = R"matcher(hasParamA. |
493 | bind("paramName") |
494 | )matcher" ; |
495 | M = Parser::parseMatcherExpression(MatcherCode&: Code, S: nullptr, NamedValues: &NamedValues, Error: &Error); |
496 | EXPECT_FALSE(M); |
497 | EXPECT_EQ("1:11: Period not followed by valid chained call." , |
498 | Error.toStringFull()); |
499 | } |
500 | |
501 | { |
502 | Diagnostics Error; |
503 | |
504 | Code = R"matcher(varDecl |
505 | () |
506 | )matcher" ; |
507 | M = Parser::parseMatcherExpression(MatcherCode&: Code, S: nullptr, NamedValues: nullptr, Error: &Error); |
508 | EXPECT_FALSE(M); |
509 | EXPECT_EQ("1:8: Error parsing matcher. Found token " |
510 | "<NewLine> while looking for '('." , |
511 | Error.toStringFull()); |
512 | } |
513 | |
514 | // Correct line/column numbers |
515 | { |
516 | Diagnostics Error; |
517 | |
518 | Code = R"matcher(varDecl( |
519 | doesNotExist() |
520 | ) |
521 | )matcher" ; |
522 | M = Parser::parseMatcherExpression(MatcherCode&: Code, S: nullptr, NamedValues: nullptr, Error: &Error); |
523 | EXPECT_FALSE(M); |
524 | StringRef Expected = R"error(1:1: Error parsing argument 1 for matcher varDecl. |
525 | 2:3: Matcher not found: doesNotExist)error" ; |
526 | EXPECT_EQ(Expected, Error.toStringFull()); |
527 | } |
528 | } |
529 | |
530 | TEST(ParserTest, CompletionRegistry) { |
531 | StringRef Code = "while" ; |
532 | std::vector<MatcherCompletion> Comps = Parser::completeExpression(Code, CompletionOffset: 5); |
533 | ASSERT_EQ(1u, Comps.size()); |
534 | EXPECT_EQ("Stmt(" , Comps[0].TypedText); |
535 | EXPECT_EQ("Matcher<Stmt> whileStmt(Matcher<WhileStmt>...)" , |
536 | Comps[0].MatcherDecl); |
537 | |
538 | Code = "whileStmt()." ; |
539 | Comps = Parser::completeExpression(Code, CompletionOffset: 12); |
540 | ASSERT_EQ(1u, Comps.size()); |
541 | EXPECT_EQ("bind(\"" , Comps[0].TypedText); |
542 | EXPECT_EQ("bind" , Comps[0].MatcherDecl); |
543 | |
544 | Code = "mapAny" ; |
545 | Comps = Parser::completeExpression(Code, CompletionOffset: 6); |
546 | ASSERT_EQ(1u, Comps.size()); |
547 | EXPECT_EQ("Of(" , Comps[0].TypedText); |
548 | EXPECT_EQ("Matcher<NestedNameSpecifierLoc|QualType|TypeLoc|...> " |
549 | "mapAnyOf(NestedNameSpecifierLoc|QualType|TypeLoc|" |
550 | "NestedNameSpecifier|Decl|Stmt|Type...)" , |
551 | Comps[0].MatcherDecl); |
552 | |
553 | Code = "mapAnyOf(ifStmt)." ; |
554 | Comps = Parser::completeExpression(Code, CompletionOffset: 17); |
555 | ASSERT_EQ(2u, Comps.size()); |
556 | EXPECT_EQ("bind(\"" , Comps[0].TypedText); |
557 | EXPECT_EQ("bind" , Comps[0].MatcherDecl); |
558 | EXPECT_EQ("with(" , Comps[1].TypedText); |
559 | EXPECT_EQ("with" , Comps[1].MatcherDecl); |
560 | |
561 | Code = "mapAnyOf(ifS" ; |
562 | Comps = Parser::completeExpression(Code, CompletionOffset: 12); |
563 | ASSERT_EQ(1u, Comps.size()); |
564 | EXPECT_EQ("tmt" , Comps[0].TypedText); |
565 | EXPECT_EQ("ifStmt" , Comps[0].MatcherDecl); |
566 | } |
567 | |
568 | TEST(ParserTest, CompletionNamedValues) { |
569 | // Can complete non-matcher types. |
570 | auto NamedValues = getTestNamedValues(); |
571 | StringRef Code = "functionDecl(hasName(" ; |
572 | std::vector<MatcherCompletion> Comps = |
573 | Parser::completeExpression(Code, CompletionOffset: Code.size(), S: nullptr, NamedValues: &NamedValues); |
574 | ASSERT_EQ(1u, Comps.size()); |
575 | EXPECT_EQ("nameX" , Comps[0].TypedText); |
576 | EXPECT_EQ("String nameX" , Comps[0].MatcherDecl); |
577 | |
578 | // Can complete if there are names in the expression. |
579 | Code = "cxxMethodDecl(hasName(nameX), " ; |
580 | Comps = Parser::completeExpression(Code, CompletionOffset: Code.size(), S: nullptr, NamedValues: &NamedValues); |
581 | EXPECT_LT(0u, Comps.size()); |
582 | |
583 | // Can complete names and registry together. |
584 | Code = "functionDecl(hasP" ; |
585 | Comps = Parser::completeExpression(Code, CompletionOffset: Code.size(), S: nullptr, NamedValues: &NamedValues); |
586 | ASSERT_EQ(3u, Comps.size()); |
587 | |
588 | EXPECT_EQ("arameter(" , Comps[0].TypedText); |
589 | EXPECT_EQ( |
590 | "Matcher<FunctionDecl> hasParameter(unsigned, Matcher<ParmVarDecl>)" , |
591 | Comps[0].MatcherDecl); |
592 | |
593 | EXPECT_EQ("aramA" , Comps[1].TypedText); |
594 | EXPECT_EQ("Matcher<Decl> hasParamA" , Comps[1].MatcherDecl); |
595 | |
596 | EXPECT_EQ("arent(" , Comps[2].TypedText); |
597 | EXPECT_EQ( |
598 | "Matcher<Decl> " |
599 | "hasParent(Matcher<NestedNameSpecifierLoc|TypeLoc|Decl|...>)" , |
600 | Comps[2].MatcherDecl); |
601 | } |
602 | |
603 | TEST(ParserTest, ParseBindOnLet) { |
604 | |
605 | auto NamedValues = getTestNamedValues(); |
606 | |
607 | Diagnostics Error; |
608 | |
609 | { |
610 | StringRef Code = "hasParamA.bind(\"parmABinding\")" ; |
611 | std::optional<DynTypedMatcher> TopLevelLetBinding( |
612 | Parser::parseMatcherExpression(MatcherCode&: Code, S: nullptr, NamedValues: &NamedValues, Error: &Error)); |
613 | EXPECT_EQ("" , Error.toStringFull()); |
614 | auto M = TopLevelLetBinding->unconditionalConvertTo<Decl>(); |
615 | |
616 | EXPECT_TRUE(matchAndVerifyResultTrue( |
617 | "void foo(int a);" , M, |
618 | std::make_unique<VerifyIdIsBoundTo<FunctionDecl>>("parmABinding" ))); |
619 | EXPECT_TRUE(matchAndVerifyResultFalse( |
620 | "void foo(int b);" , M, |
621 | std::make_unique<VerifyIdIsBoundTo<FunctionDecl>>("parmABinding" ))); |
622 | } |
623 | |
624 | { |
625 | StringRef Code = "functionDecl(hasParamA.bind(\"parmABinding\"))" ; |
626 | std::optional<DynTypedMatcher> NestedLetBinding( |
627 | Parser::parseMatcherExpression(MatcherCode&: Code, S: nullptr, NamedValues: &NamedValues, Error: &Error)); |
628 | EXPECT_EQ("" , Error.toStringFull()); |
629 | auto M = NestedLetBinding->unconditionalConvertTo<Decl>(); |
630 | |
631 | EXPECT_TRUE(matchAndVerifyResultTrue( |
632 | "void foo(int a);" , M, |
633 | std::make_unique<VerifyIdIsBoundTo<FunctionDecl>>("parmABinding" ))); |
634 | EXPECT_TRUE(matchAndVerifyResultFalse( |
635 | "void foo(int b);" , M, |
636 | std::make_unique<VerifyIdIsBoundTo<FunctionDecl>>("parmABinding" ))); |
637 | } |
638 | } |
639 | |
640 | } // end anonymous namespace |
641 | } // end namespace dynamic |
642 | } // end namespace ast_matchers |
643 | } // end namespace clang |
644 | |