1 | //===- unittest/Tooling/RangeSelectorTest.cpp -----------------------------===// |
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 "clang/Tooling/Transformer/RangeSelector.h" |
10 | #include "clang/ASTMatchers/ASTMatchers.h" |
11 | #include "clang/Frontend/ASTUnit.h" |
12 | #include "clang/Tooling/Tooling.h" |
13 | #include "clang/Tooling/Transformer/Parsing.h" |
14 | #include "clang/Tooling/Transformer/SourceCode.h" |
15 | #include "llvm/Support/Error.h" |
16 | #include "llvm/Testing/Support/Error.h" |
17 | #include "gmock/gmock.h" |
18 | #include "gtest/gtest.h" |
19 | |
20 | using namespace clang; |
21 | using namespace transformer; |
22 | using namespace ast_matchers; |
23 | |
24 | namespace { |
25 | using ::llvm::Expected; |
26 | using ::llvm::Failed; |
27 | using ::llvm::HasValue; |
28 | using ::llvm::StringError; |
29 | using ::testing::AllOf; |
30 | using ::testing::HasSubstr; |
31 | |
32 | using MatchResult = MatchFinder::MatchResult; |
33 | |
34 | struct TestMatch { |
35 | // The AST unit from which `result` is built. We bundle it because it backs |
36 | // the result. Users are not expected to access it. |
37 | std::unique_ptr<clang::ASTUnit> ASTUnit; |
38 | // The result to use in the test. References `ast_unit`. |
39 | MatchResult Result; |
40 | }; |
41 | |
42 | template <typename M> TestMatch matchCode(StringRef Code, M Matcher) { |
43 | auto ASTUnit = tooling::buildASTFromCode(Code); |
44 | assert(ASTUnit != nullptr && "AST construction failed" ); |
45 | |
46 | ASTContext &Context = ASTUnit->getASTContext(); |
47 | assert(!Context.getDiagnostics().hasErrorOccurred() && "Compilation error" ); |
48 | |
49 | TraversalKindScope RAII(Context, TK_AsIs); |
50 | auto Matches = ast_matchers::match(Matcher, Context); |
51 | // We expect a single, exact match. |
52 | assert(Matches.size() != 0 && "no matches found" ); |
53 | assert(Matches.size() == 1 && "too many matches" ); |
54 | |
55 | return TestMatch{.ASTUnit: std::move(ASTUnit), .Result: MatchResult(Matches[0], &Context)}; |
56 | } |
57 | |
58 | // Applies \p Selector to \p Match and, on success, returns the selected source. |
59 | Expected<StringRef> select(RangeSelector Selector, const TestMatch &Match) { |
60 | Expected<CharSourceRange> Range = Selector(Match.Result); |
61 | if (!Range) |
62 | return Range.takeError(); |
63 | return tooling::getText(Range: *Range, Context: *Match.Result.Context); |
64 | } |
65 | |
66 | // Applies \p Selector to a trivial match with only a single bound node with id |
67 | // "bound_node_id". For use in testing unbound-node errors. |
68 | Expected<CharSourceRange> selectFromTrivial(const RangeSelector &Selector) { |
69 | // We need to bind the result to something, or the match will fail. Use a |
70 | // binding that is not used in the unbound node tests. |
71 | TestMatch Match = |
72 | matchCode(Code: "static int x = 0;" , Matcher: varDecl().bind(ID: "bound_node_id" )); |
73 | return Selector(Match.Result); |
74 | } |
75 | |
76 | // Matches the message expected for unbound-node failures. |
77 | testing::Matcher<StringError> withUnboundNodeMessage() { |
78 | return testing::Property( |
79 | property: &StringError::getMessage, |
80 | matcher: AllOf(matchers: HasSubstr(substring: "unbound_id" ), matchers: HasSubstr(substring: "not bound" ))); |
81 | } |
82 | |
83 | // Applies \p Selector to code containing assorted node types, where the match |
84 | // binds each one: a statement ("stmt"), a (non-member) ctor-initializer |
85 | // ("init"), an expression ("expr") and a (nameless) declaration ("decl"). Used |
86 | // to test failures caused by applying selectors to nodes of the wrong type. |
87 | Expected<CharSourceRange> selectFromAssorted(RangeSelector Selector) { |
88 | StringRef Code = R"cc( |
89 | struct A {}; |
90 | class F : public A { |
91 | public: |
92 | F(int) {} |
93 | }; |
94 | void g() { F f(1); } |
95 | )cc" ; |
96 | |
97 | auto Matcher = |
98 | compoundStmt( |
99 | hasDescendant( |
100 | cxxConstructExpr( |
101 | hasDeclaration( |
102 | InnerMatcher: decl(hasDescendant(cxxCtorInitializer(isBaseInitializer()) |
103 | .bind(ID: "init" ))) |
104 | .bind(ID: "decl" ))) |
105 | .bind(ID: "expr" ))) |
106 | .bind(ID: "stmt" ); |
107 | |
108 | return Selector(matchCode(Code, Matcher).Result); |
109 | } |
110 | |
111 | // Matches the message expected for type-error failures. |
112 | testing::Matcher<StringError> withTypeErrorMessage(const std::string &NodeID) { |
113 | return testing::Property( |
114 | property: &StringError::getMessage, |
115 | matcher: AllOf(matchers: HasSubstr(substring: NodeID), matchers: HasSubstr(substring: "mismatched type" ))); |
116 | } |
117 | |
118 | TEST(RangeSelectorTest, UnboundNode) { |
119 | EXPECT_THAT_EXPECTED(selectFromTrivial(node("unbound_id" )), |
120 | Failed<StringError>(withUnboundNodeMessage())); |
121 | } |
122 | |
123 | MATCHER_P(EqualsCharSourceRange, Range, "" ) { |
124 | return Range.getAsRange() == arg.getAsRange() && |
125 | Range.isTokenRange() == arg.isTokenRange(); |
126 | } |
127 | |
128 | // FIXME: here and elsewhere: use llvm::Annotations library to explicitly mark |
129 | // points and ranges of interest, enabling more readable tests. |
130 | TEST(RangeSelectorTest, BeforeOp) { |
131 | StringRef Code = R"cc( |
132 | int f(int x, int y, int z) { return 3; } |
133 | int g() { return f(/* comment */ 3, 7 /* comment */, 9); } |
134 | )cc" ; |
135 | StringRef CallID = "call" ; |
136 | ast_matchers::internal::Matcher<Stmt> M = callExpr().bind(ID: CallID); |
137 | RangeSelector R = before(Selector: node(ID: CallID.str())); |
138 | |
139 | TestMatch Match = matchCode(Code, Matcher: M); |
140 | const auto *E = Match.Result.Nodes.getNodeAs<Expr>(ID: CallID); |
141 | assert(E != nullptr); |
142 | auto ExprBegin = E->getSourceRange().getBegin(); |
143 | EXPECT_THAT_EXPECTED( |
144 | R(Match.Result), |
145 | HasValue(EqualsCharSourceRange( |
146 | CharSourceRange::getCharRange(ExprBegin, ExprBegin)))); |
147 | } |
148 | |
149 | TEST(RangeSelectorTest, BeforeOpParsed) { |
150 | StringRef Code = R"cc( |
151 | int f(int x, int y, int z) { return 3; } |
152 | int g() { return f(/* comment */ 3, 7 /* comment */, 9); } |
153 | )cc" ; |
154 | StringRef CallID = "call" ; |
155 | ast_matchers::internal::Matcher<Stmt> M = callExpr().bind(ID: CallID); |
156 | auto R = parseRangeSelector(Input: R"rs(before(node("call")))rs" ); |
157 | ASSERT_THAT_EXPECTED(R, llvm::Succeeded()); |
158 | |
159 | TestMatch Match = matchCode(Code, Matcher: M); |
160 | const auto *E = Match.Result.Nodes.getNodeAs<Expr>(ID: CallID); |
161 | assert(E != nullptr); |
162 | auto ExprBegin = E->getSourceRange().getBegin(); |
163 | EXPECT_THAT_EXPECTED( |
164 | (*R)(Match.Result), |
165 | HasValue(EqualsCharSourceRange( |
166 | CharSourceRange::getCharRange(ExprBegin, ExprBegin)))); |
167 | } |
168 | |
169 | TEST(RangeSelectorTest, AfterOp) { |
170 | StringRef Code = R"cc( |
171 | int f(int x, int y, int z) { return 3; } |
172 | int g() { return f(/* comment */ 3, 7 /* comment */, 9); } |
173 | )cc" ; |
174 | StringRef Call = "call" ; |
175 | TestMatch Match = matchCode(Code, Matcher: callExpr().bind(ID: Call)); |
176 | const auto* E = Match.Result.Nodes.getNodeAs<Expr>(ID: Call); |
177 | assert(E != nullptr); |
178 | const SourceRange Range = E->getSourceRange(); |
179 | // The end token, a right paren, is one character wide, so advance by one, |
180 | // bringing us to the semicolon. |
181 | const SourceLocation SemiLoc = Range.getEnd().getLocWithOffset(Offset: 1); |
182 | const auto ExpectedAfter = CharSourceRange::getCharRange(B: SemiLoc, E: SemiLoc); |
183 | |
184 | // Test with a char range. |
185 | auto CharRange = CharSourceRange::getCharRange(B: Range.getBegin(), E: SemiLoc); |
186 | EXPECT_THAT_EXPECTED(after(charRange(CharRange))(Match.Result), |
187 | HasValue(EqualsCharSourceRange(ExpectedAfter))); |
188 | |
189 | // Test with a token range. |
190 | auto TokenRange = CharSourceRange::getTokenRange(R: Range); |
191 | EXPECT_THAT_EXPECTED(after(charRange(TokenRange))(Match.Result), |
192 | HasValue(EqualsCharSourceRange(ExpectedAfter))); |
193 | } |
194 | |
195 | // Gets the spelling location `Length` characters after the start of AST node |
196 | // `Id`. |
197 | static SourceLocation getSpellingLocAfter(const MatchResult &Result, |
198 | StringRef Id, int Length) { |
199 | const auto *E = Result.Nodes.getNodeAs<Expr>(ID: Id); |
200 | assert(E != nullptr); |
201 | return Result.SourceManager->getSpellingLoc(Loc: E->getBeginLoc()) |
202 | .getLocWithOffset(Length); |
203 | } |
204 | |
205 | // Test with a range that is the entire macro arg, but does not end the |
206 | // expansion itself. |
207 | TEST(RangeSelectorTest, AfterOpInMacroArg) { |
208 | StringRef Code = R"cc( |
209 | #define ISNULL(x) x == nullptr |
210 | bool g() { int* y; return ISNULL(y); } |
211 | )cc" ; |
212 | |
213 | TestMatch Match = |
214 | matchCode(Code, Matcher: declRefExpr(to(InnerMatcher: namedDecl(hasName(Name: "y" )))).bind(ID: "yvar" )); |
215 | int YVarLen = 1; |
216 | SourceLocation After = getSpellingLocAfter(Result: Match.Result, Id: "yvar" , Length: YVarLen); |
217 | CharSourceRange Expected = CharSourceRange::getCharRange(B: After, E: After); |
218 | EXPECT_THAT_EXPECTED(after(node("yvar" ))(Match.Result), |
219 | HasValue(EqualsCharSourceRange(Expected))); |
220 | } |
221 | |
222 | // Test with a range that is the entire macro arg and ends the expansion itself. |
223 | TEST(RangeSelectorTest, AfterOpInMacroArgEndsExpansion) { |
224 | StringRef Code = R"cc( |
225 | #define ISNULL(x) nullptr == x |
226 | bool g() { int* y; return ISNULL(y); } |
227 | )cc" ; |
228 | |
229 | TestMatch Match = |
230 | matchCode(Code, Matcher: declRefExpr(to(InnerMatcher: namedDecl(hasName(Name: "y" )))).bind(ID: "yvar" )); |
231 | int YVarLen = 1; |
232 | SourceLocation After = getSpellingLocAfter(Result: Match.Result, Id: "yvar" , Length: YVarLen); |
233 | CharSourceRange Expected = CharSourceRange::getCharRange(B: After, E: After); |
234 | EXPECT_THAT_EXPECTED(after(node("yvar" ))(Match.Result), |
235 | HasValue(EqualsCharSourceRange(Expected))); |
236 | } |
237 | |
238 | TEST(RangeSelectorTest, AfterOpInPartOfMacroArg) { |
239 | StringRef Code = R"cc( |
240 | #define ISNULL(x) x == nullptr |
241 | int* f(int*); |
242 | bool g() { int* y; return ISNULL(f(y)); } |
243 | )cc" ; |
244 | |
245 | TestMatch Match = |
246 | matchCode(Code, Matcher: declRefExpr(to(InnerMatcher: namedDecl(hasName(Name: "y" )))).bind(ID: "yvar" )); |
247 | int YVarLen = 1; |
248 | SourceLocation After = getSpellingLocAfter(Result: Match.Result, Id: "yvar" , Length: YVarLen); |
249 | CharSourceRange Expected = CharSourceRange::getCharRange(B: After, E: After); |
250 | EXPECT_THAT_EXPECTED(after(node("yvar" ))(Match.Result), |
251 | HasValue(EqualsCharSourceRange(Expected))); |
252 | } |
253 | |
254 | TEST(RangeSelectorTest, BetweenOp) { |
255 | StringRef Code = R"cc( |
256 | int f(int x, int y, int z) { return 3; } |
257 | int g() { return f(3, /* comment */ 7 /* comment */, 9); } |
258 | )cc" ; |
259 | auto Matcher = callExpr(hasArgument(N: 0, InnerMatcher: expr().bind(ID: "a0" )), |
260 | hasArgument(N: 1, InnerMatcher: expr().bind(ID: "a1" ))); |
261 | RangeSelector R = between(R1: node(ID: "a0" ), R2: node(ID: "a1" )); |
262 | TestMatch Match = matchCode(Code, Matcher); |
263 | EXPECT_THAT_EXPECTED(select(R, Match), HasValue(", /* comment */ " )); |
264 | } |
265 | |
266 | TEST(RangeSelectorTest, BetweenOpParsed) { |
267 | StringRef Code = R"cc( |
268 | int f(int x, int y, int z) { return 3; } |
269 | int g() { return f(3, /* comment */ 7 /* comment */, 9); } |
270 | )cc" ; |
271 | auto Matcher = callExpr(hasArgument(N: 0, InnerMatcher: expr().bind(ID: "a0" )), |
272 | hasArgument(N: 1, InnerMatcher: expr().bind(ID: "a1" ))); |
273 | auto R = parseRangeSelector(Input: R"rs(between(node("a0"), node("a1")))rs" ); |
274 | ASSERT_THAT_EXPECTED(R, llvm::Succeeded()); |
275 | TestMatch Match = matchCode(Code, Matcher); |
276 | EXPECT_THAT_EXPECTED(select(*R, Match), HasValue(", /* comment */ " )); |
277 | } |
278 | |
279 | // Node-id specific version. |
280 | TEST(RangeSelectorTest, EncloseOpNodes) { |
281 | StringRef Code = R"cc( |
282 | int f(int x, int y, int z) { return 3; } |
283 | int g() { return f(/* comment */ 3, 7 /* comment */, 9); } |
284 | )cc" ; |
285 | auto Matcher = callExpr(hasArgument(N: 0, InnerMatcher: expr().bind(ID: "a0" )), |
286 | hasArgument(N: 1, InnerMatcher: expr().bind(ID: "a1" ))); |
287 | RangeSelector R = encloseNodes(BeginID: "a0" , EndID: "a1" ); |
288 | TestMatch Match = matchCode(Code, Matcher); |
289 | EXPECT_THAT_EXPECTED(select(R, Match), HasValue("3, 7" )); |
290 | } |
291 | |
292 | TEST(RangeSelectorTest, EncloseOpGeneral) { |
293 | StringRef Code = R"cc( |
294 | int f(int x, int y, int z) { return 3; } |
295 | int g() { return f(/* comment */ 3, 7 /* comment */, 9); } |
296 | )cc" ; |
297 | auto Matcher = callExpr(hasArgument(N: 0, InnerMatcher: expr().bind(ID: "a0" )), |
298 | hasArgument(N: 1, InnerMatcher: expr().bind(ID: "a1" ))); |
299 | RangeSelector R = enclose(Begin: node(ID: "a0" ), End: node(ID: "a1" )); |
300 | TestMatch Match = matchCode(Code, Matcher); |
301 | EXPECT_THAT_EXPECTED(select(R, Match), HasValue("3, 7" )); |
302 | } |
303 | |
304 | TEST(RangeSelectorTest, EncloseOpNodesParsed) { |
305 | StringRef Code = R"cc( |
306 | int f(int x, int y, int z) { return 3; } |
307 | int g() { return f(/* comment */ 3, 7 /* comment */, 9); } |
308 | )cc" ; |
309 | auto Matcher = callExpr(hasArgument(N: 0, InnerMatcher: expr().bind(ID: "a0" )), |
310 | hasArgument(N: 1, InnerMatcher: expr().bind(ID: "a1" ))); |
311 | auto R = parseRangeSelector(Input: R"rs(encloseNodes("a0", "a1"))rs" ); |
312 | ASSERT_THAT_EXPECTED(R, llvm::Succeeded()); |
313 | TestMatch Match = matchCode(Code, Matcher); |
314 | EXPECT_THAT_EXPECTED(select(*R, Match), HasValue("3, 7" )); |
315 | } |
316 | |
317 | TEST(RangeSelectorTest, EncloseOpGeneralParsed) { |
318 | StringRef Code = R"cc( |
319 | int f(int x, int y, int z) { return 3; } |
320 | int g() { return f(/* comment */ 3, 7 /* comment */, 9); } |
321 | )cc" ; |
322 | auto Matcher = callExpr(hasArgument(N: 0, InnerMatcher: expr().bind(ID: "a0" )), |
323 | hasArgument(N: 1, InnerMatcher: expr().bind(ID: "a1" ))); |
324 | auto R = parseRangeSelector(Input: R"rs(encloseNodes("a0", "a1"))rs" ); |
325 | ASSERT_THAT_EXPECTED(R, llvm::Succeeded()); |
326 | TestMatch Match = matchCode(Code, Matcher); |
327 | EXPECT_THAT_EXPECTED(select(*R, Match), HasValue("3, 7" )); |
328 | } |
329 | |
330 | TEST(RangeSelectorTest, NodeOpStatement) { |
331 | StringRef Code = "int f() { return 3; }" ; |
332 | TestMatch Match = matchCode(Code, Matcher: returnStmt().bind(ID: "id" )); |
333 | EXPECT_THAT_EXPECTED(select(node("id" ), Match), HasValue("return 3;" )); |
334 | } |
335 | |
336 | TEST(RangeSelectorTest, NodeOpExpression) { |
337 | StringRef Code = "int f() { return 3; }" ; |
338 | TestMatch Match = matchCode(Code, Matcher: expr().bind(ID: "id" )); |
339 | EXPECT_THAT_EXPECTED(select(node("id" ), Match), HasValue("3" )); |
340 | } |
341 | |
342 | TEST(RangeSelectorTest, StatementOp) { |
343 | StringRef Code = "int f() { return 3; }" ; |
344 | TestMatch Match = matchCode(Code, Matcher: expr().bind(ID: "id" )); |
345 | RangeSelector R = statement(ID: "id" ); |
346 | EXPECT_THAT_EXPECTED(select(R, Match), HasValue("3;" )); |
347 | } |
348 | |
349 | TEST(RangeSelectorTest, StatementOpParsed) { |
350 | StringRef Code = "int f() { return 3; }" ; |
351 | TestMatch Match = matchCode(Code, Matcher: expr().bind(ID: "id" )); |
352 | auto R = parseRangeSelector(Input: R"rs(statement("id"))rs" ); |
353 | ASSERT_THAT_EXPECTED(R, llvm::Succeeded()); |
354 | EXPECT_THAT_EXPECTED(select(*R, Match), HasValue("3;" )); |
355 | } |
356 | |
357 | TEST(RangeSelectorTest, MemberOp) { |
358 | StringRef Code = R"cc( |
359 | struct S { |
360 | int member; |
361 | }; |
362 | int g() { |
363 | S s; |
364 | return s.member; |
365 | } |
366 | )cc" ; |
367 | const char *ID = "id" ; |
368 | TestMatch Match = matchCode(Code, Matcher: memberExpr().bind(ID)); |
369 | EXPECT_THAT_EXPECTED(select(member(ID), Match), HasValue("member" )); |
370 | } |
371 | |
372 | // Tests that member does not select any qualifiers on the member name. |
373 | TEST(RangeSelectorTest, MemberOpQualified) { |
374 | StringRef Code = R"cc( |
375 | struct S { |
376 | int member; |
377 | }; |
378 | struct T : public S { |
379 | int field; |
380 | }; |
381 | int g() { |
382 | T t; |
383 | return t.S::member; |
384 | } |
385 | )cc" ; |
386 | const char *ID = "id" ; |
387 | TestMatch Match = matchCode(Code, Matcher: memberExpr().bind(ID)); |
388 | EXPECT_THAT_EXPECTED(select(member(ID), Match), HasValue("member" )); |
389 | } |
390 | |
391 | TEST(RangeSelectorTest, MemberOpTemplate) { |
392 | StringRef Code = R"cc( |
393 | struct S { |
394 | template <typename T> T foo(T t); |
395 | }; |
396 | int f(int x) { |
397 | S s; |
398 | return s.template foo<int>(3); |
399 | } |
400 | )cc" ; |
401 | |
402 | const char *ID = "id" ; |
403 | TestMatch Match = matchCode(Code, Matcher: memberExpr().bind(ID)); |
404 | EXPECT_THAT_EXPECTED(select(member(ID), Match), HasValue("foo" )); |
405 | } |
406 | |
407 | TEST(RangeSelectorTest, MemberOpOperator) { |
408 | StringRef Code = R"cc( |
409 | struct S { |
410 | int operator*(); |
411 | }; |
412 | int f(int x) { |
413 | S s; |
414 | return s.operator *(); |
415 | } |
416 | )cc" ; |
417 | |
418 | const char *ID = "id" ; |
419 | TestMatch Match = matchCode(Code, Matcher: memberExpr().bind(ID)); |
420 | EXPECT_THAT_EXPECTED(select(member(ID), Match), HasValue("operator *" )); |
421 | } |
422 | |
423 | TEST(RangeSelectorTest, NameOpNamedDecl) { |
424 | StringRef Code = R"cc( |
425 | int myfun() { |
426 | return 3; |
427 | } |
428 | )cc" ; |
429 | const char *ID = "id" ; |
430 | TestMatch Match = matchCode(Code, Matcher: functionDecl().bind(ID)); |
431 | EXPECT_THAT_EXPECTED(select(name(ID), Match), HasValue("myfun" )); |
432 | } |
433 | |
434 | TEST(RangeSelectorTest, NameOpDeclRef) { |
435 | StringRef Code = R"cc( |
436 | int foo(int x) { |
437 | return x; |
438 | } |
439 | int g(int x) { return foo(x) * x; } |
440 | )cc" ; |
441 | const char *Ref = "ref" ; |
442 | TestMatch Match = matchCode(Code, Matcher: declRefExpr(to(InnerMatcher: functionDecl())).bind(ID: Ref)); |
443 | EXPECT_THAT_EXPECTED(select(name(Ref), Match), HasValue("foo" )); |
444 | } |
445 | |
446 | TEST(RangeSelectorTest, NameOpCtorInitializer) { |
447 | StringRef Code = R"cc( |
448 | class C { |
449 | public: |
450 | C() : field(3) {} |
451 | int field; |
452 | }; |
453 | )cc" ; |
454 | const char *Init = "init" ; |
455 | TestMatch Match = matchCode(Code, Matcher: cxxCtorInitializer().bind(ID: Init)); |
456 | EXPECT_THAT_EXPECTED(select(name(Init), Match), HasValue("field" )); |
457 | } |
458 | |
459 | TEST(RangeSelectorTest, NameOpTypeLoc) { |
460 | StringRef Code = R"cc( |
461 | namespace ns { |
462 | struct Foo { |
463 | Foo(); |
464 | Foo(int); |
465 | Foo(int, int); |
466 | }; |
467 | } // namespace ns |
468 | |
469 | ns::Foo a; |
470 | auto b = ns::Foo(3); |
471 | auto c = ns::Foo(1, 2); |
472 | )cc" ; |
473 | const char *CtorTy = "ctor_ty" ; |
474 | // Matches declaration of `a` |
475 | TestMatch MatchA = matchCode( |
476 | Code, Matcher: varDecl(hasName(Name: "a" ), hasTypeLoc(Inner: typeLoc().bind(ID: CtorTy)))); |
477 | EXPECT_THAT_EXPECTED(select(name(CtorTy), MatchA), HasValue("Foo" )); |
478 | // Matches call of Foo(int) |
479 | TestMatch MatchB = matchCode( |
480 | Code, Matcher: cxxFunctionalCastExpr(hasTypeLoc(Inner: typeLoc().bind(ID: CtorTy)))); |
481 | EXPECT_THAT_EXPECTED(select(name(CtorTy), MatchB), HasValue("Foo" )); |
482 | // Matches call of Foo(int, int) |
483 | TestMatch MatchC = matchCode( |
484 | Code, Matcher: cxxTemporaryObjectExpr(hasTypeLoc(Inner: typeLoc().bind(ID: CtorTy)))); |
485 | EXPECT_THAT_EXPECTED(select(name(CtorTy), MatchC), HasValue("Foo" )); |
486 | } |
487 | |
488 | TEST(RangeSelectorTest, NameOpTemplateSpecializationTypeLoc) { |
489 | StringRef Code = R"cc( |
490 | namespace ns { |
491 | template <typename T> |
492 | struct Foo {}; |
493 | } // namespace ns |
494 | |
495 | ns::Foo<int> a; |
496 | )cc" ; |
497 | const char *Loc = "tyloc" ; |
498 | // Matches declaration of `a`. |
499 | TestMatch MatchA = |
500 | matchCode(Code, Matcher: varDecl(hasName(Name: "a" ), hasTypeLoc(Inner: typeLoc().bind(ID: Loc)))); |
501 | EXPECT_THAT_EXPECTED(select(name(Loc), MatchA), HasValue("Foo" )); |
502 | } |
503 | |
504 | TEST(RangeSelectorTest, NameOpErrors) { |
505 | EXPECT_THAT_EXPECTED(selectFromTrivial(name("unbound_id" )), |
506 | Failed<StringError>(withUnboundNodeMessage())); |
507 | EXPECT_THAT_EXPECTED(selectFromAssorted(name("stmt" )), |
508 | Failed<StringError>(withTypeErrorMessage("stmt" ))); |
509 | } |
510 | |
511 | TEST(RangeSelectorTest, NameOpDeclRefError) { |
512 | StringRef Code = R"cc( |
513 | struct S { |
514 | int operator*(); |
515 | }; |
516 | int f(int x) { |
517 | S s; |
518 | return *s + x; |
519 | } |
520 | )cc" ; |
521 | const char *Ref = "ref" ; |
522 | TestMatch Match = matchCode(Code, Matcher: declRefExpr(to(InnerMatcher: functionDecl())).bind(ID: Ref)); |
523 | EXPECT_THAT_EXPECTED( |
524 | name(Ref)(Match.Result), |
525 | Failed<StringError>(testing::Property( |
526 | &StringError::getMessage, |
527 | AllOf(HasSubstr(Ref), HasSubstr("requires property 'identifier'" ))))); |
528 | } |
529 | |
530 | TEST(RangeSelectorTest, CallArgsOp) { |
531 | const StringRef Code = R"cc( |
532 | struct C { |
533 | int bar(int, int); |
534 | }; |
535 | int f() { |
536 | C x; |
537 | return x.bar(3, 4); |
538 | } |
539 | )cc" ; |
540 | const char *ID = "id" ; |
541 | TestMatch Match = matchCode(Code, Matcher: callExpr().bind(ID)); |
542 | EXPECT_THAT_EXPECTED(select(callArgs(ID), Match), HasValue("3, 4" )); |
543 | } |
544 | |
545 | TEST(RangeSelectorTest, CallArgsOpNoArgs) { |
546 | const StringRef Code = R"cc( |
547 | struct C { |
548 | int bar(); |
549 | }; |
550 | int f() { |
551 | C x; |
552 | return x.bar(); |
553 | } |
554 | )cc" ; |
555 | const char *ID = "id" ; |
556 | TestMatch Match = matchCode(Code, Matcher: callExpr().bind(ID)); |
557 | EXPECT_THAT_EXPECTED(select(callArgs(ID), Match), HasValue("" )); |
558 | } |
559 | |
560 | TEST(RangeSelectorTest, CallArgsOpNoArgsWithComments) { |
561 | const StringRef Code = R"cc( |
562 | struct C { |
563 | int bar(); |
564 | }; |
565 | int f() { |
566 | C x; |
567 | return x.bar(/*empty*/); |
568 | } |
569 | )cc" ; |
570 | const char *ID = "id" ; |
571 | TestMatch Match = matchCode(Code, Matcher: callExpr().bind(ID)); |
572 | EXPECT_THAT_EXPECTED(select(callArgs(ID), Match), HasValue("/*empty*/" )); |
573 | } |
574 | |
575 | // Tests that arguments are extracted correctly when a temporary (with parens) |
576 | // is used. |
577 | TEST(RangeSelectorTest, CallArgsOpWithParens) { |
578 | const StringRef Code = R"cc( |
579 | struct C { |
580 | int bar(int, int) { return 3; } |
581 | }; |
582 | int f() { |
583 | C x; |
584 | return C().bar(3, 4); |
585 | } |
586 | )cc" ; |
587 | const char *ID = "id" ; |
588 | TestMatch Match = |
589 | matchCode(Code, Matcher: callExpr(callee(InnerMatcher: functionDecl(hasName(Name: "bar" )))).bind(ID)); |
590 | EXPECT_THAT_EXPECTED(select(callArgs(ID), Match), HasValue("3, 4" )); |
591 | } |
592 | |
593 | TEST(RangeSelectorTest, CallArgsOpLeadingComments) { |
594 | const StringRef Code = R"cc( |
595 | struct C { |
596 | int bar(int, int) { return 3; } |
597 | }; |
598 | int f() { |
599 | C x; |
600 | return x.bar(/*leading*/ 3, 4); |
601 | } |
602 | )cc" ; |
603 | const char *ID = "id" ; |
604 | TestMatch Match = matchCode(Code, Matcher: callExpr().bind(ID)); |
605 | EXPECT_THAT_EXPECTED(select(callArgs(ID), Match), |
606 | HasValue("/*leading*/ 3, 4" )); |
607 | } |
608 | |
609 | TEST(RangeSelectorTest, CallArgsOpTrailingComments) { |
610 | const StringRef Code = R"cc( |
611 | struct C { |
612 | int bar(int, int) { return 3; } |
613 | }; |
614 | int f() { |
615 | C x; |
616 | return x.bar(3 /*trailing*/, 4); |
617 | } |
618 | )cc" ; |
619 | const char *ID = "id" ; |
620 | TestMatch Match = matchCode(Code, Matcher: callExpr().bind(ID)); |
621 | EXPECT_THAT_EXPECTED(select(callArgs(ID), Match), |
622 | HasValue("3 /*trailing*/, 4" )); |
623 | } |
624 | |
625 | TEST(RangeSelectorTest, CallArgsOpEolComments) { |
626 | const StringRef Code = R"cc( |
627 | struct C { |
628 | int bar(int, int) { return 3; } |
629 | }; |
630 | int f() { |
631 | C x; |
632 | return x.bar( // Header |
633 | 1, // foo |
634 | 2 // bar |
635 | ); |
636 | } |
637 | )cc" ; |
638 | const char *ID = "id" ; |
639 | TestMatch Match = matchCode(Code, Matcher: callExpr().bind(ID)); |
640 | std::string ExpectedString = R"( // Header |
641 | 1, // foo |
642 | 2 // bar |
643 | )" ; |
644 | EXPECT_THAT_EXPECTED(select(callArgs(ID), Match), HasValue(ExpectedString)); |
645 | } |
646 | |
647 | TEST(RangeSelectorTest, CallArgsErrors) { |
648 | EXPECT_THAT_EXPECTED(selectFromTrivial(callArgs("unbound_id" )), |
649 | Failed<StringError>(withUnboundNodeMessage())); |
650 | EXPECT_THAT_EXPECTED(selectFromAssorted(callArgs("stmt" )), |
651 | Failed<StringError>(withTypeErrorMessage("stmt" ))); |
652 | } |
653 | |
654 | TEST(RangeSelectorTest, StatementsOp) { |
655 | StringRef Code = R"cc( |
656 | void g(); |
657 | void f() { /* comment */ g(); /* comment */ g(); /* comment */ } |
658 | )cc" ; |
659 | const char *ID = "id" ; |
660 | TestMatch Match = matchCode(Code, Matcher: compoundStmt().bind(ID)); |
661 | EXPECT_THAT_EXPECTED( |
662 | select(statements(ID), Match), |
663 | HasValue(" /* comment */ g(); /* comment */ g(); /* comment */ " )); |
664 | } |
665 | |
666 | TEST(RangeSelectorTest, StatementsOpEmptyList) { |
667 | StringRef Code = "void f() {}" ; |
668 | const char *ID = "id" ; |
669 | TestMatch Match = matchCode(Code, Matcher: compoundStmt().bind(ID)); |
670 | EXPECT_THAT_EXPECTED(select(statements(ID), Match), HasValue("" )); |
671 | } |
672 | |
673 | TEST(RangeSelectorTest, StatementsOpErrors) { |
674 | EXPECT_THAT_EXPECTED(selectFromTrivial(statements("unbound_id" )), |
675 | Failed<StringError>(withUnboundNodeMessage())); |
676 | EXPECT_THAT_EXPECTED(selectFromAssorted(statements("decl" )), |
677 | Failed<StringError>(withTypeErrorMessage("decl" ))); |
678 | } |
679 | |
680 | TEST(RangeSelectorTest, ElementsOp) { |
681 | StringRef Code = R"cc( |
682 | void f() { |
683 | int v[] = {/* comment */ 3, /* comment*/ 4 /* comment */}; |
684 | (void)v; |
685 | } |
686 | )cc" ; |
687 | const char *ID = "id" ; |
688 | TestMatch Match = matchCode(Code, Matcher: initListExpr().bind(ID)); |
689 | EXPECT_THAT_EXPECTED( |
690 | select(initListElements(ID), Match), |
691 | HasValue("/* comment */ 3, /* comment*/ 4 /* comment */" )); |
692 | } |
693 | |
694 | TEST(RangeSelectorTest, ElementsOpEmptyList) { |
695 | StringRef Code = R"cc( |
696 | void f() { |
697 | int v[] = {}; |
698 | (void)v; |
699 | } |
700 | )cc" ; |
701 | const char *ID = "id" ; |
702 | TestMatch Match = matchCode(Code, Matcher: initListExpr().bind(ID)); |
703 | EXPECT_THAT_EXPECTED(select(initListElements(ID), Match), HasValue("" )); |
704 | } |
705 | |
706 | TEST(RangeSelectorTest, ElementsOpErrors) { |
707 | EXPECT_THAT_EXPECTED(selectFromTrivial(initListElements("unbound_id" )), |
708 | Failed<StringError>(withUnboundNodeMessage())); |
709 | EXPECT_THAT_EXPECTED(selectFromAssorted(initListElements("stmt" )), |
710 | Failed<StringError>(withTypeErrorMessage("stmt" ))); |
711 | } |
712 | |
713 | TEST(RangeSelectorTest, ElseBranchOpSingleStatement) { |
714 | StringRef Code = R"cc( |
715 | int f() { |
716 | int x = 0; |
717 | if (true) x = 3; |
718 | else x = 4; |
719 | return x + 5; |
720 | } |
721 | )cc" ; |
722 | const char *ID = "id" ; |
723 | TestMatch Match = matchCode(Code, Matcher: ifStmt().bind(ID)); |
724 | EXPECT_THAT_EXPECTED(select(elseBranch(ID), Match), HasValue("else x = 4;" )); |
725 | } |
726 | |
727 | TEST(RangeSelectorTest, ElseBranchOpCompoundStatement) { |
728 | StringRef Code = R"cc( |
729 | int f() { |
730 | int x = 0; |
731 | if (true) x = 3; |
732 | else { x = 4; } |
733 | return x + 5; |
734 | } |
735 | )cc" ; |
736 | const char *ID = "id" ; |
737 | TestMatch Match = matchCode(Code, Matcher: ifStmt().bind(ID)); |
738 | EXPECT_THAT_EXPECTED(select(elseBranch(ID), Match), |
739 | HasValue("else { x = 4; }" )); |
740 | } |
741 | |
742 | // Tests case where the matched node is the complete expanded text. |
743 | TEST(RangeSelectorTest, ExpansionOp) { |
744 | StringRef Code = R"cc( |
745 | #define BADDECL(E) int bad(int x) { return E; } |
746 | BADDECL(x * x) |
747 | )cc" ; |
748 | |
749 | const char *Fun = "Fun" ; |
750 | TestMatch Match = matchCode(Code, Matcher: functionDecl(hasName(Name: "bad" )).bind(ID: Fun)); |
751 | EXPECT_THAT_EXPECTED(select(expansion(node(Fun)), Match), |
752 | HasValue("BADDECL(x * x)" )); |
753 | } |
754 | |
755 | // Tests case where the matched node is (only) part of the expanded text. |
756 | TEST(RangeSelectorTest, ExpansionOpPartial) { |
757 | StringRef Code = R"cc( |
758 | #define BADDECL(E) int bad(int x) { return E; } |
759 | BADDECL(x * x) |
760 | )cc" ; |
761 | |
762 | const char *Ret = "Ret" ; |
763 | TestMatch Match = matchCode(Code, Matcher: returnStmt().bind(ID: Ret)); |
764 | EXPECT_THAT_EXPECTED(select(expansion(node(Ret)), Match), |
765 | HasValue("BADDECL(x * x)" )); |
766 | } |
767 | |
768 | TEST(RangeSelectorTest, IfBoundOpBound) { |
769 | StringRef Code = R"cc( |
770 | int f() { |
771 | return 3 + 5; |
772 | } |
773 | )cc" ; |
774 | const char *ID = "id" , *Op = "op" ; |
775 | TestMatch Match = |
776 | matchCode(Code, Matcher: binaryOperator(hasLHS(InnerMatcher: expr().bind(ID))).bind(ID: Op)); |
777 | EXPECT_THAT_EXPECTED(select(ifBound(ID, node(ID), node(Op)), Match), |
778 | HasValue("3" )); |
779 | } |
780 | |
781 | TEST(RangeSelectorTest, IfBoundOpUnbound) { |
782 | StringRef Code = R"cc( |
783 | int f() { |
784 | return 3 + 5; |
785 | } |
786 | )cc" ; |
787 | const char *ID = "id" , *Op = "op" ; |
788 | TestMatch Match = matchCode(Code, Matcher: binaryOperator().bind(ID: Op)); |
789 | EXPECT_THAT_EXPECTED(select(ifBound(ID, node(ID), node(Op)), Match), |
790 | HasValue("3 + 5" )); |
791 | } |
792 | |
793 | } // namespace |
794 | |