[PATCH] D8149: Add hasUnderlyingType narrowing matcher for TypedefDecls, functionProtoType matcher for FunctionProtoType nodes, extend parameterCountIs to FunctionProtoType nodes

Aaron Ballman via cfe-commits cfe-commits at lists.llvm.org
Thu Jan 7 07:29:40 PST 2016


aaron.ballman added inline comments.

================
Comment at: unittests/ASTMatchers/ASTMatchersTest.cpp:1567
@@ -1566,1 +1566,3 @@
+  EXPECT_TRUE(matches("void f(...);", functionDecl(parameterCountIs(0))));
+  EXPECT_TRUE(matches("void f(int, ...);", functionDecl(parameterCountIs(1))));
 }
----------------
What should be the outcome of this test (matches or not matches)?
```
matchesC("void f();", functionDecl(parameterCountIs(0)));
```
(Note, it's using C where this is variadic.) My guess is that it should match, but I'd like to make sure (and have a test for it).

================
Comment at: unittests/ASTMatchers/ASTMatchersTest.cpp:4994
@@ +4993,3 @@
+  EXPECT_TRUE(matches("typedef int hasUnderlyingTypeTest;",
+                      typedefDecl(hasUnderlyingType(asString("int")))));
+  EXPECT_TRUE(matches("typedef const int T;",
----------------
Thank you for those examples! Given the following code:
```
typedef int foo;
typedef foo bar;

bar i;
```
clang-query> match varDecl(hasType(asString("int")))
0 matches.
clang-query> match varDecl(hasType(asString("foo")))
0 matches.
clang-query> match varDecl(hasType(asString("bar")))

Match #1:

E:\Desktop\t.cpp:4:1: note: "root" binds here
bar i;
^~~~~
1 match.

So hasType() looks at what the immediate type is for the declaration (which we document, yay us!). Based on that, I don't think hasUnderlyingType() makes sense -- you should modify hasType() to work on a TypedefNameDecl (not just a TypedefDecl!) so that it looks at the immediate type of the type definition. I would expect your tests then to result in:
```
1: typedef void (fn)(void);
2: typedef fn foo;
3: typedef int bar;
4: typedef int (f);
5: typedef int (fn2)(int);
clang-query> match typedefDecl(hasType(asString("int")))

Match #1:

/tmp/a.cpp:3:1: note: "root" binds here
typedef int bar;
^~~~~~~~~~~~~~~

Match #2:

/tmp/a.cpp:4:1: note: "root" binds here
typedef int (f);
^~~~~~~~~~~~~~~
2 matches.
clang-query> match typedefDecl(hasType(typedefType()))

Match #1:

/tmp/a.cpp:2:1: note: "root" binds here
typedef fn foo;
^~~~~~~~~~~~~~
1 match.
clang-query> match typedefDecl(hasType(parenType()))

Match #1:

/tmp/a.cpp:1:1: note: "root" binds here
typedef void (fn)(void);
^~~~~~~~~~~~~~~~~~~~~~~

Match #2:

/tmp/a.cpp:4:1: note: "root" binds here
typedef int (f);
^~~~~~~~~~~~~~~

Match #3:

/tmp/a.cpp:5:1: note: "root" binds here
typedef int (fn2)(int);
^~~~~~~~~~~~~~~~~~~~~~
3 matches.
clang-query> match typedefDecl(hasType(parenType(innerType(functionType()))))

Match #1:

/tmp/a.cpp:1:1: note: "root" binds here
typedef void (fn)(void);
^~~~~~~~~~~~~~~~~~~~~~~

Match #2:

/tmp/a.cpp:5:1: note: "root" binds here
typedef int (fn2)(int);
^~~~~~~~~~~~~~~~~~~~~~
2 matches.
```
The end results are the same, so this is just changing the way the information is surfaced to the user that is logically consistent. ValueDecls have an immediate type, and so do TypedefDecls. By using TypedefNameDecl instead of TypedefDecl, this also covers the case where hasType() is useful for an alias-declaration. (We don't expose the matcher for that yet, but it seems quite reasonable to add in the future, and it would be nice for hasType to automatically work with that.)

You can implement this with a helper function to handle abstracting away the call to getType() vs getUnderlyingType(), then updating the hasType() matchers to use it. Something like:
```
template <typename Ty>
struct UnderlyingTypeGetter {
  static QualType get(const Ty &Node) {
    return Node.getType();
  }
};

template <>
QualType UnderlyingTypeGetter<TypedefNameDecl>::get(const TypedefNameDecl &Node) {
  return Node.getUnderlyingType();
}
```
(Somewhere in ASTMatchersInternal.h most likely.)



http://reviews.llvm.org/D8149





More information about the cfe-commits mailing list