This is an archive of the discontinued LLVM Phabricator instance.

Unify and simplify the behavior of the hasDeclaration matcher.
ClosedPublic

Authored by klimek on Nov 24 2016, 7:24 AM.

Details

Summary

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.

Event Timeline

klimek updated this revision to Diff 79236.Nov 24 2016, 7:24 AM
klimek retitled this revision from to Unify and simplify the behavior of the hasDeclaration matcher..
klimek updated this object.
klimek added reviewers: rsmith, lukasza.
klimek added a subscriber: cfe-commits.
lukasza edited edge metadata.Nov 28 2016, 3:21 PM

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).

unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
261

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<typename T>\n"
      "class Template {\n"
      " public:\n"
      "  void Method() {}\n"
      "};\n"
      "}  // namespace Namespace\n"
      "template <typename U>\n"
      "void Function(Namespace::Template<U> param) {\n"
      "  param.Method();\n"
      "};\n";

  // Matcher for ::Namespace::Template<T> 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)))))));
klimek updated this revision to Diff 79530.Nov 29 2016, 3:18 AM
klimek edited edge metadata.

Add tests, update hasDeclaration to work for ElaboratedType.

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.

unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
261

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

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

klimek updated this revision to Diff 108608.Jul 28 2017, 2:22 AM

Update to handle hasDeclaration for type alias template decls.

Adding Ben as reviewer.

bkramer accepted this revision.Jul 28 2017, 4:19 AM

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

This revision is now accepted and ready to land.Jul 28 2017, 4:19 AM
This revision was automatically updated to reflect the committed changes.