Matching the use of an enumerator
The main issue with your attempt is the use of varDecl
.
Instead, to match an enumerator, you must use enumConstantDecl
.
In the Clang AST, a
VarDecl
is a declaration of a stand-alone variable, like int x;
. (It is also
used for function parameters and a few other things; see the linked
document.) In contrast, an
EnumConstantDecl
is a declaration of an enumerator, like the A
in enum E {A,B,C};
.
Their nearest common ancestor class is
ValueDecl
,
reflecting the fact that each evaluates to a value when used as an
(rvalue) expression, but that is the extent of their semantic
commonality.
As a demonstration, here is an example
clang-query
command to report all uses of a particular enumerator:
clang-query -c='m declRefExpr(to(enumConstantDecl(hasName("enumDeprecated"))))' test.cc --
Test input:
// test.cc
// Test clang-query finding a specific enumerator.
enum MyEnumeration {
someEnumerator,
enumDeprecated // Goal is to report uses of this.
};
int someVariable;
void f(int);
void g()
{
// These three are not reported.
f(3);
f(someVariable);
f(someEnumerator);
// This one is reported.
f(enumDeprecated);
}
// EOF
When trying to match particular syntax, it is often useful to study the
Clang AST for that syntax carefully by running
clang -Xclang -ast-dump -fsyntax-only test.cc
. For the example above,
the key parts are the declaration of the enumerator:
| `-EnumConstantDecl 0x91fc430 <line:6:3> col:3 referenced enumDeprecated 'MyEnumeration'
and its use within a call expression:
`-CallExpr 0x91fcb08 <line:21:3, col:19> 'void'
|-ImplicitCastExpr 0x91fcaf0 <col:3> 'void (*)(int)' <FunctionToPointerDecay>
| `-DeclRefExpr 0x91fcad0 <col:3> 'void (int)' lvalue Function 0x91fc628 'f' 'void (int)'
`-ImplicitCastExpr 0x91fcb30 <col:5> 'int' <IntegralCast>
`-DeclRefExpr 0x91fcab0 <col:5> 'MyEnumeration' EnumConstant 0x91fc430 'enumDeprecated' 'MyEnumeration'
Comparing that to the
AST Matchers Reference,
we see that varDecl
only matches VarDecl
, and that matching
EnumConstantDecl
can be done with enumConstantDecl
. (In this case,
the names arguably make that conclusion trivial, but in general, the
task of associating matcher to C++ class can be difficult.)
Implicit casts and hasAnyArgument
From your post, it appears you want to find not just any use of the
enumerator in question, but specifically function calls where that
enumerator (by itself) is one of the arguments. Using
callExpr(hasAnyArgument(...))
is a sensible method, but there is a
catch. Notice how the AST above includes an ImplicitCastExpr
above
the DeclRefExpr
. That is because I declared the corresponding
function parameter as int
rather than MyEnumeration
. The implicit
cast will cause hasAnyArgument(declRefExpr(...))
to fail to match,
even though hasArgument(0, declRefExpr(...))
does match (I'm not
sure why this inconsistency exists).
In the context of a clang-query
invocation, one solution is to set
IgnoreUnlessSpelledInSource
:
clang-query \
-c='set traversal IgnoreUnlessSpelledInSource' \
-c='m callExpr(hasAnyArgument(declRefExpr(to(enumConstantDecl(hasName("enumDeprecated"))))))' \
test.cc --
That flag effectively suppresses AST nodes that represent implicit
conversions, and consqeuently hasAnyArgument(declRefExpr(...))
works
despite the implicit cast.
Note that whether this is needed or not depends on how the callee is
declared, so it might not apply in your situation.
IgnoreUnlessSpelledInSource for a C++ matcher
You are using
LibASTMatchers
directly from C++, rather than calling the clang-query
program.
Although I have not tested this myself, from reading the
source code,
the equivalent of set traversal IgnoreUnlessSpelledInSource
is
to call clang::ParentMapContext::setTraversalKind()
, which does not
have its own Doxygen documentation (and hence no stable URL) but is
referenced from
TraversalKindScope
,
and pass
TK_IgnoreUnlessSpelledInSource
as the argument.