llvm-project/clang/unittests/AST/ASTTraverserTest.cpp
Aaron Ballman 19e984ef8f Properly print unnamed TagDecl objects in diagnostics
The diagnostics engine is very smart about being passed a NamedDecl to
print as part of a diagnostic; it gets the "right" form of the name,
quotes it properly, etc. However, the result of using an unnamed tag
declaration was to print '' instead of anything useful.

This patch causes us to print the same information we'd have gotten if
we had printed the type of the declaration rather than the name of it,
as that's the most relevant information we can display.

Differential Revision: https://reviews.llvm.org/D134813
2022-10-14 08:18:28 -04:00

1928 lines
42 KiB
C++

//===- unittests/AST/ASTTraverserTest.h------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#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, /* showColors */ 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 = buildASTFromCodeWithArgs(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", {"-std=c++14"});
{
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 = buildASTFromCodeWithArgs(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", {"-std=c++14"});
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 '~(lambda at input.cc:9:3)'
|-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 'Simple'
| | `-CXXConstructExpr
| |-CXXCtorInitializer '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 '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");
}
}
} // namespace clang