r309809 - Unify and simplify the behavior of the hasDeclaration matcher.

Manuel Klimek via cfe-commits cfe-commits at lists.llvm.org
Wed Aug 2 06:04:44 PDT 2017


Author: klimek
Date: Wed Aug  2 06:04:44 2017
New Revision: 309809

URL: http://llvm.org/viewvc/llvm-project?rev=309809&view=rev
Log:
Unify and simplify the behavior of the hasDeclaration matcher.

Originally, we weren't able to match on Type nodes themselves (only QualType),
so the hasDeclaration matcher was initially written to give what we thought are
reasonable results for QualType matches.

When we chagned the matchers to allow matching on Type nodes, it turned out
that the hasDeclaration matcher was by chance written templated enough to now
allow hasDeclaration to also match on (some) Type nodes.

This patch change the hasDeclaration matcher to:
a) work the same on Type and QualType nodes,
b) be completely explicit about what nodes we can match instead of just allowing
   anything with a getDecl() to match,
c) explicitly control desugaring only one level in very specific instances.
d) adds hasSpecializedTemplate and tagType matchers to allow migrating
  existing use cases that now need more explicit matchers

Note: This patch breaks clang-tools-extra. The corresponding patch there
is approved and will land in a subsequent patch.

Differential Revision: https://reviews.llvm.org/D27104

Modified:
    cfe/trunk/docs/LibASTMatchersReference.html
    cfe/trunk/docs/ReleaseNotes.rst
    cfe/trunk/include/clang/ASTMatchers/ASTMatchers.h
    cfe/trunk/include/clang/ASTMatchers/ASTMatchersInternal.h
    cfe/trunk/unittests/AST/ASTImporterTest.cpp
    cfe/trunk/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp

Modified: cfe/trunk/docs/LibASTMatchersReference.html
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/docs/LibASTMatchersReference.html?rev=309809&r1=309808&r2=309809&view=diff
==============================================================================
--- cfe/trunk/docs/LibASTMatchersReference.html (original)
+++ cfe/trunk/docs/LibASTMatchersReference.html Wed Aug  2 06:04:44 2017
@@ -1729,6 +1729,21 @@ substTemplateTypeParmType() matches the
 </pre></td></tr>
 
 
+<tr><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1Type.html">Type</a>></td><td class="name" onclick="toggle('tagType0')"><a name="tagType0Anchor">tagType</a></td><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1TagType.html">TagType</a>>...</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<<a href="http://clang.llvm.org/doxygen/classclang_1_1Type.html">Type</a>></td><td class="name" onclick="toggle('templateSpecializationType0')"><a name="templateSpecializationType0Anchor">templateSpecializationType</a></td><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1TemplateSpecializationType.html">TemplateSpecializationType</a>>...</td></tr>
 <tr><td colspan="4" class="doc" id="templateSpecializationType0"><pre>Matches template specialization types.
 
@@ -4546,6 +4561,18 @@ functionDecl(hasAnyTemplateArgument(refe
 </pre></td></tr>
 
 
+<tr><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1ClassTemplateSpecializationDecl.html">ClassTemplateSpecializationDecl</a>></td><td class="name" onclick="toggle('hasSpecializedTemplate0')"><a name="hasSpecializedTemplate0Anchor">hasSpecializedTemplate</a></td><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1ClassTemplateDecl.html">ClassTemplateDecl</a>> InnerMatcher</td></tr>
+<tr><td colspan="4" class="doc" id="hasSpecializedTemplate0"><pre>Matches the specialized template of a specialization declaration.
+
+Given
+  tempalate<typename T> class A {};
+  typedef A<int> B;
+classTemplateSpecializationDecl(hasSpecializedTemplate(classTemplateDecl()))
+  matches 'B' with classTemplateDecl() matching the class template
+  declaration of 'A'.
+</pre></td></tr>
+
+
 <tr><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1ClassTemplateSpecializationDecl.html">ClassTemplateSpecializationDecl</a>></td><td class="name" onclick="toggle('hasTemplateArgument0')"><a name="hasTemplateArgument0Anchor">hasTemplateArgument</a></td><td>unsigned N, Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1TemplateArgument.html">TemplateArgument</a>> 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.

Modified: cfe/trunk/docs/ReleaseNotes.rst
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/docs/ReleaseNotes.rst?rev=309809&r1=309808&r2=309809&view=diff
==============================================================================
--- cfe/trunk/docs/ReleaseNotes.rst (original)
+++ cfe/trunk/docs/ReleaseNotes.rst Wed Aug  2 06:04:44 2017
@@ -151,8 +151,33 @@ this section should help get you past th
 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
 ------------

Modified: cfe/trunk/include/clang/ASTMatchers/ASTMatchers.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/ASTMatchers/ASTMatchers.h?rev=309809&r1=309808&r2=309809&view=diff
==============================================================================
--- cfe/trunk/include/clang/ASTMatchers/ASTMatchers.h (original)
+++ cfe/trunk/include/clang/ASTMatchers/ASTMatchers.h Wed Aug  2 06:04:44 2017
@@ -582,6 +582,23 @@ AST_MATCHER_P(FieldDecl, hasInClassIniti
           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, una
 /// 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.
 ///

Modified: cfe/trunk/include/clang/ASTMatchers/ASTMatchersInternal.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/ASTMatchers/ASTMatchersInternal.h?rev=309809&r1=309808&r2=309809&view=diff
==============================================================================
--- cfe/trunk/include/clang/ASTMatchers/ASTMatchersInternal.h (original)
+++ cfe/trunk/include/clang/ASTMatchers/ASTMatchersInternal.h Wed Aug  2 06:04:44 2017
@@ -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, NestedNameS
 
 /// \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

Modified: cfe/trunk/unittests/AST/ASTImporterTest.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/AST/ASTImporterTest.cpp?rev=309809&r1=309808&r2=309809&view=diff
==============================================================================
--- cfe/trunk/unittests/AST/ASTImporterTest.cpp (original)
+++ cfe/trunk/unittests/AST/ASTImporterTest.cpp Wed Aug  2 06:04:44 2017
@@ -252,35 +252,19 @@ AST_MATCHER_P(TemplateDecl, hasTemplateD
 
 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) {

Modified: cfe/trunk/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp?rev=309809&r1=309808&r2=309809&view=diff
==============================================================================
--- cfe/trunk/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp (original)
+++ cfe/trunk/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp Wed Aug  2 06:04:44 2017
@@ -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, HasDeclarationOfTem
   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, HasDeclarationOfCXX
               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, HasRepla
                  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




More information about the cfe-commits mailing list