Reputation: 25293
I'd like to rename a function on one of the classes:
class Container {
public:
int count() const; // old
int size() const; // new
};
So I've written a TransformerClangTidyCheck
along the lines of the tutorial (https://clang.llvm.org/docs/ClangTransformerTutorial.html#example-rewriting-method-calls):
ReproParenIssueCheck::ReproParenIssueCheck(llvm::StringRef Name, ClangTidyContext *Context)
: utils::TransformerClangTidyCheck(Name, Context)
{
const std::string o = "object";
auto hasTypeIgnoringPointer = [](auto type) { return anyOf(hasType(type), hasType(pointsTo(type))); };
auto derivedFromClass = [&](StringRef Class) {
auto exprOfDeclaredType = [&](auto decl) {
return hasTypeIgnoringPointer(hasUnqualifiedDesugaredType(recordType(hasDeclaration(decl))));
};
return exprOfDeclaredType(cxxRecordDecl(isSameOrDerivedFrom(hasName(Class))));
};
auto renameMethod = [&] (StringRef Class, StringRef from, StringRef to) {
return makeRule(cxxMemberCallExpr(on(expr(derivedFromClass(Class)).bind(o)),
callee(cxxMethodDecl(hasName(from), parameterCountIs(0)))),
changeTo(cat(access(o, cat(to)), "()")),
cat("use '", to, "' instead of '", from, "'"));
};
setRule(renameMethod("Container", "count", "size"));
}
This works:
int test_ok(const Container *p) {
- return p->count();
+ return p->size();
}
except that changeTo()
is losing the required ParenExpr
in the expr()
bound to o
:
int test_fail(const void *p) {
- return ((const Container*)p)->count();
+ return ((const Container*)p)->size(); // expected
+ return (const Container*)p->size(); // actual
}
Bug in changeTo()
or am I missing something? It looks like any ParenExpr
is dropped:
int test_fail2(const Container &c) {
- return (c).count();
+ return (c).size(); // expected
+ return c.size(); // actual
}
Upvotes: 1
Views: 56
Reputation: 25293
It turns out that the on()
matcher drops the parentheses:
AST_MATCHER_P(CXXMemberCallExpr, on, internal::Matcher<Expr>,
InnerMatcher) {
const Expr *ExprNode = Node.getImplicitObjectArgument()
->IgnoreParenImpCasts(); // <-- HERE
return (ExprNode != nullptr &&
InnerMatcher.matches(*ExprNode, Finder, Builder));
}
If I create a new matcher that only ignores implicit casts:
AST_MATCHER_P(CXXMemberCallExpr, onIgnoringImpCasts, Matcher<Expr>,
InnerMatcher) {
const Expr *ExprNode = Node.getImplicitObjectArgument()
->IgnoreImpCasts();
return (ExprNode != nullptr &&
InnerMatcher.matches(*ExprNode, Finder, Builder));
and use that instead of on()
, both test cases pass.
Upvotes: 1