Reputation: 526
I have a piece of code:
typedef struct S1{
int a;
int b;
} S, *PS;
I can get following AST with clang-check:
| |-CXXRecordDecl 0x3dfde48 col:16 implicit struct S1
| |-FieldDecl 0x3dfdef8 col:9 a 'int'
| -FieldDecl 0x3dfdf58 <line:4:5, col:9> col:9 b 'int'
|-TypedefDecl 0x3dfe010 <line:1:1, line:5:3> col:3 S 'struct S1':'struct S1'
|
-ElaboratedType 0x3dfdfc0 'struct S1' sugar
| -RecordType 0x3dfddc0 'struct S1'
|
-CXXRecord 0x3dfdd28 'S1'
-TypedefDecl 0x3dfe0f0 <line:1:1, line:5:7> col:7 PS 'struct S1 *'
-PointerType 0x3dfe0a0 'struct S1 *'
-ElaboratedType 0x3dfdfc0 'struct S1' sugar
-RecordType 0x3dfddc0 'struct S1'
`-CXXRecord 0x3dfdd28 'S1'
If I use typedefDecl(), I can match S and PS, but how can I get the underlying cxxRecordDecl() ?
Upvotes: 1
Views: 881
Reputation: 1111
One approach is to qualify the typedefDecl
with traversal matchers. The first hop is to the type of the typedef, the second hop is to the declaration of that type. This terminates in the cxxRecordDecl that you are looking for.
typedefDecl(
hasType(
hasDeclaration(
cxxRecordDecl().bind("the_struct")
))).bind("the_typedef")
This works, but it has (at least) two problems. First, it also matches things you likely don't want matched, and second, it fails to match the pointer typedef declaration in your code. To see the first problem, run that matcher in clang-query
. Putting your fragment into test_input_struct_type.cpp:
$ clang-query test_input_struct_type.cpp --
clang-query> let m1 typedefDecl( hasType( hasDeclaration(cxxRecordDecl().bind("the_struct"))))
clang-query> m m1
Match #1:
Match #2:
test_input_struct_type.cpp:1:1: note: "root" binds here
typedef struct S1{
^~~~~~~~~~~~~~~~~~
test_input_struct_type.cpp:1:9: note: "the_struct" binds here
typedef struct S1{
^~~~~~~~~~
test_input_struct_type.cpp:1:1: note: "the_typedef" binds here
typedef struct S1{
^~~~~~~~~~~~~~~~~~
2 matches.
Match #2 looks fine, but what was match #1? I suspect that matcher is hitting some of the typedef nodes that seem to be inserted by the compiler at the beginning of the AST for the translation unit.
One way to fix the first problem is to add some more specificity:
typedefDecl(
hasType(
elaboratedType(
namesType(
recordType(
hasDeclaration(
cxxRecordDecl().bind("the_struct")
))))).bind("the_typedef")
Back in clang-query
:
clang-query> let m2 typedefDecl(hasType(elaboratedType( namesType( recordType( hasDeclaration(cxxRecordDecl().bind("the_struct")))) ))).bind("the_typedef")
clang-query> m m2
Match #1:
test_input_struct_type.cpp:1:1: note: "root" binds here
typedef struct S1{
^~~~~~~~~~~~~~~~~~
test_input_struct_type.cpp:1:9: note: "the_struct" binds here
typedef struct S1{
^~~~~~~~~~
test_input_struct_type.cpp:1:1: note: "the_typedef" binds here
typedef struct S1{
^~~~~~~~~~~~~~~~~~
1 match.
What about the second problem--finding the pointer typedef? That requires a slightly different matcher:
typedefDecl(
hasType(
pointerType(
pointee(
hasDeclaration(
cxxRecordDecl().bind("pointee_struct")
))))).bind("the_typedef")
The two matchers can then be combined using anyOf
. Back in clang-query
:
clang-query> let m2a hasType(elaboratedType( namesType( recordType( hasDeclaration(cxxRecordDecl().bind("the_struct"))))))
clang-query> let m3a hasType(pointerType( pointee( hasDeclaration(cxxRecordDecl().bind("pointee_struct")))))
clang-query> let m4 typedefDecl( anyOf(m2a,m3a)).bind("the_typedef")
clang-query> m m4
Match #1:
test_input_struct_type.cpp:1:1: note: "root" binds here
typedef struct S1{
^~~~~~~~~~~~~~~~~~
test_input_struct_type.cpp:1:9: note: "the_struct" binds here
typedef struct S1{
^~~~~~~~~~
test_input_struct_type.cpp:1:1: note: "the_typedef" binds here
typedef struct S1{
^~~~~~~~~~~~~~~~~~
Match #2:
test_input_struct_type.cpp:1:9: note: "pointee_struct" binds here
typedef struct S1{
^~~~~~~~~~
test_input_struct_type.cpp:1:1: note: "root" binds here
typedef struct S1{
^~~~~~~~~~~~~~~~~~
test_input_struct_type.cpp:1:1: note: "the_typedef" binds here
typedef struct S1{
^~~~~~~~~~~~~~~~~~
2 matches.
Upvotes: 2