summaryrefslogtreecommitdiffstats
path: root/clang
diff options
context:
space:
mode:
Diffstat (limited to 'clang')
-rw-r--r--clang/docs/LibASTMatchersReference.html27
-rw-r--r--clang/docs/ReleaseNotes.rst27
-rw-r--r--clang/include/clang/ASTMatchers/ASTMatchers.h32
-rw-r--r--clang/include/clang/ASTMatchers/ASTMatchersInternal.h117
-rw-r--r--clang/unittests/AST/ASTImporterTest.cpp42
-rw-r--r--clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp35
6 files changed, 212 insertions, 68 deletions
diff --git a/clang/docs/LibASTMatchersReference.html b/clang/docs/LibASTMatchersReference.html
index cb5020af49c..ac90c1e629e 100644
--- a/clang/docs/LibASTMatchersReference.html
+++ b/clang/docs/LibASTMatchersReference.html
@@ -1729,6 +1729,21 @@ substTemplateTypeParmType() matches the type of 't' but not '1'
</pre></td></tr>
+<tr><td>Matcher&lt;<a href="http://clang.llvm.org/doxygen/classclang_1_1Type.html">Type</a>&gt;</td><td class="name" onclick="toggle('tagType0')"><a name="tagType0Anchor">tagType</a></td><td>Matcher&lt;<a href="http://clang.llvm.org/doxygen/classclang_1_1TagType.html">TagType</a>&gt;...</td></tr>
+<tr><td colspan="4" class="doc" id="tagType0"><pre>Matches tag types (record and enum types).
+
+Given
+ enum E {};
+ class C {};
+
+ E e;
+ C c;
+
+tagType() matches the type of the variable declarations of both e
+and c.
+</pre></td></tr>
+
+
<tr><td>Matcher&lt;<a href="http://clang.llvm.org/doxygen/classclang_1_1Type.html">Type</a>&gt;</td><td class="name" onclick="toggle('templateSpecializationType0')"><a name="templateSpecializationType0Anchor">templateSpecializationType</a></td><td>Matcher&lt;<a href="http://clang.llvm.org/doxygen/classclang_1_1TemplateSpecializationType.html">TemplateSpecializationType</a>&gt;...</td></tr>
<tr><td colspan="4" class="doc" id="templateSpecializationType0"><pre>Matches template specialization types.
@@ -4546,6 +4561,18 @@ functionDecl(hasAnyTemplateArgument(refersToType(asString("int"))))
</pre></td></tr>
+<tr><td>Matcher&lt;<a href="http://clang.llvm.org/doxygen/classclang_1_1ClassTemplateSpecializationDecl.html">ClassTemplateSpecializationDecl</a>&gt;</td><td class="name" onclick="toggle('hasSpecializedTemplate0')"><a name="hasSpecializedTemplate0Anchor">hasSpecializedTemplate</a></td><td>Matcher&lt;<a href="http://clang.llvm.org/doxygen/classclang_1_1ClassTemplateDecl.html">ClassTemplateDecl</a>&gt; InnerMatcher</td></tr>
+<tr><td colspan="4" class="doc" id="hasSpecializedTemplate0"><pre>Matches the specialized template of a specialization declaration.
+
+Given
+ tempalate&lt;typename T&gt; class A {};
+ typedef A&lt;int&gt; B;
+classTemplateSpecializationDecl(hasSpecializedTemplate(classTemplateDecl()))
+ matches 'B' with classTemplateDecl() matching the class template
+ declaration of 'A'.
+</pre></td></tr>
+
+
<tr><td>Matcher&lt;<a href="http://clang.llvm.org/doxygen/classclang_1_1ClassTemplateSpecializationDecl.html">ClassTemplateSpecializationDecl</a>&gt;</td><td class="name" onclick="toggle('hasTemplateArgument0')"><a name="hasTemplateArgument0Anchor">hasTemplateArgument</a></td><td>unsigned N, Matcher&lt;<a href="http://clang.llvm.org/doxygen/classclang_1_1TemplateArgument.html">TemplateArgument</a>&gt; InnerMatcher</td></tr>
<tr><td colspan="4" class="doc" id="hasTemplateArgument0"><pre>Matches classTemplateSpecializations, templateSpecializationType and
functionDecl where the n'th TemplateArgument matches the given InnerMatcher.
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 97b53b13a83..49ed87d9a9a 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -151,8 +151,33 @@ this section should help get you past the largest hurdles of upgrading.
AST Matchers
------------
-...
+The hasDeclaration matcher now works the same for Type and QualType and only
+ever looks through one level of sugaring in a limited number of cases.
+
+There are two main patterns affected by this:
+
+- qualType(hasDeclaration(recordDecl(...))): previously, we would look through
+ sugar like TypedefType to get at the underlying recordDecl; now, we need
+ to explicitly remove the sugaring:
+ qualType(hasUnqualifiedDesugaredType(hasDeclaration(recordDecl(...))))
+
+- hasType(recordDecl(...)): hasType internally uses hasDeclaration; previously,
+ this matcher used to match for example TypedefTypes of the RecordType, but
+ after the change they don't; to fix, use:
+
+::
+ hasType(hasUnqualifiedDesugaredType(
+ recordType(hasDeclaration(recordDecl(...)))))
+
+- templateSpecializationType(hasDeclaration(classTemplateDecl(...))):
+ previously, we would directly match the underlying ClassTemplateDecl;
+ now, we can explicitly match the ClassTemplateSpecializationDecl, but that
+ requires to explicitly get the ClassTemplateDecl:
+::
+ templateSpecializationType(hasDeclaration(
+ classTemplateSpecializationDecl(
+ hasSpecializedTemplate(classTemplateDecl(...)))))
clang-format
------------
diff --git a/clang/include/clang/ASTMatchers/ASTMatchers.h b/clang/include/clang/ASTMatchers/ASTMatchers.h
index a4fc6faa663..b2e444322c8 100644
--- a/clang/include/clang/ASTMatchers/ASTMatchers.h
+++ b/clang/include/clang/ASTMatchers/ASTMatchers.h
@@ -582,6 +582,23 @@ AST_MATCHER_P(FieldDecl, hasInClassInitializer, internal::Matcher<Expr>,
InnerMatcher.matches(*Initializer, Finder, Builder));
}
+/// \brief Matches the specialized template of a specialization declaration.
+///
+/// Given
+/// \code
+/// tempalate<typename T> class A {};
+/// typedef A<int> B;
+/// \endcode
+/// classTemplateSpecializationDecl(hasSpecializedTemplate(classTemplateDecl()))
+/// matches 'B' with classTemplateDecl() matching the class template
+/// declaration of 'A'.
+AST_MATCHER_P(ClassTemplateSpecializationDecl, hasSpecializedTemplate,
+ internal::Matcher<ClassTemplateDecl>, InnerMatcher) {
+ const ClassTemplateDecl* Decl = Node.getSpecializedTemplate();
+ return (Decl != nullptr &&
+ InnerMatcher.matches(*Decl, Finder, Builder));
+}
+
/// \brief Matches a declaration that has been implicitly added
/// by the compiler (eg. implicit default/copy constructors).
AST_MATCHER(Decl, isImplicit) {
@@ -5083,6 +5100,21 @@ AST_TYPE_MATCHER(UnaryTransformType, unaryTransformType);
/// and \c s.
AST_TYPE_MATCHER(RecordType, recordType);
+/// \brief Matches tag types (record and enum types).
+///
+/// Given
+/// \code
+/// enum E {};
+/// class C {};
+///
+/// E e;
+/// C c;
+/// \endcode
+///
+/// \c tagType() matches the type of the variable declarations of both \c e
+/// and \c c.
+AST_TYPE_MATCHER(TagType, tagType);
+
/// \brief Matches types specified with an elaborated type keyword or with a
/// qualified name.
///
diff --git a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h
index bc75e807ced..2a85ac6c99a 100644
--- a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h
+++ b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h
@@ -728,48 +728,85 @@ public:
}
private:
- /// \brief If getDecl exists as a member of U, returns whether the inner
- /// matcher matches Node.getDecl().
- template <typename U>
- bool matchesSpecialized(
- const U &Node, ASTMatchFinder *Finder, BoundNodesTreeBuilder *Builder,
- typename std::enable_if<has_getDecl<U>::value, int>::type = 0) const {
- return matchesDecl(Node.getDecl(), Finder, Builder);
- }
-
- /// \brief Extracts the TagDecl of a QualType and returns whether the inner
- /// matcher matches on it.
+ /// \brief Forwards to matching on the underlying type of the QualType.
bool matchesSpecialized(const QualType &Node, ASTMatchFinder *Finder,
BoundNodesTreeBuilder *Builder) const {
if (Node.isNull())
return false;
- if (auto *TD = Node->getAsTagDecl())
- return matchesDecl(TD, Finder, Builder);
- else if (auto *TT = Node->getAs<TypedefType>())
- return matchesDecl(TT->getDecl(), Finder, Builder);
- // Do not use getAs<TemplateTypeParmType> instead of the direct dyn_cast.
- // Calling getAs will return the canonical type, but that type does not
- // store a TemplateTypeParmDecl. We *need* the uncanonical type, if it is
- // available, and using dyn_cast ensures that.
- else if (auto *TTP = dyn_cast<TemplateTypeParmType>(Node.getTypePtr()))
- return matchesDecl(TTP->getDecl(), Finder, Builder);
- else if (auto *OCIT = Node->getAs<ObjCInterfaceType>())
- return matchesDecl(OCIT->getDecl(), Finder, Builder);
- else if (auto *UUT = Node->getAs<UnresolvedUsingType>())
- return matchesDecl(UUT->getDecl(), Finder, Builder);
- else if (auto *ICNT = Node->getAs<InjectedClassNameType>())
- return matchesDecl(ICNT->getDecl(), Finder, Builder);
+ return matchesSpecialized(*Node, Finder, Builder);
+ }
+
+ /// \brief Finds the best declaration for a type and returns whether the inner
+ /// matcher matches on it.
+ bool matchesSpecialized(const Type &Node, ASTMatchFinder *Finder,
+ BoundNodesTreeBuilder *Builder) const {
+ // First, for any types that have a declaration, extract the declaration and
+ // match on it.
+ if (const auto *S = dyn_cast<TagType>(&Node)) {
+ return matchesDecl(S->getDecl(), Finder, Builder);
+ }
+ if (const auto *S = dyn_cast<InjectedClassNameType>(&Node)) {
+ return matchesDecl(S->getDecl(), Finder, Builder);
+ }
+ if (const auto *S = dyn_cast<TemplateTypeParmType>(&Node)) {
+ return matchesDecl(S->getDecl(), Finder, Builder);
+ }
+ if (const auto *S = dyn_cast<TypedefType>(&Node)) {
+ return matchesDecl(S->getDecl(), Finder, Builder);
+ }
+ if (const auto *S = dyn_cast<UnresolvedUsingType>(&Node)) {
+ return matchesDecl(S->getDecl(), Finder, Builder);
+ }
+ if (const auto *S = dyn_cast<ObjCObjectType>(&Node)) {
+ return matchesDecl(S->getInterface(), Finder, Builder);
+ }
+
+ // A SubstTemplateTypeParmType exists solely to mark a type substitution
+ // on the instantiated template. As users usually want to match the
+ // template parameter on the uninitialized template, we can always desugar
+ // one level without loss of expressivness.
+ // For example, given:
+ // template<typename T> struct X { T t; } class A {}; X<A> a;
+ // The following matcher will match, which otherwise would not:
+ // fieldDecl(hasType(pointerType())).
+ if (const auto *S = dyn_cast<SubstTemplateTypeParmType>(&Node)) {
+ return matchesSpecialized(S->getReplacementType(), Finder, Builder);
+ }
+
+ // For template specialization types, we want to match the template
+ // declaration, as long as the type is still dependent, and otherwise the
+ // declaration of the instantiated tag type.
+ if (const auto *S = dyn_cast<TemplateSpecializationType>(&Node)) {
+ if (!S->isTypeAlias() && S->isSugared()) {
+ // If the template is non-dependent, we want to match the instantiated
+ // tag type.
+ // For example, given:
+ // template<typename T> struct X {}; X<int> a;
+ // The following matcher will match, which otherwise would not:
+ // templateSpecializationType(hasDeclaration(cxxRecordDecl())).
+ return matchesSpecialized(*S->desugar(), Finder, Builder);
+ }
+ // If the template is dependent or an alias, match the template
+ // declaration.
+ return matchesDecl(S->getTemplateName().getAsTemplateDecl(), Finder,
+ Builder);
+ }
+
+ // FIXME: We desugar elaborated types. This makes the assumption that users
+ // do never want to match on whether a type is elaborated - there are
+ // arguments for both sides; for now, continue desugaring.
+ if (const auto *S = dyn_cast<ElaboratedType>(&Node)) {
+ return matchesSpecialized(S->desugar(), Finder, Builder);
+ }
return false;
}
- /// \brief Gets the TemplateDecl from a TemplateSpecializationType
- /// and returns whether the inner matches on it.
- bool matchesSpecialized(const TemplateSpecializationType &Node,
- ASTMatchFinder *Finder,
+ /// \brief Extracts the Decl the DeclRefExpr references and returns whether
+ /// the inner matcher matches on it.
+ bool matchesSpecialized(const DeclRefExpr &Node, ASTMatchFinder *Finder,
BoundNodesTreeBuilder *Builder) const {
- return matchesDecl(Node.getTemplateName().getAsTemplateDecl(),
- Finder, Builder);
+ return matchesDecl(Node.getDecl(), Finder, Builder);
}
/// \brief Extracts the Decl of the callee of a CallExpr and returns whether
@@ -811,6 +848,13 @@ private:
return matchesDecl(Node.getLabel(), Finder, Builder);
}
+ /// \brief Extracts the declaration of a LabelStmt and returns whether the
+ /// inner matcher matches on it.
+ bool matchesSpecialized(const LabelStmt &Node, ASTMatchFinder *Finder,
+ BoundNodesTreeBuilder *Builder) const {
+ return matchesDecl(Node.getDecl(), Finder, Builder);
+ }
+
/// \brief Returns whether the inner matcher \c Node. Returns false if \c Node
/// is \c NULL.
bool matchesDecl(const Decl *Node, ASTMatchFinder *Finder,
@@ -1016,9 +1060,10 @@ typedef TypeList<Decl, Stmt, NestedNameSpecifier, NestedNameSpecifierLoc,
/// \brief All types that are supported by HasDeclarationMatcher above.
typedef TypeList<CallExpr, CXXConstructExpr, CXXNewExpr, DeclRefExpr, EnumType,
- InjectedClassNameType, LabelStmt, AddrLabelExpr, MemberExpr,
- QualType, RecordType, TagType, TemplateSpecializationType,
- TemplateTypeParmType, TypedefType, UnresolvedUsingType>
+ ElaboratedType, InjectedClassNameType, LabelStmt,
+ AddrLabelExpr, MemberExpr, QualType, RecordType, TagType,
+ TemplateSpecializationType, TemplateTypeParmType, TypedefType,
+ UnresolvedUsingType>
HasDeclarationSupportedTypes;
/// \brief Converts a \c Matcher<T> to a matcher of desired type \c To by
diff --git a/clang/unittests/AST/ASTImporterTest.cpp b/clang/unittests/AST/ASTImporterTest.cpp
index 57b41f12b0b..485cf252c15 100644
--- a/clang/unittests/AST/ASTImporterTest.cpp
+++ b/clang/unittests/AST/ASTImporterTest.cpp
@@ -252,35 +252,19 @@ AST_MATCHER_P(TemplateDecl, hasTemplateDecl,
TEST(ImportExpr, ImportParenListExpr) {
MatchVerifier<Decl> Verifier;
- EXPECT_TRUE(
- testImport(
- "template<typename T> class dummy { void f() { dummy X(*this); } };"
- "typedef dummy<int> declToImport;"
- "template class dummy<int>;",
- Lang_CXX, "", Lang_CXX, Verifier,
- typedefDecl(
- hasType(
- templateSpecializationType(
- hasDeclaration(
- classTemplateDecl(
- hasTemplateDecl(
- cxxRecordDecl(
- hasMethod(
- allOf(
- hasName("f"),
- hasBody(
- compoundStmt(
- has(
- declStmt(
- hasSingleDecl(
- varDecl(
- hasInitializer(
- parenListExpr(
- has(
- unaryOperator(
- hasOperatorName("*"),
- hasUnaryOperand(cxxThisExpr())
- )))))))))))))))))))));
+ EXPECT_TRUE(testImport(
+ "template<typename T> class dummy { void f() { dummy X(*this); } };"
+ "typedef dummy<int> declToImport;"
+ "template class dummy<int>;",
+ Lang_CXX, "", Lang_CXX, Verifier,
+ typedefDecl(hasType(templateSpecializationType(
+ hasDeclaration(classTemplateSpecializationDecl(hasSpecializedTemplate(
+ classTemplateDecl(hasTemplateDecl(cxxRecordDecl(hasMethod(allOf(
+ hasName("f"),
+ hasBody(compoundStmt(has(declStmt(hasSingleDecl(
+ varDecl(hasInitializer(parenListExpr(has(unaryOperator(
+ hasOperatorName("*"),
+ hasUnaryOperand(cxxThisExpr()))))))))))))))))))))))));
}
TEST(ImportExpr, ImportStmtExpr) {
diff --git a/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
index 3e268435ef7..e699e19262e 100644
--- a/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
+++ b/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
@@ -230,6 +230,17 @@ TEST(HasDeclaration, HasGetDeclTraitTest) {
"Expected TemplateSpecializationType to *not* have a getDecl.");
}
+TEST(HasDeclaration, ElaboratedType) {
+ EXPECT_TRUE(matches(
+ "namespace n { template <typename T> struct X {}; }"
+ "void f(n::X<int>);",
+ parmVarDecl(hasType(qualType(hasDeclaration(cxxRecordDecl()))))));
+ EXPECT_TRUE(matches(
+ "namespace n { template <typename T> struct X {}; }"
+ "void f(n::X<int>);",
+ parmVarDecl(hasType(elaboratedType(hasDeclaration(cxxRecordDecl()))))));
+}
+
TEST(HasDeclaration, HasDeclarationOfTypeWithDecl) {
EXPECT_TRUE(matches("typedef int X; X a;",
varDecl(hasName("a"),
@@ -242,6 +253,13 @@ TEST(HasDeclaration, HasDeclarationOfTemplateSpecializationType) {
EXPECT_TRUE(matches("template <typename T> class A {}; A<int> a;",
varDecl(hasType(templateSpecializationType(
hasDeclaration(namedDecl(hasName("A"))))))));
+ EXPECT_TRUE(matches("template <typename T> class A {};"
+ "template <typename T> class B { A<T> a; };",
+ fieldDecl(hasType(templateSpecializationType(
+ hasDeclaration(namedDecl(hasName("A"))))))));
+ EXPECT_TRUE(matches("template <typename T> class A {}; A<int> a;",
+ varDecl(hasType(templateSpecializationType(
+ hasDeclaration(cxxRecordDecl()))))));
}
TEST(HasDeclaration, HasDeclarationOfCXXNewExpr) {
@@ -250,6 +268,12 @@ TEST(HasDeclaration, HasDeclarationOfCXXNewExpr) {
cxxNewExpr(hasDeclaration(functionDecl(parameterCountIs(1))))));
}
+TEST(HasDeclaration, HasDeclarationOfTypeAlias) {
+ EXPECT_TRUE(matches("template <typename T> using C = T; C<int> c;",
+ varDecl(hasType(templateSpecializationType(
+ hasDeclaration(typeAliasTemplateDecl()))))));
+}
+
TEST(HasUnqualifiedDesugaredType, DesugarsUsing) {
EXPECT_TRUE(
matches("struct A {}; using B = A; B b;",
@@ -2211,8 +2235,7 @@ TEST(Matcher, HasAnyDeclaration) {
functionDecl(hasName("bar"))))));
}
-TEST(SubstTemplateTypeParmType, HasReplacementType)
-{
+TEST(SubstTemplateTypeParmType, HasReplacementType) {
std::string Fragment = "template<typename T>"
"double F(T t);"
"int i;"
@@ -2228,5 +2251,13 @@ TEST(SubstTemplateTypeParmType, HasReplacementType)
substTemplateTypeParmType(hasReplacementType(qualType()))));
}
+TEST(ClassTemplateSpecializationDecl, HasSpecializedTemplate) {
+ auto Matcher = classTemplateSpecializationDecl(
+ hasSpecializedTemplate(classTemplateDecl()));
+ EXPECT_TRUE(
+ matches("template<typename T> class A {}; typedef A<int> B;", Matcher));
+ EXPECT_TRUE(notMatches("template<typename T> class A {};", Matcher));
+}
+
} // namespace ast_matchers
} // namespace clang
OpenPOWER on IntegriCloud