如何在clang-tidy中从cxxNewExpr获取类型以替换new表达式
用Clang-Tidy替换
new T(...)为New<T>(...)的正确实现 你的问题出在直接绑定了整个CXXNewExpr节点,导致把完整的new表达式当成模板参数。要解决这个问题,必须拆解CXXNewExpr的核心组成部分:分配类型和构造参数,再拼接成目标格式。
关键实现步骤
- 提取分配类型:通过
CXXNewExpr::getAllocatedType()获取要实例化的类型,转成字符串作为模板参数。 - 提取构造参数:通过
CXXNewExpr::getConstructorArgs()遍历所有构造参数,提取每个参数的源码文本并拼接成参数列表。 - 生成替换内容:将类型和参数拼接成
New<Type>(Args)的格式,替换原有的new表达式。
完整的Clang-Tidy Check示例代码
#include "clang/AST/AST.h" #include "clang/ASTMatchers/ASTMatchers.h" #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/Tooling/Refactoring.h" #include "clang/Lex/Lexer.h" using namespace clang; using namespace clang::ast_matchers; using namespace clang::tooling; class NewToNewTemplateCallback : public MatchFinder::MatchCallback { public: void run(const MatchFinder::MatchResult &Result) override { const auto *NewExpr = Result.Nodes.getNodeAs<CXXNewExpr>("newExpr"); if (!NewExpr || NewExpr->isArray()) { // 跳过数组形式的new[] return; } // 获取分配类型的字符串 QualType AllocType = NewExpr->getAllocatedType(); std::string TypeStr = AllocType.getAsString(); // 拼接构造参数 std::string ArgsStr; for (unsigned i = 0; i < NewExpr->getNumConstructorArgs(); ++i) { const Expr *Arg = NewExpr->getConstructorArg(i); if (i != 0) { ArgsStr += ", "; } // 提取参数的源码文本 ArgsStr += Lexer::getSourceText( CharSourceRange::getTokenRange(Arg->getSourceRange()), *Result.SourceManager, Result.Context->getLangOpts()); } // 生成替换字符串 std::string Replacement = "New<" + TypeStr + ">(" + ArgsStr + ")"; // 执行替换 auto &Replaces = *Result.Context->get<Replacements>(); (void)Replaces.add( Replacement(*Result.SourceManager, NewExpr->getSourceRange()), Replacement); } }; // 注册Matcher void RegisterMatchers(MatchFinder &Finder) { Finder.addMatcher(cxxNewExpr().bind("newExpr"), new NewToNewTemplateCallback()); }
注意事项
- 代码中加入了
NewExpr->isArray()判断,避免误替换数组分配的new T[]形式。 - 如果你的
New模板需要处理const、引用等修饰的类型,getAllocatedType()会自动保留这些修饰符,无需额外处理。 - 对于复杂构造参数(比如带运算符、函数调用的参数),
Lexer::getSourceText会完整保留原始源码格式,确保替换后的参数逻辑不变。
内容的提问来源于stack exchange,提问作者CoffeeTableEspresso
相关产品推荐
相关产品推荐

