Hi Gabor,
I encountered the same issue, and wrote a similar solution. However, then I
realized the ast_matchers::match documentation says:
/// If you want to find all matches on the sub-tree rooted at \c Node
(rather
/// than only the matches on \c Node itself), surround the \c Matcher with a
/// \c findAll().
So, we should be able to do something like
Decl* someRootDeclContext = ...;
clang::ast_matchers::match(
decl(findAll(callExpr()))
, *someRootDeclContext, *Result.Context);
However, that does not work because findAll is implemented with eachOf and
decl(eachOf(callExpr(), expr()))
doesn't work for the same reason
decl(callExpr())
doesn't work - a decl is never a callExpr.
I filed a bug for this: https://bugs.llvm.org/show_bug.cgi?id=38318
Meanwhile, findAll might work for your specific case, or you might be able
to write a replacement or use something more specific.
A case I encountered involved replacing the type of certain varDecl nodes in
the source code, but porting them if they are passed by reference in a
function call (a case I handled separately).
Given
void takeRef(int&)
{
}
void takeConstRef(int const&)
{
}
void takeVal(int)
{
}
struct IntWrapper
{
IntWrapper(int i) : m_i(i) {}
operator int() { return m_i; }
private:
int m_i;
};
int main()
{
int a = 42;
int b = 7;
takeVal(a);
takeConstRef(a);
takeVal(b);
takeRef(b);
}
the goal is to replace
int a = 42;
with
IntWrapper a = 42;
but leave
int b = 7;
untouched because
takeRef(b);
would not compile if the varDecl is ported.
Here is my solution which uses match(). You should be able to do something
similar for your case without the additional API.
Thanks,
Stephen.
#include "PortToIntwrapperCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace misc {
void PortToIntwrapperCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(
varDecl(
hasType(asString("int")),
unless(parmVarDecl()),
hasDeclContext(decl().bind("varDeclContext"))
).bind("portToIntWrapperIfPossible")
, this);
}
void PortToIntwrapperCheck::check(const MatchFinder::MatchResult
&Result) {
if (const auto *portToIntWrapper =
Result.Nodes.getNodeAs<VarDecl>("portToIntWrapperIfPossible"))
{
const auto *varDeclContext =
Result.Nodes.getNodeAs<Decl>("varDeclContext");
// Got a match for a particular var.
// Check if it is passed by reference in any calls
// within the context it is defined in
auto usesByReference = clang::ast_matchers::match(
decl(forEachDescendant(
callExpr(forEachArgumentWithParam(
declRefExpr(
to(varDecl(equalsNode(portToIntWrapper)))
).bind("arg"),
parmVarDecl(
hasType(lValueReferenceType(
unless(pointee(isConstQualified()))
))
)
))
))
, *varDeclContext, *Result.Context);
if (usesByReference.empty())
{
// All uses are safe to port to IntWrapper
auto tInfo = portToIntWrapper->getTypeSourceInfo();
auto tLoc = tInfo->getTypeLoc();
SourceRange typeRange = tLoc.getSourceRange();
diag(typeRange.getBegin(), "type of %0 should be IntWrapper")
<< portToIntWrapper
<< FixItHint::CreateReplacement(typeRange, "IntWrapper");
}
}
}
} // namespace misc
} // namespace tidy
} // namespace clang