c++oding="utf-8" ?>
clang::astcontext 是 ast 遍历的唯一合法起点,必须在 handletranslationunit 回调中使用,不可缓存指针;应优先继承 recursiveastvisitor 并注意编译参数与目标代码严格一致。

clang::ASTContext 是 AST 遍历的起点,不是 AST 本身
很多人一上来就找“怎么拿到 AST”,结果在 clang::ASTUnit 或 clang::CompilerInstance 里绕半天。其实真正承载所有节点的是 clang::ASTContext,它由编译器前端构造完成,必须等 clang::FrontendAction 进入 Execute() 后才可用。
常见错误现象:getASTContext() 返回空指针、访问 getTranslationUnitDecl() 崩溃——根本原因是过早调用,比如在 BeginSourceFile() 里就试图取 AST。
- 正确时机:只在
clang::ASTConsumer::HandleTranslationUnit(clang::ASTContext &Ctx)回调里使用Ctx - 不要缓存
ASTContext*到类成员,它生命周期绑定于单次编译单元处理 - 若需跨函数传参,直接传引用
clang::ASTContext&,别转成指针再判空
用 RecursiveASTVisitor 比手写 VisitXXX 函数更安全
直接继承 clang::RecursiveASTVisitor,而不是手动重载一堆 VisitFunctionDecl、VisitCXXRecordDecl——前者自动处理子树遍历顺序、跳过无效节点、兼容 Clang 版本演进;后者容易漏掉 VisitCXXMethodDecl(它不继承 FunctionDecl 的虚函数链)、或误入模板实例化中间节点。
使用场景:想提取所有带 [[nodiscard]] 的函数声明,但又不想被 SFINAE 生成的无效 FunctionTemplateDecl 干扰。
- 默认行为会递归进入模板定义体,如需跳过,重写
shouldTraversePostOrder()或在VisitFunctionTemplateDecl里 return false -
VisitDeclRefExpr拿到的是表达式节点,其getDecl()可能返回nullptr(比如未解析的依赖名),务必判空 - 访问
clang::Type时,优先用getTypeClassName()而非getAsString(),后者依赖PrintingPolicy,跨 Clang 版本输出不稳定
libtooling 的编译参数必须匹配目标代码,否则 AST 解析失败
Clang 不是“通用 C++ 解析器”,它严格按预处理器宏、-std=、系统头路径重建语义环境。用 clang++ -std=c++17 编译的代码,若 tooling 传入 -std=c++14,std::optional 会被当作文法错误,整个 TranslationUnitDecl 可能为空。
常见错误现象:HandleTranslationUnit 被调用,但 Ctx.getTranslationUnitDecl()->decls() 迭代器为空;或 VisitCXXRecordDecl 根本不触发——大概率是编译参数没对齐。
- 用
clang -Xclang -ast-dump -fsyntax-only xxx.cpp验证参数是否能让 Clang 自身成功 parse - 在
createInvocationFromCommandLine前,确保参数列表包含目标文件路径,且位置在所有-I、-D之后 - Linux 下注意
/usr/include/c++/版本与 tooling 链接的 libstdc++ 是否一致,不一致会导致std::vector等基础类型解析失败
AST 节点生命周期由 ASTContext 管理,不能长期持有 raw pointer
所有 clang::Decl*、clang::Stmt*、clang::Type* 都是 arena-allocated,内存随 ASTContext 销毁而释放。存 std::vector<:functiondecl></:functiondecl> 看似方便,但若该 vector 在多次 HandleTranslationUnit 调用间复用,第二次访问时指针已悬空。
性能影响:有人为“避免拷贝”把 clang::QualType 存成 std::string,结果发现 getAsString() 内部要重建 PrintingPolicy 并格式化,比直接传引用慢一个数量级。
- 短期操作(如单次遍历中收集、过滤)可用 raw pointer,但禁止跨回调保存
- 需要跨函数传递类型信息,用
clang::QualType(它内部是轻量 handle)或clang::CanQualType - 真要持久化节点语义,提取关键字段:
Decl::getNameAsString()、Decl::getLocation().printToString(Ctx.getSourceManager())、getType().getAsString()
最易被忽略的是 clang::SourceLocation:它本身不包含文件名或行号,只是个偏移量,必须配合 clang::SourceManager 才能解析。单独存 SourceLocation 然后下次想还原位置?不行,SourceManager 是 per-ASTContext 的。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











