如何在Clang AST中从已替换模板类型参数恢复typedef/类型别名
场景设置
我用Clang 18开发AST插件,需要打印代码的元数据类型信息,核心需求是恢复类模板特化时作为模板参数使用的类型别名的原始书写名称。
示例代码如下:
// 类模板定义 template<typename T> struct Container { T value; }; // 类型别名 using SpecificType = GenericType<1, 2, -3>; // 某个下游模板的实例化 // 使用类型别名实例化模板 class Foo : public Container<SpecificType> {};
当前插件输出的value类型是GenericType<1, 2, -3>,但我需要的是原始书写的SpecificType。
已尝试方案
我通过getTypeSourceInfo()定位到基说明符的TemplateSpecializationTypeLoc,再遍历模板参数的getArgLoc(i)。对于Container<SpecificType>的参数,得到的是嵌套的TemplateSpecializationTypeLoc,其第一个参数位置是SubstTemplateTypeParmTypeLoc(因为实例化时T被替换为SpecificType):
TypeLoc tl = base_spec.getTypeSourceInfo()->getTypeLoc(); // 导航到TemplateSpecializationTypeLoc... TemplateArgumentLoc arg = spec_loc.getArgLoc(0); // arg.getTypeSourceInfo()->getTypeLoc() 是 SubstTemplateTypeParmTypeLoc
SubstTemplateTypeParmTypeLoc是叶子节点,没有子TypeLoc可深入。我尝试获取替换类型,但dyn_cast<TypedefType>返回nullptr,typedef的语法糖在替换生成SubstTemplateTypeParmType时被剥离了:
auto* subst = tl.getAs<SubstTemplateTypeParmTypeLoc>(); QualType replacement = subst.getTypePtr() ->getAs<SubstTemplateTypeParmType>() ->getReplacementType(); // dyn_cast<TypedefType>(replacement.getTypePtr()) → nullptr
问题
SubstTemplateTypeParmType或其关联的TypeLoc中是否保留了typedef语法糖?如果是,该如何恢复?如果没有,Clang AST中有没有其他机制可以获取调用点原本书写的SpecificType?
解决方案
1. 直接从TemplateArgumentLoc获取原始书写的类型别名
不需要深入到SubstTemplateTypeParmTypeLoc,而是直接处理模板参数的原始源位置信息。TemplateArgumentLoc本身携带了用户书写时的类型源位置,而非实例化后的替换类型。
修改思路:
- 从
TemplateSpecializationTypeLoc拿到TemplateArgumentLoc后,不要直接获取其Type(实例化后的类型),而是获取它的TypeLoc,然后遍历这个TypeLoc的语法糖层级。
示例代码:
TemplateSpecializationTypeLoc spec_loc = ...; // 从基说明符获取的Container<SpecificType>的位置 TemplateArgumentLoc arg_loc = spec_loc.getArgLoc(0); if (arg_loc.getArgument().getKind() == TemplateArgument::Type) { TypeLoc type_arg_loc = arg_loc.getTypeSourceInfo()->getTypeLoc(); // 遍历语法糖,找到最外层的TypedefTypeLoc while (true) { if (auto typedef_loc = type_arg_loc.getAs<TypedefTypeLoc>()) { // 获取类型别名的原始名称 StringRef alias_name = typedef_loc.getTypedefNameDecl()->getName(); llvm::outs() << alias_name << "\n"; // 输出SpecificType break; } // 否则深入到下层类型位置 if (!type_arg_loc.getNextTypeLoc()) break; type_arg_loc = type_arg_loc.getNextTypeLoc(); } }
2. 失败原因说明
SubstTemplateTypeParmType是模板实例化过程中生成的语义类型,它代表模板参数被替换后的实际类型,此时已经丢失了原始书写的语法糖信息。而TemplateArgumentLoc保存的是用户书写代码时的原始位置信息,也就是SpecificType对应的TypedefTypeLoc,这才是正确的入口。
3. 嵌套语法糖处理
如果类型别名本身嵌套了其他语法糖(比如using SpecificType = std::vector<int>),上面的循环会自动遍历到最外层的TypedefTypeLoc,确保拿到用户书写的原始名称。
内容的提问来源于stack exchange,提问作者Peter B

