1//===- unittest/Tooling/ASTSelectionTest.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 "TestVisitor.h"
10#include "clang/Basic/SourceManager.h"
11#include "clang/Tooling/Refactoring/ASTSelection.h"
12#include <optional>
13
14using namespace clang;
15using namespace tooling;
16
17namespace {
18
19struct FileLocation {
20 unsigned Line, Column;
21
22 SourceLocation translate(const SourceManager &SM) {
23 return SM.translateLineCol(FID: SM.getMainFileID(), Line, Col: Column);
24 }
25};
26
27using FileRange = std::pair<FileLocation, FileLocation>;
28
29class SelectionFinderVisitor : public TestVisitor<SelectionFinderVisitor> {
30 FileLocation Location;
31 std::optional<FileRange> SelectionRange;
32 llvm::function_ref<void(SourceRange SelectionRange,
33 std::optional<SelectedASTNode>)>
34 Consumer;
35
36public:
37 SelectionFinderVisitor(
38 FileLocation Location, std::optional<FileRange> SelectionRange,
39 llvm::function_ref<void(SourceRange SelectionRange,
40 std::optional<SelectedASTNode>)>
41 Consumer)
42 : Location(Location), SelectionRange(SelectionRange), Consumer(Consumer) {
43 }
44
45 bool VisitTranslationUnitDecl(const TranslationUnitDecl *TU) {
46 const ASTContext &Context = TU->getASTContext();
47 const SourceManager &SM = Context.getSourceManager();
48
49 SourceRange SelRange;
50 if (SelectionRange) {
51 SelRange = SourceRange(SelectionRange->first.translate(SM),
52 SelectionRange->second.translate(SM));
53 } else {
54 SourceLocation Loc = Location.translate(SM);
55 SelRange = SourceRange(Loc, Loc);
56 }
57 Consumer(SelRange, findSelectedASTNodes(Context, SelRange));
58 return false;
59 }
60};
61
62/// This is a test utility function that computes the AST selection at the
63/// given location with an optional selection range.
64///
65/// A location roughly corresponds to a cursor location in an editor, while
66/// the optional range corresponds to the selection range in an editor.
67void findSelectedASTNodesWithRange(
68 StringRef Source, FileLocation Location,
69 std::optional<FileRange> SelectionRange,
70 llvm::function_ref<void(SourceRange SelectionRange,
71 std::optional<SelectedASTNode>)>
72 Consumer,
73 SelectionFinderVisitor::Language Language =
74 SelectionFinderVisitor::Lang_CXX11) {
75 SelectionFinderVisitor Visitor(Location, SelectionRange, Consumer);
76 EXPECT_TRUE(Visitor.runOver(Source, Language));
77}
78
79void findSelectedASTNodes(
80 StringRef Source, FileLocation Location,
81 std::optional<FileRange> SelectionRange,
82 llvm::function_ref<void(std::optional<SelectedASTNode>)> Consumer,
83 SelectionFinderVisitor::Language Language =
84 SelectionFinderVisitor::Lang_CXX11) {
85 findSelectedASTNodesWithRange(
86 Source, Location, SelectionRange,
87 Consumer: [&](SourceRange, std::optional<SelectedASTNode> Selection) {
88 Consumer(std::move(Selection));
89 },
90 Language);
91}
92
93void checkNodeImpl(bool IsTypeMatched, const SelectedASTNode &Node,
94 SourceSelectionKind SelectionKind, unsigned NumChildren) {
95 ASSERT_TRUE(IsTypeMatched);
96 EXPECT_EQ(Node.Children.size(), NumChildren);
97 ASSERT_EQ(Node.SelectionKind, SelectionKind);
98}
99
100void checkDeclName(const SelectedASTNode &Node, StringRef Name) {
101 const auto *ND = Node.Node.get<NamedDecl>();
102 EXPECT_TRUE(!!ND);
103 ASSERT_EQ(ND->getName(), Name);
104}
105
106template <typename T>
107const SelectedASTNode &
108checkNode(const SelectedASTNode &StmtNode, SourceSelectionKind SelectionKind,
109 unsigned NumChildren = 0,
110 std::enable_if_t<std::is_base_of_v<Stmt, T>, T> *StmtOverloadChecker =
111 nullptr) {
112 checkNodeImpl(isa<T>(StmtNode.Node.get<Stmt>()), StmtNode, SelectionKind,
113 NumChildren);
114 return StmtNode;
115}
116
117template <typename T>
118const SelectedASTNode &
119checkNode(const SelectedASTNode &DeclNode, SourceSelectionKind SelectionKind,
120 unsigned NumChildren = 0, StringRef Name = "",
121 std::enable_if_t<std::is_base_of_v<Decl, T>, T> *DeclOverloadChecker =
122 nullptr) {
123 checkNodeImpl(isa<T>(DeclNode.Node.get<Decl>()), DeclNode, SelectionKind,
124 NumChildren);
125 if (!Name.empty())
126 checkDeclName(Node: DeclNode, Name);
127 return DeclNode;
128}
129
130struct ForAllChildrenOf {
131 const SelectedASTNode &Node;
132
133 static void childKindVerifier(const SelectedASTNode &Node,
134 SourceSelectionKind SelectionKind) {
135 for (const SelectedASTNode &Child : Node.Children) {
136 ASSERT_EQ(Node.SelectionKind, SelectionKind);
137 childKindVerifier(Node: Child, SelectionKind);
138 }
139 }
140
141public:
142 ForAllChildrenOf(const SelectedASTNode &Node) : Node(Node) {}
143
144 void shouldHaveSelectionKind(SourceSelectionKind Kind) {
145 childKindVerifier(Node, SelectionKind: Kind);
146 }
147};
148
149ForAllChildrenOf allChildrenOf(const SelectedASTNode &Node) {
150 return ForAllChildrenOf(Node);
151}
152
153TEST(ASTSelectionFinder, CursorNoSelection) {
154 findSelectedASTNodes(
155 Source: " void f() { }", Location: {.Line: 1, .Column: 1}, SelectionRange: std::nullopt,
156 Consumer: [](std::optional<SelectedASTNode> Node) { EXPECT_FALSE(Node); });
157}
158
159TEST(ASTSelectionFinder, CursorAtStartOfFunction) {
160 findSelectedASTNodes(
161 Source: "void f() { }", Location: {.Line: 1, .Column: 1}, SelectionRange: std::nullopt,
162 Consumer: [](std::optional<SelectedASTNode> Node) {
163 EXPECT_TRUE(Node);
164 checkNode<TranslationUnitDecl>(DeclNode: *Node, SelectionKind: SourceSelectionKind::None,
165 /*NumChildren=*/1);
166 checkNode<FunctionDecl>(DeclNode: Node->Children[0],
167 SelectionKind: SourceSelectionKind::ContainsSelection,
168 /*NumChildren=*/0, /*Name=*/"f");
169
170 // Check that the dumping works.
171 std::string DumpValue;
172 llvm::raw_string_ostream OS(DumpValue);
173 Node->Children[0].dump(OS);
174 ASSERT_EQ(OS.str(), "FunctionDecl \"f\" contains-selection\n");
175 });
176}
177
178TEST(ASTSelectionFinder, RangeNoSelection) {
179 findSelectedASTNodes(
180 Source: " void f() { }", Location: {.Line: 1, .Column: 1}, SelectionRange: FileRange{{.Line: 1, .Column: 1}, {.Line: 1, .Column: 1}},
181 Consumer: [](std::optional<SelectedASTNode> Node) { EXPECT_FALSE(Node); });
182 findSelectedASTNodes(
183 Source: " void f() { }", Location: {.Line: 1, .Column: 1}, SelectionRange: FileRange{{.Line: 1, .Column: 1}, {.Line: 1, .Column: 2}},
184 Consumer: [](std::optional<SelectedASTNode> Node) { EXPECT_FALSE(Node); });
185}
186
187TEST(ASTSelectionFinder, EmptyRangeFallbackToCursor) {
188 findSelectedASTNodes(Source: "void f() { }", Location: {.Line: 1, .Column: 1}, SelectionRange: FileRange{{.Line: 1, .Column: 1}, {.Line: 1, .Column: 1}},
189 Consumer: [](std::optional<SelectedASTNode> Node) {
190 EXPECT_TRUE(Node);
191 checkNode<FunctionDecl>(
192 DeclNode: Node->Children[0],
193 SelectionKind: SourceSelectionKind::ContainsSelection,
194 /*NumChildren=*/0, /*Name=*/"f");
195 });
196}
197
198TEST(ASTSelectionFinder, WholeFunctionSelection) {
199 StringRef Source = "int f(int x) { return x;\n}\nvoid f2() { }";
200 // From 'int' until just after '}':
201
202 findSelectedASTNodes(
203 Source, Location: {.Line: 1, .Column: 1}, SelectionRange: FileRange{{.Line: 1, .Column: 1}, {.Line: 2, .Column: 2}},
204 Consumer: [](std::optional<SelectedASTNode> Node) {
205 EXPECT_TRUE(Node);
206 EXPECT_EQ(Node->Children.size(), 1u);
207 const auto &Fn = checkNode<FunctionDecl>(
208 DeclNode: Node->Children[0], SelectionKind: SourceSelectionKind::ContainsSelection,
209 /*NumChildren=*/2, /*Name=*/"f");
210 checkNode<ParmVarDecl>(DeclNode: Fn.Children[0],
211 SelectionKind: SourceSelectionKind::InsideSelection);
212 const auto &Body = checkNode<CompoundStmt>(
213 StmtNode: Fn.Children[1], SelectionKind: SourceSelectionKind::InsideSelection,
214 /*NumChildren=*/1);
215 const auto &Return = checkNode<ReturnStmt>(
216 StmtNode: Body.Children[0], SelectionKind: SourceSelectionKind::InsideSelection,
217 /*NumChildren=*/1);
218 checkNode<ImplicitCastExpr>(StmtNode: Return.Children[0],
219 SelectionKind: SourceSelectionKind::InsideSelection,
220 /*NumChildren=*/1);
221 checkNode<DeclRefExpr>(StmtNode: Return.Children[0].Children[0],
222 SelectionKind: SourceSelectionKind::InsideSelection);
223 });
224
225 // From 'int' until just before '}':
226 findSelectedASTNodes(
227 Source, Location: {.Line: 2, .Column: 1}, SelectionRange: FileRange{{.Line: 1, .Column: 1}, {.Line: 2, .Column: 1}},
228 Consumer: [](std::optional<SelectedASTNode> Node) {
229 EXPECT_TRUE(Node);
230 EXPECT_EQ(Node->Children.size(), 1u);
231 const auto &Fn = checkNode<FunctionDecl>(
232 DeclNode: Node->Children[0], SelectionKind: SourceSelectionKind::ContainsSelection,
233 /*NumChildren=*/2, /*Name=*/"f");
234 const auto &Body = checkNode<CompoundStmt>(
235 StmtNode: Fn.Children[1], SelectionKind: SourceSelectionKind::ContainsSelectionEnd,
236 /*NumChildren=*/1);
237 checkNode<ReturnStmt>(StmtNode: Body.Children[0],
238 SelectionKind: SourceSelectionKind::InsideSelection,
239 /*NumChildren=*/1);
240 });
241 // From '{' until just after '}':
242 findSelectedASTNodes(
243 Source, Location: {.Line: 1, .Column: 14}, SelectionRange: FileRange{{.Line: 1, .Column: 14}, {.Line: 2, .Column: 2}},
244 Consumer: [](std::optional<SelectedASTNode> Node) {
245 EXPECT_TRUE(Node);
246 EXPECT_EQ(Node->Children.size(), 1u);
247 const auto &Fn = checkNode<FunctionDecl>(
248 DeclNode: Node->Children[0], SelectionKind: SourceSelectionKind::ContainsSelection,
249 /*NumChildren=*/1, /*Name=*/"f");
250 const auto &Body = checkNode<CompoundStmt>(
251 StmtNode: Fn.Children[0], SelectionKind: SourceSelectionKind::ContainsSelection,
252 /*NumChildren=*/1);
253 checkNode<ReturnStmt>(StmtNode: Body.Children[0],
254 SelectionKind: SourceSelectionKind::InsideSelection,
255 /*NumChildren=*/1);
256 });
257 // From 'x' until just after '}':
258 findSelectedASTNodes(
259 Source, Location: {.Line: 2, .Column: 2}, SelectionRange: FileRange{{.Line: 1, .Column: 11}, {.Line: 2, .Column: 2}},
260 Consumer: [](std::optional<SelectedASTNode> Node) {
261 EXPECT_TRUE(Node);
262 EXPECT_EQ(Node->Children.size(), 1u);
263 const auto &Fn = checkNode<FunctionDecl>(
264 DeclNode: Node->Children[0], SelectionKind: SourceSelectionKind::ContainsSelection,
265 /*NumChildren=*/2, /*Name=*/"f");
266 checkNode<ParmVarDecl>(DeclNode: Fn.Children[0],
267 SelectionKind: SourceSelectionKind::ContainsSelectionStart);
268 const auto &Body = checkNode<CompoundStmt>(
269 StmtNode: Fn.Children[1], SelectionKind: SourceSelectionKind::InsideSelection,
270 /*NumChildren=*/1);
271 checkNode<ReturnStmt>(StmtNode: Body.Children[0],
272 SelectionKind: SourceSelectionKind::InsideSelection,
273 /*NumChildren=*/1);
274 });
275}
276
277TEST(ASTSelectionFinder, MultipleFunctionSelection) {
278 StringRef Source = R"(void f0() {
279}
280void f1() { }
281void f2() { }
282void f3() { }
283)";
284 auto SelectedF1F2 = [](std::optional<SelectedASTNode> Node) {
285 EXPECT_TRUE(Node);
286 EXPECT_EQ(Node->Children.size(), 2u);
287 checkNode<FunctionDecl>(DeclNode: Node->Children[0],
288 SelectionKind: SourceSelectionKind::InsideSelection,
289 /*NumChildren=*/1, /*Name=*/"f1");
290 checkNode<FunctionDecl>(DeclNode: Node->Children[1],
291 SelectionKind: SourceSelectionKind::InsideSelection,
292 /*NumChildren=*/1, /*Name=*/"f2");
293 };
294 // Just after '}' of f0 and just before 'void' of f3:
295 findSelectedASTNodes(Source, Location: {.Line: 2, .Column: 2}, SelectionRange: FileRange{{.Line: 2, .Column: 2}, {.Line: 5, .Column: 1}}, Consumer: SelectedF1F2);
296 // Just before 'void' of f1 and just after '}' of f2:
297 findSelectedASTNodes(Source, Location: {.Line: 3, .Column: 1}, SelectionRange: FileRange{{.Line: 3, .Column: 1}, {.Line: 4, .Column: 14}},
298 Consumer: SelectedF1F2);
299}
300
301TEST(ASTSelectionFinder, MultipleStatementSelection) {
302 StringRef Source = R"(void f(int x, int y) {
303 int z = x;
304 f(2, 3);
305 if (x == 0) {
306 return;
307 }
308 x = 1;
309 return;
310})";
311 // From 'f(2,3)' until just before 'x = 1;':
312 findSelectedASTNodes(
313 Source, Location: {.Line: 3, .Column: 2}, SelectionRange: FileRange{{.Line: 3, .Column: 2}, {.Line: 7, .Column: 1}},
314 Consumer: [](std::optional<SelectedASTNode> Node) {
315 EXPECT_TRUE(Node);
316 EXPECT_EQ(Node->Children.size(), 1u);
317 const auto &Fn = checkNode<FunctionDecl>(
318 DeclNode: Node->Children[0], SelectionKind: SourceSelectionKind::ContainsSelection,
319 /*NumChildren=*/1, /*Name=*/"f");
320 const auto &Body = checkNode<CompoundStmt>(
321 StmtNode: Fn.Children[0], SelectionKind: SourceSelectionKind::ContainsSelection,
322 /*NumChildren=*/2);
323 allChildrenOf(Node: checkNode<CallExpr>(StmtNode: Body.Children[0],
324 SelectionKind: SourceSelectionKind::InsideSelection,
325 /*NumChildren=*/3))
326 .shouldHaveSelectionKind(Kind: SourceSelectionKind::InsideSelection);
327 allChildrenOf(Node: checkNode<IfStmt>(StmtNode: Body.Children[1],
328 SelectionKind: SourceSelectionKind::InsideSelection,
329 /*NumChildren=*/2))
330 .shouldHaveSelectionKind(Kind: SourceSelectionKind::InsideSelection);
331 });
332 // From 'f(2,3)' until just before ';' in 'x = 1;':
333 findSelectedASTNodes(
334 Source, Location: {.Line: 3, .Column: 2}, SelectionRange: FileRange{{.Line: 3, .Column: 2}, {.Line: 7, .Column: 8}},
335 Consumer: [](std::optional<SelectedASTNode> Node) {
336 EXPECT_TRUE(Node);
337 EXPECT_EQ(Node->Children.size(), 1u);
338 const auto &Fn = checkNode<FunctionDecl>(
339 DeclNode: Node->Children[0], SelectionKind: SourceSelectionKind::ContainsSelection,
340 /*NumChildren=*/1, /*Name=*/"f");
341 const auto &Body = checkNode<CompoundStmt>(
342 StmtNode: Fn.Children[0], SelectionKind: SourceSelectionKind::ContainsSelection,
343 /*NumChildren=*/3);
344 checkNode<CallExpr>(StmtNode: Body.Children[0],
345 SelectionKind: SourceSelectionKind::InsideSelection,
346 /*NumChildren=*/3);
347 checkNode<IfStmt>(StmtNode: Body.Children[1],
348 SelectionKind: SourceSelectionKind::InsideSelection,
349 /*NumChildren=*/2);
350 checkNode<BinaryOperator>(StmtNode: Body.Children[2],
351 SelectionKind: SourceSelectionKind::InsideSelection,
352 /*NumChildren=*/2);
353 });
354 // From the middle of 'int z = 3' until the middle of 'x = 1;':
355 findSelectedASTNodes(
356 Source, Location: {.Line: 2, .Column: 10}, SelectionRange: FileRange{{.Line: 2, .Column: 10}, {.Line: 7, .Column: 5}},
357 Consumer: [](std::optional<SelectedASTNode> Node) {
358 EXPECT_TRUE(Node);
359 EXPECT_EQ(Node->Children.size(), 1u);
360 const auto &Fn = checkNode<FunctionDecl>(
361 DeclNode: Node->Children[0], SelectionKind: SourceSelectionKind::ContainsSelection,
362 /*NumChildren=*/1, /*Name=*/"f");
363 const auto &Body = checkNode<CompoundStmt>(
364 StmtNode: Fn.Children[0], SelectionKind: SourceSelectionKind::ContainsSelection,
365 /*NumChildren=*/4);
366 checkNode<DeclStmt>(StmtNode: Body.Children[0],
367 SelectionKind: SourceSelectionKind::ContainsSelectionStart,
368 /*NumChildren=*/1);
369 checkNode<CallExpr>(StmtNode: Body.Children[1],
370 SelectionKind: SourceSelectionKind::InsideSelection,
371 /*NumChildren=*/3);
372 checkNode<IfStmt>(StmtNode: Body.Children[2],
373 SelectionKind: SourceSelectionKind::InsideSelection,
374 /*NumChildren=*/2);
375 checkNode<BinaryOperator>(StmtNode: Body.Children[3],
376 SelectionKind: SourceSelectionKind::ContainsSelectionEnd,
377 /*NumChildren=*/1);
378 });
379}
380
381TEST(ASTSelectionFinder, SelectionInFunctionInObjCImplementation) {
382 StringRef Source = R"(
383@interface I
384@end
385@implementation I
386
387int notSelected() { }
388
389int selected(int x) {
390 return x;
391}
392
393@end
394@implementation I(Cat)
395
396void catF() { }
397
398@end
399
400void outerFunction() { }
401)";
402 // Just the 'x' expression in 'selected':
403 findSelectedASTNodes(
404 Source, Location: {.Line: 9, .Column: 10}, SelectionRange: FileRange{{.Line: 9, .Column: 10}, {.Line: 9, .Column: 11}},
405 Consumer: [](std::optional<SelectedASTNode> Node) {
406 EXPECT_TRUE(Node);
407 EXPECT_EQ(Node->Children.size(), 1u);
408 const auto &Impl = checkNode<ObjCImplementationDecl>(
409 DeclNode: Node->Children[0], SelectionKind: SourceSelectionKind::ContainsSelection,
410 /*NumChildren=*/1, /*Name=*/"I");
411 const auto &Fn = checkNode<FunctionDecl>(
412 DeclNode: Impl.Children[0], SelectionKind: SourceSelectionKind::ContainsSelection,
413 /*NumChildren=*/1, /*Name=*/"selected");
414 allChildrenOf(Node: Fn).shouldHaveSelectionKind(
415 Kind: SourceSelectionKind::ContainsSelection);
416 },
417 Language: SelectionFinderVisitor::Lang_OBJC);
418 // The entire 'catF':
419 findSelectedASTNodes(
420 Source, Location: {.Line: 15, .Column: 1}, SelectionRange: FileRange{{.Line: 15, .Column: 1}, {.Line: 15, .Column: 16}},
421 Consumer: [](std::optional<SelectedASTNode> Node) {
422 EXPECT_TRUE(Node);
423 EXPECT_EQ(Node->Children.size(), 1u);
424 const auto &Impl = checkNode<ObjCCategoryImplDecl>(
425 DeclNode: Node->Children[0], SelectionKind: SourceSelectionKind::ContainsSelection,
426 /*NumChildren=*/1, /*Name=*/"Cat");
427 const auto &Fn = checkNode<FunctionDecl>(
428 DeclNode: Impl.Children[0], SelectionKind: SourceSelectionKind::ContainsSelection,
429 /*NumChildren=*/1, /*Name=*/"catF");
430 allChildrenOf(Node: Fn).shouldHaveSelectionKind(
431 Kind: SourceSelectionKind::ContainsSelection);
432 },
433 Language: SelectionFinderVisitor::Lang_OBJC);
434 // From the line before 'selected' to the line after 'catF':
435 findSelectedASTNodes(
436 Source, Location: {.Line: 16, .Column: 1}, SelectionRange: FileRange{{.Line: 7, .Column: 1}, {.Line: 16, .Column: 1}},
437 Consumer: [](std::optional<SelectedASTNode> Node) {
438 EXPECT_TRUE(Node);
439 EXPECT_EQ(Node->Children.size(), 2u);
440 const auto &Impl = checkNode<ObjCImplementationDecl>(
441 DeclNode: Node->Children[0], SelectionKind: SourceSelectionKind::ContainsSelectionStart,
442 /*NumChildren=*/1, /*Name=*/"I");
443 const auto &Selected = checkNode<FunctionDecl>(
444 DeclNode: Impl.Children[0], SelectionKind: SourceSelectionKind::InsideSelection,
445 /*NumChildren=*/2, /*Name=*/"selected");
446 allChildrenOf(Node: Selected).shouldHaveSelectionKind(
447 Kind: SourceSelectionKind::InsideSelection);
448 const auto &Cat = checkNode<ObjCCategoryImplDecl>(
449 DeclNode: Node->Children[1], SelectionKind: SourceSelectionKind::ContainsSelectionEnd,
450 /*NumChildren=*/1, /*Name=*/"Cat");
451 const auto &CatF = checkNode<FunctionDecl>(
452 DeclNode: Cat.Children[0], SelectionKind: SourceSelectionKind::InsideSelection,
453 /*NumChildren=*/1, /*Name=*/"catF");
454 allChildrenOf(Node: CatF).shouldHaveSelectionKind(
455 Kind: SourceSelectionKind::InsideSelection);
456 },
457 Language: SelectionFinderVisitor::Lang_OBJC);
458 // Just the 'outer' function:
459 findSelectedASTNodes(
460 Source, Location: {.Line: 19, .Column: 1}, SelectionRange: FileRange{{.Line: 19, .Column: 1}, {.Line: 19, .Column: 25}},
461 Consumer: [](std::optional<SelectedASTNode> Node) {
462 EXPECT_TRUE(Node);
463 EXPECT_EQ(Node->Children.size(), 1u);
464 checkNode<FunctionDecl>(DeclNode: Node->Children[0],
465 SelectionKind: SourceSelectionKind::ContainsSelection,
466 /*NumChildren=*/1, /*Name=*/"outerFunction");
467 },
468 Language: SelectionFinderVisitor::Lang_OBJC);
469}
470
471TEST(ASTSelectionFinder, FunctionInObjCImplementationCarefulWithEarlyExit) {
472 StringRef Source = R"(
473@interface I
474@end
475@implementation I
476
477void selected() {
478}
479
480- (void) method { }
481
482@end
483)";
484 // Just 'selected'
485 findSelectedASTNodes(
486 Source, Location: {.Line: 6, .Column: 1}, SelectionRange: FileRange{{.Line: 6, .Column: 1}, {.Line: 7, .Column: 2}},
487 Consumer: [](std::optional<SelectedASTNode> Node) {
488 EXPECT_TRUE(Node);
489 EXPECT_EQ(Node->Children.size(), 1u);
490 const auto &Impl = checkNode<ObjCImplementationDecl>(
491 DeclNode: Node->Children[0], SelectionKind: SourceSelectionKind::ContainsSelection,
492 /*NumChildren=*/1, /*Name=*/"I");
493 checkNode<FunctionDecl>(DeclNode: Impl.Children[0],
494 SelectionKind: SourceSelectionKind::ContainsSelection,
495 /*NumChildren=*/1, /*Name=*/"selected");
496 },
497 Language: SelectionFinderVisitor::Lang_OBJC);
498}
499
500TEST(ASTSelectionFinder, AvoidImplicitDeclarations) {
501 StringRef Source = R"(
502struct Copy {
503 int x;
504};
505void foo() {
506 Copy x;
507 Copy y = x;
508}
509)";
510 // The entire struct 'Copy':
511 findSelectedASTNodes(
512 Source, Location: {.Line: 2, .Column: 1}, SelectionRange: FileRange{{.Line: 2, .Column: 1}, {.Line: 4, .Column: 3}},
513 Consumer: [](std::optional<SelectedASTNode> Node) {
514 EXPECT_TRUE(Node);
515 EXPECT_EQ(Node->Children.size(), 1u);
516 const auto &Record = checkNode<CXXRecordDecl>(
517 DeclNode: Node->Children[0], SelectionKind: SourceSelectionKind::InsideSelection,
518 /*NumChildren=*/1, /*Name=*/"Copy");
519 checkNode<FieldDecl>(DeclNode: Record.Children[0],
520 SelectionKind: SourceSelectionKind::InsideSelection);
521 });
522}
523
524TEST(ASTSelectionFinder, CorrectEndForObjectiveCImplementation) {
525 StringRef Source = R"(
526@interface I
527@end
528@implementation I
529@ end
530)";
531 // Just after '@ end'
532 findSelectedASTNodes(
533 Source, Location: {.Line: 5, .Column: 6}, SelectionRange: std::nullopt,
534 Consumer: [](std::optional<SelectedASTNode> Node) {
535 EXPECT_TRUE(Node);
536 EXPECT_EQ(Node->Children.size(), 1u);
537 checkNode<ObjCImplementationDecl>(
538 DeclNode: Node->Children[0], SelectionKind: SourceSelectionKind::ContainsSelection);
539 },
540 Language: SelectionFinderVisitor::Lang_OBJC);
541}
542
543const SelectedASTNode &checkFnBody(const std::optional<SelectedASTNode> &Node,
544 StringRef Name) {
545 EXPECT_TRUE(Node);
546 EXPECT_EQ(Node->Children.size(), 1u);
547 const auto &Fn = checkNode<FunctionDecl>(
548 Node->Children[0], SourceSelectionKind::ContainsSelection,
549 /*NumChildren=*/1, Name);
550 return checkNode<CompoundStmt>(Fn.Children[0],
551 SourceSelectionKind::ContainsSelection,
552 /*NumChildren=*/1);
553}
554
555TEST(ASTSelectionFinder, SelectObjectiveCPseudoObjectExprs) {
556 StringRef Source = R"(
557@interface I
558@property(readwrite) int prop;
559@end
560void selectProp(I *i) {
561(void)i.prop;
562i.prop = 21;
563}
564
565
566@interface NSMutableArray
567- (id)objectAtIndexedSubscript:(unsigned int)index;
568- (void)setObject:(id)object atIndexedSubscript:(unsigned int)index;
569@end
570
571void selectSubscript(NSMutableArray *array, I *i) {
572 (void)array[10];
573 array[i.prop] = i;
574}
575)";
576 // Just 'i.prop'.
577 findSelectedASTNodes(
578 Source, Location: {.Line: 6, .Column: 7}, SelectionRange: FileRange{{.Line: 6, .Column: 7}, {.Line: 6, .Column: 13}},
579 Consumer: [](std::optional<SelectedASTNode> Node) {
580 const auto &CS = checkFnBody(Node, /*Name=*/"selectProp");
581 const auto &CCast = checkNode<CStyleCastExpr>(
582 StmtNode: CS.Children[0], SelectionKind: SourceSelectionKind::ContainsSelection,
583 /*NumChildren=*/1);
584 const auto &POE = checkNode<PseudoObjectExpr>(
585 StmtNode: CCast.Children[0], SelectionKind: SourceSelectionKind::ContainsSelection,
586 /*NumChildren=*/1);
587 const auto &PRE = checkNode<ObjCPropertyRefExpr>(
588 StmtNode: POE.Children[0], SelectionKind: SourceSelectionKind::ContainsSelection,
589 /*NumChildren=*/1);
590 const auto &Cast = checkNode<ImplicitCastExpr>(
591 StmtNode: PRE.Children[0], SelectionKind: SourceSelectionKind::InsideSelection,
592 /*NumChildren=*/1);
593 checkNode<DeclRefExpr>(StmtNode: Cast.Children[0],
594 SelectionKind: SourceSelectionKind::InsideSelection);
595 },
596 Language: SelectionFinderVisitor::Lang_OBJC);
597 // Just 'i.prop = 21'
598 findSelectedASTNodes(
599 Source, Location: {.Line: 7, .Column: 1}, SelectionRange: FileRange{{.Line: 7, .Column: 1}, {.Line: 7, .Column: 12}},
600 Consumer: [](std::optional<SelectedASTNode> Node) {
601 const auto &CS = checkFnBody(Node, /*Name=*/"selectProp");
602 const auto &POE = checkNode<PseudoObjectExpr>(
603 StmtNode: CS.Children[0], SelectionKind: SourceSelectionKind::ContainsSelection,
604 /*NumChildren=*/1);
605 const auto &BinOp = checkNode<BinaryOperator>(
606 StmtNode: POE.Children[0], SelectionKind: SourceSelectionKind::ContainsSelection,
607 /*NumChildren=*/2);
608 const auto &PRE = checkNode<ObjCPropertyRefExpr>(
609 StmtNode: BinOp.Children[0], SelectionKind: SourceSelectionKind::InsideSelection,
610 /*NumChildren=*/1);
611 const auto &Cast = checkNode<ImplicitCastExpr>(
612 StmtNode: PRE.Children[0], SelectionKind: SourceSelectionKind::InsideSelection,
613 /*NumChildren=*/1);
614 checkNode<DeclRefExpr>(StmtNode: Cast.Children[0],
615 SelectionKind: SourceSelectionKind::InsideSelection);
616 checkNode<IntegerLiteral>(StmtNode: BinOp.Children[1],
617 SelectionKind: SourceSelectionKind::InsideSelection);
618 },
619 Language: SelectionFinderVisitor::Lang_OBJC);
620 // Just 'array[10]'
621 findSelectedASTNodes(
622 Source, Location: {.Line: 17, .Column: 9}, SelectionRange: FileRange{{.Line: 17, .Column: 9}, {.Line: 17, .Column: 18}},
623 Consumer: [](std::optional<SelectedASTNode> Node) {
624 const auto &CS = checkFnBody(Node, /*Name=*/"selectSubscript");
625 const auto &CCast = checkNode<CStyleCastExpr>(
626 StmtNode: CS.Children[0], SelectionKind: SourceSelectionKind::ContainsSelection,
627 /*NumChildren=*/1);
628 const auto &POE = checkNode<PseudoObjectExpr>(
629 StmtNode: CCast.Children[0], SelectionKind: SourceSelectionKind::ContainsSelection,
630 /*NumChildren=*/1);
631 const auto &SRE = checkNode<ObjCSubscriptRefExpr>(
632 StmtNode: POE.Children[0], SelectionKind: SourceSelectionKind::ContainsSelection,
633 /*NumChildren=*/2);
634 const auto &Cast = checkNode<ImplicitCastExpr>(
635 StmtNode: SRE.Children[0], SelectionKind: SourceSelectionKind::InsideSelection,
636 /*NumChildren=*/1);
637 checkNode<DeclRefExpr>(StmtNode: Cast.Children[0],
638 SelectionKind: SourceSelectionKind::InsideSelection);
639 checkNode<IntegerLiteral>(StmtNode: SRE.Children[1],
640 SelectionKind: SourceSelectionKind::InsideSelection);
641 },
642 Language: SelectionFinderVisitor::Lang_OBJC);
643 // Just 'array[i.prop] = array'
644 findSelectedASTNodes(
645 Source, Location: {.Line: 18, .Column: 3}, SelectionRange: FileRange{{.Line: 18, .Column: 3}, {.Line: 18, .Column: 20}},
646 Consumer: [](std::optional<SelectedASTNode> Node) {
647 const auto &CS = checkFnBody(Node, /*Name=*/"selectSubscript");
648 const auto &POE = checkNode<PseudoObjectExpr>(
649 StmtNode: CS.Children[0], SelectionKind: SourceSelectionKind::ContainsSelection,
650 /*NumChildren=*/1);
651 const auto &BinOp = checkNode<BinaryOperator>(
652 StmtNode: POE.Children[0], SelectionKind: SourceSelectionKind::ContainsSelection,
653 /*NumChildren=*/2);
654 const auto &SRE = checkNode<ObjCSubscriptRefExpr>(
655 StmtNode: BinOp.Children[0], SelectionKind: SourceSelectionKind::InsideSelection,
656 /*NumChildren=*/2);
657 const auto &Cast = checkNode<ImplicitCastExpr>(
658 StmtNode: SRE.Children[0], SelectionKind: SourceSelectionKind::InsideSelection,
659 /*NumChildren=*/1);
660 checkNode<DeclRefExpr>(StmtNode: Cast.Children[0],
661 SelectionKind: SourceSelectionKind::InsideSelection);
662 const auto &POE2 = checkNode<PseudoObjectExpr>(
663 StmtNode: SRE.Children[1], SelectionKind: SourceSelectionKind::InsideSelection,
664 /*NumChildren=*/1);
665 const auto &PRE = checkNode<ObjCPropertyRefExpr>(
666 StmtNode: POE2.Children[0], SelectionKind: SourceSelectionKind::InsideSelection,
667 /*NumChildren=*/1);
668 const auto &Cast2 = checkNode<ImplicitCastExpr>(
669 StmtNode: PRE.Children[0], SelectionKind: SourceSelectionKind::InsideSelection,
670 /*NumChildren=*/1);
671 checkNode<DeclRefExpr>(StmtNode: Cast2.Children[0],
672 SelectionKind: SourceSelectionKind::InsideSelection);
673 checkNode<DeclRefExpr>(StmtNode: BinOp.Children[1],
674 SelectionKind: SourceSelectionKind::InsideSelection);
675 },
676 Language: SelectionFinderVisitor::Lang_OBJC);
677}
678
679TEST(ASTSelectionFinder, SimpleCodeRangeASTSelection) {
680 StringRef Source = R"(void f(int x, int y) {
681 int z = x;
682 f(2, 3);
683 if (x == 0) {
684 return;
685 }
686 x = 1;
687 return;
688}
689void f2() {
690 int m = 0;
691}
692)";
693 // No selection range.
694 findSelectedASTNodesWithRange(
695 Source, Location: {.Line: 2, .Column: 2}, SelectionRange: std::nullopt,
696 Consumer: [](SourceRange SelectionRange, std::optional<SelectedASTNode> Node) {
697 EXPECT_TRUE(Node);
698 std::optional<CodeRangeASTSelection> SelectedCode =
699 CodeRangeASTSelection::create(SelectionRange, ASTSelection: std::move(*Node));
700 EXPECT_FALSE(SelectedCode);
701 });
702 findSelectedASTNodesWithRange(
703 Source, Location: {.Line: 2, .Column: 2}, SelectionRange: FileRange{{.Line: 2, .Column: 2}, {.Line: 2, .Column: 2}},
704 Consumer: [](SourceRange SelectionRange, std::optional<SelectedASTNode> Node) {
705 EXPECT_TRUE(Node);
706 std::optional<CodeRangeASTSelection> SelectedCode =
707 CodeRangeASTSelection::create(SelectionRange, ASTSelection: std::move(*Node));
708 EXPECT_FALSE(SelectedCode);
709 });
710 // Range that spans multiple functions is an invalid code range.
711 findSelectedASTNodesWithRange(
712 Source, Location: {.Line: 2, .Column: 2}, SelectionRange: FileRange{{.Line: 7, .Column: 2}, {.Line: 12, .Column: 1}},
713 Consumer: [](SourceRange SelectionRange, std::optional<SelectedASTNode> Node) {
714 EXPECT_TRUE(Node);
715 std::optional<CodeRangeASTSelection> SelectedCode =
716 CodeRangeASTSelection::create(SelectionRange, ASTSelection: std::move(*Node));
717 EXPECT_FALSE(SelectedCode);
718 });
719 // Just 'z = x;':
720 findSelectedASTNodesWithRange(
721 Source, Location: {.Line: 2, .Column: 2}, SelectionRange: FileRange{{.Line: 2, .Column: 2}, {.Line: 2, .Column: 13}},
722 Consumer: [](SourceRange SelectionRange, std::optional<SelectedASTNode> Node) {
723 EXPECT_TRUE(Node);
724 std::optional<CodeRangeASTSelection> SelectedCode =
725 CodeRangeASTSelection::create(SelectionRange, ASTSelection: std::move(*Node));
726 EXPECT_TRUE(SelectedCode);
727 EXPECT_EQ(SelectedCode->size(), 1u);
728 EXPECT_TRUE(isa<DeclStmt>((*SelectedCode)[0]));
729 ArrayRef<SelectedASTNode::ReferenceType> Parents =
730 SelectedCode->getParents();
731 EXPECT_EQ(Parents.size(), 3u);
732 EXPECT_TRUE(
733 isa<TranslationUnitDecl>(Parents[0].get().Node.get<Decl>()));
734 // Function 'f' definition.
735 EXPECT_TRUE(isa<FunctionDecl>(Parents[1].get().Node.get<Decl>()));
736 // Function body of function 'F'.
737 EXPECT_TRUE(isa<CompoundStmt>(Parents[2].get().Node.get<Stmt>()));
738 });
739 // From 'f(2,3)' until just before 'x = 1;':
740 findSelectedASTNodesWithRange(
741 Source, Location: {.Line: 3, .Column: 2}, SelectionRange: FileRange{{.Line: 3, .Column: 2}, {.Line: 7, .Column: 1}},
742 Consumer: [](SourceRange SelectionRange, std::optional<SelectedASTNode> Node) {
743 EXPECT_TRUE(Node);
744 std::optional<CodeRangeASTSelection> SelectedCode =
745 CodeRangeASTSelection::create(SelectionRange, ASTSelection: std::move(*Node));
746 EXPECT_TRUE(SelectedCode);
747 EXPECT_EQ(SelectedCode->size(), 2u);
748 EXPECT_TRUE(isa<CallExpr>((*SelectedCode)[0]));
749 EXPECT_TRUE(isa<IfStmt>((*SelectedCode)[1]));
750 ArrayRef<SelectedASTNode::ReferenceType> Parents =
751 SelectedCode->getParents();
752 EXPECT_EQ(Parents.size(), 3u);
753 EXPECT_TRUE(
754 isa<TranslationUnitDecl>(Parents[0].get().Node.get<Decl>()));
755 // Function 'f' definition.
756 EXPECT_TRUE(isa<FunctionDecl>(Parents[1].get().Node.get<Decl>()));
757 // Function body of function 'F'.
758 EXPECT_TRUE(isa<CompoundStmt>(Parents[2].get().Node.get<Stmt>()));
759 });
760 // From 'f(2,3)' until just before ';' in 'x = 1;':
761 findSelectedASTNodesWithRange(
762 Source, Location: {.Line: 3, .Column: 2}, SelectionRange: FileRange{{.Line: 3, .Column: 2}, {.Line: 7, .Column: 8}},
763 Consumer: [](SourceRange SelectionRange, std::optional<SelectedASTNode> Node) {
764 EXPECT_TRUE(Node);
765 std::optional<CodeRangeASTSelection> SelectedCode =
766 CodeRangeASTSelection::create(SelectionRange, ASTSelection: std::move(*Node));
767 EXPECT_TRUE(SelectedCode);
768 EXPECT_EQ(SelectedCode->size(), 3u);
769 EXPECT_TRUE(isa<CallExpr>((*SelectedCode)[0]));
770 EXPECT_TRUE(isa<IfStmt>((*SelectedCode)[1]));
771 EXPECT_TRUE(isa<BinaryOperator>((*SelectedCode)[2]));
772 });
773 // From the middle of 'int z = 3' until the middle of 'x = 1;':
774 findSelectedASTNodesWithRange(
775 Source, Location: {.Line: 2, .Column: 10}, SelectionRange: FileRange{{.Line: 2, .Column: 10}, {.Line: 7, .Column: 5}},
776 Consumer: [](SourceRange SelectionRange, std::optional<SelectedASTNode> Node) {
777 EXPECT_TRUE(Node);
778 EXPECT_TRUE(Node);
779 std::optional<CodeRangeASTSelection> SelectedCode =
780 CodeRangeASTSelection::create(SelectionRange, ASTSelection: std::move(*Node));
781 EXPECT_TRUE(SelectedCode);
782 EXPECT_EQ(SelectedCode->size(), 4u);
783 EXPECT_TRUE(isa<DeclStmt>((*SelectedCode)[0]));
784 EXPECT_TRUE(isa<CallExpr>((*SelectedCode)[1]));
785 EXPECT_TRUE(isa<IfStmt>((*SelectedCode)[2]));
786 EXPECT_TRUE(isa<BinaryOperator>((*SelectedCode)[3]));
787 });
788}
789
790TEST(ASTSelectionFinder, OutOfBodyCodeRange) {
791 StringRef Source = R"(
792int codeRange = 2 + 3;
793)";
794 // '2+3' expression.
795 findSelectedASTNodesWithRange(
796 Source, Location: {.Line: 2, .Column: 17}, SelectionRange: FileRange{{.Line: 2, .Column: 17}, {.Line: 2, .Column: 22}},
797 Consumer: [](SourceRange SelectionRange, std::optional<SelectedASTNode> Node) {
798 EXPECT_TRUE(Node);
799 std::optional<CodeRangeASTSelection> SelectedCode =
800 CodeRangeASTSelection::create(SelectionRange, ASTSelection: std::move(*Node));
801 EXPECT_TRUE(SelectedCode);
802 EXPECT_EQ(SelectedCode->size(), 1u);
803 EXPECT_TRUE(isa<BinaryOperator>((*SelectedCode)[0]));
804 ArrayRef<SelectedASTNode::ReferenceType> Parents =
805 SelectedCode->getParents();
806 EXPECT_EQ(Parents.size(), 2u);
807 EXPECT_TRUE(
808 isa<TranslationUnitDecl>(Parents[0].get().Node.get<Decl>()));
809 // Variable 'codeRange'.
810 EXPECT_TRUE(isa<VarDecl>(Parents[1].get().Node.get<Decl>()));
811 });
812}
813
814TEST(ASTSelectionFinder, SelectVarDeclStmt) {
815 StringRef Source = R"(
816void f() {
817 {
818 int a;
819 }
820}
821)";
822 // 'int a'
823 findSelectedASTNodesWithRange(
824 Source, Location: {.Line: 4, .Column: 8}, SelectionRange: FileRange{{.Line: 4, .Column: 8}, {.Line: 4, .Column: 14}},
825 Consumer: [](SourceRange SelectionRange, std::optional<SelectedASTNode> Node) {
826 EXPECT_TRUE(Node);
827 std::optional<CodeRangeASTSelection> SelectedCode =
828 CodeRangeASTSelection::create(SelectionRange, ASTSelection: std::move(*Node));
829 EXPECT_TRUE(SelectedCode);
830 EXPECT_EQ(SelectedCode->size(), 1u);
831 EXPECT_TRUE(isa<DeclStmt>((*SelectedCode)[0]));
832 ArrayRef<SelectedASTNode::ReferenceType> Parents =
833 SelectedCode->getParents();
834 EXPECT_EQ(Parents.size(), 4u);
835 EXPECT_TRUE(
836 isa<TranslationUnitDecl>(Parents[0].get().Node.get<Decl>()));
837 // Function 'f' definition.
838 EXPECT_TRUE(isa<FunctionDecl>(Parents[1].get().Node.get<Decl>()));
839 // Function body of function 'F'.
840 EXPECT_TRUE(isa<CompoundStmt>(Parents[2].get().Node.get<Stmt>()));
841 // Compound statement in body of 'F'.
842 EXPECT_TRUE(isa<CompoundStmt>(Parents[3].get().Node.get<Stmt>()));
843 });
844}
845
846TEST(ASTSelectionFinder, SelectEntireDeclStmtRange) {
847 StringRef Source = R"(
848void f(int x, int y) {
849 int a = x * y;
850}
851)";
852 // 'int a = x * y'
853 findSelectedASTNodesWithRange(
854 Source, Location: {.Line: 3, .Column: 4}, SelectionRange: FileRange{{.Line: 3, .Column: 4}, {.Line: 3, .Column: 17}},
855 Consumer: [](SourceRange SelectionRange, std::optional<SelectedASTNode> Node) {
856 EXPECT_TRUE(Node);
857 std::optional<CodeRangeASTSelection> SelectedCode =
858 CodeRangeASTSelection::create(SelectionRange, ASTSelection: std::move(*Node));
859 EXPECT_TRUE(SelectedCode);
860 EXPECT_EQ(SelectedCode->size(), 1u);
861 EXPECT_TRUE(isa<DeclStmt>((*SelectedCode)[0]));
862 ArrayRef<SelectedASTNode::ReferenceType> Parents =
863 SelectedCode->getParents();
864 EXPECT_EQ(Parents.size(), 3u);
865 EXPECT_TRUE(
866 isa<TranslationUnitDecl>(Parents[0].get().Node.get<Decl>()));
867 // Function 'f' definition.
868 EXPECT_TRUE(isa<FunctionDecl>(Parents[1].get().Node.get<Decl>()));
869 // Function body of function 'F'.
870 EXPECT_TRUE(isa<CompoundStmt>(Parents[2].get().Node.get<Stmt>()));
871 });
872}
873
874TEST(ASTSelectionFinder, SelectEntireDeclStmtRangeWithMultipleDecls) {
875 StringRef Source = R"(
876void f(int x, int y) {
877 int a = x * y, b = x - y;
878}
879)";
880 // 'b = x - y'
881 findSelectedASTNodesWithRange(
882 Source, Location: {.Line: 3, .Column: 19}, SelectionRange: FileRange{{.Line: 3, .Column: 19}, {.Line: 3, .Column: 28}},
883 Consumer: [](SourceRange SelectionRange, std::optional<SelectedASTNode> Node) {
884 EXPECT_TRUE(Node);
885 std::optional<CodeRangeASTSelection> SelectedCode =
886 CodeRangeASTSelection::create(SelectionRange, ASTSelection: std::move(*Node));
887 EXPECT_TRUE(SelectedCode);
888 EXPECT_EQ(SelectedCode->size(), 1u);
889 EXPECT_TRUE(isa<DeclStmt>((*SelectedCode)[0]));
890 ArrayRef<SelectedASTNode::ReferenceType> Parents =
891 SelectedCode->getParents();
892 EXPECT_EQ(Parents.size(), 3u);
893 EXPECT_TRUE(
894 isa<TranslationUnitDecl>(Parents[0].get().Node.get<Decl>()));
895 // Function 'f' definition.
896 EXPECT_TRUE(isa<FunctionDecl>(Parents[1].get().Node.get<Decl>()));
897 // Function body of function 'F'.
898 EXPECT_TRUE(isa<CompoundStmt>(Parents[2].get().Node.get<Stmt>()));
899 });
900}
901
902TEST(ASTSelectionFinder, SimpleCodeRangeASTSelectionInObjCMethod) {
903 StringRef Source = R"(@interface I @end
904@implementation I
905- (void) f:(int)x with:(int) y {
906 int z = x;
907 [self f: 2 with: 3];
908 if (x == 0) {
909 return;
910 }
911 x = 1;
912 return;
913}
914- (void)f2 {
915 int m = 0;
916}
917@end
918)";
919 // Range that spans multiple methods is an invalid code range.
920 findSelectedASTNodesWithRange(
921 Source, Location: {.Line: 9, .Column: 2}, SelectionRange: FileRange{{.Line: 9, .Column: 2}, {.Line: 13, .Column: 1}},
922 Consumer: [](SourceRange SelectionRange, std::optional<SelectedASTNode> Node) {
923 EXPECT_TRUE(Node);
924 std::optional<CodeRangeASTSelection> SelectedCode =
925 CodeRangeASTSelection::create(SelectionRange, ASTSelection: std::move(*Node));
926 EXPECT_FALSE(SelectedCode);
927 },
928 Language: SelectionFinderVisitor::Lang_OBJC);
929 // Just 'z = x;':
930 findSelectedASTNodesWithRange(
931 Source, Location: {.Line: 4, .Column: 2}, SelectionRange: FileRange{{.Line: 4, .Column: 2}, {.Line: 4, .Column: 13}},
932 Consumer: [](SourceRange SelectionRange, std::optional<SelectedASTNode> Node) {
933 EXPECT_TRUE(Node);
934 std::optional<CodeRangeASTSelection> SelectedCode =
935 CodeRangeASTSelection::create(SelectionRange, ASTSelection: std::move(*Node));
936 EXPECT_TRUE(SelectedCode);
937 EXPECT_EQ(SelectedCode->size(), 1u);
938 EXPECT_TRUE(isa<DeclStmt>((*SelectedCode)[0]));
939 ArrayRef<SelectedASTNode::ReferenceType> Parents =
940 SelectedCode->getParents();
941 EXPECT_EQ(Parents.size(), 4u);
942 EXPECT_TRUE(
943 isa<TranslationUnitDecl>(Parents[0].get().Node.get<Decl>()));
944 // 'I' @implementation.
945 EXPECT_TRUE(isa<ObjCImplDecl>(Parents[1].get().Node.get<Decl>()));
946 // Function 'f' definition.
947 EXPECT_TRUE(isa<ObjCMethodDecl>(Parents[2].get().Node.get<Decl>()));
948 // Function body of function 'F'.
949 EXPECT_TRUE(isa<CompoundStmt>(Parents[3].get().Node.get<Stmt>()));
950 },
951 Language: SelectionFinderVisitor::Lang_OBJC);
952 // From '[self f: 2 with: 3]' until just before 'x = 1;':
953 findSelectedASTNodesWithRange(
954 Source, Location: {.Line: 5, .Column: 2}, SelectionRange: FileRange{{.Line: 5, .Column: 2}, {.Line: 9, .Column: 1}},
955 Consumer: [](SourceRange SelectionRange, std::optional<SelectedASTNode> Node) {
956 EXPECT_TRUE(Node);
957 std::optional<CodeRangeASTSelection> SelectedCode =
958 CodeRangeASTSelection::create(SelectionRange, ASTSelection: std::move(*Node));
959 EXPECT_TRUE(SelectedCode);
960 EXPECT_EQ(SelectedCode->size(), 2u);
961 EXPECT_TRUE(isa<ObjCMessageExpr>((*SelectedCode)[0]));
962 EXPECT_TRUE(isa<IfStmt>((*SelectedCode)[1]));
963 ArrayRef<SelectedASTNode::ReferenceType> Parents =
964 SelectedCode->getParents();
965 EXPECT_EQ(Parents.size(), 4u);
966 EXPECT_TRUE(
967 isa<TranslationUnitDecl>(Parents[0].get().Node.get<Decl>()));
968 // 'I' @implementation.
969 EXPECT_TRUE(isa<ObjCImplDecl>(Parents[1].get().Node.get<Decl>()));
970 // Function 'f' definition.
971 EXPECT_TRUE(isa<ObjCMethodDecl>(Parents[2].get().Node.get<Decl>()));
972 // Function body of function 'F'.
973 EXPECT_TRUE(isa<CompoundStmt>(Parents[3].get().Node.get<Stmt>()));
974 },
975 Language: SelectionFinderVisitor::Lang_OBJC);
976}
977
978TEST(ASTSelectionFinder, CanonicalizeObjCStringLiteral) {
979 StringRef Source = R"(
980void foo() {
981 (void)@"test";
982}
983 )";
984 // Just '"test"':
985 findSelectedASTNodesWithRange(
986 Source, Location: {.Line: 3, .Column: 10}, SelectionRange: FileRange{{.Line: 3, .Column: 10}, {.Line: 3, .Column: 16}},
987 Consumer: [](SourceRange SelectionRange, std::optional<SelectedASTNode> Node) {
988 EXPECT_TRUE(Node);
989 std::optional<CodeRangeASTSelection> SelectedCode =
990 CodeRangeASTSelection::create(SelectionRange, ASTSelection: std::move(*Node));
991 EXPECT_TRUE(SelectedCode);
992 EXPECT_EQ(SelectedCode->size(), 1u);
993 EXPECT_TRUE(isa<ObjCStringLiteral>((*SelectedCode)[0]));
994 },
995 Language: SelectionFinderVisitor::Lang_OBJC);
996 // Just 'test':
997 findSelectedASTNodesWithRange(
998 Source, Location: {.Line: 3, .Column: 11}, SelectionRange: FileRange{{.Line: 3, .Column: 11}, {.Line: 3, .Column: 15}},
999 Consumer: [](SourceRange SelectionRange, std::optional<SelectedASTNode> Node) {
1000 EXPECT_TRUE(Node);
1001 std::optional<CodeRangeASTSelection> SelectedCode =
1002 CodeRangeASTSelection::create(SelectionRange, ASTSelection: std::move(*Node));
1003 EXPECT_TRUE(SelectedCode);
1004 EXPECT_EQ(SelectedCode->size(), 1u);
1005 EXPECT_TRUE(isa<ObjCStringLiteral>((*SelectedCode)[0]));
1006 },
1007 Language: SelectionFinderVisitor::Lang_OBJC);
1008}
1009
1010TEST(ASTSelectionFinder, CanonicalizeMemberCalleeToCall) {
1011 StringRef Source = R"(
1012class AClass { public:
1013 void method();
1014 int afield;
1015 void selectWholeCallWhenJustMethodSelected(int &i) {
1016 method();
1017 }
1018};
1019void selectWholeCallWhenJustMethodSelected() {
1020 AClass a;
1021 a.method();
1022}
1023void dontSelectArgument(AClass &a) {
1024 a.selectWholeCallWhenJustMethodSelected(a.afield);
1025}
1026 )";
1027 // Just 'method' with implicit 'this':
1028 findSelectedASTNodesWithRange(
1029 Source, Location: {.Line: 6, .Column: 5}, SelectionRange: FileRange{{.Line: 6, .Column: 5}, {.Line: 6, .Column: 11}},
1030 Consumer: [](SourceRange SelectionRange, std::optional<SelectedASTNode> Node) {
1031 EXPECT_TRUE(Node);
1032 std::optional<CodeRangeASTSelection> SelectedCode =
1033 CodeRangeASTSelection::create(SelectionRange, ASTSelection: std::move(*Node));
1034 EXPECT_TRUE(SelectedCode);
1035 EXPECT_EQ(SelectedCode->size(), 1u);
1036 EXPECT_TRUE(isa<CXXMemberCallExpr>((*SelectedCode)[0]));
1037 });
1038 // Just 'method':
1039 findSelectedASTNodesWithRange(
1040 Source, Location: {.Line: 11, .Column: 5}, SelectionRange: FileRange{{.Line: 11, .Column: 5}, {.Line: 11, .Column: 11}},
1041 Consumer: [](SourceRange SelectionRange, std::optional<SelectedASTNode> Node) {
1042 EXPECT_TRUE(Node);
1043 std::optional<CodeRangeASTSelection> SelectedCode =
1044 CodeRangeASTSelection::create(SelectionRange, ASTSelection: std::move(*Node));
1045 EXPECT_TRUE(SelectedCode);
1046 EXPECT_EQ(SelectedCode->size(), 1u);
1047 EXPECT_TRUE(isa<CXXMemberCallExpr>((*SelectedCode)[0]));
1048 });
1049 // Just 'afield', which should not select the call.
1050 findSelectedASTNodesWithRange(
1051 Source, Location: {.Line: 14, .Column: 5}, SelectionRange: FileRange{{.Line: 14, .Column: 45}, {.Line: 14, .Column: 51}},
1052 Consumer: [](SourceRange SelectionRange, std::optional<SelectedASTNode> Node) {
1053 EXPECT_TRUE(Node);
1054 std::optional<CodeRangeASTSelection> SelectedCode =
1055 CodeRangeASTSelection::create(SelectionRange, ASTSelection: std::move(*Node));
1056 EXPECT_TRUE(SelectedCode);
1057 EXPECT_EQ(SelectedCode->size(), 1u);
1058 EXPECT_FALSE(isa<CXXMemberCallExpr>((*SelectedCode)[0]));
1059 });
1060}
1061
1062TEST(ASTSelectionFinder, CanonicalizeFuncCalleeToCall) {
1063 StringRef Source = R"(
1064void function();
1065
1066void test() {
1067 function();
1068}
1069 )";
1070 // Just 'function':
1071 findSelectedASTNodesWithRange(
1072 Source, Location: {.Line: 5, .Column: 3}, SelectionRange: FileRange{{.Line: 5, .Column: 3}, {.Line: 5, .Column: 11}},
1073 Consumer: [](SourceRange SelectionRange, std::optional<SelectedASTNode> Node) {
1074 EXPECT_TRUE(Node);
1075 Node->dump();
1076 std::optional<CodeRangeASTSelection> SelectedCode =
1077 CodeRangeASTSelection::create(SelectionRange, ASTSelection: std::move(*Node));
1078 EXPECT_TRUE(SelectedCode);
1079 EXPECT_EQ(SelectedCode->size(), 1u);
1080 EXPECT_TRUE(isa<CallExpr>((*SelectedCode)[0]));
1081 EXPECT_TRUE(isa<CompoundStmt>(
1082 SelectedCode->getParents()[SelectedCode->getParents().size() - 1]
1083 .get()
1084 .Node.get<Stmt>()));
1085 });
1086}
1087
1088} // end anonymous namespace
1089

source code of clang/unittests/Tooling/ASTSelectionTest.cpp