#include "TestVisitor.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Tooling/Refactoring/ASTSelection.h"
using namespace clang;
using namespace tooling;
namespace {
struct FileLocation {
unsigned Line, Column;
SourceLocation translate(const SourceManager &SM) {
return SM.translateLineCol(SM.getMainFileID(), Line, Column);
}
};
using FileRange = std::pair<FileLocation, FileLocation>;
class SelectionFinderVisitor : public TestVisitor<SelectionFinderVisitor> {
FileLocation Location;
Optional<FileRange> SelectionRange;
llvm::function_ref<void(SourceRange SelectionRange,
Optional<SelectedASTNode>)>
Consumer;
public:
SelectionFinderVisitor(FileLocation Location,
Optional<FileRange> SelectionRange,
llvm::function_ref<void(SourceRange SelectionRange,
Optional<SelectedASTNode>)>
Consumer)
: Location(Location), SelectionRange(SelectionRange), Consumer(Consumer) {
}
bool VisitTranslationUnitDecl(const TranslationUnitDecl *TU) {
const ASTContext &Context = TU->getASTContext();
const SourceManager &SM = Context.getSourceManager();
SourceRange SelRange;
if (SelectionRange) {
SelRange = SourceRange(SelectionRange->first.translate(SM),
SelectionRange->second.translate(SM));
} else {
SourceLocation Loc = Location.translate(SM);
SelRange = SourceRange(Loc, Loc);
}
Consumer(SelRange, findSelectedASTNodes(Context, SelRange));
return false;
}
};
void findSelectedASTNodesWithRange(
StringRef Source, FileLocation Location, Optional<FileRange> SelectionRange,
llvm::function_ref<void(SourceRange SelectionRange,
Optional<SelectedASTNode>)>
Consumer,
SelectionFinderVisitor::Language Language =
SelectionFinderVisitor::Lang_CXX11) {
SelectionFinderVisitor Visitor(Location, SelectionRange, Consumer);
EXPECT_TRUE(Visitor.runOver(Source, Language));
}
void findSelectedASTNodes(
StringRef Source, FileLocation Location, Optional<FileRange> SelectionRange,
llvm::function_ref<void(Optional<SelectedASTNode>)> Consumer,
SelectionFinderVisitor::Language Language =
SelectionFinderVisitor::Lang_CXX11) {
findSelectedASTNodesWithRange(
Source, Location, SelectionRange,
[&](SourceRange, Optional<SelectedASTNode> Selection) {
Consumer(std::move(Selection));
},
Language);
}
void checkNodeImpl(bool IsTypeMatched, const SelectedASTNode &Node,
SourceSelectionKind SelectionKind, unsigned NumChildren) {
ASSERT_TRUE(IsTypeMatched);
EXPECT_EQ(Node.Children.size(), NumChildren);
ASSERT_EQ(Node.SelectionKind, SelectionKind);
}
void checkDeclName(const SelectedASTNode &Node, StringRef Name) {
const auto *ND = Node.Node.get<NamedDecl>();
EXPECT_TRUE(!!ND);
ASSERT_EQ(ND->getName(), Name);
}
template <typename T>
const SelectedASTNode &checkNode(
const SelectedASTNode &StmtNode, SourceSelectionKind SelectionKind,
unsigned NumChildren = 0,
std::enable_if_t<std::is_base_of<Stmt, T>::value, T> *StmtOverloadChecker =
nullptr) {
checkNodeImpl(isa<T>(StmtNode.Node.get<Stmt>()), StmtNode, SelectionKind,
NumChildren);
return StmtNode;
}
template <typename T>
const SelectedASTNode &checkNode(
const SelectedASTNode &DeclNode, SourceSelectionKind SelectionKind,
unsigned NumChildren = 0, StringRef Name = "",
std::enable_if_t<std::is_base_of<Decl, T>::value, T> *DeclOverloadChecker =
nullptr) {
checkNodeImpl(isa<T>(DeclNode.Node.get<Decl>()), DeclNode, SelectionKind,
NumChildren);
if (!Name.empty())
checkDeclName(DeclNode, Name);
return DeclNode;
}
struct ForAllChildrenOf {
const SelectedASTNode &Node;
static void childKindVerifier(const SelectedASTNode &Node,
SourceSelectionKind SelectionKind) {
for (const SelectedASTNode &Child : Node.Children) {
ASSERT_EQ(Node.SelectionKind, SelectionKind);
childKindVerifier(Child, SelectionKind);
}
}
public:
ForAllChildrenOf(const SelectedASTNode &Node) : Node(Node) {}
void shouldHaveSelectionKind(SourceSelectionKind Kind) {
childKindVerifier(Node, Kind);
}
};
ForAllChildrenOf allChildrenOf(const SelectedASTNode &Node) {
return ForAllChildrenOf(Node);
}
TEST(ASTSelectionFinder, CursorNoSelection) {
findSelectedASTNodes(
" void f() { }", {1, 1}, None,
[](Optional<SelectedASTNode> Node) { EXPECT_FALSE(Node); });
}
TEST(ASTSelectionFinder, CursorAtStartOfFunction) {
findSelectedASTNodes(
"void f() { }", {1, 1}, None, [](Optional<SelectedASTNode> Node) {
EXPECT_TRUE(Node);
checkNode<TranslationUnitDecl>(*Node, SourceSelectionKind::None,
1);
checkNode<FunctionDecl>(Node->Children[0],
SourceSelectionKind::ContainsSelection,
0, "f");
std::string DumpValue;
llvm::raw_string_ostream OS(DumpValue);
Node->Children[0].dump(OS);
ASSERT_EQ(OS.str(), "FunctionDecl \"f\" contains-selection\n");
});
}
TEST(ASTSelectionFinder, RangeNoSelection) {
findSelectedASTNodes(
" void f() { }", {1, 1}, FileRange{{1, 1}, {1, 1}},
[](Optional<SelectedASTNode> Node) { EXPECT_FALSE(Node); });
findSelectedASTNodes(
" void f() { }", {1, 1}, FileRange{{1, 1}, {1, 2}},
[](Optional<SelectedASTNode> Node) { EXPECT_FALSE(Node); });
}
TEST(ASTSelectionFinder, EmptyRangeFallbackToCursor) {
findSelectedASTNodes("void f() { }", {1, 1}, FileRange{{1, 1}, {1, 1}},
[](Optional<SelectedASTNode> Node) {
EXPECT_TRUE(Node);
checkNode<FunctionDecl>(
Node->Children[0],
SourceSelectionKind::ContainsSelection,
0, "f");
});
}
TEST(ASTSelectionFinder, WholeFunctionSelection) {
StringRef Source = "int f(int x) { return x;\n}\nvoid f2() { }";
findSelectedASTNodes(
Source, {1, 1}, FileRange{{1, 1}, {2, 2}},
[](Optional<SelectedASTNode> Node) {
EXPECT_TRUE(Node);
EXPECT_EQ(Node->Children.size(), 1u);
const auto &Fn = checkNode<FunctionDecl>(
Node->Children[0], SourceSelectionKind::ContainsSelection,
2, "f");
checkNode<ParmVarDecl>(Fn.Children[0],
SourceSelectionKind::InsideSelection);
const auto &Body = checkNode<CompoundStmt>(
Fn.Children[1], SourceSelectionKind::InsideSelection,
1);
const auto &Return = checkNode<ReturnStmt>(
Body.Children[0], SourceSelectionKind::InsideSelection,
1);
checkNode<ImplicitCastExpr>(Return.Children[0],
SourceSelectionKind::InsideSelection,
1);
checkNode<DeclRefExpr>(Return.Children[0].Children[0],
SourceSelectionKind::InsideSelection);
});
findSelectedASTNodes(
Source, {2, 1}, FileRange{{1, 1}, {2, 1}},
[](Optional<SelectedASTNode> Node) {
EXPECT_TRUE(Node);
EXPECT_EQ(Node->Children.size(), 1u);
const auto &Fn = checkNode<FunctionDecl>(
Node->Children[0], SourceSelectionKind::ContainsSelection,
2, "f");
const auto &Body = checkNode<CompoundStmt>(
Fn.Children[1], SourceSelectionKind::ContainsSelectionEnd,
1);
checkNode<ReturnStmt>(Body.Children[0],
SourceSelectionKind::InsideSelection,
1);
});
findSelectedASTNodes(
Source, {1, 14}, FileRange{{1, 14}, {2, 2}},
[](Optional<SelectedASTNode> Node) {
EXPECT_TRUE(Node);
EXPECT_EQ(Node->Children.size(), 1u);
const auto &Fn = checkNode<FunctionDecl>(
Node->Children[0], SourceSelectionKind::ContainsSelection,
1, "f");
const auto &Body = checkNode<CompoundStmt>(
Fn.Children[0], SourceSelectionKind::ContainsSelection,
1);
checkNode<ReturnStmt>(Body.Children[0],
SourceSelectionKind::InsideSelection,
1);
});
findSelectedASTNodes(
Source, {2, 2}, FileRange{{1, 11}, {2, 2}},
[](Optional<SelectedASTNode> Node) {
EXPECT_TRUE(Node);
EXPECT_EQ(Node->Children.size(), 1u);
const auto &Fn = checkNode<FunctionDecl>(
Node->Children[0], SourceSelectionKind::ContainsSelection,
2, "f");
checkNode<ParmVarDecl>(Fn.Children[0],
SourceSelectionKind::ContainsSelectionStart);
const auto &Body = checkNode<CompoundStmt>(
Fn.Children[1], SourceSelectionKind::InsideSelection,
1);
checkNode<ReturnStmt>(Body.Children[0],
SourceSelectionKind::InsideSelection,
1);
});
}
TEST(ASTSelectionFinder, MultipleFunctionSelection) {
StringRef Source = R"(void f0() {
}
void f1() { }
void f2() { }
void f3() { }
)";
auto SelectedF1F2 = [](Optional<SelectedASTNode> Node) {
EXPECT_TRUE(Node);
EXPECT_EQ(Node->Children.size(), 2u);
checkNode<FunctionDecl>(Node->Children[0],
SourceSelectionKind::InsideSelection,
1, "f1");
checkNode<FunctionDecl>(Node->Children[1],
SourceSelectionKind::InsideSelection,
1, "f2");
};
findSelectedASTNodes(Source, {2, 2}, FileRange{{2, 2}, {5, 1}}, SelectedF1F2);
findSelectedASTNodes(Source, {3, 1}, FileRange{{3, 1}, {4, 14}},
SelectedF1F2);
}
TEST(ASTSelectionFinder, MultipleStatementSelection) {
StringRef Source = R"(void f(int x, int y) {
int z = x;
f(2, 3);
if (x == 0) {
return;
}
x = 1;
return;
})";
findSelectedASTNodes(
Source, {3, 2}, FileRange{{3, 2}, {7, 1}},
[](Optional<SelectedASTNode> Node) {
EXPECT_TRUE(Node);
EXPECT_EQ(Node->Children.size(), 1u);
const auto &Fn = checkNode<FunctionDecl>(
Node->Children[0], SourceSelectionKind::ContainsSelection,
1, "f");
const auto &Body = checkNode<CompoundStmt>(
Fn.Children[0], SourceSelectionKind::ContainsSelection,
2);
allChildrenOf(checkNode<CallExpr>(Body.Children[0],
SourceSelectionKind::InsideSelection,
3))
.shouldHaveSelectionKind(SourceSelectionKind::InsideSelection);
allChildrenOf(checkNode<IfStmt>(Body.Children[1],
SourceSelectionKind::InsideSelection,
2))
.shouldHaveSelectionKind(SourceSelectionKind::InsideSelection);
});
findSelectedASTNodes(
Source, {3, 2}, FileRange{{3, 2}, {7, 8}},
[](Optional<SelectedASTNode> Node) {
EXPECT_TRUE(Node);
EXPECT_EQ(Node->Children.size(), 1u);
const auto &Fn = checkNode<FunctionDecl>(
Node->Children[0], SourceSelectionKind::ContainsSelection,
1, "f");
const auto &Body = checkNode<CompoundStmt>(
Fn.Children[0], SourceSelectionKind::ContainsSelection,
3);
checkNode<CallExpr>(Body.Children[0],
SourceSelectionKind::InsideSelection,
3);
checkNode<IfStmt>(Body.Children[1],
SourceSelectionKind::InsideSelection,
2);
checkNode<BinaryOperator>(Body.Children[2],
SourceSelectionKind::InsideSelection,
2);
});
findSelectedASTNodes(
Source, {2, 10}, FileRange{{2, 10}, {7, 5}},
[](Optional<SelectedASTNode> Node) {
EXPECT_TRUE(Node);
EXPECT_EQ(Node->Children.size(), 1u);
const auto &Fn = checkNode<FunctionDecl>(
Node->Children[0], SourceSelectionKind::ContainsSelection,
1, "f");
const auto &Body = checkNode<CompoundStmt>(
Fn.Children[0], SourceSelectionKind::ContainsSelection,
4);
checkNode<DeclStmt>(Body.Children[0],
SourceSelectionKind::ContainsSelectionStart,
1);
checkNode<CallExpr>(Body.Children[1],
SourceSelectionKind::InsideSelection,
3);
checkNode<IfStmt>(Body.Children[2],
SourceSelectionKind::InsideSelection,
2);
checkNode<BinaryOperator>(Body.Children[3],
SourceSelectionKind::ContainsSelectionEnd,
1);
});
}
TEST(ASTSelectionFinder, SelectionInFunctionInObjCImplementation) {
StringRef Source = R"(
@interface I
@end
@implementation I
int notSelected() { }
int selected(int x) {
return x;
}
@end
@implementation I(Cat)
void catF() { }
@end
void outerFunction() { }
)";
findSelectedASTNodes(
Source, {9, 10}, FileRange{{9, 10}, {9, 11}},
[](Optional<SelectedASTNode> Node) {
EXPECT_TRUE(Node);
EXPECT_EQ(Node->Children.size(), 1u);
const auto &Impl = checkNode<ObjCImplementationDecl>(
Node->Children[0], SourceSelectionKind::ContainsSelection,
1, "I");
const auto &Fn = checkNode<FunctionDecl>(
Impl.Children[0], SourceSelectionKind::ContainsSelection,
1, "selected");
allChildrenOf(Fn).shouldHaveSelectionKind(
SourceSelectionKind::ContainsSelection);
},
SelectionFinderVisitor::Lang_OBJC);
findSelectedASTNodes(
Source, {15, 1}, FileRange{{15, 1}, {15, 16}},
[](Optional<SelectedASTNode> Node) {
EXPECT_TRUE(Node);
EXPECT_EQ(Node->Children.size(), 1u);
const auto &Impl = checkNode<ObjCCategoryImplDecl>(
Node->Children[0], SourceSelectionKind::ContainsSelection,
1, "Cat");
const auto &Fn = checkNode<FunctionDecl>(
Impl.Children[0], SourceSelectionKind::ContainsSelection,
1, "catF");
allChildrenOf(Fn).shouldHaveSelectionKind(
SourceSelectionKind::ContainsSelection);
},
SelectionFinderVisitor::Lang_OBJC);
findSelectedASTNodes(
Source, {16, 1}, FileRange{{7, 1}, {16, 1}},
[](Optional<SelectedASTNode> Node) {
EXPECT_TRUE(Node);
EXPECT_EQ(Node->Children.size(), 2u);
const auto &Impl = checkNode<ObjCImplementationDecl>(
Node->Children[0], SourceSelectionKind::ContainsSelectionStart,
1, "I");
const auto &Selected = checkNode<FunctionDecl>(
Impl.Children[0], SourceSelectionKind::InsideSelection,
2, "selected");
allChildrenOf(Selected).shouldHaveSelectionKind(
SourceSelectionKind::InsideSelection);
const auto &Cat = checkNode<ObjCCategoryImplDecl>(
Node->Children[1], SourceSelectionKind::ContainsSelectionEnd,
1, "Cat");
const auto &CatF = checkNode<FunctionDecl>(
Cat.Children[0], SourceSelectionKind::InsideSelection,
1, "catF");
allChildrenOf(CatF).shouldHaveSelectionKind(
SourceSelectionKind::InsideSelection);
},
SelectionFinderVisitor::Lang_OBJC);
findSelectedASTNodes(Source, {19, 1}, FileRange{{19, 1}, {19, 25}},
[](Optional<SelectedASTNode> Node) {
EXPECT_TRUE(Node);
EXPECT_EQ(Node->Children.size(), 1u);
checkNode<FunctionDecl>(
Node->Children[0],
SourceSelectionKind::ContainsSelection,
1, "outerFunction");
},
SelectionFinderVisitor::Lang_OBJC);
}
TEST(ASTSelectionFinder, FunctionInObjCImplementationCarefulWithEarlyExit) {
StringRef Source = R"(
@interface I
@end
@implementation I
void selected() {
}
- (void) method { }
@end
)";
findSelectedASTNodes(
Source, {6, 1}, FileRange{{6, 1}, {7, 2}},
[](Optional<SelectedASTNode> Node) {
EXPECT_TRUE(Node);
EXPECT_EQ(Node->Children.size(), 1u);
const auto &Impl = checkNode<ObjCImplementationDecl>(
Node->Children[0], SourceSelectionKind::ContainsSelection,
1, "I");
checkNode<FunctionDecl>(Impl.Children[0],
SourceSelectionKind::ContainsSelection,
1, "selected");
},
SelectionFinderVisitor::Lang_OBJC);
}
TEST(ASTSelectionFinder, AvoidImplicitDeclarations) {
StringRef Source = R"(
struct Copy {
int x;
};
void foo() {
Copy x;
Copy y = x;
}
)";
findSelectedASTNodes(
Source, {2, 1}, FileRange{{2, 1}, {4, 3}},
[](Optional<SelectedASTNode> Node) {
EXPECT_TRUE(Node);
EXPECT_EQ(Node->Children.size(), 1u);
const auto &Record = checkNode<CXXRecordDecl>(
Node->Children[0], SourceSelectionKind::InsideSelection,
1, "Copy");
checkNode<FieldDecl>(Record.Children[0],
SourceSelectionKind::InsideSelection);
});
}
TEST(ASTSelectionFinder, CorrectEndForObjectiveCImplementation) {
StringRef Source = R"(
@interface I
@end
@implementation I
@ end
)";
findSelectedASTNodes(Source, {5, 6}, None,
[](Optional<SelectedASTNode> Node) {
EXPECT_TRUE(Node);
EXPECT_EQ(Node->Children.size(), 1u);
checkNode<ObjCImplementationDecl>(
Node->Children[0],
SourceSelectionKind::ContainsSelection);
},
SelectionFinderVisitor::Lang_OBJC);
}
const SelectedASTNode &checkFnBody(const Optional<SelectedASTNode> &Node,
StringRef Name) {
EXPECT_TRUE(Node);
EXPECT_EQ(Node->Children.size(), 1u);
const auto &Fn = checkNode<FunctionDecl>(
Node->Children[0], SourceSelectionKind::ContainsSelection,
1, Name);
return checkNode<CompoundStmt>(Fn.Children[0],
SourceSelectionKind::ContainsSelection,
1);
}
TEST(ASTSelectionFinder, SelectObjectiveCPseudoObjectExprs) {
StringRef Source = R"(
@interface I
@property(readwrite) int prop;
@end
void selectProp(I *i) {
(void)i.prop;
i.prop = 21;
}
@interface NSMutableArray
- (id)objectAtIndexedSubscript:(unsigned int)index;
- (void)setObject:(id)object atIndexedSubscript:(unsigned int)index;
@end
void selectSubscript(NSMutableArray *array, I *i) {
(void)array[10];
array[i.prop] = i;
}
)";
findSelectedASTNodes(
Source, {6, 7}, FileRange{{6, 7}, {6, 13}},
[](Optional<SelectedASTNode> Node) {
const auto &CS = checkFnBody(Node, "selectProp");
const auto &CCast = checkNode<CStyleCastExpr>(
CS.Children[0], SourceSelectionKind::ContainsSelection,
1);
const auto &POE = checkNode<PseudoObjectExpr>(
CCast.Children[0], SourceSelectionKind::ContainsSelection,
1);
const auto &PRE = checkNode<ObjCPropertyRefExpr>(
POE.Children[0], SourceSelectionKind::ContainsSelection,
1);
const auto &Cast = checkNode<ImplicitCastExpr>(
PRE.Children[0], SourceSelectionKind::InsideSelection,
1);
checkNode<DeclRefExpr>(Cast.Children[0],
SourceSelectionKind::InsideSelection);
},
SelectionFinderVisitor::Lang_OBJC);
findSelectedASTNodes(
Source, {7, 1}, FileRange{{7, 1}, {7, 12}},
[](Optional<SelectedASTNode> Node) {
const auto &CS = checkFnBody(Node, "selectProp");
const auto &POE = checkNode<PseudoObjectExpr>(
CS.Children[0], SourceSelectionKind::ContainsSelection,
1);
const auto &BinOp = checkNode<BinaryOperator>(
POE.Children[0], SourceSelectionKind::ContainsSelection,
2);
const auto &PRE = checkNode<ObjCPropertyRefExpr>(
BinOp.Children[0], SourceSelectionKind::InsideSelection,
1);
const auto &Cast = checkNode<ImplicitCastExpr>(
PRE.Children[0], SourceSelectionKind::InsideSelection,
1);
checkNode<DeclRefExpr>(Cast.Children[0],
SourceSelectionKind::InsideSelection);
checkNode<IntegerLiteral>(BinOp.Children[1],
SourceSelectionKind::InsideSelection);
},
SelectionFinderVisitor::Lang_OBJC);
findSelectedASTNodes(
Source, {17, 9}, FileRange{{17, 9}, {17, 18}},
[](Optional<SelectedASTNode> Node) {
const auto &CS = checkFnBody(Node, "selectSubscript");
const auto &CCast = checkNode<CStyleCastExpr>(
CS.Children[0], SourceSelectionKind::ContainsSelection,
1);
const auto &POE = checkNode<PseudoObjectExpr>(
CCast.Children[0], SourceSelectionKind::ContainsSelection,
1);
const auto &SRE = checkNode<ObjCSubscriptRefExpr>(
POE.Children[0], SourceSelectionKind::ContainsSelection,
2);
const auto &Cast = checkNode<ImplicitCastExpr>(
SRE.Children[0], SourceSelectionKind::InsideSelection,
1);
checkNode<DeclRefExpr>(Cast.Children[0],
SourceSelectionKind::InsideSelection);
checkNode<IntegerLiteral>(SRE.Children[1],
SourceSelectionKind::InsideSelection);
},
SelectionFinderVisitor::Lang_OBJC);
findSelectedASTNodes(
Source, {18, 3}, FileRange{{18, 3}, {18, 20}},
[](Optional<SelectedASTNode> Node) {
const auto &CS = checkFnBody(Node, "selectSubscript");
const auto &POE = checkNode<PseudoObjectExpr>(
CS.Children[0], SourceSelectionKind::ContainsSelection,
1);
const auto &BinOp = checkNode<BinaryOperator>(
POE.Children[0], SourceSelectionKind::ContainsSelection,
2);
const auto &SRE = checkNode<ObjCSubscriptRefExpr>(
BinOp.Children[0], SourceSelectionKind::InsideSelection,
2);
const auto &Cast = checkNode<ImplicitCastExpr>(
SRE.Children[0], SourceSelectionKind::InsideSelection,
1);
checkNode<DeclRefExpr>(Cast.Children[0],
SourceSelectionKind::InsideSelection);
const auto &POE2 = checkNode<PseudoObjectExpr>(
SRE.Children[1], SourceSelectionKind::InsideSelection,
1);
const auto &PRE = checkNode<ObjCPropertyRefExpr>(
POE2.Children[0], SourceSelectionKind::InsideSelection,
1);
const auto &Cast2 = checkNode<ImplicitCastExpr>(
PRE.Children[0], SourceSelectionKind::InsideSelection,
1);
checkNode<DeclRefExpr>(Cast2.Children[0],
SourceSelectionKind::InsideSelection);
checkNode<DeclRefExpr>(BinOp.Children[1],
SourceSelectionKind::InsideSelection);
},
SelectionFinderVisitor::Lang_OBJC);
}
TEST(ASTSelectionFinder, SimpleCodeRangeASTSelection) {
StringRef Source = R"(void f(int x, int y) {
int z = x;
f(2, 3);
if (x == 0) {
return;
}
x = 1;
return;
}
void f2() {
int m = 0;
}
)";
findSelectedASTNodesWithRange(
Source, {2, 2}, None,
[](SourceRange SelectionRange, Optional<SelectedASTNode> Node) {
EXPECT_TRUE(Node);
Optional<CodeRangeASTSelection> SelectedCode =
CodeRangeASTSelection::create(SelectionRange, std::move(*Node));
EXPECT_FALSE(SelectedCode);
});
findSelectedASTNodesWithRange(
Source, {2, 2}, FileRange{{2, 2}, {2, 2}},
[](SourceRange SelectionRange, Optional<SelectedASTNode> Node) {
EXPECT_TRUE(Node);
Optional<CodeRangeASTSelection> SelectedCode =
CodeRangeASTSelection::create(SelectionRange, std::move(*Node));
EXPECT_FALSE(SelectedCode);
});
findSelectedASTNodesWithRange(
Source, {2, 2}, FileRange{{7, 2}, {12, 1}},
[](SourceRange SelectionRange, Optional<SelectedASTNode> Node) {
EXPECT_TRUE(Node);
Optional<CodeRangeASTSelection> SelectedCode =
CodeRangeASTSelection::create(SelectionRange, std::move(*Node));
EXPECT_FALSE(SelectedCode);
});
findSelectedASTNodesWithRange(
Source, {2, 2}, FileRange{{2, 2}, {2, 13}},
[](SourceRange SelectionRange, Optional<SelectedASTNode> Node) {
EXPECT_TRUE(Node);
Optional<CodeRangeASTSelection> SelectedCode =
CodeRangeASTSelection::create(SelectionRange, std::move(*Node));
EXPECT_TRUE(SelectedCode);
EXPECT_EQ(SelectedCode->size(), 1u);
EXPECT_TRUE(isa<DeclStmt>((*SelectedCode)[0]));
ArrayRef<SelectedASTNode::ReferenceType> Parents =
SelectedCode->getParents();
EXPECT_EQ(Parents.size(), 3u);
EXPECT_TRUE(
isa<TranslationUnitDecl>(Parents[0].get().Node.get<Decl>()));
EXPECT_TRUE(isa<FunctionDecl>(Parents[1].get().Node.get<Decl>()));
EXPECT_TRUE(isa<CompoundStmt>(Parents[2].get().Node.get<Stmt>()));
});
findSelectedASTNodesWithRange(
Source, {3, 2}, FileRange{{3, 2}, {7, 1}},
[](SourceRange SelectionRange, Optional<SelectedASTNode> Node) {
EXPECT_TRUE(Node);
Optional<CodeRangeASTSelection> SelectedCode =
CodeRangeASTSelection::create(SelectionRange, std::move(*Node));
EXPECT_TRUE(SelectedCode);
EXPECT_EQ(SelectedCode->size(), 2u);
EXPECT_TRUE(isa<CallExpr>((*SelectedCode)[0]));
EXPECT_TRUE(isa<IfStmt>((*SelectedCode)[1]));
ArrayRef<SelectedASTNode::ReferenceType> Parents =
SelectedCode->getParents();
EXPECT_EQ(Parents.size(), 3u);
EXPECT_TRUE(
isa<TranslationUnitDecl>(Parents[0].get().Node.get<Decl>()));
EXPECT_TRUE(isa<FunctionDecl>(Parents[1].get().Node.get<Decl>()));
EXPECT_TRUE(isa<CompoundStmt>(Parents[2].get().Node.get<Stmt>()));
});
findSelectedASTNodesWithRange(
Source, {3, 2}, FileRange{{3, 2}, {7, 8}},
[](SourceRange SelectionRange, Optional<SelectedASTNode> Node) {
EXPECT_TRUE(Node);
Optional<CodeRangeASTSelection> SelectedCode =
CodeRangeASTSelection::create(SelectionRange, std::move(*Node));
EXPECT_TRUE(SelectedCode);
EXPECT_EQ(SelectedCode->size(), 3u);
EXPECT_TRUE(isa<CallExpr>((*SelectedCode)[0]));
EXPECT_TRUE(isa<IfStmt>((*SelectedCode)[1]));
EXPECT_TRUE(isa<BinaryOperator>((*SelectedCode)[2]));
});
findSelectedASTNodesWithRange(
Source, {2, 10}, FileRange{{2, 10}, {7, 5}},
[](SourceRange SelectionRange, Optional<SelectedASTNode> Node) {
EXPECT_TRUE(Node);
EXPECT_TRUE(Node);
Optional<CodeRangeASTSelection> SelectedCode =
CodeRangeASTSelection::create(SelectionRange, std::move(*Node));
EXPECT_TRUE(SelectedCode);
EXPECT_EQ(SelectedCode->size(), 4u);
EXPECT_TRUE(isa<DeclStmt>((*SelectedCode)[0]));
EXPECT_TRUE(isa<CallExpr>((*SelectedCode)[1]));
EXPECT_TRUE(isa<IfStmt>((*SelectedCode)[2]));
EXPECT_TRUE(isa<BinaryOperator>((*SelectedCode)[3]));
});
}
TEST(ASTSelectionFinder, OutOfBodyCodeRange) {
StringRef Source = R"(
int codeRange = 2 + 3;
)";
findSelectedASTNodesWithRange(
Source, {2, 17}, FileRange{{2, 17}, {2, 22}},
[](SourceRange SelectionRange, Optional<SelectedASTNode> Node) {
EXPECT_TRUE(Node);
Optional<CodeRangeASTSelection> SelectedCode =
CodeRangeASTSelection::create(SelectionRange, std::move(*Node));
EXPECT_TRUE(SelectedCode);
EXPECT_EQ(SelectedCode->size(), 1u);
EXPECT_TRUE(isa<BinaryOperator>((*SelectedCode)[0]));
ArrayRef<SelectedASTNode::ReferenceType> Parents =
SelectedCode->getParents();
EXPECT_EQ(Parents.size(), 2u);
EXPECT_TRUE(
isa<TranslationUnitDecl>(Parents[0].get().Node.get<Decl>()));
EXPECT_TRUE(isa<VarDecl>(Parents[1].get().Node.get<Decl>()));
});
}
TEST(ASTSelectionFinder, SelectVarDeclStmt) {
StringRef Source = R"(
void f() {
{
int a;
}
}
)";
findSelectedASTNodesWithRange(
Source, {4, 8}, FileRange{{4, 8}, {4, 14}},
[](SourceRange SelectionRange, Optional<SelectedASTNode> Node) {
EXPECT_TRUE(Node);
Optional<CodeRangeASTSelection> SelectedCode =
CodeRangeASTSelection::create(SelectionRange, std::move(*Node));
EXPECT_TRUE(SelectedCode);
EXPECT_EQ(SelectedCode->size(), 1u);
EXPECT_TRUE(isa<DeclStmt>((*SelectedCode)[0]));
ArrayRef<SelectedASTNode::ReferenceType> Parents =
SelectedCode->getParents();
EXPECT_EQ(Parents.size(), 4u);
EXPECT_TRUE(
isa<TranslationUnitDecl>(Parents[0].get().Node.get<Decl>()));
EXPECT_TRUE(isa<FunctionDecl>(Parents[1].get().Node.get<Decl>()));
EXPECT_TRUE(isa<CompoundStmt>(Parents[2].get().Node.get<Stmt>()));
EXPECT_TRUE(isa<CompoundStmt>(Parents[3].get().Node.get<Stmt>()));
});
}
TEST(ASTSelectionFinder, SelectEntireDeclStmtRange) {
StringRef Source = R"(
void f(int x, int y) {
int a = x * y;
}
)";
findSelectedASTNodesWithRange(
Source, {3, 4}, FileRange{{3, 4}, {3, 17}},
[](SourceRange SelectionRange, Optional<SelectedASTNode> Node) {
EXPECT_TRUE(Node);
Optional<CodeRangeASTSelection> SelectedCode =
CodeRangeASTSelection::create(SelectionRange, std::move(*Node));
EXPECT_TRUE(SelectedCode);
EXPECT_EQ(SelectedCode->size(), 1u);
EXPECT_TRUE(isa<DeclStmt>((*SelectedCode)[0]));
ArrayRef<SelectedASTNode::ReferenceType> Parents =
SelectedCode->getParents();
EXPECT_EQ(Parents.size(), 3u);
EXPECT_TRUE(
isa<TranslationUnitDecl>(Parents[0].get().Node.get<Decl>()));
EXPECT_TRUE(isa<FunctionDecl>(Parents[1].get().Node.get<Decl>()));
EXPECT_TRUE(isa<CompoundStmt>(Parents[2].get().Node.get<Stmt>()));
});
}
TEST(ASTSelectionFinder, SelectEntireDeclStmtRangeWithMultipleDecls) {
StringRef Source = R"(
void f(int x, int y) {
int a = x * y, b = x - y;
}
)";
findSelectedASTNodesWithRange(
Source, {3, 19}, FileRange{{3, 19}, {3, 28}},
[](SourceRange SelectionRange, Optional<SelectedASTNode> Node) {
EXPECT_TRUE(Node);
Optional<CodeRangeASTSelection> SelectedCode =
CodeRangeASTSelection::create(SelectionRange, std::move(*Node));
EXPECT_TRUE(SelectedCode);
EXPECT_EQ(SelectedCode->size(), 1u);
EXPECT_TRUE(isa<DeclStmt>((*SelectedCode)[0]));
ArrayRef<SelectedASTNode::ReferenceType> Parents =
SelectedCode->getParents();
EXPECT_EQ(Parents.size(), 3u);
EXPECT_TRUE(
isa<TranslationUnitDecl>(Parents[0].get().Node.get<Decl>()));
EXPECT_TRUE(isa<FunctionDecl>(Parents[1].get().Node.get<Decl>()));
EXPECT_TRUE(isa<CompoundStmt>(Parents[2].get().Node.get<Stmt>()));
});
}
TEST(ASTSelectionFinder, SimpleCodeRangeASTSelectionInObjCMethod) {
StringRef Source = R"(@interface I @end
@implementation I
- (void) f:(int)x with:(int) y {
int z = x;
[self f: 2 with: 3];
if (x == 0) {
return;
}
x = 1;
return;
}
- (void)f2 {
int m = 0;
}
@end
)";
findSelectedASTNodesWithRange(
Source, {9, 2}, FileRange{{9, 2}, {13, 1}},
[](SourceRange SelectionRange, Optional<SelectedASTNode> Node) {
EXPECT_TRUE(Node);
Optional<CodeRangeASTSelection> SelectedCode =
CodeRangeASTSelection::create(SelectionRange, std::move(*Node));
EXPECT_FALSE(SelectedCode);
},
SelectionFinderVisitor::Lang_OBJC);
findSelectedASTNodesWithRange(
Source, {4, 2}, FileRange{{4, 2}, {4, 13}},
[](SourceRange SelectionRange, Optional<SelectedASTNode> Node) {
EXPECT_TRUE(Node);
Optional<CodeRangeASTSelection> SelectedCode =
CodeRangeASTSelection::create(SelectionRange, std::move(*Node));
EXPECT_TRUE(SelectedCode);
EXPECT_EQ(SelectedCode->size(), 1u);
EXPECT_TRUE(isa<DeclStmt>((*SelectedCode)[0]));
ArrayRef<SelectedASTNode::ReferenceType> Parents =
SelectedCode->getParents();
EXPECT_EQ(Parents.size(), 4u);
EXPECT_TRUE(
isa<TranslationUnitDecl>(Parents[0].get().Node.get<Decl>()));
EXPECT_TRUE(isa<ObjCImplDecl>(Parents[1].get().Node.get<Decl>()));
EXPECT_TRUE(isa<ObjCMethodDecl>(Parents[2].get().Node.get<Decl>()));
EXPECT_TRUE(isa<CompoundStmt>(Parents[3].get().Node.get<Stmt>()));
},
SelectionFinderVisitor::Lang_OBJC);
findSelectedASTNodesWithRange(
Source, {5, 2}, FileRange{{5, 2}, {9, 1}},
[](SourceRange SelectionRange, Optional<SelectedASTNode> Node) {
EXPECT_TRUE(Node);
Optional<CodeRangeASTSelection> SelectedCode =
CodeRangeASTSelection::create(SelectionRange, std::move(*Node));
EXPECT_TRUE(SelectedCode);
EXPECT_EQ(SelectedCode->size(), 2u);
EXPECT_TRUE(isa<ObjCMessageExpr>((*SelectedCode)[0]));
EXPECT_TRUE(isa<IfStmt>((*SelectedCode)[1]));
ArrayRef<SelectedASTNode::ReferenceType> Parents =
SelectedCode->getParents();
EXPECT_EQ(Parents.size(), 4u);
EXPECT_TRUE(
isa<TranslationUnitDecl>(Parents[0].get().Node.get<Decl>()));
EXPECT_TRUE(isa<ObjCImplDecl>(Parents[1].get().Node.get<Decl>()));
EXPECT_TRUE(isa<ObjCMethodDecl>(Parents[2].get().Node.get<Decl>()));
EXPECT_TRUE(isa<CompoundStmt>(Parents[3].get().Node.get<Stmt>()));
},
SelectionFinderVisitor::Lang_OBJC);
}
TEST(ASTSelectionFinder, CanonicalizeObjCStringLiteral) {
StringRef Source = R"(
void foo() {
(void)@"test";
}
)";
findSelectedASTNodesWithRange(
Source, {3, 10}, FileRange{{3, 10}, {3, 16}},
[](SourceRange SelectionRange, Optional<SelectedASTNode> Node) {
EXPECT_TRUE(Node);
Optional<CodeRangeASTSelection> SelectedCode =
CodeRangeASTSelection::create(SelectionRange, std::move(*Node));
EXPECT_TRUE(SelectedCode);
EXPECT_EQ(SelectedCode->size(), 1u);
EXPECT_TRUE(isa<ObjCStringLiteral>((*SelectedCode)[0]));
},
SelectionFinderVisitor::Lang_OBJC);
findSelectedASTNodesWithRange(
Source, {3, 11}, FileRange{{3, 11}, {3, 15}},
[](SourceRange SelectionRange, Optional<SelectedASTNode> Node) {
EXPECT_TRUE(Node);
Optional<CodeRangeASTSelection> SelectedCode =
CodeRangeASTSelection::create(SelectionRange, std::move(*Node));
EXPECT_TRUE(SelectedCode);
EXPECT_EQ(SelectedCode->size(), 1u);
EXPECT_TRUE(isa<ObjCStringLiteral>((*SelectedCode)[0]));
},
SelectionFinderVisitor::Lang_OBJC);
}
TEST(ASTSelectionFinder, CanonicalizeMemberCalleeToCall) {
StringRef Source = R"(
class AClass { public:
void method();
int afield;
void selectWholeCallWhenJustMethodSelected(int &i) {
method();
}
};
void selectWholeCallWhenJustMethodSelected() {
AClass a;
a.method();
}
void dontSelectArgument(AClass &a) {
a.selectWholeCallWhenJustMethodSelected(a.afield);
}
)";
findSelectedASTNodesWithRange(
Source, {6, 5}, FileRange{{6, 5}, {6, 11}},
[](SourceRange SelectionRange, Optional<SelectedASTNode> Node) {
EXPECT_TRUE(Node);
Optional<CodeRangeASTSelection> SelectedCode =
CodeRangeASTSelection::create(SelectionRange, std::move(*Node));
EXPECT_TRUE(SelectedCode);
EXPECT_EQ(SelectedCode->size(), 1u);
EXPECT_TRUE(isa<CXXMemberCallExpr>((*SelectedCode)[0]));
});
findSelectedASTNodesWithRange(
Source, {11, 5}, FileRange{{11, 5}, {11, 11}},
[](SourceRange SelectionRange, Optional<SelectedASTNode> Node) {
EXPECT_TRUE(Node);
Optional<CodeRangeASTSelection> SelectedCode =
CodeRangeASTSelection::create(SelectionRange, std::move(*Node));
EXPECT_TRUE(SelectedCode);
EXPECT_EQ(SelectedCode->size(), 1u);
EXPECT_TRUE(isa<CXXMemberCallExpr>((*SelectedCode)[0]));
});
findSelectedASTNodesWithRange(
Source, {14, 5}, FileRange{{14, 45}, {14, 51}},
[](SourceRange SelectionRange, Optional<SelectedASTNode> Node) {
EXPECT_TRUE(Node);
Optional<CodeRangeASTSelection> SelectedCode =
CodeRangeASTSelection::create(SelectionRange, std::move(*Node));
EXPECT_TRUE(SelectedCode);
EXPECT_EQ(SelectedCode->size(), 1u);
EXPECT_FALSE(isa<CXXMemberCallExpr>((*SelectedCode)[0]));
});
}
TEST(ASTSelectionFinder, CanonicalizeFuncCalleeToCall) {
StringRef Source = R"(
void function();
void test() {
function();
}
)";
findSelectedASTNodesWithRange(
Source, {5, 3}, FileRange{{5, 3}, {5, 11}},
[](SourceRange SelectionRange, Optional<SelectedASTNode> Node) {
EXPECT_TRUE(Node);
Node->dump();
Optional<CodeRangeASTSelection> SelectedCode =
CodeRangeASTSelection::create(SelectionRange, std::move(*Node));
EXPECT_TRUE(SelectedCode);
EXPECT_EQ(SelectedCode->size(), 1u);
EXPECT_TRUE(isa<CallExpr>((*SelectedCode)[0]));
EXPECT_TRUE(isa<CompoundStmt>(
SelectedCode->getParents()[SelectedCode->getParents().size() - 1]
.get()
.Node.get<Stmt>()));
});
}
}