blob: 6b76bf06cb59942e9a301c014bdb17332b28aadc [file] [log] [blame]
Alex Lorenza844f392017-08-24 13:51:09 +00001//===- unittest/Tooling/ASTSelectionTest.cpp ------------------------------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#include "TestVisitor.h"
11#include "clang/Basic/SourceManager.h"
12#include "clang/Tooling/Refactoring/ASTSelection.h"
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(SM.getMainFileID(), Line, Column);
24 }
25};
26
27using FileRange = std::pair<FileLocation, FileLocation>;
28
29class SelectionFinderVisitor : public TestVisitor<SelectionFinderVisitor> {
30 FileLocation Location;
31 Optional<FileRange> SelectionRange;
Alex Lorenz39265ca2017-08-24 14:53:48 +000032 llvm::function_ref<void(Optional<SelectedASTNode>)> Consumer;
Alex Lorenza844f392017-08-24 13:51:09 +000033
34public:
Alex Lorenz39265ca2017-08-24 14:53:48 +000035 SelectionFinderVisitor(
36 FileLocation Location, Optional<FileRange> SelectionRange,
37 llvm::function_ref<void(Optional<SelectedASTNode>)> Consumer)
38 : Location(Location), SelectionRange(SelectionRange), Consumer(Consumer) {
39 }
Alex Lorenza844f392017-08-24 13:51:09 +000040
41 bool VisitTranslationUnitDecl(const TranslationUnitDecl *TU) {
42 const ASTContext &Context = TU->getASTContext();
43 const SourceManager &SM = Context.getSourceManager();
44
45 SourceRange SelRange;
46 if (SelectionRange) {
47 SelRange = SourceRange(SelectionRange->first.translate(SM),
48 SelectionRange->second.translate(SM));
49 } else {
50 SourceLocation Loc = Location.translate(SM);
51 SelRange = SourceRange(Loc, Loc);
52 }
Alex Lorenz39265ca2017-08-24 14:53:48 +000053 Consumer(findSelectedASTNodes(Context, SelRange));
Alex Lorenza844f392017-08-24 13:51:09 +000054 return false;
55 }
56};
57
Alex Lorenz39265ca2017-08-24 14:53:48 +000058void findSelectedASTNodes(
59 StringRef Source, FileLocation Location, Optional<FileRange> SelectionRange,
60 llvm::function_ref<void(Optional<SelectedASTNode>)> Consumer,
61 SelectionFinderVisitor::Language Language =
62 SelectionFinderVisitor::Lang_CXX11) {
63 SelectionFinderVisitor Visitor(Location, SelectionRange, Consumer);
Alex Lorenza844f392017-08-24 13:51:09 +000064 EXPECT_TRUE(Visitor.runOver(Source, Language));
Alex Lorenza844f392017-08-24 13:51:09 +000065}
66
67void checkNodeImpl(bool IsTypeMatched, const SelectedASTNode &Node,
68 SourceSelectionKind SelectionKind, unsigned NumChildren) {
69 ASSERT_TRUE(IsTypeMatched);
70 EXPECT_EQ(Node.Children.size(), NumChildren);
71 ASSERT_EQ(Node.SelectionKind, SelectionKind);
72}
73
74void checkDeclName(const SelectedASTNode &Node, StringRef Name) {
75 const auto *ND = Node.Node.get<NamedDecl>();
76 EXPECT_TRUE(!!ND);
77 ASSERT_EQ(ND->getName(), Name);
78}
79
80template <typename T>
81const SelectedASTNode &
82checkNode(const SelectedASTNode &StmtNode, SourceSelectionKind SelectionKind,
83 unsigned NumChildren = 0,
84 typename std::enable_if<std::is_base_of<Stmt, T>::value, T>::type
85 *StmtOverloadChecker = nullptr) {
86 checkNodeImpl(isa<T>(StmtNode.Node.get<Stmt>()), StmtNode, SelectionKind,
87 NumChildren);
88 return StmtNode;
89}
90
91template <typename T>
92const SelectedASTNode &
93checkNode(const SelectedASTNode &DeclNode, SourceSelectionKind SelectionKind,
94 unsigned NumChildren = 0, StringRef Name = "",
95 typename std::enable_if<std::is_base_of<Decl, T>::value, T>::type
96 *DeclOverloadChecker = nullptr) {
97 checkNodeImpl(isa<T>(DeclNode.Node.get<Decl>()), DeclNode, SelectionKind,
98 NumChildren);
99 if (!Name.empty())
100 checkDeclName(DeclNode, Name);
101 return DeclNode;
102}
103
104struct ForAllChildrenOf {
105 const SelectedASTNode &Node;
106
107 static void childKindVerifier(const SelectedASTNode &Node,
108 SourceSelectionKind SelectionKind) {
109 for (const SelectedASTNode &Child : Node.Children) {
110 ASSERT_EQ(Node.SelectionKind, SelectionKind);
111 childKindVerifier(Child, SelectionKind);
112 }
113 }
114
115public:
116 ForAllChildrenOf(const SelectedASTNode &Node) : Node(Node) {}
117
118 void shouldHaveSelectionKind(SourceSelectionKind Kind) {
119 childKindVerifier(Node, Kind);
120 }
121};
122
123ForAllChildrenOf allChildrenOf(const SelectedASTNode &Node) {
124 return ForAllChildrenOf(Node);
125}
126
127TEST(ASTSelectionFinder, CursorNoSelection) {
Alex Lorenz39265ca2017-08-24 14:53:48 +0000128 findSelectedASTNodes(
129 " void f() { }", {1, 1}, None,
130 [](Optional<SelectedASTNode> Node) { EXPECT_FALSE(Node); });
Alex Lorenza844f392017-08-24 13:51:09 +0000131}
132
133TEST(ASTSelectionFinder, CursorAtStartOfFunction) {
Alex Lorenz39265ca2017-08-24 14:53:48 +0000134 findSelectedASTNodes(
135 "void f() { }", {1, 1}, None, [](Optional<SelectedASTNode> Node) {
136 EXPECT_TRUE(Node);
137 checkNode<TranslationUnitDecl>(*Node, SourceSelectionKind::None,
138 /*NumChildren=*/1);
139 checkNode<FunctionDecl>(Node->Children[0],
140 SourceSelectionKind::ContainsSelection,
141 /*NumChildren=*/0, /*Name=*/"f");
Alex Lorenza844f392017-08-24 13:51:09 +0000142
Alex Lorenz39265ca2017-08-24 14:53:48 +0000143 // Check that the dumping works.
144 std::string DumpValue;
145 llvm::raw_string_ostream OS(DumpValue);
146 Node->Children[0].dump(OS);
147 ASSERT_EQ(OS.str(), "FunctionDecl \"f\" contains-selection\n");
148 });
Alex Lorenza844f392017-08-24 13:51:09 +0000149}
150
151TEST(ASTSelectionFinder, RangeNoSelection) {
Alex Lorenz39265ca2017-08-24 14:53:48 +0000152 findSelectedASTNodes(
153 " void f() { }", {1, 1}, FileRange{{1, 1}, {1, 1}},
154 [](Optional<SelectedASTNode> Node) { EXPECT_FALSE(Node); });
155 findSelectedASTNodes(
156 " void f() { }", {1, 1}, FileRange{{1, 1}, {1, 2}},
157 [](Optional<SelectedASTNode> Node) { EXPECT_FALSE(Node); });
Alex Lorenza844f392017-08-24 13:51:09 +0000158}
159
160TEST(ASTSelectionFinder, EmptyRangeFallbackToCursor) {
Alex Lorenz39265ca2017-08-24 14:53:48 +0000161 findSelectedASTNodes("void f() { }", {1, 1}, FileRange{{1, 1}, {1, 1}},
162 [](Optional<SelectedASTNode> Node) {
163 EXPECT_TRUE(Node);
164 checkNode<FunctionDecl>(
165 Node->Children[0],
166 SourceSelectionKind::ContainsSelection,
167 /*NumChildren=*/0, /*Name=*/"f");
168 });
Alex Lorenza844f392017-08-24 13:51:09 +0000169}
170
171TEST(ASTSelectionFinder, WholeFunctionSelection) {
172 StringRef Source = "int f(int x) { return x;\n}\nvoid f2() { }";
173 // From 'int' until just after '}':
Alex Lorenz39265ca2017-08-24 14:53:48 +0000174
175 findSelectedASTNodes(
176 Source, {1, 1}, FileRange{{1, 1}, {2, 2}},
177 [](Optional<SelectedASTNode> Node) {
178 EXPECT_TRUE(Node);
179 EXPECT_EQ(Node->Children.size(), 1u);
180 const auto &Fn = checkNode<FunctionDecl>(
181 Node->Children[0], SourceSelectionKind::ContainsSelection,
182 /*NumChildren=*/2, /*Name=*/"f");
183 checkNode<ParmVarDecl>(Fn.Children[0],
184 SourceSelectionKind::InsideSelection);
185 const auto &Body = checkNode<CompoundStmt>(
186 Fn.Children[1], SourceSelectionKind::InsideSelection,
187 /*NumChildren=*/1);
188 const auto &Return = checkNode<ReturnStmt>(
189 Body.Children[0], SourceSelectionKind::InsideSelection,
190 /*NumChildren=*/1);
191 checkNode<ImplicitCastExpr>(Return.Children[0],
192 SourceSelectionKind::InsideSelection,
193 /*NumChildren=*/1);
194 checkNode<DeclRefExpr>(Return.Children[0].Children[0],
195 SourceSelectionKind::InsideSelection);
196 });
197
Alex Lorenza844f392017-08-24 13:51:09 +0000198 // From 'int' until just before '}':
Alex Lorenz39265ca2017-08-24 14:53:48 +0000199 findSelectedASTNodes(
200 Source, {2, 1}, FileRange{{1, 1}, {2, 1}},
201 [](Optional<SelectedASTNode> Node) {
202 EXPECT_TRUE(Node);
203 EXPECT_EQ(Node->Children.size(), 1u);
204 const auto &Fn = checkNode<FunctionDecl>(
205 Node->Children[0], SourceSelectionKind::ContainsSelection,
206 /*NumChildren=*/2, /*Name=*/"f");
207 const auto &Body = checkNode<CompoundStmt>(
208 Fn.Children[1], SourceSelectionKind::ContainsSelectionEnd,
209 /*NumChildren=*/1);
210 checkNode<ReturnStmt>(Body.Children[0],
211 SourceSelectionKind::InsideSelection,
212 /*NumChildren=*/1);
213 });
Alex Lorenza844f392017-08-24 13:51:09 +0000214 // From '{' until just after '}':
Alex Lorenz39265ca2017-08-24 14:53:48 +0000215 findSelectedASTNodes(
216 Source, {1, 14}, FileRange{{1, 14}, {2, 2}},
217 [](Optional<SelectedASTNode> Node) {
218 EXPECT_TRUE(Node);
219 EXPECT_EQ(Node->Children.size(), 1u);
220 const auto &Fn = checkNode<FunctionDecl>(
221 Node->Children[0], SourceSelectionKind::ContainsSelection,
222 /*NumChildren=*/1, /*Name=*/"f");
223 const auto &Body = checkNode<CompoundStmt>(
224 Fn.Children[0], SourceSelectionKind::ContainsSelection,
225 /*NumChildren=*/1);
226 checkNode<ReturnStmt>(Body.Children[0],
227 SourceSelectionKind::InsideSelection,
228 /*NumChildren=*/1);
229 });
Alex Lorenza844f392017-08-24 13:51:09 +0000230 // From 'x' until just after '}':
Alex Lorenz39265ca2017-08-24 14:53:48 +0000231 findSelectedASTNodes(
232 Source, {2, 2}, FileRange{{1, 11}, {2, 2}},
233 [](Optional<SelectedASTNode> Node) {
234 EXPECT_TRUE(Node);
235 EXPECT_EQ(Node->Children.size(), 1u);
236 const auto &Fn = checkNode<FunctionDecl>(
237 Node->Children[0], SourceSelectionKind::ContainsSelection,
238 /*NumChildren=*/2, /*Name=*/"f");
239 checkNode<ParmVarDecl>(Fn.Children[0],
240 SourceSelectionKind::ContainsSelectionStart);
241 const auto &Body = checkNode<CompoundStmt>(
242 Fn.Children[1], SourceSelectionKind::InsideSelection,
243 /*NumChildren=*/1);
244 checkNode<ReturnStmt>(Body.Children[0],
245 SourceSelectionKind::InsideSelection,
246 /*NumChildren=*/1);
247 });
Alex Lorenza844f392017-08-24 13:51:09 +0000248}
249
250TEST(ASTSelectionFinder, MultipleFunctionSelection) {
251 StringRef Source = R"(void f0() {
252}
253void f1() { }
254void f2() { }
255void f3() { }
256)";
257 auto SelectedF1F2 = [](Optional<SelectedASTNode> Node) {
258 EXPECT_TRUE(Node);
259 EXPECT_EQ(Node->Children.size(), 2u);
260 checkNode<FunctionDecl>(Node->Children[0],
261 SourceSelectionKind::InsideSelection,
262 /*NumChildren=*/1, /*Name=*/"f1");
263 checkNode<FunctionDecl>(Node->Children[1],
264 SourceSelectionKind::InsideSelection,
265 /*NumChildren=*/1, /*Name=*/"f2");
266 };
267 // Just after '}' of f0 and just before 'void' of f3:
Alex Lorenz39265ca2017-08-24 14:53:48 +0000268 findSelectedASTNodes(Source, {2, 2}, FileRange{{2, 2}, {5, 1}}, SelectedF1F2);
Alex Lorenza844f392017-08-24 13:51:09 +0000269 // Just before 'void' of f1 and just after '}' of f2:
Alex Lorenz39265ca2017-08-24 14:53:48 +0000270 findSelectedASTNodes(Source, {3, 1}, FileRange{{3, 1}, {4, 14}},
271 SelectedF1F2);
Alex Lorenza844f392017-08-24 13:51:09 +0000272}
273
274TEST(ASTSelectionFinder, MultipleStatementSelection) {
275 StringRef Source = R"(void f(int x, int y) {
276 int z = x;
277 f(2, 3);
278 if (x == 0) {
279 return;
280 }
281 x = 1;
282 return;
283})";
284 // From 'f(2,3)' until just before 'x = 1;':
Alex Lorenz39265ca2017-08-24 14:53:48 +0000285 findSelectedASTNodes(
286 Source, {3, 2}, FileRange{{3, 2}, {7, 1}},
287 [](Optional<SelectedASTNode> Node) {
288 EXPECT_TRUE(Node);
289 EXPECT_EQ(Node->Children.size(), 1u);
290 const auto &Fn = checkNode<FunctionDecl>(
291 Node->Children[0], SourceSelectionKind::ContainsSelection,
292 /*NumChildren=*/1, /*Name=*/"f");
293 const auto &Body = checkNode<CompoundStmt>(
294 Fn.Children[0], SourceSelectionKind::ContainsSelection,
295 /*NumChildren=*/2);
296 allChildrenOf(checkNode<CallExpr>(Body.Children[0],
297 SourceSelectionKind::InsideSelection,
298 /*NumChildren=*/3))
299 .shouldHaveSelectionKind(SourceSelectionKind::InsideSelection);
300 allChildrenOf(checkNode<IfStmt>(Body.Children[1],
301 SourceSelectionKind::InsideSelection,
302 /*NumChildren=*/2))
303 .shouldHaveSelectionKind(SourceSelectionKind::InsideSelection);
304 });
Alex Lorenza844f392017-08-24 13:51:09 +0000305 // From 'f(2,3)' until just before ';' in 'x = 1;':
Alex Lorenz39265ca2017-08-24 14:53:48 +0000306 findSelectedASTNodes(
307 Source, {3, 2}, FileRange{{3, 2}, {7, 8}},
308 [](Optional<SelectedASTNode> Node) {
309 EXPECT_TRUE(Node);
310 EXPECT_EQ(Node->Children.size(), 1u);
311 const auto &Fn = checkNode<FunctionDecl>(
312 Node->Children[0], SourceSelectionKind::ContainsSelection,
313 /*NumChildren=*/1, /*Name=*/"f");
314 const auto &Body = checkNode<CompoundStmt>(
315 Fn.Children[0], SourceSelectionKind::ContainsSelection,
316 /*NumChildren=*/3);
317 checkNode<CallExpr>(Body.Children[0],
318 SourceSelectionKind::InsideSelection,
319 /*NumChildren=*/3);
320 checkNode<IfStmt>(Body.Children[1],
321 SourceSelectionKind::InsideSelection,
322 /*NumChildren=*/2);
323 checkNode<BinaryOperator>(Body.Children[2],
324 SourceSelectionKind::InsideSelection,
325 /*NumChildren=*/2);
326 });
Alex Lorenza844f392017-08-24 13:51:09 +0000327 // From the middle of 'int z = 3' until the middle of 'x = 1;':
Alex Lorenz39265ca2017-08-24 14:53:48 +0000328 findSelectedASTNodes(
329 Source, {2, 10}, FileRange{{2, 10}, {7, 5}},
330 [](Optional<SelectedASTNode> Node) {
331 EXPECT_TRUE(Node);
332 EXPECT_EQ(Node->Children.size(), 1u);
333 const auto &Fn = checkNode<FunctionDecl>(
334 Node->Children[0], SourceSelectionKind::ContainsSelection,
335 /*NumChildren=*/1, /*Name=*/"f");
336 const auto &Body = checkNode<CompoundStmt>(
337 Fn.Children[0], SourceSelectionKind::ContainsSelection,
338 /*NumChildren=*/4);
339 checkNode<DeclStmt>(Body.Children[0],
340 SourceSelectionKind::ContainsSelectionStart,
341 /*NumChildren=*/1);
342 checkNode<CallExpr>(Body.Children[1],
343 SourceSelectionKind::InsideSelection,
344 /*NumChildren=*/3);
345 checkNode<IfStmt>(Body.Children[2],
346 SourceSelectionKind::InsideSelection,
347 /*NumChildren=*/2);
348 checkNode<BinaryOperator>(Body.Children[3],
349 SourceSelectionKind::ContainsSelectionEnd,
350 /*NumChildren=*/1);
351 });
Alex Lorenza844f392017-08-24 13:51:09 +0000352}
353
354TEST(ASTSelectionFinder, SelectionInFunctionInObjCImplementation) {
355 StringRef Source = R"(
356@interface I
357@end
358@implementation I
359
360int notSelected() { }
361
362int selected(int x) {
363 return x;
364}
365
366@end
367@implementation I(Cat)
368
369void catF() { }
370
371@end
372
373void outerFunction() { }
374)";
375 // Just the 'x' expression in 'selected':
Alex Lorenz39265ca2017-08-24 14:53:48 +0000376 findSelectedASTNodes(
377 Source, {9, 10}, FileRange{{9, 10}, {9, 11}},
378 [](Optional<SelectedASTNode> Node) {
379 EXPECT_TRUE(Node);
380 EXPECT_EQ(Node->Children.size(), 1u);
381 const auto &Impl = checkNode<ObjCImplementationDecl>(
382 Node->Children[0], SourceSelectionKind::ContainsSelection,
383 /*NumChildren=*/1, /*Name=*/"I");
384 const auto &Fn = checkNode<FunctionDecl>(
385 Impl.Children[0], SourceSelectionKind::ContainsSelection,
386 /*NumChildren=*/1, /*Name=*/"selected");
387 allChildrenOf(Fn).shouldHaveSelectionKind(
388 SourceSelectionKind::ContainsSelection);
389 },
390 SelectionFinderVisitor::Lang_OBJC);
Alex Lorenza844f392017-08-24 13:51:09 +0000391 // The entire 'catF':
Alex Lorenz39265ca2017-08-24 14:53:48 +0000392 findSelectedASTNodes(
393 Source, {15, 1}, FileRange{{15, 1}, {15, 16}},
394 [](Optional<SelectedASTNode> Node) {
395 EXPECT_TRUE(Node);
396 EXPECT_EQ(Node->Children.size(), 1u);
397 const auto &Impl = checkNode<ObjCCategoryImplDecl>(
398 Node->Children[0], SourceSelectionKind::ContainsSelection,
399 /*NumChildren=*/1, /*Name=*/"Cat");
400 const auto &Fn = checkNode<FunctionDecl>(
401 Impl.Children[0], SourceSelectionKind::ContainsSelection,
402 /*NumChildren=*/1, /*Name=*/"catF");
403 allChildrenOf(Fn).shouldHaveSelectionKind(
404 SourceSelectionKind::ContainsSelection);
405 },
406 SelectionFinderVisitor::Lang_OBJC);
Alex Lorenza844f392017-08-24 13:51:09 +0000407 // From the line before 'selected' to the line after 'catF':
Alex Lorenz39265ca2017-08-24 14:53:48 +0000408 findSelectedASTNodes(
409 Source, {16, 1}, FileRange{{7, 1}, {16, 1}},
410 [](Optional<SelectedASTNode> Node) {
411 EXPECT_TRUE(Node);
412 EXPECT_EQ(Node->Children.size(), 2u);
413 const auto &Impl = checkNode<ObjCImplementationDecl>(
414 Node->Children[0], SourceSelectionKind::ContainsSelectionStart,
415 /*NumChildren=*/1, /*Name=*/"I");
416 const auto &Selected = checkNode<FunctionDecl>(
417 Impl.Children[0], SourceSelectionKind::InsideSelection,
418 /*NumChildren=*/2, /*Name=*/"selected");
419 allChildrenOf(Selected).shouldHaveSelectionKind(
420 SourceSelectionKind::InsideSelection);
421 const auto &Cat = checkNode<ObjCCategoryImplDecl>(
422 Node->Children[1], SourceSelectionKind::ContainsSelectionEnd,
423 /*NumChildren=*/1, /*Name=*/"Cat");
424 const auto &CatF = checkNode<FunctionDecl>(
425 Cat.Children[0], SourceSelectionKind::InsideSelection,
426 /*NumChildren=*/1, /*Name=*/"catF");
427 allChildrenOf(CatF).shouldHaveSelectionKind(
428 SourceSelectionKind::InsideSelection);
429 },
430 SelectionFinderVisitor::Lang_OBJC);
Alex Lorenza844f392017-08-24 13:51:09 +0000431 // Just the 'outer' function:
Alex Lorenz39265ca2017-08-24 14:53:48 +0000432 findSelectedASTNodes(Source, {19, 1}, FileRange{{19, 1}, {19, 25}},
433 [](Optional<SelectedASTNode> Node) {
434 EXPECT_TRUE(Node);
435 EXPECT_EQ(Node->Children.size(), 1u);
436 checkNode<FunctionDecl>(
437 Node->Children[0],
438 SourceSelectionKind::ContainsSelection,
439 /*NumChildren=*/1, /*Name=*/"outerFunction");
440 },
441 SelectionFinderVisitor::Lang_OBJC);
Alex Lorenza844f392017-08-24 13:51:09 +0000442}
443
444TEST(ASTSelectionFinder, FunctionInObjCImplementationCarefulWithEarlyExit) {
445 StringRef Source = R"(
446@interface I
447@end
448@implementation I
449
450void selected() {
451}
452
453- (void) method { }
454
455@end
456)";
457 // Just 'selected'
Alex Lorenz39265ca2017-08-24 14:53:48 +0000458 findSelectedASTNodes(
459 Source, {6, 1}, FileRange{{6, 1}, {7, 2}},
460 [](Optional<SelectedASTNode> Node) {
461 EXPECT_TRUE(Node);
462 EXPECT_EQ(Node->Children.size(), 1u);
463 const auto &Impl = checkNode<ObjCImplementationDecl>(
464 Node->Children[0], SourceSelectionKind::ContainsSelection,
465 /*NumChildren=*/1, /*Name=*/"I");
466 checkNode<FunctionDecl>(Impl.Children[0],
467 SourceSelectionKind::ContainsSelection,
468 /*NumChildren=*/1, /*Name=*/"selected");
469 },
470 SelectionFinderVisitor::Lang_OBJC);
Alex Lorenza844f392017-08-24 13:51:09 +0000471}
472
473TEST(ASTSelectionFinder, AvoidImplicitDeclarations) {
474 StringRef Source = R"(
475struct Copy {
476 int x;
477};
478void foo() {
479 Copy x;
480 Copy y = x;
481}
482)";
483 // The entire struct 'Copy':
Alex Lorenz39265ca2017-08-24 14:53:48 +0000484 findSelectedASTNodes(
485 Source, {2, 1}, FileRange{{2, 1}, {4, 3}},
486 [](Optional<SelectedASTNode> Node) {
487 EXPECT_TRUE(Node);
488 EXPECT_EQ(Node->Children.size(), 1u);
489 const auto &Record = checkNode<CXXRecordDecl>(
490 Node->Children[0], SourceSelectionKind::InsideSelection,
491 /*NumChildren=*/1, /*Name=*/"Copy");
492 checkNode<FieldDecl>(Record.Children[0],
493 SourceSelectionKind::InsideSelection);
494 });
Alex Lorenza844f392017-08-24 13:51:09 +0000495}
496
Alex Lorenz23654b52017-08-30 13:24:37 +0000497TEST(ASTSelectionFinder, CorrectEndForObjectiveCImplementation) {
498 StringRef Source = R"(
499@interface I
500@end
501@implementation I
502@ end
503)";
504 // Just after '@ end'
505 findSelectedASTNodes(Source, {5, 6}, None,
506 [](Optional<SelectedASTNode> Node) {
507 EXPECT_TRUE(Node);
508 EXPECT_EQ(Node->Children.size(), 1u);
509 checkNode<ObjCImplementationDecl>(
510 Node->Children[0],
511 SourceSelectionKind::ContainsSelection);
512 },
513 SelectionFinderVisitor::Lang_OBJC);
514}
515
Alex Lorenz410ef382017-08-30 15:28:01 +0000516const SelectedASTNode &checkFnBody(const Optional<SelectedASTNode> &Node,
517 StringRef Name) {
518 EXPECT_TRUE(Node);
519 EXPECT_EQ(Node->Children.size(), 1u);
520 const auto &Fn = checkNode<FunctionDecl>(
521 Node->Children[0], SourceSelectionKind::ContainsSelection,
522 /*NumChildren=*/1, Name);
523 return checkNode<CompoundStmt>(Fn.Children[0],
524 SourceSelectionKind::ContainsSelection,
525 /*NumChildren=*/1);
526}
527
528TEST(ASTSelectionFinder, SelectObjectiveCPseudoObjectExprs) {
529 StringRef Source = R"(
530@interface I
531@property(readwrite) int prop;
532@end
533void selectProp(I *i) {
534(void)i.prop;
535i.prop = 21;
536}
537
538typedef unsigned int size_t;
539@interface NSMutableArray
540- (id)objectAtIndexedSubscript:(size_t)index;
541- (void)setObject:(id)object atIndexedSubscript:(size_t)index;
542@end
543
544void selectSubscript(NSMutableArray *array, I *i) {
545 (void)array[10];
546 array[i.prop] = i;
547}
548)";
549 // Just 'i.prop'.
550 findSelectedASTNodes(
551 Source, {6, 7}, FileRange{{6, 7}, {6, 13}},
552 [](Optional<SelectedASTNode> Node) {
553 const auto &CS = checkFnBody(Node, /*Name=*/"selectProp");
554 const auto &CCast = checkNode<CStyleCastExpr>(
555 CS.Children[0], SourceSelectionKind::ContainsSelection,
556 /*NumChildren=*/1);
557 const auto &POE = checkNode<PseudoObjectExpr>(
558 CCast.Children[0], SourceSelectionKind::ContainsSelection,
559 /*NumChildren=*/1);
560 const auto &PRE = checkNode<ObjCPropertyRefExpr>(
561 POE.Children[0], SourceSelectionKind::ContainsSelection,
562 /*NumChildren=*/1);
563 const auto &Cast = checkNode<ImplicitCastExpr>(
564 PRE.Children[0], SourceSelectionKind::InsideSelection,
565 /*NumChildren=*/1);
566 checkNode<DeclRefExpr>(Cast.Children[0],
567 SourceSelectionKind::InsideSelection);
568 },
569 SelectionFinderVisitor::Lang_OBJC);
570 // Just 'i.prop = 21'
571 findSelectedASTNodes(
572 Source, {7, 1}, FileRange{{7, 1}, {7, 12}},
573 [](Optional<SelectedASTNode> Node) {
574 const auto &CS = checkFnBody(Node, /*Name=*/"selectProp");
575 const auto &POE = checkNode<PseudoObjectExpr>(
576 CS.Children[0], SourceSelectionKind::ContainsSelection,
577 /*NumChildren=*/1);
578 const auto &BinOp = checkNode<BinaryOperator>(
579 POE.Children[0], SourceSelectionKind::ContainsSelection,
580 /*NumChildren=*/2);
581 const auto &PRE = checkNode<ObjCPropertyRefExpr>(
582 BinOp.Children[0], SourceSelectionKind::InsideSelection,
583 /*NumChildren=*/1);
584 const auto &Cast = checkNode<ImplicitCastExpr>(
585 PRE.Children[0], SourceSelectionKind::InsideSelection,
586 /*NumChildren=*/1);
587 checkNode<DeclRefExpr>(Cast.Children[0],
588 SourceSelectionKind::InsideSelection);
589 checkNode<IntegerLiteral>(BinOp.Children[1],
590 SourceSelectionKind::InsideSelection);
591 },
592 SelectionFinderVisitor::Lang_OBJC);
593 // Just 'array[10]'
594 findSelectedASTNodes(
595 Source, {17, 9}, FileRange{{17, 9}, {17, 18}},
596 [](Optional<SelectedASTNode> Node) {
597 const auto &CS = checkFnBody(Node, /*Name=*/"selectSubscript");
598 const auto &CCast = checkNode<CStyleCastExpr>(
599 CS.Children[0], SourceSelectionKind::ContainsSelection,
600 /*NumChildren=*/1);
601 const auto &POE = checkNode<PseudoObjectExpr>(
602 CCast.Children[0], SourceSelectionKind::ContainsSelection,
603 /*NumChildren=*/1);
604 const auto &SRE = checkNode<ObjCSubscriptRefExpr>(
605 POE.Children[0], SourceSelectionKind::ContainsSelection,
606 /*NumChildren=*/2);
607 const auto &Cast = checkNode<ImplicitCastExpr>(
608 SRE.Children[0], SourceSelectionKind::InsideSelection,
609 /*NumChildren=*/1);
610 checkNode<DeclRefExpr>(Cast.Children[0],
611 SourceSelectionKind::InsideSelection);
612 checkNode<IntegerLiteral>(SRE.Children[1],
613 SourceSelectionKind::InsideSelection);
614 },
615 SelectionFinderVisitor::Lang_OBJC);
616 // Just 'array[i.prop] = array'
617 findSelectedASTNodes(
618 Source, {18, 3}, FileRange{{18, 3}, {18, 20}},
619 [](Optional<SelectedASTNode> Node) {
620 const auto &CS = checkFnBody(Node, /*Name=*/"selectSubscript");
621 const auto &POE = checkNode<PseudoObjectExpr>(
622 CS.Children[0], SourceSelectionKind::ContainsSelection,
623 /*NumChildren=*/1);
624 const auto &BinOp = checkNode<BinaryOperator>(
625 POE.Children[0], SourceSelectionKind::ContainsSelection,
626 /*NumChildren=*/2);
627 const auto &SRE = checkNode<ObjCSubscriptRefExpr>(
628 BinOp.Children[0], SourceSelectionKind::InsideSelection,
629 /*NumChildren=*/2);
630 const auto &Cast = checkNode<ImplicitCastExpr>(
631 SRE.Children[0], SourceSelectionKind::InsideSelection,
632 /*NumChildren=*/1);
633 checkNode<DeclRefExpr>(Cast.Children[0],
634 SourceSelectionKind::InsideSelection);
635 const auto &POE2 = checkNode<PseudoObjectExpr>(
636 SRE.Children[1], SourceSelectionKind::InsideSelection,
637 /*NumChildren=*/1);
638 const auto &PRE = checkNode<ObjCPropertyRefExpr>(
639 POE2.Children[0], SourceSelectionKind::InsideSelection,
640 /*NumChildren=*/1);
641 const auto &Cast2 = checkNode<ImplicitCastExpr>(
642 PRE.Children[0], SourceSelectionKind::InsideSelection,
643 /*NumChildren=*/1);
644 checkNode<DeclRefExpr>(Cast2.Children[0],
645 SourceSelectionKind::InsideSelection);
646 checkNode<DeclRefExpr>(BinOp.Children[1],
647 SourceSelectionKind::InsideSelection);
648 },
649 SelectionFinderVisitor::Lang_OBJC);
650}
651
Alex Lorenza844f392017-08-24 13:51:09 +0000652} // end anonymous namespace