forked from OSchip/llvm-project
[AST] Ignore implicit nodes in IgnoreUnlessSpelledInSource mode
Update the ASTNodeTraverser to dump only nodes spelled in source. There are only a few which need to be handled, but Decl nodes for which isImplicit() is true are handled together. Update the RAV instances used in ASTMatchFinder to ignore the nodes too. As with handling of template instantiations, it is necessary to allow the RAV to process the implicit nodes because they need to be visitable before the first traverse() matcher is encountered. An exception to this is in the MatchChildASTVisitor, because we sometimes wish to make a node matchable but make its children not-matchable. This is the case for defaulted CXXMethodDecls for example. Extend TransformerTests to illustrate the kinds of problems that can arise when performing source code rewriting due to matching implicit nodes. This change accounts for handling nodes not spelled in source when using direct matching of nodes, and when using the has() and hasDescendant() matchers. Other matchers such as cxxRecordDecl(hasMethod(cxxMethodDecl())) still succeed for compiler-generated methods for example after this change. Updating the implementations of hasMethod() and other matchers is for a follow-up patch. Differential Revision: https://reviews.llvm.org/D90982
This commit is contained in:
parent
34c0f3cbf1
commit
246b428fb3
|
@ -230,7 +230,9 @@ release of Clang. Users of the build system should adjust accordingly.
|
|||
AST Matchers
|
||||
------------
|
||||
|
||||
- ...
|
||||
- The behavior of TK_IgnoreUnlessSpelledInSource with the traverse() matcher
|
||||
has been changed to no longer match on template instantiations or on
|
||||
implicit nodes which are not spelled in the source.
|
||||
|
||||
clang-format
|
||||
------------
|
||||
|
|
|
@ -85,6 +85,9 @@ public:
|
|||
TraversalKind GetTraversalKind() const { return Traversal; }
|
||||
|
||||
void Visit(const Decl *D) {
|
||||
if (Traversal == TK_IgnoreUnlessSpelledInSource && D->isImplicit())
|
||||
return;
|
||||
|
||||
getNodeDelegate().AddChild([=] {
|
||||
getNodeDelegate().Visit(D);
|
||||
if (!D)
|
||||
|
@ -144,7 +147,8 @@ public:
|
|||
if (isa<DeclStmt>(S) || isa<GenericSelectionExpr>(S))
|
||||
return;
|
||||
|
||||
if (isa<LambdaExpr>(S) && Traversal == TK_IgnoreUnlessSpelledInSource)
|
||||
if (Traversal == TK_IgnoreUnlessSpelledInSource &&
|
||||
isa<LambdaExpr, CXXForRangeStmt, CallExpr>(S))
|
||||
return;
|
||||
|
||||
for (const Stmt *SubStmt : S->children())
|
||||
|
@ -185,6 +189,8 @@ public:
|
|||
}
|
||||
|
||||
void Visit(const CXXCtorInitializer *Init) {
|
||||
if (Traversal == TK_IgnoreUnlessSpelledInSource && !Init->isWritten())
|
||||
return;
|
||||
getNodeDelegate().AddChild([=] {
|
||||
getNodeDelegate().Visit(Init);
|
||||
Visit(Init->getInit());
|
||||
|
@ -401,6 +407,9 @@ public:
|
|||
if (const Expr *TRC = D->getTrailingRequiresClause())
|
||||
Visit(TRC);
|
||||
|
||||
if (Traversal == TK_IgnoreUnlessSpelledInSource && D->isDefaulted())
|
||||
return;
|
||||
|
||||
if (const auto *C = dyn_cast<CXXConstructorDecl>(D))
|
||||
for (const auto *I : C->inits())
|
||||
Visit(I);
|
||||
|
@ -417,6 +426,9 @@ public:
|
|||
}
|
||||
|
||||
void VisitVarDecl(const VarDecl *D) {
|
||||
if (Traversal == TK_IgnoreUnlessSpelledInSource && D->isCXXForRangeDecl())
|
||||
return;
|
||||
|
||||
if (D->hasInit())
|
||||
Visit(D->getInit());
|
||||
}
|
||||
|
@ -717,6 +729,26 @@ public:
|
|||
Visit(CatchParam);
|
||||
}
|
||||
|
||||
void VisitCXXForRangeStmt(const CXXForRangeStmt *Node) {
|
||||
if (Traversal == TK_IgnoreUnlessSpelledInSource) {
|
||||
Visit(Node->getInit());
|
||||
Visit(Node->getLoopVariable());
|
||||
Visit(Node->getRangeInit());
|
||||
Visit(Node->getBody());
|
||||
}
|
||||
}
|
||||
|
||||
void VisitCallExpr(const CallExpr *Node) {
|
||||
for (const auto *Child :
|
||||
make_filter_range(Node->children(), [this](const Stmt *Child) {
|
||||
if (Traversal != TK_IgnoreUnlessSpelledInSource)
|
||||
return false;
|
||||
return !isa<CXXDefaultArgExpr>(Child);
|
||||
})) {
|
||||
Visit(Child);
|
||||
}
|
||||
}
|
||||
|
||||
void VisitExpressionTemplateArgument(const TemplateArgument &TA) {
|
||||
Visit(TA.getAsExpr());
|
||||
}
|
||||
|
|
|
@ -95,9 +95,12 @@ public:
|
|||
// matching the descendants.
|
||||
MatchChildASTVisitor(const DynTypedMatcher *Matcher, ASTMatchFinder *Finder,
|
||||
BoundNodesTreeBuilder *Builder, int MaxDepth,
|
||||
TraversalKind Traversal, ASTMatchFinder::BindKind Bind)
|
||||
TraversalKind Traversal, bool IgnoreImplicitChildren,
|
||||
ASTMatchFinder::BindKind Bind)
|
||||
: Matcher(Matcher), Finder(Finder), Builder(Builder), CurrentDepth(0),
|
||||
MaxDepth(MaxDepth), Traversal(Traversal), Bind(Bind), Matches(false) {}
|
||||
MaxDepth(MaxDepth), Traversal(Traversal),
|
||||
IgnoreImplicitChildren(IgnoreImplicitChildren), Bind(Bind),
|
||||
Matches(false) {}
|
||||
|
||||
// Returns true if a match is found in the subtree rooted at the
|
||||
// given AST node. This is done via a set of mutually recursive
|
||||
|
@ -145,6 +148,11 @@ public:
|
|||
// They are public only to allow CRTP to work. They are *not *part
|
||||
// of the public API of this class.
|
||||
bool TraverseDecl(Decl *DeclNode) {
|
||||
|
||||
if (DeclNode && DeclNode->isImplicit() &&
|
||||
Finder->isTraversalIgnoringImplicitNodes())
|
||||
return baseTraverse(*DeclNode);
|
||||
|
||||
ScopedIncrement ScopedDepth(&CurrentDepth);
|
||||
return (DeclNode == nullptr) || traverse(*DeclNode);
|
||||
}
|
||||
|
@ -176,6 +184,10 @@ public:
|
|||
Stmt *StmtToTraverse = getStmtToTraverse(StmtNode);
|
||||
if (!StmtToTraverse)
|
||||
return true;
|
||||
|
||||
if (IgnoreImplicitChildren && isa<CXXDefaultArgExpr>(StmtNode))
|
||||
return true;
|
||||
|
||||
if (!match(*StmtToTraverse))
|
||||
return false;
|
||||
return VisitorBase::TraverseStmt(StmtToTraverse, Queue);
|
||||
|
@ -265,7 +277,7 @@ public:
|
|||
}
|
||||
|
||||
bool shouldVisitTemplateInstantiations() const { return true; }
|
||||
bool shouldVisitImplicitCode() const { return true; }
|
||||
bool shouldVisitImplicitCode() const { return !IgnoreImplicitChildren; }
|
||||
|
||||
private:
|
||||
// Used for updating the depth during traversal.
|
||||
|
@ -360,6 +372,7 @@ private:
|
|||
int CurrentDepth;
|
||||
const int MaxDepth;
|
||||
const TraversalKind Traversal;
|
||||
const bool IgnoreImplicitChildren;
|
||||
const ASTMatchFinder::BindKind Bind;
|
||||
bool Matches;
|
||||
};
|
||||
|
@ -497,19 +510,21 @@ public:
|
|||
const DynTypedMatcher &Matcher,
|
||||
BoundNodesTreeBuilder *Builder, int MaxDepth,
|
||||
TraversalKind Traversal, BindKind Bind) {
|
||||
bool ScopedTraversal = TraversingASTNodeNotSpelledInSource;
|
||||
bool ScopedTraversal = TraversingASTNodeNotSpelledInSource ||
|
||||
TraversingASTChildrenNotSpelledInSource;
|
||||
|
||||
if (const auto *CTSD = Node.get<ClassTemplateSpecializationDecl>()) {
|
||||
int SK = CTSD->getSpecializationKind();
|
||||
if (SK == TSK_ExplicitInstantiationDeclaration ||
|
||||
SK == TSK_ExplicitInstantiationDefinition)
|
||||
bool IgnoreImplicitChildren = false;
|
||||
|
||||
if (isTraversalIgnoringImplicitNodes()) {
|
||||
IgnoreImplicitChildren = true;
|
||||
if (Node.get<CXXForRangeStmt>())
|
||||
ScopedTraversal = true;
|
||||
}
|
||||
|
||||
ASTNodeNotSpelledInSourceScope RAII(this, ScopedTraversal);
|
||||
|
||||
MatchChildASTVisitor Visitor(
|
||||
&Matcher, this, Builder, MaxDepth, Traversal, Bind);
|
||||
MatchChildASTVisitor Visitor(&Matcher, this, Builder, MaxDepth, Traversal,
|
||||
IgnoreImplicitChildren, Bind);
|
||||
return Visitor.findMatch(Node);
|
||||
}
|
||||
|
||||
|
@ -616,6 +631,7 @@ public:
|
|||
|
||||
private:
|
||||
bool TraversingASTNodeNotSpelledInSource = false;
|
||||
bool TraversingASTChildrenNotSpelledInSource = false;
|
||||
|
||||
struct ASTNodeNotSpelledInSourceScope {
|
||||
ASTNodeNotSpelledInSourceScope(MatchASTVisitor *V, bool B)
|
||||
|
@ -631,6 +647,20 @@ private:
|
|||
bool MB;
|
||||
};
|
||||
|
||||
struct ASTChildrenNotSpelledInSource {
|
||||
ASTChildrenNotSpelledInSource(MatchASTVisitor *V, bool B)
|
||||
: MV(V), MB(V->TraversingASTChildrenNotSpelledInSource) {
|
||||
V->TraversingASTChildrenNotSpelledInSource = B;
|
||||
}
|
||||
~ASTChildrenNotSpelledInSource() {
|
||||
MV->TraversingASTChildrenNotSpelledInSource = MB;
|
||||
}
|
||||
|
||||
private:
|
||||
MatchASTVisitor *MV;
|
||||
bool MB;
|
||||
};
|
||||
|
||||
class TimeBucketRegion {
|
||||
public:
|
||||
TimeBucketRegion() : Bucket(nullptr) {}
|
||||
|
@ -1050,6 +1080,24 @@ bool MatchASTVisitor::TraverseDecl(Decl *DeclNode) {
|
|||
if (!DeclNode) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ScopedTraversal =
|
||||
TraversingASTNodeNotSpelledInSource || DeclNode->isImplicit();
|
||||
bool ScopedChildren = TraversingASTChildrenNotSpelledInSource;
|
||||
|
||||
if (const auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(DeclNode)) {
|
||||
auto SK = CTSD->getSpecializationKind();
|
||||
if (SK == TSK_ExplicitInstantiationDeclaration ||
|
||||
SK == TSK_ExplicitInstantiationDefinition)
|
||||
ScopedChildren = true;
|
||||
} else if (const auto *FD = dyn_cast<FunctionDecl>(DeclNode)) {
|
||||
if (FD->isDefaulted())
|
||||
ScopedChildren = true;
|
||||
}
|
||||
|
||||
ASTNodeNotSpelledInSourceScope RAII1(this, ScopedTraversal);
|
||||
ASTChildrenNotSpelledInSource RAII2(this, ScopedChildren);
|
||||
|
||||
match(*DeclNode);
|
||||
return RecursiveASTVisitor<MatchASTVisitor>::TraverseDecl(DeclNode);
|
||||
}
|
||||
|
@ -1058,6 +1106,10 @@ bool MatchASTVisitor::TraverseStmt(Stmt *StmtNode, DataRecursionQueue *Queue) {
|
|||
if (!StmtNode) {
|
||||
return true;
|
||||
}
|
||||
bool ScopedTraversal = TraversingASTNodeNotSpelledInSource ||
|
||||
TraversingASTChildrenNotSpelledInSource;
|
||||
|
||||
ASTNodeNotSpelledInSourceScope RAII(this, ScopedTraversal);
|
||||
match(*StmtNode);
|
||||
return RecursiveASTVisitor<MatchASTVisitor>::TraverseStmt(StmtNode, Queue);
|
||||
}
|
||||
|
@ -1103,6 +1155,14 @@ bool MatchASTVisitor::TraverseConstructorInitializer(
|
|||
if (!CtorInit)
|
||||
return true;
|
||||
|
||||
bool ScopedTraversal = TraversingASTNodeNotSpelledInSource ||
|
||||
TraversingASTChildrenNotSpelledInSource;
|
||||
|
||||
if (!CtorInit->isWritten())
|
||||
ScopedTraversal = true;
|
||||
|
||||
ASTNodeNotSpelledInSourceScope RAII1(this, ScopedTraversal);
|
||||
|
||||
match(*CtorInit);
|
||||
|
||||
return RecursiveASTVisitor<MatchASTVisitor>::TraverseConstructorInitializer(
|
||||
|
|
|
@ -35,6 +35,10 @@ public:
|
|||
}
|
||||
|
||||
void Visit(const Stmt *S) {
|
||||
if (!S) {
|
||||
OS << "<<<NULL>>>";
|
||||
return;
|
||||
}
|
||||
OS << S->getStmtClassName();
|
||||
if (auto *E = dyn_cast<DeclRefExpr>(S)) {
|
||||
OS << " '" << E->getDecl()->getDeclName() << "'";
|
||||
|
@ -51,7 +55,14 @@ public:
|
|||
OS << C->getCommentKindName();
|
||||
}
|
||||
|
||||
void Visit(const CXXCtorInitializer *Init) { OS << "CXXCtorInitializer"; }
|
||||
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().getAsString() << "'";
|
||||
}
|
||||
}
|
||||
|
||||
void Visit(const Attr *A) {
|
||||
switch (A->getKind()) {
|
||||
|
@ -226,7 +237,7 @@ WarnUnusedResultAttr
|
|||
|
||||
verifyWithDynNode(Init,
|
||||
R"cpp(
|
||||
CXXCtorInitializer
|
||||
CXXCtorInitializer 'm_number'
|
||||
`-IntegerLiteral
|
||||
)cpp");
|
||||
|
||||
|
@ -1050,6 +1061,438 @@ LambdaExpr
|
|||
}
|
||||
}
|
||||
|
||||
TEST(Traverse, IgnoreUnlessSpelledInSourceImplicit) {
|
||||
{
|
||||
auto AST = buildASTFromCode(R"cpp(
|
||||
int i = 0;
|
||||
)cpp");
|
||||
const auto *TUDecl = AST->getASTContext().getTranslationUnitDecl();
|
||||
|
||||
#if _WIN32
|
||||
EXPECT_EQ(dumpASTString(TK_AsIs, TUDecl),
|
||||
R"cpp(
|
||||
TranslationUnitDecl
|
||||
|-CXXRecordDecl '_GUID'
|
||||
| `-TypeVisibilityAttr
|
||||
|-TypedefDecl '__int128_t'
|
||||
| `-BuiltinType
|
||||
|-TypedefDecl '__uint128_t'
|
||||
| `-BuiltinType
|
||||
|-TypedefDecl '__NSConstantString'
|
||||
| `-RecordType
|
||||
|-CXXRecordDecl 'type_info'
|
||||
| `-TypeVisibilityAttr
|
||||
|-TypedefDecl 'size_t'
|
||||
| `-BuiltinType
|
||||
|-TypedefDecl '__builtin_ms_va_list'
|
||||
| `-PointerType
|
||||
| `-BuiltinType
|
||||
|-TypedefDecl '__builtin_va_list'
|
||||
| `-PointerType
|
||||
| `-BuiltinType
|
||||
`-VarDecl 'i'
|
||||
`-IntegerLiteral
|
||||
)cpp");
|
||||
#else
|
||||
EXPECT_EQ(dumpASTString(TK_AsIs, TUDecl),
|
||||
R"cpp(
|
||||
TranslationUnitDecl
|
||||
|-TypedefDecl '__int128_t'
|
||||
| `-BuiltinType
|
||||
|-TypedefDecl '__uint128_t'
|
||||
| `-BuiltinType
|
||||
|-TypedefDecl '__NSConstantString'
|
||||
| `-RecordType
|
||||
|-TypedefDecl '__builtin_ms_va_list'
|
||||
| `-PointerType
|
||||
| `-BuiltinType
|
||||
|-TypedefDecl '__builtin_va_list'
|
||||
| `-ConstantArrayType
|
||||
| `-RecordType
|
||||
`-VarDecl 'i'
|
||||
`-IntegerLiteral
|
||||
)cpp");
|
||||
#endif
|
||||
|
||||
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);
|
||||
}
|
||||
)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");
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Traverse, IgnoreUnlessSpelledInSourceTemplateInstantiations) {
|
||||
|
||||
auto AST = buildASTFromCode(R"cpp(
|
||||
|
@ -1108,7 +1551,6 @@ template<> bool timesTwo<bool>(bool) {
|
|||
ClassTemplateDecl 'TemplStruct'
|
||||
|-TemplateTypeParmDecl 'T'
|
||||
`-CXXRecordDecl 'TemplStruct'
|
||||
|-CXXRecordDecl 'TemplStruct'
|
||||
|-CXXConstructorDecl 'TemplStruct<T>'
|
||||
| `-CompoundStmt
|
||||
|-CXXDestructorDecl '~TemplStruct<T>'
|
||||
|
@ -1216,7 +1658,6 @@ ClassTemplateSpecializationDecl 'TemplStruct'
|
|||
ClassTemplateSpecializationDecl 'TemplStruct'
|
||||
|-TemplateArgument type _Bool
|
||||
| `-BuiltinType
|
||||
|-CXXRecordDecl 'TemplStruct'
|
||||
|-CXXConstructorDecl 'TemplStruct'
|
||||
| `-CompoundStmt
|
||||
|-CXXDestructorDecl '~TemplStruct'
|
||||
|
|
|
@ -2331,6 +2331,260 @@ template<> bool timesTwo<bool>(bool){
|
|||
}
|
||||
}
|
||||
|
||||
TEST(Traversal, traverseNoImplicit) {
|
||||
StringRef Code = R"cpp(
|
||||
struct NonTrivial {
|
||||
NonTrivial() {}
|
||||
NonTrivial(const NonTrivial&) {}
|
||||
NonTrivial& operator=(const NonTrivial&) { return *this; }
|
||||
|
||||
~NonTrivial() {}
|
||||
};
|
||||
|
||||
struct NoSpecialMethods {
|
||||
NonTrivial nt;
|
||||
};
|
||||
|
||||
struct ContainsArray {
|
||||
NonTrivial arr[2];
|
||||
ContainsArray& operator=(const ContainsArray &other) = default;
|
||||
};
|
||||
|
||||
void copyIt()
|
||||
{
|
||||
NoSpecialMethods nc1;
|
||||
NoSpecialMethods nc2(nc1);
|
||||
nc2 = nc1;
|
||||
|
||||
ContainsArray ca;
|
||||
ContainsArray ca2;
|
||||
ca2 = ca;
|
||||
}
|
||||
|
||||
struct HasCtorInits : NoSpecialMethods, NonTrivial
|
||||
{
|
||||
int m_i;
|
||||
NonTrivial m_nt;
|
||||
HasCtorInits() : NoSpecialMethods(), m_i(42) {}
|
||||
};
|
||||
|
||||
)cpp";
|
||||
{
|
||||
auto M = cxxRecordDecl(hasName("NoSpecialMethods"),
|
||||
has(cxxRecordDecl(hasName("NoSpecialMethods"))));
|
||||
EXPECT_TRUE(matches(Code, traverse(TK_AsIs, M)));
|
||||
EXPECT_FALSE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, M)));
|
||||
|
||||
M = cxxRecordDecl(hasName("NoSpecialMethods"),
|
||||
has(cxxConstructorDecl(isCopyConstructor())));
|
||||
EXPECT_TRUE(matches(Code, traverse(TK_AsIs, M)));
|
||||
EXPECT_FALSE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, M)));
|
||||
|
||||
M = cxxRecordDecl(hasName("NoSpecialMethods"),
|
||||
has(cxxMethodDecl(isCopyAssignmentOperator())));
|
||||
EXPECT_TRUE(matches(Code, traverse(TK_AsIs, M)));
|
||||
EXPECT_FALSE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, M)));
|
||||
|
||||
M = cxxRecordDecl(hasName("NoSpecialMethods"),
|
||||
has(cxxConstructorDecl(isDefaultConstructor())));
|
||||
EXPECT_TRUE(matches(Code, traverse(TK_AsIs, M)));
|
||||
EXPECT_FALSE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, M)));
|
||||
|
||||
M = cxxRecordDecl(hasName("NoSpecialMethods"), has(cxxDestructorDecl()));
|
||||
EXPECT_TRUE(matches(Code, traverse(TK_AsIs, M)));
|
||||
EXPECT_FALSE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, M)));
|
||||
}
|
||||
{
|
||||
// Compiler generates a forStmt to copy the array
|
||||
EXPECT_TRUE(matches(Code, traverse(TK_AsIs, forStmt())));
|
||||
EXPECT_FALSE(
|
||||
matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, forStmt())));
|
||||
}
|
||||
{
|
||||
// The defaulted method declaration can be matched, but not its
|
||||
// definition, in IgnoreUnlessSpelledInSource mode
|
||||
auto MDecl = cxxMethodDecl(ofClass(cxxRecordDecl(hasName("ContainsArray"))),
|
||||
isCopyAssignmentOperator(), isDefaulted());
|
||||
|
||||
EXPECT_TRUE(matches(Code, traverse(TK_AsIs, MDecl)));
|
||||
EXPECT_TRUE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, MDecl)));
|
||||
|
||||
auto MDef = cxxMethodDecl(MDecl, has(compoundStmt()));
|
||||
|
||||
EXPECT_TRUE(matches(Code, traverse(TK_AsIs, MDef)));
|
||||
EXPECT_FALSE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, MDef)));
|
||||
|
||||
// The parameter of the defaulted method can still be matched.
|
||||
auto MParm =
|
||||
cxxMethodDecl(MDecl, hasParameter(0, parmVarDecl(hasName("other"))));
|
||||
|
||||
EXPECT_TRUE(matches(Code, traverse(TK_AsIs, MParm)));
|
||||
EXPECT_TRUE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, MParm)));
|
||||
}
|
||||
{
|
||||
auto M =
|
||||
cxxConstructorDecl(hasName("HasCtorInits"),
|
||||
has(cxxCtorInitializer(forField(hasName("m_i")))));
|
||||
EXPECT_TRUE(matches(Code, traverse(TK_AsIs, M)));
|
||||
EXPECT_TRUE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, M)));
|
||||
}
|
||||
{
|
||||
auto M =
|
||||
cxxConstructorDecl(hasName("HasCtorInits"),
|
||||
has(cxxCtorInitializer(forField(hasName("m_nt")))));
|
||||
EXPECT_TRUE(matches(Code, traverse(TK_AsIs, M)));
|
||||
EXPECT_FALSE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, M)));
|
||||
}
|
||||
{
|
||||
auto M = cxxConstructorDecl(
|
||||
hasName("HasCtorInits"),
|
||||
has(cxxCtorInitializer(withInitializer(cxxConstructExpr(hasDeclaration(
|
||||
cxxConstructorDecl(hasName("NoSpecialMethods"))))))));
|
||||
EXPECT_TRUE(matches(Code, traverse(TK_AsIs, M)));
|
||||
EXPECT_TRUE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, M)));
|
||||
}
|
||||
{
|
||||
auto M = cxxConstructorDecl(
|
||||
hasName("HasCtorInits"),
|
||||
has(cxxCtorInitializer(withInitializer(cxxConstructExpr(
|
||||
hasDeclaration(cxxConstructorDecl(hasName("NonTrivial"))))))));
|
||||
EXPECT_TRUE(matches(Code, traverse(TK_AsIs, M)));
|
||||
EXPECT_FALSE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, M)));
|
||||
}
|
||||
{
|
||||
auto M = cxxCtorInitializer(forField(hasName("m_nt")));
|
||||
EXPECT_TRUE(matches(Code, traverse(TK_AsIs, M)));
|
||||
EXPECT_FALSE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, M)));
|
||||
}
|
||||
|
||||
Code = R"cpp(
|
||||
void rangeFor()
|
||||
{
|
||||
int arr[2];
|
||||
for (auto i : arr)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
)cpp";
|
||||
{
|
||||
auto M = cxxForRangeStmt(has(binaryOperator(hasOperatorName("!="))));
|
||||
EXPECT_TRUE(matches(Code, traverse(TK_AsIs, M)));
|
||||
EXPECT_FALSE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, M)));
|
||||
}
|
||||
{
|
||||
auto M =
|
||||
cxxForRangeStmt(hasDescendant(binaryOperator(hasOperatorName("+"))));
|
||||
EXPECT_TRUE(matches(Code, traverse(TK_AsIs, M)));
|
||||
EXPECT_FALSE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, M)));
|
||||
}
|
||||
{
|
||||
auto M =
|
||||
cxxForRangeStmt(hasDescendant(unaryOperator(hasOperatorName("++"))));
|
||||
EXPECT_TRUE(matches(Code, traverse(TK_AsIs, M)));
|
||||
EXPECT_FALSE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, M)));
|
||||
}
|
||||
{
|
||||
auto M = cxxForRangeStmt(has(declStmt()));
|
||||
EXPECT_TRUE(matches(Code, traverse(TK_AsIs, M)));
|
||||
EXPECT_FALSE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, M)));
|
||||
}
|
||||
{
|
||||
auto M =
|
||||
cxxForRangeStmt(hasLoopVariable(varDecl(hasName("i"))),
|
||||
hasRangeInit(declRefExpr(to(varDecl(hasName("arr"))))));
|
||||
EXPECT_TRUE(matches(Code, traverse(TK_AsIs, M)));
|
||||
EXPECT_TRUE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, M)));
|
||||
}
|
||||
{
|
||||
auto M = cxxForRangeStmt(unless(hasInitStatement(stmt())));
|
||||
EXPECT_TRUE(matches(Code, traverse(TK_AsIs, M)));
|
||||
EXPECT_TRUE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, M)));
|
||||
}
|
||||
{
|
||||
auto M = cxxForRangeStmt(hasBody(stmt()));
|
||||
EXPECT_TRUE(matches(Code, traverse(TK_AsIs, M)));
|
||||
EXPECT_TRUE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, M)));
|
||||
}
|
||||
|
||||
Code = R"cpp(
|
||||
void rangeFor()
|
||||
{
|
||||
int arr[2];
|
||||
for (auto& a = arr; auto i : a)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
)cpp";
|
||||
{
|
||||
auto M = cxxForRangeStmt(has(binaryOperator(hasOperatorName("!="))));
|
||||
EXPECT_TRUE(
|
||||
matchesConditionally(Code, traverse(TK_AsIs, M), true, {"-std=c++20"}));
|
||||
EXPECT_FALSE(
|
||||
matchesConditionally(Code, traverse(TK_IgnoreUnlessSpelledInSource, M),
|
||||
true, {"-std=c++20"}));
|
||||
}
|
||||
{
|
||||
auto M =
|
||||
cxxForRangeStmt(hasDescendant(binaryOperator(hasOperatorName("+"))));
|
||||
EXPECT_TRUE(
|
||||
matchesConditionally(Code, traverse(TK_AsIs, M), true, {"-std=c++20"}));
|
||||
EXPECT_FALSE(
|
||||
matchesConditionally(Code, traverse(TK_IgnoreUnlessSpelledInSource, M),
|
||||
true, {"-std=c++20"}));
|
||||
}
|
||||
{
|
||||
auto M =
|
||||
cxxForRangeStmt(hasDescendant(unaryOperator(hasOperatorName("++"))));
|
||||
EXPECT_TRUE(
|
||||
matchesConditionally(Code, traverse(TK_AsIs, M), true, {"-std=c++20"}));
|
||||
EXPECT_FALSE(
|
||||
matchesConditionally(Code, traverse(TK_IgnoreUnlessSpelledInSource, M),
|
||||
true, {"-std=c++20"}));
|
||||
}
|
||||
{
|
||||
auto M = cxxForRangeStmt(has(declStmt()));
|
||||
EXPECT_TRUE(
|
||||
matchesConditionally(Code, traverse(TK_AsIs, M), true, {"-std=c++20"}));
|
||||
EXPECT_FALSE(
|
||||
matchesConditionally(Code, traverse(TK_IgnoreUnlessSpelledInSource, M),
|
||||
true, {"-std=c++20"}));
|
||||
}
|
||||
{
|
||||
auto M = cxxForRangeStmt(
|
||||
hasInitStatement(declStmt(hasSingleDecl(varDecl(
|
||||
hasName("a"),
|
||||
hasInitializer(declRefExpr(to(varDecl(hasName("arr"))))))))),
|
||||
hasLoopVariable(varDecl(hasName("i"))),
|
||||
hasRangeInit(declRefExpr(to(varDecl(hasName("a"))))));
|
||||
EXPECT_TRUE(
|
||||
matchesConditionally(Code, traverse(TK_AsIs, M), true, {"-std=c++20"}));
|
||||
EXPECT_TRUE(
|
||||
matchesConditionally(Code, traverse(TK_IgnoreUnlessSpelledInSource, M),
|
||||
true, {"-std=c++20"}));
|
||||
}
|
||||
Code = R"cpp(
|
||||
void hasDefaultArg(int i, int j = 0)
|
||||
{
|
||||
}
|
||||
void callDefaultArg()
|
||||
{
|
||||
hasDefaultArg(42);
|
||||
}
|
||||
)cpp";
|
||||
{
|
||||
auto M = callExpr(has(integerLiteral(equals(42))));
|
||||
EXPECT_TRUE(matches(Code, traverse(TK_AsIs, M)));
|
||||
EXPECT_TRUE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, M)));
|
||||
}
|
||||
{
|
||||
auto M = callExpr(has(cxxDefaultArgExpr()));
|
||||
EXPECT_TRUE(matches(Code, traverse(TK_AsIs, M)));
|
||||
EXPECT_FALSE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, M)));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename MatcherT>
|
||||
bool matcherTemplateWithBinding(StringRef Code, const MatcherT &M) {
|
||||
return matchAndVerifyResultTrue(
|
||||
|
|
|
@ -132,6 +132,13 @@ protected:
|
|||
compareSnippets(Expected, rewrite(Input));
|
||||
}
|
||||
|
||||
template <typename R> void testRuleFailure(R Rule, StringRef Input) {
|
||||
Transformers.push_back(
|
||||
std::make_unique<Transformer>(std::move(Rule), consumer()));
|
||||
Transformers.back()->registerMatchers(&MatchFinder);
|
||||
ASSERT_FALSE(rewrite(Input)) << "Expected failure to rewrite code";
|
||||
}
|
||||
|
||||
// Transformers are referenced by MatchFinder.
|
||||
std::vector<std::unique_ptr<Transformer>> Transformers;
|
||||
clang::ast_matchers::MatchFinder MatchFinder;
|
||||
|
@ -1067,6 +1074,179 @@ TEST_F(TransformerTest, ErrorOccurredMatchSkipped) {
|
|||
EXPECT_EQ(ErrorCount, 0);
|
||||
}
|
||||
|
||||
TEST_F(TransformerTest, ImplicitNodes_ConstructorDecl) {
|
||||
|
||||
std::string OtherStructPrefix = R"cpp(
|
||||
struct Other {
|
||||
)cpp";
|
||||
std::string OtherStructSuffix = "};";
|
||||
|
||||
std::string CopyableStructName = "struct Copyable";
|
||||
std::string BrokenStructName = "struct explicit Copyable";
|
||||
|
||||
std::string CodeSuffix = R"cpp(
|
||||
{
|
||||
Other m_i;
|
||||
Copyable();
|
||||
};
|
||||
)cpp";
|
||||
|
||||
std::string CopyCtor = "Other(const Other&) = default;";
|
||||
std::string ExplicitCopyCtor = "explicit Other(const Other&) = default;";
|
||||
std::string BrokenExplicitCopyCtor =
|
||||
"explicit explicit explicit Other(const Other&) = default;";
|
||||
|
||||
std::string RewriteInput = OtherStructPrefix + CopyCtor + OtherStructSuffix +
|
||||
CopyableStructName + CodeSuffix;
|
||||
std::string ExpectedRewriteOutput = OtherStructPrefix + ExplicitCopyCtor +
|
||||
OtherStructSuffix + CopyableStructName +
|
||||
CodeSuffix;
|
||||
std::string BrokenRewriteOutput = OtherStructPrefix + BrokenExplicitCopyCtor +
|
||||
OtherStructSuffix + BrokenStructName +
|
||||
CodeSuffix;
|
||||
|
||||
auto MatchedRecord =
|
||||
cxxConstructorDecl(isCopyConstructor()).bind("copyConstructor");
|
||||
|
||||
auto RewriteRule =
|
||||
changeTo(before(node("copyConstructor")), cat("explicit "));
|
||||
|
||||
testRule(makeRule(traverse(TK_IgnoreUnlessSpelledInSource, MatchedRecord),
|
||||
RewriteRule),
|
||||
RewriteInput, ExpectedRewriteOutput);
|
||||
|
||||
testRule(makeRule(traverse(TK_AsIs, MatchedRecord), RewriteRule),
|
||||
RewriteInput, BrokenRewriteOutput);
|
||||
}
|
||||
|
||||
TEST_F(TransformerTest, ImplicitNodes_RangeFor) {
|
||||
|
||||
std::string CodePrefix = R"cpp(
|
||||
struct Container
|
||||
{
|
||||
int* begin() const;
|
||||
int* end() const;
|
||||
int* cbegin() const;
|
||||
int* cend() const;
|
||||
};
|
||||
|
||||
void foo()
|
||||
{
|
||||
const Container c;
|
||||
)cpp";
|
||||
|
||||
std::string BeginCallBefore = " c.begin();";
|
||||
std::string BeginCallAfter = " c.cbegin();";
|
||||
|
||||
std::string ForLoop = "for (auto i : c)";
|
||||
std::string BrokenForLoop = "for (auto i :.cbegin() c)";
|
||||
|
||||
std::string CodeSuffix = R"cpp(
|
||||
{
|
||||
}
|
||||
}
|
||||
)cpp";
|
||||
|
||||
std::string RewriteInput =
|
||||
CodePrefix + BeginCallBefore + ForLoop + CodeSuffix;
|
||||
std::string ExpectedRewriteOutput =
|
||||
CodePrefix + BeginCallAfter + ForLoop + CodeSuffix;
|
||||
std::string BrokenRewriteOutput =
|
||||
CodePrefix + BeginCallAfter + BrokenForLoop + CodeSuffix;
|
||||
|
||||
auto MatchedRecord =
|
||||
cxxMemberCallExpr(on(expr(hasType(qualType(isConstQualified(),
|
||||
hasDeclaration(cxxRecordDecl(
|
||||
hasName("Container"))))))
|
||||
.bind("callTarget")),
|
||||
callee(cxxMethodDecl(hasName("begin"))))
|
||||
.bind("constBeginCall");
|
||||
|
||||
auto RewriteRule =
|
||||
changeTo(node("constBeginCall"), cat(name("callTarget"), ".cbegin()"));
|
||||
|
||||
testRule(makeRule(traverse(TK_IgnoreUnlessSpelledInSource, MatchedRecord),
|
||||
RewriteRule),
|
||||
RewriteInput, ExpectedRewriteOutput);
|
||||
|
||||
testRule(makeRule(traverse(TK_AsIs, MatchedRecord), RewriteRule),
|
||||
RewriteInput, BrokenRewriteOutput);
|
||||
}
|
||||
|
||||
TEST_F(TransformerTest, ImplicitNodes_ForStmt) {
|
||||
|
||||
std::string CodePrefix = R"cpp(
|
||||
struct NonTrivial {
|
||||
NonTrivial() {}
|
||||
NonTrivial(NonTrivial&) {}
|
||||
NonTrivial& operator=(NonTrivial const&) { return *this; }
|
||||
|
||||
~NonTrivial() {}
|
||||
};
|
||||
|
||||
struct ContainsArray {
|
||||
NonTrivial arr[2];
|
||||
ContainsArray& operator=(ContainsArray const&) = default;
|
||||
};
|
||||
|
||||
void testIt()
|
||||
{
|
||||
ContainsArray ca1;
|
||||
ContainsArray ca2;
|
||||
ca2 = ca1;
|
||||
)cpp";
|
||||
|
||||
auto CodeSuffix = "}";
|
||||
|
||||
auto LoopBody = R"cpp(
|
||||
{
|
||||
|
||||
}
|
||||
)cpp";
|
||||
|
||||
auto RawLoop = "for (auto i = 0; i != 5; ++i)";
|
||||
|
||||
auto RangeLoop = "for (auto i : boost::irange(5))";
|
||||
|
||||
// Expect to rewrite the raw loop to the ranged loop.
|
||||
// This works in TK_IgnoreUnlessSpelledInSource mode, but TK_AsIs
|
||||
// mode also matches the hidden for loop generated in the copy assignment
|
||||
// operator of ContainsArray. Transformer then fails to transform the code at
|
||||
// all.
|
||||
|
||||
auto RewriteInput =
|
||||
CodePrefix + RawLoop + LoopBody + RawLoop + LoopBody + CodeSuffix;
|
||||
|
||||
auto RewriteOutput =
|
||||
CodePrefix + RangeLoop + LoopBody + RangeLoop + LoopBody + CodeSuffix;
|
||||
{
|
||||
auto MatchedLoop = forStmt(
|
||||
has(declStmt(
|
||||
hasSingleDecl(varDecl(hasInitializer(integerLiteral(equals(0))))
|
||||
.bind("loopVar")))),
|
||||
has(binaryOperator(hasOperatorName("!="),
|
||||
hasLHS(ignoringImplicit(declRefExpr(
|
||||
to(varDecl(equalsBoundNode("loopVar")))))),
|
||||
hasRHS(expr().bind("upperBoundExpr")))),
|
||||
has(unaryOperator(hasOperatorName("++"),
|
||||
hasUnaryOperand(declRefExpr(
|
||||
to(varDecl(equalsBoundNode("loopVar"))))))
|
||||
.bind("incrementOp")));
|
||||
|
||||
auto RewriteRule =
|
||||
changeTo(transformer::enclose(node("loopVar"), node("incrementOp")),
|
||||
cat("auto ", name("loopVar"), " : boost::irange(",
|
||||
node("upperBoundExpr"), ")"));
|
||||
|
||||
testRule(makeRule(traverse(TK_IgnoreUnlessSpelledInSource, MatchedLoop),
|
||||
RewriteRule),
|
||||
RewriteInput, RewriteOutput);
|
||||
|
||||
testRuleFailure(makeRule(traverse(TK_AsIs, MatchedLoop), RewriteRule),
|
||||
RewriteInput);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(TransformerTest, TemplateInstantiation) {
|
||||
|
||||
std::string NonTemplatesInput = R"cpp(
|
||||
|
|
Loading…
Reference in New Issue