Skip to content

Commit

Permalink
Ignore template instantiations if not in AsIs mode
Browse files Browse the repository at this point in the history
Summary:
IgnoreUnlessSpelledInSource mode should ignore these because they are
not written in the source.  This matters for example when trying to
replace types or values which are templated.  The new test in
TransformerTest.cpp in this commit demonstrates the problem.

In existing matcher code, users can write
`unless(isInTemplateInstantiation())` or `unless(isInstantiated())` (the
user must know which to use).  The point of the
TK_IgnoreUnlessSpelledInSource mode is to allow the novice to avoid such
details.  This patch changes the IgnoreUnlessSpelledInSource mode to
skip over implicit template instantiations.

This patch does not change the TK_AsIs mode.

Note: An obvious attempt at an alternative implementation would simply
change the shouldVisitTemplateInstantiations() in ASTMatchFinder.cpp to
return something conditional on the operational TraversalKind.  That
does not work because shouldVisitTemplateInstantiations() is called
before a possible top-level traverse() matcher changes the operational
TraversalKind.

Reviewers: sammccall, aaron.ballman, gribozavr2, ymandel, klimek

Subscribers: cfe-commits

Tags: #clang

Differential Revision: https://reviews.llvm.org/D80961
  • Loading branch information
steveire committed Nov 2, 2020
1 parent 537cc6d commit 53df3be
Show file tree
Hide file tree
Showing 9 changed files with 295 additions and 14 deletions.
7 changes: 5 additions & 2 deletions clang/include/clang/AST/ASTNodeTraverser.h
Expand Up @@ -82,6 +82,7 @@ class ASTNodeTraverser
bool getDeserialize() const { return Deserialize; }

void SetTraversalKind(TraversalKind TK) { Traversal = TK; }
TraversalKind GetTraversalKind() const { return Traversal; }

