#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTNodeTraverser.h"
#include "clang/AST/TextNodeDumper.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Tooling/Tooling.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
using namespace clang::tooling;
using namespace clang::ast_matchers;
namespace clang {
class NodeTreePrinter : public TextTreeStructure {
llvm::raw_ostream &OS;
public:
NodeTreePrinter(llvm::raw_ostream &OS)
: TextTreeStructure(OS, false), OS(OS) {}
void Visit(const Decl *D) {
OS << D->getDeclKindName() << "Decl";
if (auto *ND = dyn_cast<NamedDecl>(D)) {
OS << " '" << ND->getDeclName() << "'";
}
}
void Visit(const Stmt *S) {
if (!S) {
OS << "<<<NULL>>>";
return;
}
OS << S->getStmtClassName();
if (auto *E = dyn_cast<DeclRefExpr>(S)) {
OS << " '" << E->getDecl()->getDeclName() << "'";
}
}
void Visit(QualType QT) {
OS << "QualType " << QT.split().Quals.getAsString();
}
void Visit(const Type *T) { OS << T->getTypeClassName() << "Type"; }
void Visit(const comments::Comment *C, const comments::FullComment *FC) {
OS << C->getCommentKindName();
}
void Visit(const CXXCtorInitializer *Init) {
OS << "CXXCtorInitializer";
if (const auto *F = Init->getAnyMember()) {
OS << " '" << F->getNameAsString() << "'";
} else if (auto const *TSI = Init->getTypeSourceInfo()) {
OS << " '" << TSI->getType() << "'";
}
}
void Visit(const Attr *A) {
switch (A->getKind()) {
#define ATTR(X) \
case attr::X: \
OS << #X; \
break;
#include "clang/Basic/AttrList.inc"
}
OS << "Attr";
}
void Visit(const OMPClause *C) { OS << "OMPClause"; }
void Visit(const TemplateArgument &A, SourceRange R = {},
const Decl *From = nullptr, const char *Label = nullptr) {
OS << "TemplateArgument";
switch (A.getKind()) {
case TemplateArgument::Type: {
OS << " type " << A.getAsType();
break;
}
default:
break;
}
}
template <typename... T> void Visit(T...) {}
};
class TestASTDumper : public ASTNodeTraverser<TestASTDumper, NodeTreePrinter> {
NodeTreePrinter MyNodeRecorder;
public:
TestASTDumper(llvm::raw_ostream &OS) : MyNodeRecorder(OS) {}
NodeTreePrinter &doGetNodeDelegate() { return MyNodeRecorder; }
};
template <typename... NodeType> std::string dumpASTString(NodeType &&... N) {
std::string Buffer;
llvm::raw_string_ostream OS(Buffer);
TestASTDumper Dumper(OS);
OS << "\n";
Dumper.Visit(std::forward<NodeType &&>(N)...);
return Buffer;
}
template <typename... NodeType>
std::string dumpASTString(TraversalKind TK, NodeType &&... N) {
std::string Buffer;
llvm::raw_string_ostream OS(Buffer);
TestASTDumper Dumper(OS);
Dumper.SetTraversalKind(TK);
OS << "\n";
Dumper.Visit(std::forward<NodeType &&>(N)...);
return Buffer;
}
const FunctionDecl *getFunctionNode(clang::ASTUnit *AST,
const std::string &Name) {
auto Result = ast_matchers::match(functionDecl(hasName(Name)).bind("fn"),
AST->getASTContext());
EXPECT_EQ(Result.size(), 1u);
return Result[0].getNodeAs<FunctionDecl>("fn");
}
template <typename T> struct Verifier {
static void withDynNode(T Node, const std::string &DumpString) {
EXPECT_EQ(dumpASTString(DynTypedNode::create(Node)), DumpString);
}
};
template <typename T> struct Verifier<T *> {
static void withDynNode(T *Node, const std::string &DumpString) {
EXPECT_EQ(dumpASTString(DynTypedNode::create(*Node)), DumpString);
}
};
template <typename T>
void verifyWithDynNode(T Node, const std::string &DumpString) {
EXPECT_EQ(dumpASTString(Node), DumpString);
Verifier<T>::withDynNode(Node, DumpString);
}
TEST(Traverse, Dump) {
auto AST = buildASTFromCode(R"cpp(
struct A {
int m_number;
/// CTor
A() : m_number(42) {}
[[nodiscard]] const int func() {
return 42;
}
};
template<typename T>
struct templ
{
};
template<>
struct templ<int>
{
};
void parmvardecl_attr(struct A __attribute__((address_space(19)))*);
)cpp");
const FunctionDecl *Func = getFunctionNode(AST.get(), "func");
verifyWithDynNode(Func,
R"cpp(
CXXMethodDecl 'func'
|-CompoundStmt
| `-ReturnStmt
| `-IntegerLiteral
`-WarnUnusedResultAttr
)cpp");
Stmt *Body = Func->getBody();
verifyWithDynNode(Body,
R"cpp(
CompoundStmt
`-ReturnStmt
`-IntegerLiteral
)cpp");
QualType QT = Func->getType();
verifyWithDynNode(QT,
R"cpp(
FunctionProtoType
`-QualType const
`-BuiltinType
)cpp");
const FunctionDecl *CTorFunc = getFunctionNode(AST.get(), "A");
verifyWithDynNode(CTorFunc->getType(),
R"cpp(
FunctionProtoType
`-BuiltinType
)cpp");
Attr *A = *Func->attr_begin();
{
std::string expectedString = R"cpp(
WarnUnusedResultAttr
)cpp";
EXPECT_EQ(dumpASTString(A), expectedString);
}
auto *CTor = dyn_cast<CXXConstructorDecl>(CTorFunc);
const CXXCtorInitializer *Init = *CTor->init_begin();
verifyWithDynNode(Init,
R"cpp(
CXXCtorInitializer 'm_number'
`-IntegerLiteral
)cpp");
const comments::FullComment *Comment =
AST->getASTContext().getLocalCommentForDeclUncached(CTorFunc);
{
std::string expectedString = R"cpp(
FullComment
`-ParagraphComment
`-TextComment
)cpp";
EXPECT_EQ(dumpASTString(Comment, Comment), expectedString);
}
auto Result = ast_matchers::match(
classTemplateSpecializationDecl(hasName("templ")).bind("fn"),
AST->getASTContext());
EXPECT_EQ(Result.size(), 1u);
auto Templ = Result[0].getNodeAs<ClassTemplateSpecializationDecl>("fn");
TemplateArgument TA = Templ->getTemplateArgs()[0];
verifyWithDynNode(TA,
R"cpp(
TemplateArgument type int
`-BuiltinType
)cpp");
Func = getFunctionNode(AST.get(), "parmvardecl_attr");
const auto *Parm = Func->getParamDecl(0);
const auto TL = Parm->getTypeSourceInfo()->getTypeLoc();
ASSERT_TRUE(TL.getType()->isPointerType());
const auto ATL = TL.getNextTypeLoc().getAs<AttributedTypeLoc>();
const auto *AS = cast<AddressSpaceAttr>(ATL.getAttr());
EXPECT_EQ(toTargetAddressSpace(static_cast<LangAS>(AS->getAddressSpace())),
19u);
}
TEST(Traverse, IgnoreUnlessSpelledInSourceVars) {
auto AST = buildASTFromCode(R"cpp(
struct String
{
String(const char*, int = -1) {}
int overloaded() const;
int& overloaded();
};
void stringConstruct()
{
String s = "foo";
s = "bar";
}
void overloadCall()
{
String s = "foo";
(s).overloaded();
}
struct C1 {};
struct C2 { operator C1(); };
void conversionOperator()
{
C2* c2;
C1 c1 = (*c2);
}
template <unsigned alignment>
void template_test() {
static_assert(alignment, "");
}
void actual_template_test() {
template_test<4>();
}
struct OneParamCtor {
explicit OneParamCtor(int);
};
struct TwoParamCtor {
explicit TwoParamCtor(int, int);
};
void varDeclCtors() {
{
auto var1 = OneParamCtor(5);
auto var2 = TwoParamCtor(6, 7);
}
{
OneParamCtor var3(5);
TwoParamCtor var4(6, 7);
}
int i = 0;
{
auto var5 = OneParamCtor(i);
auto var6 = TwoParamCtor(i, 7);
}
{
OneParamCtor var7(i);
TwoParamCtor var8(i, 7);
}
}
)cpp");
{
auto FN =
ast_matchers::match(functionDecl(hasName("stringConstruct")).bind("fn"),
AST->getASTContext());
EXPECT_EQ(FN.size(), 1u);
EXPECT_EQ(dumpASTString(TK_AsIs, FN[0].getNodeAs<Decl>("fn")),
R"cpp(
FunctionDecl 'stringConstruct'
`-CompoundStmt
|-DeclStmt
| `-VarDecl 's'
| `-ExprWithCleanups
| `-CXXConstructExpr
| `-MaterializeTemporaryExpr
| `-ImplicitCastExpr
| `-CXXConstructExpr
| |-ImplicitCastExpr
| | `-StringLiteral
| `-CXXDefaultArgExpr
`-ExprWithCleanups
`-CXXOperatorCallExpr
|-ImplicitCastExpr
| `-DeclRefExpr 'operator='
|-DeclRefExpr 's'
`-MaterializeTemporaryExpr
`-CXXConstructExpr
|-ImplicitCastExpr
| `-StringLiteral
`-CXXDefaultArgExpr
)cpp");
EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource,
FN[0].getNodeAs<Decl>("fn")),
R"cpp(
FunctionDecl 'stringConstruct'
`-CompoundStmt
|-DeclStmt
| `-VarDecl 's'
| `-StringLiteral
`-CXXOperatorCallExpr
|-DeclRefExpr 'operator='
|-DeclRefExpr 's'
`-StringLiteral
)cpp");
}
{
auto FN =
ast_matchers::match(functionDecl(hasName("overloadCall")).bind("fn"),
AST->getASTContext());
EXPECT_EQ(FN.size(), 1u);
EXPECT_EQ(dumpASTString(TK_AsIs, FN[0].getNodeAs<Decl>("fn")),
R"cpp(
FunctionDecl 'overloadCall'
`-CompoundStmt
|-DeclStmt
| `-VarDecl 's'
| `-ExprWithCleanups
| `-CXXConstructExpr
| `-MaterializeTemporaryExpr
| `-ImplicitCastExpr
| `-CXXConstructExpr
| |-ImplicitCastExpr
| | `-StringLiteral
| `-CXXDefaultArgExpr
`-CXXMemberCallExpr
`-MemberExpr
`-ParenExpr
`-DeclRefExpr 's'
)cpp");
EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource,
FN[0].getNodeAs<Decl>("fn")),
R"cpp(
FunctionDecl 'overloadCall'
`-CompoundStmt
|-DeclStmt
| `-VarDecl 's'
| `-StringLiteral
`-CXXMemberCallExpr
`-MemberExpr
`-DeclRefExpr 's'
)cpp");
}
{
auto FN = ast_matchers::match(
functionDecl(hasName("conversionOperator"),
hasDescendant(varDecl(hasName("c1")).bind("var"))),
AST->getASTContext());
EXPECT_EQ(FN.size(), 1u);
EXPECT_EQ(dumpASTString(TK_AsIs, FN[0].getNodeAs<Decl>("var")),
R"cpp(
VarDecl 'c1'
`-ExprWithCleanups
`-CXXConstructExpr
`-MaterializeTemporaryExpr
`-ImplicitCastExpr
`-CXXMemberCallExpr
`-MemberExpr
`-ParenExpr
`-UnaryOperator
`-ImplicitCastExpr
`-DeclRefExpr 'c2'
)cpp");
EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource,
FN[0].getNodeAs<Decl>("var")),
R"cpp(
VarDecl 'c1'
`-UnaryOperator
`-DeclRefExpr 'c2'
)cpp");
}
{
auto FN = ast_matchers::match(
functionDecl(hasName("template_test"),
hasDescendant(staticAssertDecl().bind("staticAssert"))),
AST->getASTContext());
EXPECT_EQ(FN.size(), 2u);
EXPECT_EQ(dumpASTString(TK_AsIs, FN[1].getNodeAs<Decl>("staticAssert")),
R"cpp(
StaticAssertDecl
|-ImplicitCastExpr
| `-SubstNonTypeTemplateParmExpr
| |-NonTypeTemplateParmDecl 'alignment'
| `-IntegerLiteral
`-StringLiteral
)cpp");
EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource,
FN[1].getNodeAs<Decl>("staticAssert")),
R"cpp(
StaticAssertDecl
|-IntegerLiteral
`-StringLiteral
)cpp");
}
auto varChecker = [&AST](StringRef varName, StringRef SemanticDump,
StringRef SyntacticDump) {
auto FN = ast_matchers::match(
functionDecl(
hasName("varDeclCtors"),
forEachDescendant(varDecl(hasName(varName)).bind("varDeclCtor"))),
AST->getASTContext());
EXPECT_EQ(FN.size(), 1u);
EXPECT_EQ(dumpASTString(TK_AsIs, FN[0].getNodeAs<Decl>("varDeclCtor")),
SemanticDump);
EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource,
FN[0].getNodeAs<Decl>("varDeclCtor")),
SyntacticDump);
};
varChecker("var1",
R"cpp(
VarDecl 'var1'
`-ExprWithCleanups
`-CXXConstructExpr
`-MaterializeTemporaryExpr
`-CXXFunctionalCastExpr
`-CXXConstructExpr
`-IntegerLiteral
)cpp",
R"cpp(
VarDecl 'var1'
`-CXXConstructExpr
`-IntegerLiteral
)cpp");
varChecker("var2",
R"cpp(
VarDecl 'var2'
`-ExprWithCleanups
`-CXXConstructExpr
`-MaterializeTemporaryExpr
`-CXXTemporaryObjectExpr
|-IntegerLiteral
`-IntegerLiteral
)cpp",
R"cpp(
VarDecl 'var2'
`-CXXTemporaryObjectExpr
|-IntegerLiteral
`-IntegerLiteral
)cpp");
varChecker("var3",
R"cpp(
VarDecl 'var3'
`-CXXConstructExpr
`-IntegerLiteral
)cpp",
R"cpp(
VarDecl 'var3'
`-CXXConstructExpr
`-IntegerLiteral
)cpp");
varChecker("var4",
R"cpp(
VarDecl 'var4'
`-CXXConstructExpr
|-IntegerLiteral
`-IntegerLiteral
)cpp",
R"cpp(
VarDecl 'var4'
`-CXXConstructExpr
|-IntegerLiteral
`-IntegerLiteral
)cpp");
varChecker("var5",
R"cpp(
VarDecl 'var5'
`-ExprWithCleanups
`-CXXConstructExpr
`-MaterializeTemporaryExpr
`-CXXFunctionalCastExpr
`-CXXConstructExpr
`-ImplicitCastExpr
`-DeclRefExpr 'i'
)cpp",
R"cpp(
VarDecl 'var5'
`-CXXConstructExpr
`-DeclRefExpr 'i'
)cpp");
varChecker("var6",
R"cpp(
VarDecl 'var6'
`-ExprWithCleanups
`-CXXConstructExpr
`-MaterializeTemporaryExpr
`-CXXTemporaryObjectExpr
|-ImplicitCastExpr
| `-DeclRefExpr 'i'
`-IntegerLiteral
)cpp",
R"cpp(
VarDecl 'var6'
`-CXXTemporaryObjectExpr
|-DeclRefExpr 'i'
`-IntegerLiteral
)cpp");
varChecker("var7",
R"cpp(
VarDecl 'var7'
`-CXXConstructExpr
`-ImplicitCastExpr
`-DeclRefExpr 'i'
)cpp",
R"cpp(
VarDecl 'var7'
`-CXXConstructExpr
`-DeclRefExpr 'i'
)cpp");
varChecker("var8",
R"cpp(
VarDecl 'var8'
`-CXXConstructExpr
|-ImplicitCastExpr
| `-DeclRefExpr 'i'
`-IntegerLiteral
)cpp",
R"cpp(
VarDecl 'var8'
`-CXXConstructExpr
|-DeclRefExpr 'i'
`-IntegerLiteral
)cpp");
}
TEST(Traverse, IgnoreUnlessSpelledInSourceStructs) {
auto AST = buildASTFromCode(R"cpp(
struct MyStruct {
MyStruct();
MyStruct(int i) {
MyStruct();
}
~MyStruct();
};
)cpp");
auto BN = ast_matchers::match(
cxxConstructorDecl(hasName("MyStruct"),
hasParameter(0, parmVarDecl(hasType(isInteger()))))
.bind("ctor"),
AST->getASTContext());
EXPECT_EQ(BN.size(), 1u);
EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource,
BN[0].getNodeAs<Decl>("ctor")),
R"cpp(
CXXConstructorDecl 'MyStruct'
|-ParmVarDecl 'i'
`-CompoundStmt
`-CXXTemporaryObjectExpr
)cpp");
EXPECT_EQ(dumpASTString(TK_AsIs, BN[0].getNodeAs<Decl>("ctor")),
R"cpp(
CXXConstructorDecl 'MyStruct'
|-ParmVarDecl 'i'
`-CompoundStmt
`-ExprWithCleanups
`-CXXBindTemporaryExpr
`-CXXTemporaryObjectExpr
)cpp");
}
TEST(Traverse, IgnoreUnlessSpelledInSourceReturnStruct) {
auto AST = buildASTFromCode(R"cpp(
struct Retval {
Retval() {}
~Retval() {}
};
Retval someFun();
void foo()
{
someFun();
}
)cpp");
auto BN = ast_matchers::match(functionDecl(hasName("foo")).bind("fn"),
AST->getASTContext());
EXPECT_EQ(BN.size(), 1u);
EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource,
BN[0].getNodeAs<Decl>("fn")),
R"cpp(
FunctionDecl 'foo'
`-CompoundStmt
`-CallExpr
`-DeclRefExpr 'someFun'
)cpp");
EXPECT_EQ(dumpASTString(TK_AsIs, BN[0].getNodeAs<Decl>("fn")),
R"cpp(
FunctionDecl 'foo'
`-CompoundStmt
`-ExprWithCleanups
`-CXXBindTemporaryExpr
`-CallExpr
`-ImplicitCastExpr
`-DeclRefExpr 'someFun'
)cpp");
}
TEST(Traverse, IgnoreUnlessSpelledInSourceReturns) {
auto AST = buildASTFromCode(R"cpp(
struct A
{
};
struct B
{
B(int);
B(A const& a);
B();
};
struct C
{
operator B();
};
B func1() {
return 42;
}
B func2() {
return B{42};
}
B func3() {
return B(42);
}
B func4() {
return B();
}
B func5() {
return B{};
}
B func6() {
return C();
}
B func7() {
return A();
}
B func8() {
return C{};
}
B func9() {
return A{};
}
B func10() {
A a;
return a;
}
B func11() {
B b;
return b;
}
B func12() {
C c;
return c;
}
)cpp");
auto getFunctionNode = [&AST](const std::string &name) {
auto BN = ast_matchers::match(functionDecl(hasName(name)).bind("fn"),
AST->getASTContext());
EXPECT_EQ(BN.size(), 1u);
return BN[0].getNodeAs<Decl>("fn");
};
{
auto FN = getFunctionNode("func1");
llvm::StringRef Expected = R"cpp(
FunctionDecl 'func1'
`-CompoundStmt
`-ReturnStmt
`-ExprWithCleanups
`-CXXConstructExpr
`-MaterializeTemporaryExpr
`-ImplicitCastExpr
`-CXXConstructExpr
`-IntegerLiteral
)cpp";
EXPECT_EQ(dumpASTString(TK_AsIs, FN), Expected);
Expected = R"cpp(
FunctionDecl 'func1'
`-CompoundStmt
`-ReturnStmt
`-IntegerLiteral
)cpp";
EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource, FN), Expected);
}
llvm::StringRef Expected = R"cpp(
FunctionDecl 'func2'
`-CompoundStmt
`-ReturnStmt
`-CXXTemporaryObjectExpr
`-IntegerLiteral
)cpp";
EXPECT_EQ(
dumpASTString(TK_IgnoreUnlessSpelledInSource, getFunctionNode("func2")),
Expected);
Expected = R"cpp(
FunctionDecl 'func3'
`-CompoundStmt
`-ReturnStmt
`-CXXConstructExpr
`-IntegerLiteral
)cpp";
EXPECT_EQ(
dumpASTString(TK_IgnoreUnlessSpelledInSource, getFunctionNode("func3")),
Expected);
Expected = R"cpp(
FunctionDecl 'func4'
`-CompoundStmt
`-ReturnStmt
`-CXXTemporaryObjectExpr
)cpp";
EXPECT_EQ(
dumpASTString(TK_IgnoreUnlessSpelledInSource, getFunctionNode("func4")),
Expected);
Expected = R"cpp(
FunctionDecl 'func5'
`-CompoundStmt
`-ReturnStmt
`-CXXTemporaryObjectExpr
)cpp";
EXPECT_EQ(
dumpASTString(TK_IgnoreUnlessSpelledInSource, getFunctionNode("func5")),
Expected);
Expected = R"cpp(
FunctionDecl 'func6'
`-CompoundStmt
`-ReturnStmt
`-CXXTemporaryObjectExpr
)cpp";
EXPECT_EQ(
dumpASTString(TK_IgnoreUnlessSpelledInSource, getFunctionNode("func6")),
Expected);
Expected = R"cpp(
FunctionDecl 'func7'
`-CompoundStmt
`-ReturnStmt
`-CXXTemporaryObjectExpr
)cpp";
EXPECT_EQ(
dumpASTString(TK_IgnoreUnlessSpelledInSource, getFunctionNode("func7")),
Expected);
Expected = R"cpp(
FunctionDecl 'func8'
`-CompoundStmt
`-ReturnStmt
`-CXXFunctionalCastExpr
`-InitListExpr
)cpp";
EXPECT_EQ(
dumpASTString(TK_IgnoreUnlessSpelledInSource, getFunctionNode("func8")),
Expected);
Expected = R"cpp(
FunctionDecl 'func9'
`-CompoundStmt
`-ReturnStmt
`-CXXFunctionalCastExpr
`-InitListExpr
)cpp";
EXPECT_EQ(
dumpASTString(TK_IgnoreUnlessSpelledInSource, getFunctionNode("func9")),
Expected);
Expected = R"cpp(
FunctionDecl 'func10'
`-CompoundStmt
|-DeclStmt
| `-VarDecl 'a'
| `-CXXConstructExpr
`-ReturnStmt
`-DeclRefExpr 'a'
)cpp";
EXPECT_EQ(
dumpASTString(TK_IgnoreUnlessSpelledInSource, getFunctionNode("func10")),
Expected);
Expected = R"cpp(
FunctionDecl 'func11'
`-CompoundStmt
|-DeclStmt
| `-VarDecl 'b'
| `-CXXConstructExpr
`-ReturnStmt
`-DeclRefExpr 'b'
)cpp";
EXPECT_EQ(
dumpASTString(TK_IgnoreUnlessSpelledInSource, getFunctionNode("func11")),
Expected);
Expected = R"cpp(
FunctionDecl 'func12'
`-CompoundStmt
|-DeclStmt
| `-VarDecl 'c'
| `-CXXConstructExpr
`-ReturnStmt
`-DeclRefExpr 'c'
)cpp";
EXPECT_EQ(
dumpASTString(TK_IgnoreUnlessSpelledInSource, getFunctionNode("func12")),
Expected);
}
TEST(Traverse, LambdaUnlessSpelledInSource) {
auto AST =
buildASTFromCodeWithArgs(R"cpp(
void captures() {
int a = 0;
int b = 0;
int d = 0;
int f = 0;
[a, &b, c = d, &e = f](int g, int h = 42) {};
}
void templated() {
int a = 0;
[a]<typename T>(T t) {};
}
struct SomeStruct {
int a = 0;
void capture_this() {
[this]() {};
}
void capture_this_copy() {
[self = *this]() {};
}
};
)cpp",
{"-Wno-unused-value", "-Wno-c++2a-extensions"});
auto getLambdaNode = [&AST](const std::string &name) {
auto BN = ast_matchers::match(
lambdaExpr(hasAncestor(functionDecl(hasName(name)))).bind("lambda"),
AST->getASTContext());
EXPECT_EQ(BN.size(), 1u);
return BN[0].getNodeAs<LambdaExpr>("lambda");
};
{
auto L = getLambdaNode("captures");
llvm::StringRef Expected = R"cpp(
LambdaExpr
|-DeclRefExpr 'a'
|-DeclRefExpr 'b'
|-VarDecl 'c'
| `-DeclRefExpr 'd'
|-VarDecl 'e'
| `-DeclRefExpr 'f'
|-ParmVarDecl 'g'
|-ParmVarDecl 'h'
| `-IntegerLiteral
`-CompoundStmt
)cpp";
EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource, L), Expected);
Expected = R"cpp(
LambdaExpr
|-CXXRecordDecl ''
| |-CXXMethodDecl 'operator()'
| | |-ParmVarDecl 'g'
| | |-ParmVarDecl 'h'
| | | `-IntegerLiteral
| | `-CompoundStmt
| |-FieldDecl ''
| |-FieldDecl ''
| |-FieldDecl ''
| |-FieldDecl ''
| `-CXXDestructorDecl '~'
|-ImplicitCastExpr
| `-DeclRefExpr 'a'
|-DeclRefExpr 'b'
|-ImplicitCastExpr
| `-DeclRefExpr 'd'
|-DeclRefExpr 'f'
`-CompoundStmt
)cpp";
EXPECT_EQ(dumpASTString(TK_AsIs, L), Expected);
}
{
auto L = getLambdaNode("templated");
llvm::StringRef Expected = R"cpp(
LambdaExpr
|-DeclRefExpr 'a'
|-TemplateTypeParmDecl 'T'
|-ParmVarDecl 't'
`-CompoundStmt
)cpp";
EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource, L), Expected);
}
{
auto L = getLambdaNode("capture_this");
llvm::StringRef Expected = R"cpp(
LambdaExpr
|-CXXThisExpr
`-CompoundStmt
)cpp";
EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource, L), Expected);
}
{
auto L = getLambdaNode("capture_this_copy");
llvm::StringRef Expected = R"cpp(
LambdaExpr
|-VarDecl 'self'
| `-UnaryOperator
| `-CXXThisExpr
`-CompoundStmt
)cpp";
EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource, L), Expected);
}
}
TEST(Traverse, IgnoreUnlessSpelledInSourceImplicit) {
{
auto AST = buildASTFromCode(R"cpp(
int i = 0;
)cpp");
const auto *TUDecl = AST->getASTContext().getTranslationUnitDecl();
EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource, TUDecl),
R"cpp(
TranslationUnitDecl
`-VarDecl 'i'
`-IntegerLiteral
)cpp");
}
auto AST2 = buildASTFromCodeWithArgs(R"cpp(
struct Simple {
};
struct Other {
};
struct Record : Simple, Other {
Record() : Simple(), m_i(42) {}
private:
int m_i;
int m_i2 = 42;
Simple m_s;
};
struct NonTrivial {
NonTrivial() {}
NonTrivial(NonTrivial&) {}
NonTrivial& operator=(NonTrivial&) { return *this; }
~NonTrivial() {}
};
struct ContainsArray {
NonTrivial arr[2];
int irr[2];
ContainsArray& operator=(ContainsArray &) = default;
};
void copyIt()
{
ContainsArray ca;
ContainsArray ca2;
ca2 = ca;
}
void forLoop()
{
int arr[2];
for (auto i : arr)
{
}
for (auto& a = arr; auto i : a)
{
}
}
struct DefaultedAndDeleted {
NonTrivial nt;
DefaultedAndDeleted() = default;
~DefaultedAndDeleted() = default;
DefaultedAndDeleted(DefaultedAndDeleted &) = default;
DefaultedAndDeleted& operator=(DefaultedAndDeleted &) = default;
DefaultedAndDeleted(DefaultedAndDeleted &&) = delete;
DefaultedAndDeleted& operator=(DefaultedAndDeleted &&) = delete;
};
void copyIt2()
{
DefaultedAndDeleted ca;
DefaultedAndDeleted ca2;
ca2 = ca;
}
void hasDefaultArg(int i, int j = 0)
{
}
void callDefaultArg()
{
hasDefaultArg(42);
}
void decomposition()
{
int arr[3];
auto &[f, s, t] = arr;
f = 42;
}
typedef __typeof(sizeof(int)) size_t;
struct Pair
{
int x, y;
};
// Note: these utilities are required to force binding to tuple like structure
namespace std
{
template <typename E>
struct tuple_size
{
};
template <>
struct tuple_size<Pair>
{
static constexpr size_t value = 2;
};
template <size_t I, class T>
struct tuple_element
{
using type = int;
};
};
template <size_t I>
int &&get(Pair &&p);
void decompTuple()
{
Pair p{1, 2};
auto [a, b] = p;
a = 3;
}
)cpp",
{"-std=c++20"});
{
auto BN = ast_matchers::match(
cxxRecordDecl(hasName("Record"), unless(isImplicit())).bind("rec"),
AST2->getASTContext());
EXPECT_EQ(BN.size(), 1u);
EXPECT_EQ(dumpASTString(TK_AsIs, BN[0].getNodeAs<Decl>("rec")),
R"cpp(
CXXRecordDecl 'Record'
|-CXXRecordDecl 'Record'
|-CXXConstructorDecl 'Record'
| |-CXXCtorInitializer 'struct Simple'
| | `-CXXConstructExpr
| |-CXXCtorInitializer 'struct Other'
| | `-CXXConstructExpr
| |-CXXCtorInitializer 'm_i'
| | `-IntegerLiteral
| |-CXXCtorInitializer 'm_i2'
| | `-CXXDefaultInitExpr
| |-CXXCtorInitializer 'm_s'
| | `-CXXConstructExpr
| `-CompoundStmt
|-AccessSpecDecl
|-FieldDecl 'm_i'
|-FieldDecl 'm_i2'
| `-IntegerLiteral
`-FieldDecl 'm_s'
)cpp");
EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource,
BN[0].getNodeAs<Decl>("rec")),
R"cpp(
CXXRecordDecl 'Record'
|-CXXConstructorDecl 'Record'
| |-CXXCtorInitializer 'struct Simple'
| | `-CXXConstructExpr
| |-CXXCtorInitializer 'm_i'
| | `-IntegerLiteral
| `-CompoundStmt
|-AccessSpecDecl
|-FieldDecl 'm_i'
|-FieldDecl 'm_i2'
| `-IntegerLiteral
`-FieldDecl 'm_s'
)cpp");
}
{
auto BN = ast_matchers::match(
cxxRecordDecl(hasName("ContainsArray"), unless(isImplicit()))
.bind("rec"),
AST2->getASTContext());
EXPECT_EQ(BN.size(), 1u);
EXPECT_EQ(dumpASTString(TK_AsIs, BN[0].getNodeAs<Decl>("rec")),
R"cpp(
CXXRecordDecl 'ContainsArray'
|-CXXRecordDecl 'ContainsArray'
|-FieldDecl 'arr'
|-FieldDecl 'irr'
|-CXXMethodDecl 'operator='
| |-ParmVarDecl ''
| `-CompoundStmt
| |-ForStmt
| | |-DeclStmt
| | | `-VarDecl '__i0'
| | | `-IntegerLiteral
| | |-<<<NULL>>>
| | |-BinaryOperator
| | | |-ImplicitCastExpr
| | | | `-DeclRefExpr '__i0'
| | | `-IntegerLiteral
| | |-UnaryOperator
| | | `-DeclRefExpr '__i0'
| | `-CXXMemberCallExpr
| | |-MemberExpr
| | | `-ArraySubscriptExpr
| | | |-ImplicitCastExpr
| | | | `-MemberExpr
| | | | `-CXXThisExpr
| | | `-ImplicitCastExpr
| | | `-DeclRefExpr '__i0'
| | `-ArraySubscriptExpr
| | |-ImplicitCastExpr
| | | `-MemberExpr
| | | `-DeclRefExpr ''
| | `-ImplicitCastExpr
| | `-DeclRefExpr '__i0'
| |-CallExpr
| | |-ImplicitCastExpr
| | | `-DeclRefExpr '__builtin_memcpy'
| | |-ImplicitCastExpr
| | | `-UnaryOperator
| | | `-MemberExpr
| | | `-CXXThisExpr
| | |-ImplicitCastExpr
| | | `-UnaryOperator
| | | `-MemberExpr
| | | `-DeclRefExpr ''
| | `-IntegerLiteral
| `-ReturnStmt
| `-UnaryOperator
| `-CXXThisExpr
|-CXXConstructorDecl 'ContainsArray'
| `-ParmVarDecl ''
|-CXXDestructorDecl '~ContainsArray'
| `-CompoundStmt
`-CXXConstructorDecl 'ContainsArray'
|-CXXCtorInitializer 'arr'
| `-CXXConstructExpr
`-CompoundStmt
)cpp");
EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource,
BN[0].getNodeAs<Decl>("rec")),
R"cpp(
CXXRecordDecl 'ContainsArray'
|-FieldDecl 'arr'
|-FieldDecl 'irr'
`-CXXMethodDecl 'operator='
`-ParmVarDecl ''
)cpp");
}
{
auto BN = ast_matchers::match(functionDecl(hasName("forLoop")).bind("func"),
AST2->getASTContext());
EXPECT_EQ(BN.size(), 1u);
EXPECT_EQ(dumpASTString(TK_AsIs, BN[0].getNodeAs<Decl>("func")),
R"cpp(
FunctionDecl 'forLoop'
`-CompoundStmt
|-DeclStmt
| `-VarDecl 'arr'
|-CXXForRangeStmt
| |-<<<NULL>>>
| |-DeclStmt
| | `-VarDecl '__range1'
| | `-DeclRefExpr 'arr'
| |-DeclStmt
| | `-VarDecl '__begin1'
| | `-ImplicitCastExpr
| | `-DeclRefExpr '__range1'
| |-DeclStmt
| | `-VarDecl '__end1'
| | `-BinaryOperator
| | |-ImplicitCastExpr
| | | `-DeclRefExpr '__range1'
| | `-IntegerLiteral
| |-BinaryOperator
| | |-ImplicitCastExpr
| | | `-DeclRefExpr '__begin1'
| | `-ImplicitCastExpr
| | `-DeclRefExpr '__end1'
| |-UnaryOperator
| | `-DeclRefExpr '__begin1'
| |-DeclStmt
| | `-VarDecl 'i'
| | `-ImplicitCastExpr
| | `-UnaryOperator
| | `-ImplicitCastExpr
| | `-DeclRefExpr '__begin1'
| `-CompoundStmt
`-CXXForRangeStmt
|-DeclStmt
| `-VarDecl 'a'
| `-DeclRefExpr 'arr'
|-DeclStmt
| `-VarDecl '__range1'
| `-DeclRefExpr 'a'
|-DeclStmt
| `-VarDecl '__begin1'
| `-ImplicitCastExpr
| `-DeclRefExpr '__range1'
|-DeclStmt
| `-VarDecl '__end1'
| `-BinaryOperator
| |-ImplicitCastExpr
| | `-DeclRefExpr '__range1'
| `-IntegerLiteral
|-BinaryOperator
| |-ImplicitCastExpr
| | `-DeclRefExpr '__begin1'
| `-ImplicitCastExpr
| `-DeclRefExpr '__end1'
|-UnaryOperator
| `-DeclRefExpr '__begin1'
|-DeclStmt
| `-VarDecl 'i'
| `-ImplicitCastExpr
| `-UnaryOperator
| `-ImplicitCastExpr
| `-DeclRefExpr '__begin1'
`-CompoundStmt
)cpp");
EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource,
BN[0].getNodeAs<Decl>("func")),
R"cpp(
FunctionDecl 'forLoop'
`-CompoundStmt
|-DeclStmt
| `-VarDecl 'arr'
|-CXXForRangeStmt
| |-<<<NULL>>>
| |-VarDecl 'i'
| |-DeclRefExpr 'arr'
| `-CompoundStmt
`-CXXForRangeStmt
|-DeclStmt
| `-VarDecl 'a'
| `-DeclRefExpr 'arr'
|-VarDecl 'i'
|-DeclRefExpr 'a'
`-CompoundStmt
)cpp");
}
{
auto BN = ast_matchers::match(
cxxRecordDecl(hasName("DefaultedAndDeleted"), unless(isImplicit()))
.bind("rec"),
AST2->getASTContext());
EXPECT_EQ(BN.size(), 1u);
EXPECT_EQ(dumpASTString(TK_AsIs, BN[0].getNodeAs<Decl>("rec")),
R"cpp(
CXXRecordDecl 'DefaultedAndDeleted'
|-CXXRecordDecl 'DefaultedAndDeleted'
|-FieldDecl 'nt'
|-CXXConstructorDecl 'DefaultedAndDeleted'
| |-CXXCtorInitializer 'nt'
| | `-CXXConstructExpr
| `-CompoundStmt
|-CXXDestructorDecl '~DefaultedAndDeleted'
| `-CompoundStmt
|-CXXConstructorDecl 'DefaultedAndDeleted'
| `-ParmVarDecl ''
|-CXXMethodDecl 'operator='
| |-ParmVarDecl ''
| `-CompoundStmt
| |-CXXMemberCallExpr
| | |-MemberExpr
| | | `-MemberExpr
| | | `-CXXThisExpr
| | `-MemberExpr
| | `-DeclRefExpr ''
| `-ReturnStmt
| `-UnaryOperator
| `-CXXThisExpr
|-CXXConstructorDecl 'DefaultedAndDeleted'
| `-ParmVarDecl ''
`-CXXMethodDecl 'operator='
`-ParmVarDecl ''
)cpp");
EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource,
BN[0].getNodeAs<Decl>("rec")),
R"cpp(
CXXRecordDecl 'DefaultedAndDeleted'
|-FieldDecl 'nt'
|-CXXConstructorDecl 'DefaultedAndDeleted'
|-CXXDestructorDecl '~DefaultedAndDeleted'
|-CXXConstructorDecl 'DefaultedAndDeleted'
| `-ParmVarDecl ''
|-CXXMethodDecl 'operator='
| `-ParmVarDecl ''
|-CXXConstructorDecl 'DefaultedAndDeleted'
| `-ParmVarDecl ''
`-CXXMethodDecl 'operator='
`-ParmVarDecl ''
)cpp");
}
{
auto BN = ast_matchers::match(
callExpr(callee(functionDecl(hasName("hasDefaultArg"))))
.bind("funcCall"),
AST2->getASTContext());
EXPECT_EQ(BN.size(), 1u);
EXPECT_EQ(dumpASTString(TK_AsIs, BN[0].getNodeAs<CallExpr>("funcCall")),
R"cpp(
CallExpr
|-ImplicitCastExpr
| `-DeclRefExpr 'hasDefaultArg'
|-IntegerLiteral
`-CXXDefaultArgExpr
)cpp");
EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource,
BN[0].getNodeAs<CallExpr>("funcCall")),
R"cpp(
CallExpr
|-DeclRefExpr 'hasDefaultArg'
`-IntegerLiteral
)cpp");
}
{
auto FN = ast_matchers::match(
functionDecl(hasName("decomposition"),
hasDescendant(decompositionDecl().bind("decomp"))),
AST2->getASTContext());
EXPECT_EQ(FN.size(), 1u);
EXPECT_EQ(
dumpASTString(TK_AsIs, FN[0].getNodeAs<DecompositionDecl>("decomp")),
R"cpp(
DecompositionDecl ''
|-DeclRefExpr 'arr'
|-BindingDecl 'f'
| `-ArraySubscriptExpr
| |-ImplicitCastExpr
| | `-DeclRefExpr ''
| `-IntegerLiteral
|-BindingDecl 's'
| `-ArraySubscriptExpr
| |-ImplicitCastExpr
| | `-DeclRefExpr ''
| `-IntegerLiteral
`-BindingDecl 't'
`-ArraySubscriptExpr
|-ImplicitCastExpr
| `-DeclRefExpr ''
`-IntegerLiteral
)cpp");
EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource,
FN[0].getNodeAs<DecompositionDecl>("decomp")),
R"cpp(
DecompositionDecl ''
|-DeclRefExpr 'arr'
|-BindingDecl 'f'
|-BindingDecl 's'
`-BindingDecl 't'
)cpp");
}
{
auto FN = ast_matchers::match(
functionDecl(hasName("decompTuple"),
hasDescendant(decompositionDecl().bind("decomp"))),
AST2->getASTContext());
EXPECT_EQ(FN.size(), 1u);
EXPECT_EQ(
dumpASTString(TK_AsIs, FN[0].getNodeAs<DecompositionDecl>("decomp")),
R"cpp(
DecompositionDecl ''
|-CXXConstructExpr
| `-ImplicitCastExpr
| `-DeclRefExpr 'p'
|-BindingDecl 'a'
| |-VarDecl 'a'
| | `-CallExpr
| | |-ImplicitCastExpr
| | | `-DeclRefExpr 'get'
| | `-ImplicitCastExpr
| | `-DeclRefExpr ''
| `-DeclRefExpr 'a'
`-BindingDecl 'b'
|-VarDecl 'b'
| `-CallExpr
| |-ImplicitCastExpr
| | `-DeclRefExpr 'get'
| `-ImplicitCastExpr
| `-DeclRefExpr ''
`-DeclRefExpr 'b'
)cpp");
EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource,
FN[0].getNodeAs<DecompositionDecl>("decomp")),
R"cpp(
DecompositionDecl ''
|-DeclRefExpr 'p'
|-BindingDecl 'a'
`-BindingDecl 'b'
)cpp");
}
}
TEST(Traverse, IgnoreUnlessSpelledInSourceTemplateInstantiations) {
auto AST = buildASTFromCode(R"cpp(
template<typename T>
struct TemplStruct {
TemplStruct() {}
~TemplStruct() {}
private:
T m_t;
};
template<typename T>
T timesTwo(T input)
{
return input * 2;
}
void instantiate()
{
TemplStruct<int> ti;
TemplStruct<double> td;
(void)timesTwo<int>(2);
(void)timesTwo<double>(2);
}
template class TemplStruct<float>;
extern template class TemplStruct<long>;
template<> class TemplStruct<bool> {
TemplStruct() {}
~TemplStruct() {}
void foo() {}
private:
bool m_t;
};
// Explicit instantiation of template functions do not appear in the AST
template float timesTwo(float);
template<> bool timesTwo<bool>(bool) {
return true;
}
)cpp");
{
auto BN = ast_matchers::match(
classTemplateDecl(hasName("TemplStruct")).bind("rec"),
AST->getASTContext());
EXPECT_EQ(BN.size(), 1u);
EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource,
BN[0].getNodeAs<Decl>("rec")),
R"cpp(
ClassTemplateDecl 'TemplStruct'
|-TemplateTypeParmDecl 'T'
`-CXXRecordDecl 'TemplStruct'
|-CXXConstructorDecl 'TemplStruct<T>'
| `-CompoundStmt
|-CXXDestructorDecl '~TemplStruct<T>'
| `-CompoundStmt
|-AccessSpecDecl
`-FieldDecl 'm_t'
)cpp");
EXPECT_EQ(dumpASTString(TK_AsIs, BN[0].getNodeAs<Decl>("rec")),
R"cpp(
ClassTemplateDecl 'TemplStruct'
|-TemplateTypeParmDecl 'T'
|-CXXRecordDecl 'TemplStruct'
| |-CXXRecordDecl 'TemplStruct'
| |-CXXConstructorDecl 'TemplStruct<T>'
| | `-CompoundStmt
| |-CXXDestructorDecl '~TemplStruct<T>'
| | `-CompoundStmt
| |-AccessSpecDecl
| `-FieldDecl 'm_t'
|-ClassTemplateSpecializationDecl 'TemplStruct'
| |-TemplateArgument type int
| | `-BuiltinType
| |-CXXRecordDecl 'TemplStruct'
| |-CXXConstructorDecl 'TemplStruct'
| | `-CompoundStmt
| |-CXXDestructorDecl '~TemplStruct'
| | `-CompoundStmt
| |-AccessSpecDecl
| |-FieldDecl 'm_t'
| `-CXXConstructorDecl 'TemplStruct'
| `-ParmVarDecl ''
|-ClassTemplateSpecializationDecl 'TemplStruct'
| |-TemplateArgument type double
| | `-BuiltinType
| |-CXXRecordDecl 'TemplStruct'
| |-CXXConstructorDecl 'TemplStruct'
| | `-CompoundStmt
| |-CXXDestructorDecl '~TemplStruct'
| | `-CompoundStmt
| |-AccessSpecDecl
| |-FieldDecl 'm_t'
| `-CXXConstructorDecl 'TemplStruct'
| `-ParmVarDecl ''
|-ClassTemplateSpecializationDecl 'TemplStruct'
| |-TemplateArgument type float
| | `-BuiltinType
| |-CXXRecordDecl 'TemplStruct'
| |-CXXConstructorDecl 'TemplStruct'
| | `-CompoundStmt
| |-CXXDestructorDecl '~TemplStruct'
| | `-CompoundStmt
| |-AccessSpecDecl
| `-FieldDecl 'm_t'
|-ClassTemplateSpecializationDecl 'TemplStruct'
| |-TemplateArgument type long
| | `-BuiltinType
| |-CXXRecordDecl 'TemplStruct'
| |-CXXConstructorDecl 'TemplStruct'
| |-CXXDestructorDecl '~TemplStruct'
| |-AccessSpecDecl
| `-FieldDecl 'm_t'
`-ClassTemplateSpecializationDecl 'TemplStruct'
|-TemplateArgument type _Bool
| `-BuiltinType
|-CXXRecordDecl 'TemplStruct'
|-CXXConstructorDecl 'TemplStruct'
| `-CompoundStmt
|-CXXDestructorDecl '~TemplStruct'
| `-CompoundStmt
|-CXXMethodDecl 'foo'
| `-CompoundStmt
|-AccessSpecDecl
`-FieldDecl 'm_t'
)cpp");
}
{
auto BN = ast_matchers::match(
classTemplateSpecializationDecl(
hasTemplateArgument(
0, templateArgument(refersToType(asString("_Bool")))))
.bind("templSpec"),
AST->getASTContext());
EXPECT_EQ(BN.size(), 1u);
EXPECT_EQ(dumpASTString(TK_AsIs, BN[0].getNodeAs<Decl>("templSpec")),
R"cpp(
ClassTemplateSpecializationDecl 'TemplStruct'
|-TemplateArgument type _Bool
| `-BuiltinType
|-CXXRecordDecl 'TemplStruct'
|-CXXConstructorDecl 'TemplStruct'
| `-CompoundStmt
|-CXXDestructorDecl '~TemplStruct'
| `-CompoundStmt
|-CXXMethodDecl 'foo'
| `-CompoundStmt
|-AccessSpecDecl
`-FieldDecl 'm_t'
)cpp");
EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource,
BN[0].getNodeAs<Decl>("templSpec")),
R"cpp(
ClassTemplateSpecializationDecl 'TemplStruct'
|-TemplateArgument type _Bool
| `-BuiltinType
|-CXXConstructorDecl 'TemplStruct'
| `-CompoundStmt
|-CXXDestructorDecl '~TemplStruct'
| `-CompoundStmt
|-CXXMethodDecl 'foo'
| `-CompoundStmt
|-AccessSpecDecl
`-FieldDecl 'm_t'
)cpp");
}
{
auto BN = ast_matchers::match(
functionTemplateDecl(hasName("timesTwo")).bind("fn"),
AST->getASTContext());
EXPECT_EQ(BN.size(), 1u);
EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource,
BN[0].getNodeAs<Decl>("fn")),
R"cpp(
FunctionTemplateDecl 'timesTwo'
|-TemplateTypeParmDecl 'T'
`-FunctionDecl 'timesTwo'
|-ParmVarDecl 'input'
`-CompoundStmt
`-ReturnStmt
`-BinaryOperator
|-DeclRefExpr 'input'
`-IntegerLiteral
)cpp");
EXPECT_EQ(dumpASTString(TK_AsIs, BN[0].getNodeAs<Decl>("fn")),
R"cpp(
FunctionTemplateDecl 'timesTwo'
|-TemplateTypeParmDecl 'T'
|-FunctionDecl 'timesTwo'
| |-ParmVarDecl 'input'
| `-CompoundStmt
| `-ReturnStmt
| `-BinaryOperator
| |-DeclRefExpr 'input'
| `-IntegerLiteral
|-FunctionDecl 'timesTwo'
| |-TemplateArgument type int
| | `-BuiltinType
| |-ParmVarDecl 'input'
| `-CompoundStmt
| `-ReturnStmt
| `-BinaryOperator
| |-ImplicitCastExpr
| | `-DeclRefExpr 'input'
| `-IntegerLiteral
|-FunctionDecl 'timesTwo'
| |-TemplateArgument type double
| | `-BuiltinType
| |-ParmVarDecl 'input'
| `-CompoundStmt
| `-ReturnStmt
| `-BinaryOperator
| |-ImplicitCastExpr
| | `-DeclRefExpr 'input'
| `-ImplicitCastExpr
| `-IntegerLiteral
|-FunctionDecl 'timesTwo'
| |-TemplateArgument type float
| | `-BuiltinType
| |-ParmVarDecl 'input'
| `-CompoundStmt
| `-ReturnStmt
| `-BinaryOperator
| |-ImplicitCastExpr
| | `-DeclRefExpr 'input'
| `-ImplicitCastExpr
| `-IntegerLiteral
|-FunctionDecl 'timesTwo'
| |-TemplateArgument type _Bool
| | `-BuiltinType
| |-ParmVarDecl ''
| `-CompoundStmt
| `-ReturnStmt
| `-CXXBoolLiteralExpr
`-FunctionDecl 'timesTwo'
|-TemplateArgument type _Bool
| `-BuiltinType
`-ParmVarDecl 'input'
)cpp");
}
{
auto BN = ast_matchers::match(
classTemplateSpecializationDecl(
hasName("TemplStruct"),
hasTemplateArgument(
0, templateArgument(refersToType(asString("float")))),
hasParent(translationUnitDecl()))
.bind("rec"),
AST->getASTContext());
EXPECT_EQ(BN.size(), 1u);
EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource,
BN[0].getNodeAs<Decl>("rec")),
R"cpp(
ClassTemplateSpecializationDecl 'TemplStruct'
`-TemplateArgument type float
`-BuiltinType
)cpp");
EXPECT_EQ(dumpASTString(TK_AsIs, BN[0].getNodeAs<Decl>("rec")),
R"cpp(
ClassTemplateSpecializationDecl 'TemplStruct'
|-TemplateArgument type float
| `-BuiltinType
|-CXXRecordDecl 'TemplStruct'
|-CXXConstructorDecl 'TemplStruct'
| `-CompoundStmt
|-CXXDestructorDecl '~TemplStruct'
| `-CompoundStmt
|-AccessSpecDecl
`-FieldDecl 'm_t'
)cpp");
}
}
TEST(Traverse, CXXRewrittenBinaryOperator) {
auto AST = buildASTFromCodeWithArgs(R"cpp(
namespace std {
struct strong_ordering {
int n;
constexpr operator int() const { return n; }
static const strong_ordering equal, greater, less;
};
constexpr strong_ordering strong_ordering::equal = {0};
constexpr strong_ordering strong_ordering::greater = {1};
constexpr strong_ordering strong_ordering::less = {-1};
}
struct HasSpaceshipMem {
int a;
constexpr auto operator<=>(const HasSpaceshipMem&) const = default;
};
void binop()
{
HasSpaceshipMem hs1, hs2;
if (hs1 < hs2)
return;
}
)cpp",
{"-std=c++20"});
{
auto BN = ast_matchers::match(cxxRewrittenBinaryOperator().bind("binop"),
AST->getASTContext());
EXPECT_EQ(BN.size(), 1u);
EXPECT_EQ(dumpASTString(TK_AsIs, BN[0].getNodeAs<Stmt>("binop")),
R"cpp(
CXXRewrittenBinaryOperator
`-BinaryOperator
|-ImplicitCastExpr
| `-CXXMemberCallExpr
| `-MemberExpr
| `-ImplicitCastExpr
| `-MaterializeTemporaryExpr
| `-CXXOperatorCallExpr
| |-ImplicitCastExpr
| | `-DeclRefExpr 'operator<=>'
| |-ImplicitCastExpr
| | `-DeclRefExpr 'hs1'
| `-ImplicitCastExpr
| `-DeclRefExpr 'hs2'
`-IntegerLiteral
)cpp");
EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource,
BN[0].getNodeAs<Stmt>("binop")),
R"cpp(
CXXRewrittenBinaryOperator
|-DeclRefExpr 'hs1'
`-DeclRefExpr 'hs2'
)cpp");
}
}
}