Mike Crowe
Mike Crowe

Reputation: 706

Clang AST matching method call on class, derived class or typedef to either

I have a matcher that works perfectly for matching operator() calls on instances of a class or classes derived from that class. For example, it matches the final line of:

class MyBase { void operator()(...) {} };
MyBase b;
b(parameters);

using a matcher like:

const auto MyBaseExpr =                                                                                                                                                                                
  expr(hasType(cxxRecordDecl(isSameOrDerivedFrom("::MyBase"))));                                                                                                                                        
Finder->addMatcher(traverse(
                   TK_AsIs, cxxOperatorCallExpr(
                    hasOverloadedOperatorName("()"),
                    hasArgument(0, anyOf(MyBaseExpr, MyOtherBaseExpr)),
                    hasAnyArgument(...),
                  this);

But I'd also like to be able to match such calls on instances of typedefs for the base or derived types like in the last line below:

typedef MyBase MyTypedef;
MyTypedef t;
t(parameters);

and I can't seem to fathom the correct way to specify this match. Attempting to use hasUnqualifiedDesugaredType rather than hasType doesn't work since it works on a type rather than a Decl and if I try to do more matching with the type then I can't use isSameOrDerived which returns a Matcher<CXXRecordDecl>. A similar problem occurs when trying to use hasCanonicalType:

.../RedundantStringCStrCheck.cpp:193:40: error: invalid initialization of reference of type ‘const clang::ast_matchers:
:internal::Matcher<clang::QualType>&’ from expression of type ‘clang::ast_matchers::internal::BindableMatcher<clang::Decl>’
  193 |     expr(hasCanonicalType(cxxRecordDecl(isSameOrDerivedFrom("::MyBase"))));

Upvotes: 1

Views: 592

Answers (2)

Mike Crowe
Mike Crowe

Reputation: 706

Thien Tran provided the pointer which led me to the right answer. Here's my original expression

const auto MyBaseExpr =
  expr(hasType(cxxRecordDecl(isSameOrDerivedFrom("::MyBase"))));

I was trying to use:

const auto MyBaseExpr =
  expr(hasCanonicalType(cxxRecordDecl(isSameOrDerivedFrom("::MyBase"))));

but the description of hasCanonicalType in LibASTMatchersReference shows that it takes and returns Matcher<QualType> yet cxxRecordDecl has type Matcher<Decl>, so this did not compile.

The mismatch of types can be corrected by inserting a call to hasDeclaration. It's then also necessary to keep the call to hasType in order to turn the Matcher<QualType> result of hasCanonicalType back into something that can be passed to expr.

After all that I ended up with:

const auto MyBaseExpr =                                                                                                                                                                                
  expr(hasType(hasCanonicalType(hasDeclaration(cxxRecordDecl(isSameOrDerivedFrom("::MyBase"))))));

which seems to work perfectly.

Upvotes: 2

Thien Tran
Thien Tran

Reputation: 326

MyTypedef is defined from MyBase so its Canonical Type should be MyBase. More information about canonical type: https://clang.llvm.org/docs/InternalsManual.html#canonical-types

This is the example from LibASTMatchersReference , it uses hasType().

enter image description here

Upvotes: 2

Related Questions