Undo Revert "Ignore template instantiations if not in AsIs mode"

MaskRay already fixed the ASan bug.
This commit is contained in:
Matt Morehouse 2020-11-03 13:59:01 -08:00
parent 72531ae6e6
commit a6d15d4070
9 changed files with 295 additions and 14 deletions

View File

@ -82,6 +82,7 @@ public:
bool getDeserialize() const { return Deserialize; }
void SetTraversalKind(TraversalKind TK) { Traversal = TK; }
TraversalKind GetTraversalKind() const { return Traversal; }
void Visit(const Decl *D) {
getNodeDelegate().AddChild([=] {
@ -481,8 +482,10 @@ public:
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) {

View File

@ -461,6 +461,13 @@ public:
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);
@ -469,12 +476,6 @@ private:
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);

View File

@ -586,6 +586,10 @@ public:
return this->InnerMatcher.matches(DynTypedNode::create(*Node), Finder,
Builder);
}
llvm::Optional<clang::TraversalKind> TraversalKind() const override {
return this->InnerMatcher.getTraversalKind();
}
};
private:
@ -1056,6 +1060,8 @@ public:
virtual ASTContext &getASTContext() const = 0;
virtual bool isMatchingInImplicitTemplateInstantiation() const = 0;
protected:
virtual bool matchesChildOf(const DynTypedNode &Node, ASTContext &Ctx,
const DynTypedMatcher &Matcher,

View File

@ -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) {

View File

@ -584,7 +584,45 @@ public:
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) {}

View File

@ -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);
@ -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);

View File

@ -68,6 +68,14 @@ public:
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...) {}
@ -243,7 +251,7 @@ FullComment
verifyWithDynNode(TA,
R"cpp(
TemplateArgument
TemplateArgument type int
`-BuiltinType
)cpp");
@ -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

View File

@ -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(

View File

@ -1067,6 +1067,70 @@ TEST_F(TransformerTest, ErrorOccurredMatchSkipped) {
EXPECT_EQ(ErrorCount, 0);
}
TEST_F(TransformerTest, TemplateInstantiation) {
std::string NonTemplatesInput = R"cpp(
struct S {
int m_i;
};
)cpp";
std::string NonTemplatesExpected = R"cpp(
struct S {
safe_int m_i;
};
)cpp";
std::string TemplatesInput = R"cpp(
template<typename T>
struct TemplStruct {
TemplStruct() {}
~TemplStruct() {}
private:
T m_t;
};
void instantiate()
{
TemplStruct<int> ti;
}
)cpp";
auto MatchedField = fieldDecl(hasType(asString("int"))).bind("theField");
// Changes the 'int' in 'S', but not the 'T' in 'TemplStruct':
testRule(makeRule(traverse(TK_IgnoreUnlessSpelledInSource, MatchedField),
changeTo(cat("safe_int ", name("theField")))),
NonTemplatesInput + TemplatesInput,
NonTemplatesExpected + TemplatesInput);
// In AsIs mode, template instantiations are modified, which is
// often not desired:
std::string IncorrectTemplatesExpected = R"cpp(
template<typename T>
struct TemplStruct {
TemplStruct() {}
~TemplStruct() {}
private:
safe_int m_t;
};
void instantiate()
{
TemplStruct<int> ti;
}
)cpp";
// Changes the 'int' in 'S', and (incorrectly) the 'T' in 'TemplStruct':
testRule(makeRule(traverse(TK_AsIs, MatchedField),
changeTo(cat("safe_int ", name("theField")))),
NonTemplatesInput + TemplatesInput,
NonTemplatesExpected + IncorrectTemplatesExpected);
}
// Transformation of macro source text when the change encompasses the entirety
// of the expanded text.
TEST_F(TransformerTest, SimpleMacro) {