Is this a bug? I’d expect there to be only one match.
The goal is to find variables whose type refers to a class instantiation, is this a good approach?
Behavior occurs in clang-query and the C++ API on Apple, on both clang 11.0.0 and 10.0.1; didn’t yet have the possibility to test 11.0.1.
I get two matches which are exactly the same:
Match #1:
Binding for "root":
VarTemplateSpecializationDecl 0x7fe879820a28 <test:16:1, col:6> col:6 used c 'C<float>':'C<float>' callinit
>-TemplateArgument type 'float'
> `-BuiltinType 0x7fe87901ee00 'float'
`-CXXConstructExpr 0x7fe879852190 <col:6> 'C<float>':'C<float>' 'void () noexcept'
Match #2:
Binding for "root":
VarTemplateSpecializationDecl 0x7fe879820a28 <test:16:1, col:6> col:6 used c 'C<float>':'C<float>' callinit
>-TemplateArgument type 'float'
> `-BuiltinType 0x7fe87901ee00 'float'
`-CXXConstructExpr 0x7fe879852190 <col:6> 'C<float>':'C<float>' 'void () noexcept'
Is this a bug? I'd expect there to be only one match.
The goal is to find variables whose type refers to a class instantiation, is this a good approach?
Behavior occurs in clang-query and the C++ API on Apple, on both clang 11.0.0 and 10.0.1; didn't yet have the possibility to test 11.0.1.
I think the AST matchers are properly reflecting the information in
the AST: Compiler Explorer
-VarTemplateDecl <line:3:1, line:4:6> col:6 c
>-TemplateTypeParmDecl <line:3:10, col:19> col:19 referenced
typename depth 0 index 0 T
>-VarDecl <line:4:1, col:6> col:6 c 'C<T>'
`-VarTemplateSpecializationDecl <col:1, col:6> col:6 used c
VarTemplateSpecialization 0x55f3fcf22b50 'c' 'C<float>':'C<float>'
`-VarTemplateSpecializationDecl <line:4:1, col:6> col:6 used c
'C<float>':'C<float>' callinit
>-TemplateArgument type 'float'
> `-BuiltinType 'float'
`-CXXConstructExpr <col:6> 'C<float>':'C<float>' 'void () noexcept'
So I don't think this is an AST matcher bug. However, I'm not certain
why the AST reflects the same information twice -- this has been the
behavior as far back as Clang 3.4: Compiler Explorer
I think this is indeed a bug, not in ASTMatchers but in RecursiveASTVisitor, since the intent of RecursiveASTVisitor is to never visit a node more than once.
The problem is this:
RecursiveASTVisitor method shouldVisitTemplateInstantiations(). By default it returns false, but ASTMatchers overrides it to return true. When it returns true, immediately upon traversing any Class/Function/VarTemplateDecl, the visitor also traverses its implicit instantiations (and those of any partial specializations thereof).
Still though, the intent of this is to only visit implicit instantiations, not e.g. explicit specializations — the intent is to only visit stuff that won’t be visited during subsequent traversals.
This works fine for classes and functions, but not for VarTemplateDecls, because of a peculiarity of implementation: implicit instantiations of VarTemplates are added to the enclosing DeclContext, presumably to aid lookup.
This results in the implicitly instantiated VarTemplateSpecializationDecls always being visited twice when shouldVisitTemplateInstantiations() returns true.
So one or other of the traversals of these nodes must be eliminated in RecursiveASTVIsitor. Probably better to eliminate the traversal of implicit VTSDs while looping over a DeclContext, since that is an implementation detail, and keep the the traversals while looping over the template instantiations.