[PATCH] D27104: Unify and simplify the behavior of the hasDeclaration matcher.

2017-08-02 Thread Manuel Klimek via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rL309809: Unify and simplify the behavior of the 
hasDeclaration matcher. (authored by klimek).

Changed prior to commit:
  https://reviews.llvm.org/D27104?vs=108608=109325#toc

Repository:
  rL LLVM

https://reviews.llvm.org/D27104

Files:
  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

Index: cfe/trunk/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
===
--- cfe/trunk/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
+++ cfe/trunk/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
@@ -230,6 +230,17 @@
 "Expected TemplateSpecializationType to *not* have a getDecl.");
 }
 
+TEST(HasDeclaration, ElaboratedType) {
+  EXPECT_TRUE(matches(
+  "namespace n { template  struct X {}; }"
+  "void f(n::X);",
+  parmVarDecl(hasType(qualType(hasDeclaration(cxxRecordDecl()));
+  EXPECT_TRUE(matches(
+  "namespace n { template  struct X {}; }"
+  "void f(n::X);",
+  parmVarDecl(hasType(elaboratedType(hasDeclaration(cxxRecordDecl()));
+}
+
 TEST(HasDeclaration, HasDeclarationOfTypeWithDecl) {
   EXPECT_TRUE(matches("typedef int X; X a;",
   varDecl(hasName("a"),
@@ -242,14 +253,27 @@
   EXPECT_TRUE(matches("template  class A {}; A a;",
   varDecl(hasType(templateSpecializationType(
 hasDeclaration(namedDecl(hasName("A";
+  EXPECT_TRUE(matches("template  class A {};"
+  "template  class B { A a; };",
+  fieldDecl(hasType(templateSpecializationType(
+hasDeclaration(namedDecl(hasName("A";
+  EXPECT_TRUE(matches("template  class A {}; A a;",
+  varDecl(hasType(templateSpecializationType(
+  hasDeclaration(cxxRecordDecl()));
 }
 
 TEST(HasDeclaration, HasDeclarationOfCXXNewExpr) {
   EXPECT_TRUE(
   matches("int *A = new int();",
   cxxNewExpr(hasDeclaration(functionDecl(parameterCountIs(1));
 }
 
+TEST(HasDeclaration, HasDeclarationOfTypeAlias) {
+  EXPECT_TRUE(matches("template  using C = T; C c;",
+  varDecl(hasType(templateSpecializationType(
+  hasDeclaration(typeAliasTemplateDecl()));
+}
+
 TEST(HasUnqualifiedDesugaredType, DesugarsUsing) {
   EXPECT_TRUE(
   matches("struct A {}; using B = A; B b;",
@@ -2211,8 +2235,7 @@
functionDecl(hasName("bar"));
 }
 
-TEST(SubstTemplateTypeParmType, HasReplacementType)
-{
+TEST(SubstTemplateTypeParmType, HasReplacementType) {
   std::string Fragment = "template"
  "double F(T t);"
  "int i;"
@@ -2228,5 +2251,13 @@
  substTemplateTypeParmType(hasReplacementType(qualType();
 }
 
+TEST(ClassTemplateSpecializationDecl, HasSpecializedTemplate) {
+  auto Matcher = classTemplateSpecializationDecl(
+  hasSpecializedTemplate(classTemplateDecl()));
+  EXPECT_TRUE(
+  matches("template class A {}; typedef A B;", Matcher));
+  EXPECT_TRUE(notMatches("template class A {};", Matcher));
+}
+
 } // namespace ast_matchers
 } // namespace clang
Index: cfe/trunk/unittests/AST/ASTImporterTest.cpp
===
--- cfe/trunk/unittests/AST/ASTImporterTest.cpp
+++ cfe/trunk/unittests/AST/ASTImporterTest.cpp
@@ -252,35 +252,19 @@
 
 TEST(ImportExpr, ImportParenListExpr) {
   MatchVerifier Verifier;
-  EXPECT_TRUE(
-testImport(
-  "template class dummy { void f() { dummy X(*this); } };"
-  "typedef dummy declToImport;"
-  "template class dummy;",
-  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("*"),
-

[PATCH] D27104: Unify and simplify the behavior of the hasDeclaration matcher.

2017-07-28 Thread Benjamin Kramer via Phabricator via cfe-commits
bkramer accepted this revision.
bkramer added a comment.
This revision is now accepted and ready to land.

lg under the precondition that clang-tidy tests still work.


https://reviews.llvm.org/D27104



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D27104: Unify and simplify the behavior of the hasDeclaration matcher.

2017-07-28 Thread Manuel Klimek via Phabricator via cfe-commits
klimek added a reviewer: bkramer.
klimek added a comment.

Adding Ben as reviewer.


https://reviews.llvm.org/D27104



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D27104: Unify and simplify the behavior of the hasDeclaration matcher.

2017-07-28 Thread Manuel Klimek via Phabricator via cfe-commits
klimek updated this revision to Diff 108608.
klimek added a comment.

Update to handle hasDeclaration for type alias template decls.


https://reviews.llvm.org/D27104

Files:
  include/clang/ASTMatchers/ASTMatchers.h
  include/clang/ASTMatchers/ASTMatchersInternal.h
  unittests/AST/ASTImporterTest.cpp
  unittests/ASTMatchers/ASTMatchersTraversalTest.cpp

Index: unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
===
--- unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
+++ unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
@@ -230,6 +230,17 @@
 "Expected TemplateSpecializationType to *not* have a getDecl.");
 }
 
+TEST(HasDeclaration, ElaboratedType) {
+  EXPECT_TRUE(matches(
+  "namespace n { template  struct X {}; }"
+  "void f(n::X);",
+  parmVarDecl(hasType(qualType(hasDeclaration(cxxRecordDecl()));
+  EXPECT_TRUE(matches(
+  "namespace n { template  struct X {}; }"
+  "void f(n::X);",
+  parmVarDecl(hasType(elaboratedType(hasDeclaration(cxxRecordDecl()));
+}
+
 TEST(HasDeclaration, HasDeclarationOfTypeWithDecl) {
   EXPECT_TRUE(matches("typedef int X; X a;",
   varDecl(hasName("a"),
@@ -242,14 +253,27 @@
   EXPECT_TRUE(matches("template  class A {}; A a;",
   varDecl(hasType(templateSpecializationType(
 hasDeclaration(namedDecl(hasName("A";
+  EXPECT_TRUE(matches("template  class A {};"
+  "template  class B { A a; };",
+  fieldDecl(hasType(templateSpecializationType(
+hasDeclaration(namedDecl(hasName("A";
+  EXPECT_TRUE(matches("template  class A {}; A a;",
+  varDecl(hasType(templateSpecializationType(
+  hasDeclaration(cxxRecordDecl()));
 }
 
 TEST(HasDeclaration, HasDeclarationOfCXXNewExpr) {
   EXPECT_TRUE(
   matches("int *A = new int();",
   cxxNewExpr(hasDeclaration(functionDecl(parameterCountIs(1));
 }
 
+TEST(HasDeclaration, HasDeclarationOfTypeAlias) {
+  EXPECT_TRUE(matches("template  using C = T; C c;",
+  varDecl(hasType(templateSpecializationType(
+  hasDeclaration(typeAliasTemplateDecl()));
+}
+
 TEST(HasUnqualifiedDesugaredType, DesugarsUsing) {
   EXPECT_TRUE(
   matches("struct A {}; using B = A; B b;",
@@ -2207,8 +2231,7 @@
functionDecl(hasName("bar"));
 }
 
-TEST(SubstTemplateTypeParmType, HasReplacementType)
-{
+TEST(SubstTemplateTypeParmType, HasReplacementType) {
   std::string Fragment = "template"
  "double F(T t);"
  "int i;"
@@ -2224,5 +2247,13 @@
  substTemplateTypeParmType(hasReplacementType(qualType();
 }
 
+TEST(ClassTemplateSpecializationDecl, HasSpecializedTemplate) {
+  auto Matcher = classTemplateSpecializationDecl(
+  hasSpecializedTemplate(classTemplateDecl()));
+  EXPECT_TRUE(
+  matches("template class A {}; typedef A B;", Matcher));
+  EXPECT_TRUE(notMatches("template class A {};", Matcher));
+}
+
 } // namespace ast_matchers
 } // namespace clang
Index: unittests/AST/ASTImporterTest.cpp
===
--- unittests/AST/ASTImporterTest.cpp
+++ unittests/AST/ASTImporterTest.cpp
@@ -252,35 +252,19 @@
 
 TEST(ImportExpr, ImportParenListExpr) {
   MatchVerifier Verifier;
-  EXPECT_TRUE(
-testImport(
-  "template class dummy { void f() { dummy X(*this); } };"
-  "typedef dummy declToImport;"
-  "template class dummy;",
-  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 class dummy { void f() { dummy X(*this); } };"
+  "typedef dummy declToImport;"
+  "template class dummy;",
+  Lang_CXX, "", Lang_CXX, Verifier,
+  

[PATCH] D27104: Unify and simplify the behavior of the hasDeclaration matcher.

2016-11-29 Thread Łukasz Anforowicz via Phabricator via cfe-commits
lukasza added a comment.

Do we also need to update the documentation (e.g. to say that ElaboratedType is 
covered by hasDeclaration)?

Other than that, I think this CL LGTM, although I would like to have a 
specific, working equivalent of the old, deep-maching hasDeclaration (at least 
when the inner, deep match is for tag types).  Let's continue this discussion 
in https://reviews.llvm.org/D27207


https://reviews.llvm.org/D27104



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D27104: Unify and simplify the behavior of the hasDeclaration matcher.

2016-11-29 Thread Manuel Klimek via Phabricator via cfe-commits
klimek added a comment.

In https://reviews.llvm.org/D27104#607161, @lukasza wrote:

> Forcing shallow matching means that unit test below will stop passing after 
> this CL.
>
>   TEST(HasDeclaration, DeepTagType) {
> std::string input =
> "class Foo {};\n"
> "using Bar = Foo;\n"
> "void Function(Bar param) {}\n";
>   
> // Matcher for declaration of the Foo class.
> auto param_type_decl_matcher = cxxRecordDecl(hasName("Foo"));
>   
> // hasDeclaration / qualType-flavour.
> EXPECT_TRUE(matches(input, parmVarDecl(
> hasName("param"),
> hasType(qualType(hasDeclaration(decl(param_type_decl_matcher)));
>   }
>   
>
> I am working on a tool that renames methods in a specific namespace - the 
> tool depends on hasDeclaration doing deep-matching to tell whether an 
> UnresolvedUsingValueDecl and/or CXXDependentScopeMemberExpr end up pointing 
> at a record or template from the namespace of interest.
>
> Q1: I assume that the breaking aspect of this CL is understood and accepted?


Yes. That this worked was by chance.

> Q2: Can you please provide an example code (here or in the CL description) 
> for how to achieve deep matching (since AFAIU hasDeclaration would no longer 
> do deep matching after the CL under review)?  Can deep matching be achieved 
> by combining existing matchers with the now shallow hasDeclaration?  Or does 
> deep matching require authoring a custom matcher?  How would the custom 
> matcher look like (i.e. would it have to consider all possible types that can 
> be desugared - e.g. requiring separate code for each possible type node - 
> i.e. having separate code for hopping over a type alias and over other type 
> nodes).

I think we should either:
a) add a hasAnyDeclaration matcher, or
b) add a desugarsTo() matcher that just triest to run getAs<> on the node.

I'm going to get you an implementation in a different CL.




Comment at: unittests/ASTMatchers/ASTMatchersTraversalTest.cpp:250
 }
 
 TEST(HasDeclaration, HasDeclarationOfCXXNewExpr) {

lukasza wrote:
> Could you please add a unit test that covers a scenario made possible by your 
> CL and which involves an elaborated type?
> 
> TEST(HasDeclaration, ElaboratedTypeAndTemplateSpecializationType) {
>   std::string input =
>   "namespace Namespace {\n"
>   "template\n"
>   "class Template {\n"
>   " public:\n"
>   "  void Method() {}\n"
>   "};\n"
>   "}  // namespace Namespace\n"
>   "template \n"
>   "void Function(Namespace::Template param) {\n"
>   "  param.Method();\n"
>   "};\n";
> 
>   // Matcher for ::Namespace::Template template decl.
>   auto param_type_decl_matcher = classTemplateDecl(
>   hasName("Template"),
>   hasParent(namespaceDecl(hasName("Namespace"))),
>   has(templateTypeParmDecl(hasName("T";
> 
>   // hasDeclaration / qualType-flavour.
>   EXPECT_TRUE(matches(input, parmVarDecl(
>   hasName("param"),
>   hasType(qualType(hasDeclaration(decl(param_type_decl_matcher)));
> }
> 
> I was a little bit surprised that ElaboratedType is handled when going via 
> qualType, but not when matching the type directly - the test code below (an 
> extension of the unit test proposed above) fails to compile.  Is this 
> expected (for this CL?  for long-term?)?
> 
>   // hasDeclaration / elaboratedType-flavour.
>   EXPECT_TRUE(matches(input, parmVarDecl(
>   hasName("param"),
>   
> hasType(elaboratedType(hasDeclaration(decl(param_type_decl_matcher)));
> 

Added unit test and added ElaboratedType to the list of allowed types for 
hasDeclaration.


https://reviews.llvm.org/D27104



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D27104: Unify and simplify the behavior of the hasDeclaration matcher.

2016-11-29 Thread Manuel Klimek via Phabricator via cfe-commits
klimek updated this revision to Diff 79530.
klimek added a comment.

Add tests, update hasDeclaration to work for ElaboratedType.


https://reviews.llvm.org/D27104

Files:
  include/clang/ASTMatchers/ASTMatchersInternal.h
  unittests/ASTMatchers/ASTMatchersTraversalTest.cpp

Index: unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
===
--- unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
+++ unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
@@ -227,6 +227,17 @@
   EXPECT_FALSE(internal::has_getDecl::value);
 }
 
+TEST(HasDeclaration, ElaboratedType) {
+  EXPECT_TRUE(matches(
+  "namespace n { template  struct X {}; }"
+  "void f(n::X);",
+  parmVarDecl(hasType(qualType(hasDeclaration(cxxRecordDecl()));
+  EXPECT_TRUE(matches(
+  "namespace n { template  struct X {}; }"
+  "void f(n::X);",
+  parmVarDecl(hasType(elaboratedType(hasDeclaration(cxxRecordDecl()));
+}
+
 TEST(HasDeclaration, HasDeclarationOfTypeWithDecl) {
   EXPECT_TRUE(matches("typedef int X; X a;",
   varDecl(hasName("a"),
@@ -239,6 +250,13 @@
   EXPECT_TRUE(matches("template  class A {}; A a;",
   varDecl(hasType(templateSpecializationType(
 hasDeclaration(namedDecl(hasName("A";
+  EXPECT_TRUE(matches("template  class A {};"
+  "template  class B { A a; };",
+  fieldDecl(hasType(templateSpecializationType(
+hasDeclaration(namedDecl(hasName("A";
+  EXPECT_TRUE(matches("template  class A {}; A a;",
+  varDecl(hasType(templateSpecializationType(
+  hasDeclaration(cxxRecordDecl()));
 }
 
 TEST(HasDeclaration, HasDeclarationOfCXXNewExpr) {
Index: include/clang/ASTMatchers/ASTMatchersInternal.h
===
--- include/clang/ASTMatchers/ASTMatchersInternal.h
+++ include/clang/ASTMatchers/ASTMatchersInternal.h
@@ -728,48 +728,84 @@
   }
 
 private:
-  /// \brief If getDecl exists as a member of U, returns whether the inner
-  /// matcher matches Node.getDecl().
-  template 
-  bool matchesSpecialized(
-  const U , ASTMatchFinder *Finder, BoundNodesTreeBuilder *Builder,
-  typename std::enable_if::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 , 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())
-  return matchesDecl(TT->getDecl(), Finder, Builder);
-// Do not use getAs 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(Node.getTypePtr()))
-  return matchesDecl(TTP->getDecl(), Finder, Builder);
-else if (auto *OCIT = Node->getAs())
-  return matchesDecl(OCIT->getDecl(), Finder, Builder);
-else if (auto *UUT = Node->getAs())
-  return matchesDecl(UUT->getDecl(), Finder, Builder);
-else if (auto *ICNT = Node->getAs())
-  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 , 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()) {
+  return matchesDecl(S->getDecl(), Finder, Builder);
+}
+if (const auto *S = dyn_cast()) {
+  return matchesDecl(S->getDecl(), Finder, Builder);
+}
+if (const auto *S = dyn_cast()) {
+  return matchesDecl(S->getDecl(), Finder, Builder);
+}
+if (const auto *S = dyn_cast()) {
+  return matchesDecl(S->getDecl(), Finder, Builder);
+}
+if (const auto *S = dyn_cast()) {
+  return matchesDecl(S->getDecl(), Finder, Builder);
+}
+if (const auto *S = dyn_cast()) {
+  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 

[PATCH] D27104: Unify and simplify the behavior of the hasDeclaration matcher.

2016-11-28 Thread Łukasz Anforowicz via Phabricator via cfe-commits
lukasza added a comment.

Forcing shallow matching means that unit test below will stop passing after 
this CL.

  TEST(HasDeclaration, DeepTagType) {
std::string input =
"class Foo {};\n"
"using Bar = Foo;\n"
"void Function(Bar param) {}\n";
  
// Matcher for declaration of the Foo class.
auto param_type_decl_matcher = cxxRecordDecl(hasName("Foo"));
  
// hasDeclaration / qualType-flavour.
EXPECT_TRUE(matches(input, parmVarDecl(
hasName("param"),
hasType(qualType(hasDeclaration(decl(param_type_decl_matcher)));
  }

I am working on a tool that renames methods in a specific namespace - the tool 
depends on hasDeclaration doing deep-matching to tell whether an 
UnresolvedUsingValueDecl and/or CXXDependentScopeMemberExpr end up pointing at 
a record or template from the namespace of interest.

Q1: I assume that the breaking aspect of this CL is understood and accepted?

Q2: Can you please provide an example code (here or in the CL description) for 
how to achieve deep matching (since AFAIU hasDeclaration would no longer do 
deep matching after the CL under review)?  Can deep matching be achieved by 
combining existing matchers with the now shallow hasDeclaration?  Or does deep 
matching require authoring a custom matcher?  How would the custom matcher look 
like (i.e. would it have to consider all possible types that can be desugared - 
e.g. requiring separate code for each possible type node - i.e. having separate 
code for hopping over a type alias and over other type nodes).




Comment at: unittests/ASTMatchers/ASTMatchersTraversalTest.cpp:250
 }
 
 TEST(HasDeclaration, HasDeclarationOfCXXNewExpr) {

Could you please add a unit test that covers a scenario made possible by your 
CL and which involves an elaborated type?

TEST(HasDeclaration, ElaboratedTypeAndTemplateSpecializationType) {
  std::string input =
  "namespace Namespace {\n"
  "template\n"
  "class Template {\n"
  " public:\n"
  "  void Method() {}\n"
  "};\n"
  "}  // namespace Namespace\n"
  "template \n"
  "void Function(Namespace::Template param) {\n"
  "  param.Method();\n"
  "};\n";

  // Matcher for ::Namespace::Template template decl.
  auto param_type_decl_matcher = classTemplateDecl(
  hasName("Template"),
  hasParent(namespaceDecl(hasName("Namespace"))),
  has(templateTypeParmDecl(hasName("T";

  // hasDeclaration / qualType-flavour.
  EXPECT_TRUE(matches(input, parmVarDecl(
  hasName("param"),
  hasType(qualType(hasDeclaration(decl(param_type_decl_matcher)));
}

I was a little bit surprised that ElaboratedType is handled when going via 
qualType, but not when matching the type directly - the test code below (an 
extension of the unit test proposed above) fails to compile.  Is this expected 
(for this CL?  for long-term?)?

  // hasDeclaration / elaboratedType-flavour.
  EXPECT_TRUE(matches(input, parmVarDecl(
  hasName("param"),
  hasType(elaboratedType(hasDeclaration(decl(param_type_decl_matcher)));



https://reviews.llvm.org/D27104



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D27104: Unify and simplify the behavior of the hasDeclaration matcher.

2016-11-24 Thread Manuel Klimek via cfe-commits
klimek created this revision.
klimek added reviewers: rsmith, lukasza.
klimek added a subscriber: cfe-commits.

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.


https://reviews.llvm.org/D27104

Files:
  include/clang/ASTMatchers/ASTMatchersInternal.h
  unittests/ASTMatchers/ASTMatchersTraversalTest.cpp

Index: unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
===
--- unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
+++ unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
@@ -239,6 +239,13 @@
   EXPECT_TRUE(matches("template  class A {}; A a;",
   varDecl(hasType(templateSpecializationType(
 hasDeclaration(namedDecl(hasName("A";
+  EXPECT_TRUE(matches("template  class A {};"
+  "template  class B { A a; };",
+  fieldDecl(hasType(templateSpecializationType(
+hasDeclaration(namedDecl(hasName("A";
+  EXPECT_TRUE(matches("template  class A {}; A a;",
+  varDecl(hasType(templateSpecializationType(
+  hasDeclaration(cxxRecordDecl()));
 }
 
 TEST(HasDeclaration, HasDeclarationOfCXXNewExpr) {
Index: include/clang/ASTMatchers/ASTMatchersInternal.h
===
--- include/clang/ASTMatchers/ASTMatchersInternal.h
+++ include/clang/ASTMatchers/ASTMatchersInternal.h
@@ -728,48 +728,84 @@
   }
 
 private:
-  /// \brief If getDecl exists as a member of U, returns whether the inner
-  /// matcher matches Node.getDecl().
-  template 
-  bool matchesSpecialized(
-  const U , ASTMatchFinder *Finder, BoundNodesTreeBuilder *Builder,
-  typename std::enable_if::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 , 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())
-  return matchesDecl(TT->getDecl(), Finder, Builder);
-// Do not use getAs 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(Node.getTypePtr()))
-  return matchesDecl(TTP->getDecl(), Finder, Builder);
-else if (auto *OCIT = Node->getAs())
-  return matchesDecl(OCIT->getDecl(), Finder, Builder);
-else if (auto *UUT = Node->getAs())
-  return matchesDecl(UUT->getDecl(), Finder, Builder);
-else if (auto *ICNT = Node->getAs())
-  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 , 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()) {
+  return matchesDecl(S->getDecl(), Finder, Builder);
+}
+if (const auto *S = dyn_cast()) {
+  return matchesDecl(S->getDecl(), Finder, Builder);
+}
+if (const auto *S = dyn_cast()) {
+  return matchesDecl(S->getDecl(), Finder, Builder);
+}
+if (const auto *S = dyn_cast()) {
+  return matchesDecl(S->getDecl(), Finder, Builder);
+}
+if (const auto *S = dyn_cast()) {
+  return matchesDecl(S->getDecl(), Finder, Builder);
+}
+if (const auto *S = dyn_cast()) {
+  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