diff --git a/clang/include/clang/AST/ASTNodeTraverser.h b/clang/include/clang/AST/ASTNodeTraverser.h --- a/clang/include/clang/AST/ASTNodeTraverser.h +++ b/clang/include/clang/AST/ASTNodeTraverser.h @@ -101,6 +101,8 @@ // Decls within functions are visited by the body. if (!isa(*D) && !isa(*D)) { + if (isa(*D) && Traversal != TK_AsIs) + return; if (const auto *DC = dyn_cast(D)) dumpDeclContext(DC); } diff --git a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h --- a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h +++ b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h @@ -1060,7 +1060,7 @@ virtual ASTContext &getASTContext() const = 0; - virtual bool isMatchingInImplicitTemplateInstantiation() const = 0; + virtual bool IsMatchingInTemplateInstantiationNotSpelledInSource() const = 0; protected: virtual bool matchesChildOf(const DynTypedNode &Node, ASTContext &Ctx, diff --git a/clang/lib/ASTMatchers/ASTMatchFinder.cpp b/clang/lib/ASTMatchers/ASTMatchFinder.cpp --- a/clang/lib/ASTMatchers/ASTMatchFinder.cpp +++ b/clang/lib/ASTMatchers/ASTMatchFinder.cpp @@ -500,6 +500,17 @@ const DynTypedMatcher &Matcher, BoundNodesTreeBuilder *Builder, int MaxDepth, TraversalKind Traversal, BindKind Bind) { + bool ScopedTraversal = TraversingTemplateInstantiationNotSpelledInSource; + + if (const auto *CTSD = Node.get()) { + int SK = CTSD->getSpecializationKind(); + if (SK == TSK_ExplicitInstantiationDeclaration || + SK == TSK_ExplicitInstantiationDefinition) + ScopedTraversal = true; + } + + TemplateInstantiationNotSpelledInSourceScope RAII(this, ScopedTraversal); + MatchChildASTVisitor Visitor( &Matcher, this, Builder, MaxDepth, Traversal, Bind); return Visitor.findMatch(Node); @@ -584,38 +595,38 @@ bool shouldVisitTemplateInstantiations() const { return true; } bool shouldVisitImplicitCode() const { return true; } - bool isMatchingInImplicitTemplateInstantiation() const override { - return TraversingImplicitTemplateInstantiation; + bool IsMatchingInTemplateInstantiationNotSpelledInSource() const override { + return TraversingTemplateInstantiationNotSpelledInSource; } bool TraverseTemplateInstantiations(ClassTemplateDecl *D) { - ImplicitTemplateInstantiationScope RAII(this, true); + TemplateInstantiationNotSpelledInSourceScope RAII(this, true); return RecursiveASTVisitor::TraverseTemplateInstantiations( D); } bool TraverseTemplateInstantiations(VarTemplateDecl *D) { - ImplicitTemplateInstantiationScope RAII(this, true); + TemplateInstantiationNotSpelledInSourceScope RAII(this, true); return RecursiveASTVisitor::TraverseTemplateInstantiations( D); } bool TraverseTemplateInstantiations(FunctionTemplateDecl *D) { - ImplicitTemplateInstantiationScope RAII(this, true); + TemplateInstantiationNotSpelledInSourceScope RAII(this, true); return RecursiveASTVisitor::TraverseTemplateInstantiations( D); } private: - bool TraversingImplicitTemplateInstantiation = false; + bool TraversingTemplateInstantiationNotSpelledInSource = false; - struct ImplicitTemplateInstantiationScope { - ImplicitTemplateInstantiationScope(MatchASTVisitor *V, bool B) - : MV(V), MB(V->TraversingImplicitTemplateInstantiation) { - V->TraversingImplicitTemplateInstantiation = B; + struct TemplateInstantiationNotSpelledInSourceScope { + TemplateInstantiationNotSpelledInSourceScope(MatchASTVisitor *V, bool B) + : MV(V), MB(V->TraversingTemplateInstantiationNotSpelledInSource) { + V->TraversingTemplateInstantiationNotSpelledInSource = B; } - ~ImplicitTemplateInstantiationScope() { - MV->TraversingImplicitTemplateInstantiation = MB; + ~TemplateInstantiationNotSpelledInSourceScope() { + MV->TraversingTemplateInstantiationNotSpelledInSource = MB; } private: diff --git a/clang/lib/ASTMatchers/ASTMatchersInternal.cpp b/clang/lib/ASTMatchers/ASTMatchersInternal.cpp --- a/clang/lib/ASTMatchers/ASTMatchersInternal.cpp +++ b/clang/lib/ASTMatchers/ASTMatchersInternal.cpp @@ -286,7 +286,7 @@ if (Finder->getASTContext().getParentMapContext().getTraversalKind() == TK_IgnoreUnlessSpelledInSource && - Finder->isMatchingInImplicitTemplateInstantiation()) + Finder->IsMatchingInTemplateInstantiationNotSpelledInSource()) return false; auto N = @@ -311,7 +311,7 @@ if (Finder->getASTContext().getParentMapContext().getTraversalKind() == TK_IgnoreUnlessSpelledInSource && - Finder->isMatchingInImplicitTemplateInstantiation()) + Finder->IsMatchingInTemplateInstantiationNotSpelledInSource()) return false; auto N = diff --git a/clang/unittests/AST/ASTTraverserTest.cpp b/clang/unittests/AST/ASTTraverserTest.cpp --- a/clang/unittests/AST/ASTTraverserTest.cpp +++ b/clang/unittests/AST/ASTTraverserTest.cpp @@ -1075,6 +1075,26 @@ (void)timesTwo(2); (void)timesTwo(2); } + +template class TemplStruct; + +extern template class TemplStruct; + +template<> class TemplStruct { + 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) { + return true; +} )cpp"); { auto BN = ast_matchers::match( @@ -1121,18 +1141,48 @@ | |-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 double + |-TemplateArgument type _Bool | `-BuiltinType |-CXXRecordDecl 'TemplStruct' |-CXXConstructorDecl 'TemplStruct' | `-CompoundStmt |-CXXDestructorDecl '~TemplStruct' | `-CompoundStmt + |-CXXMethodDecl 'foo' + | `-CompoundStmt |-AccessSpecDecl - |-FieldDecl 'm_t' - `-CXXConstructorDecl 'TemplStruct' - `-ParmVarDecl '' + `-FieldDecl 'm_t' )cpp"); } { @@ -1176,17 +1226,72 @@ | |-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 double + |-TemplateArgument type _Bool | `-BuiltinType - |-ParmVarDecl 'input' - `-CompoundStmt - `-ReturnStmt - `-BinaryOperator - |-ImplicitCastExpr - | `-DeclRefExpr 'input' - `-ImplicitCastExpr - `-IntegerLiteral + `-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("rec")), + R"cpp( +ClassTemplateSpecializationDecl 'TemplStruct' +`-TemplateArgument type float + `-BuiltinType +)cpp"); + + EXPECT_EQ(dumpASTString(TK_AsIs, BN[0].getNodeAs("rec")), + R"cpp( +ClassTemplateSpecializationDecl 'TemplStruct' +|-TemplateArgument type float +| `-BuiltinType +|-CXXRecordDecl 'TemplStruct' +|-CXXConstructorDecl 'TemplStruct' +| `-CompoundStmt +|-CXXDestructorDecl '~TemplStruct' +| `-CompoundStmt +|-AccessSpecDecl +`-FieldDecl 'm_t' )cpp"); } } diff --git a/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp --- a/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp +++ b/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp @@ -2214,6 +2214,23 @@ (void)timesTwo(2); } +template class TemplStruct; + +extern template class TemplStruct; + +template<> class TemplStruct { + TemplStruct() {} + ~TemplStruct() {} + + void boolSpecializationMethodOnly() {} +private: + bool m_t; +}; + +template float timesTwo(float); +template<> bool timesTwo(bool){ + return true; +} )cpp"; { auto M = cxxRecordDecl(hasName("TemplStruct"), @@ -2241,6 +2258,77 @@ EXPECT_TRUE(matches(Code, traverse(TK_AsIs, M))); EXPECT_FALSE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, M))); } + { + // Match on the integer literal in the explicit instantiation: + auto MDef = + functionDecl(hasName("timesTwo"), + hasParameter(0, parmVarDecl(hasType(asString("float")))), + hasDescendant(integerLiteral(equals(2)))); + EXPECT_TRUE(matches(Code, traverse(TK_AsIs, MDef))); + EXPECT_FALSE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, MDef))); + + auto MTempl = + functionDecl(hasName("timesTwo"), + hasTemplateArgument(0, refersToType(asString("float")))); + EXPECT_TRUE(matches(Code, traverse(TK_AsIs, MTempl))); + // TODO: If we could match on explicit instantiations of function templates, + // this would be EXPECT_TRUE. + EXPECT_FALSE( + matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, MTempl))); + } + { + auto M = functionDecl(hasName("timesTwo"), + hasParameter(0, parmVarDecl(hasType(booleanType())))); + EXPECT_TRUE(matches(Code, traverse(TK_AsIs, M))); + EXPECT_TRUE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, M))); + } + { + // Match on the field within the explicit instantiation: + auto MRecord = cxxRecordDecl(hasName("TemplStruct"), + has(fieldDecl(hasType(asString("float"))))); + EXPECT_TRUE(matches(Code, traverse(TK_AsIs, MRecord))); + EXPECT_FALSE( + matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, MRecord))); + + // Match on the explicit template instantiation itself: + auto MTempl = classTemplateSpecializationDecl( + hasName("TemplStruct"), + hasTemplateArgument(0, + templateArgument(refersToType(asString("float"))))); + EXPECT_TRUE(matches(Code, traverse(TK_AsIs, MTempl))); + EXPECT_TRUE( + matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, MTempl))); + } + { + // The template argument is matchable, but the instantiation is not: + auto M = classTemplateSpecializationDecl( + hasName("TemplStruct"), + hasTemplateArgument(0, + templateArgument(refersToType(asString("float")))), + has(cxxConstructorDecl(hasName("TemplStruct")))); + EXPECT_TRUE(matches(Code, traverse(TK_AsIs, M))); + EXPECT_FALSE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, M))); + } + { + // The template argument is matchable, but the instantiation is not: + auto M = classTemplateSpecializationDecl( + hasName("TemplStruct"), + hasTemplateArgument(0, + templateArgument(refersToType(asString("long")))), + has(cxxConstructorDecl(hasName("TemplStruct")))); + EXPECT_TRUE(matches(Code, traverse(TK_AsIs, M))); + EXPECT_FALSE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, M))); + } + { + // Explicit specialization is written in source and it matches: + auto M = classTemplateSpecializationDecl( + hasName("TemplStruct"), + hasTemplateArgument(0, templateArgument(refersToType(booleanType()))), + has(cxxConstructorDecl(hasName("TemplStruct"))), + has(cxxMethodDecl(hasName("boolSpecializationMethodOnly")))); + EXPECT_TRUE(matches(Code, traverse(TK_AsIs, M))); + EXPECT_TRUE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, M))); + } } template @@ -3249,10 +3337,9 @@ EXPECT_TRUE(matches( code, cxxThrowExpr(hasDescendant(integerLiteral())))); - EXPECT_TRUE(notMatches(code, - cxxThrowExpr(allOf( - hasDescendant(integerLiteral()), - has(integerLiteral()))))); + EXPECT_TRUE( + notMatches(code, cxxThrowExpr(allOf(hasDescendant(integerLiteral()), + has(integerLiteral()))))); } TEST(HasAncestor, MatchesAllAncestors) { EXPECT_TRUE(matches(