void Visit(const Decl *D) {
getNodeDelegate().AddChild([=] {
Expand Down Expand Up @@ -481,8 +482,10 @@ class ASTNodeTraverser

Visit(D->getTemplatedDecl());

for (const auto *Child : D->specializations())
dumpTemplateDeclSpecialization(Child);
if (Traversal == TK_AsIs) {
for (const auto *Child : D->specializations())
dumpTemplateDeclSpecialization(Child);
}
}

void VisitTypeAliasDecl(const TypeAliasDecl *D) {
Expand Down
13 changes: 7 additions & 6 deletions clang/include/clang/AST/RecursiveASTVisitor.h
Expand Up @@ -461,6 +461,13 @@ template <typename Derived> class RecursiveASTVisitor {

bool canIgnoreChildDeclWhileTraversingDeclContext(const Decl *Child);

#define DEF_TRAVERSE_TMPL_INST(TMPLDECLKIND) \
bool TraverseTemplateInstantiations(TMPLDECLKIND##TemplateDecl *D);
DEF_TRAVERSE_TMPL_INST(Class)
DEF_TRAVERSE_TMPL_INST(Var)
DEF_TRAVERSE_TMPL_INST(Function)
#undef DEF_TRAVERSE_TMPL_INST

private:
// These are helper methods used by more than one Traverse* method.
bool TraverseTemplateParameterListHelper(TemplateParameterList *TPL);
Expand All @@ -469,12 +476,6 @@ template <typename Derived> class RecursiveASTVisitor {
template <typename T>
bool TraverseDeclTemplateParameterLists(T *D);

#define DEF_TRAVERSE_TMPL_INST(TMPLDECLKIND) \
bool TraverseTemplateInstantiations(TMPLDECLKIND##TemplateDecl *D);
DEF_TRAVERSE_TMPL_INST(Class)
DEF_TRAVERSE_TMPL_INST(Var)
DEF_TRAVERSE_TMPL_INST(Function)
#undef DEF_TRAVERSE_TMPL_INST
bool TraverseTemplateArgumentLocsHelper(const TemplateArgumentLoc *TAL,
unsigned Count);
bool TraverseArrayTypeLocHelper(ArrayTypeLoc TL);
Expand Down
6 changes: 6 additions & 0 deletions clang/include/clang/ASTMatchers/ASTMatchersInternal.h
Expand Up @@ -586,6 +586,10 @@ class Matcher {
return this->InnerMatcher.matches(DynTypedNode::create(*Node), Finder,
Builder);
}

llvm::Optional<clang::TraversalKind> TraversalKind() const override {
return this->InnerMatcher.getTraversalKind();
}
};

private:
Expand Down Expand Up @@ -1056,6 +1060,8 @@ class ASTMatchFinder {

virtual ASTContext &getASTContext() const = 0;

virtual bool isMatchingInImplicitTemplateInstantiation() const = 0;

protected:
virtual bool matchesChildOf(const DynTypedNode &Node, ASTContext &Ctx,
const DynTypedMatcher &Matcher,
Expand Down
8 changes: 5 additions & 3 deletions clang/lib/AST/ASTDumper.cpp
Expand Up @@ -129,9 +129,11 @@ void ASTDumper::dumpTemplateDecl(const TemplateDecl *D, bool DumpExplicitInst) {

Visit(D->getTemplatedDecl());

for (const auto *Child : D->specializations())
dumpTemplateDeclSpecialization(Child, DumpExplicitInst,
!D->isCanonicalDecl());
if (GetTraversalKind() == TK_AsIs) {
for (const auto *Child : D->specializations())
dumpTemplateDeclSpecialization(Child, DumpExplicitInst,
!D->isCanonicalDecl());
}
}

void ASTDumper::VisitFunctionTemplateDecl(const FunctionTemplateDecl *D) {
Expand Down
38 changes: 38 additions & 0 deletions clang/lib/ASTMatchers/ASTMatchFinder.cpp
Expand Up @@ -584,7 +584,45 @@ class MatchASTVisitor : public RecursiveASTVisitor<MatchASTVisitor>,
bool shouldVisitTemplateInstantiations() const { return true; }
bool shouldVisitImplicitCode() const { return true; }

bool isMatchingInImplicitTemplateInstantiation() const override {
return TraversingImplicitTemplateInstantiation;
}

bool TraverseTemplateInstantiations(ClassTemplateDecl *D) {
ImplicitTemplateInstantiationScope RAII(this, true);
return RecursiveASTVisitor<MatchASTVisitor>::TraverseTemplateInstantiations(
D);
}

bool TraverseTemplateInstantiations(VarTemplateDecl *D) {
ImplicitTemplateInstantiationScope RAII(this, true);
return RecursiveASTVisitor<MatchASTVisitor>::TraverseTemplateInstantiations(
D);
}

bool TraverseTemplateInstantiations(FunctionTemplateDecl *D) {
ImplicitTemplateInstantiationScope RAII(this, true);
return RecursiveASTVisitor<MatchASTVisitor>::TraverseTemplateInstantiations(
D);
}

private:
bool TraversingImplicitTemplateInstantiation = false;

struct ImplicitTemplateInstantiationScope {
ImplicitTemplateInstantiationScope(MatchASTVisitor *V, bool B)
: MV(V), MB(V->TraversingImplicitTemplateInstantiation) {
V->TraversingImplicitTemplateInstantiation = B;
}
~ImplicitTemplateInstantiationScope() {
MV->TraversingImplicitTemplateInstantiation = MB;
}

private:
MatchASTVisitor *MV;
bool MB;
};

class TimeBucketRegion {
public:
TimeBucketRegion() : Bucket(nullptr) {}
Expand Down
10 changes: 10 additions & 0 deletions clang/lib/ASTMatchers/ASTMatchersInternal.cpp
Expand Up @@ -284,6 +284,11 @@ bool DynTypedMatcher::matches(const DynTypedNode &DynNode,
TraversalKindScope RAII(Finder->getASTContext(),
Implementation->TraversalKind());

if (Finder->getASTContext().getParentMapContext().getTraversalKind() ==
TK_IgnoreUnlessSpelledInSource &&
Finder->isMatchingInImplicitTemplateInstantiation())
return false;

auto N =
Finder->getASTContext().getParentMapContext().traverseIgnored(DynNode);

Expand All @@ -304,6 +309,11 @@ bool DynTypedMatcher::matchesNoKindCheck(const DynTypedNode &DynNode,
TraversalKindScope raii(Finder->getASTContext(),
Implementation->TraversalKind());

if (Finder->getASTContext().getParentMapContext().getTraversalKind() ==
TK_IgnoreUnlessSpelledInSource &&
Finder->isMatchingInImplicitTemplateInstantiation())
return false;

auto N =
Finder->getASTContext().getParentMapContext().traverseIgnored(DynNode);

Expand Down
151 changes: 150 additions & 1 deletion clang/unittests/AST/ASTTraverserTest.cpp
Expand Up @@ -68,6 +68,14 @@ class NodeTreePrinter : public TextTreeStructure {
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().getAsString();
break;
}
default:
break;
}
}

template <typename... T> void Visit(T...) {}
Expand Down Expand Up @@ -243,7 +251,7 @@ FullComment

verifyWithDynNode(TA,
R"cpp(
TemplateArgument
TemplateArgument type int
`-BuiltinType
)cpp");

Expand Down Expand Up @@ -1042,4 +1050,145 @@ LambdaExpr
}
}

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);
}
)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'
|-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 ''
)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
)cpp");
}
}

} // namespace clang
12 changes: 10 additions & 2 deletions clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
Expand Up @@ -2085,9 +2085,17 @@ void actual_template_test() {
traverse(TK_AsIs,
staticAssertDecl(has(implicitCastExpr(has(
substNonTypeTemplateParmExpr(has(integerLiteral())))))))));
EXPECT_TRUE(matches(
Code, traverse(TK_IgnoreUnlessSpelledInSource,
staticAssertDecl(has(declRefExpr(
to(nonTypeTemplateParmDecl(hasName("alignment"))),
hasType(asString("unsigned int"))))))));

EXPECT_TRUE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource,
staticAssertDecl(has(integerLiteral())))));
EXPECT_TRUE(matches(Code, traverse(TK_AsIs, staticAssertDecl(hasDescendant(
integerLiteral())))));
EXPECT_FALSE(matches(
Code, traverse(TK_IgnoreUnlessSpelledInSource,
staticAssertDecl(hasDescendant(integerLiteral())))));

Code = R"cpp(
Expand Down

0 comments on commit 53df3be

Please sign in to comment.