编写clang-tidy检查:如何可靠检测宏定义的各类类型?
解决clang-tidy检测宏定义类型的可靠方法
别再用手动lookup或字符串匹配了,直接用Clang自带的Preprocessor解析类型才是最靠谱的方案——你的原方法局限性太大:内置类型没有对应的TypeDecl,标准库类型在命名空间里,模板实例化类型是特殊AST节点,手动处理根本覆盖不全。下面是具体实现思路和代码:
核心思路:用Preprocessor::ParseType解析宏替换文本
Clang的Preprocessor本身具备完整的C++类型解析能力,能处理内置类型、命名空间类型、模板实例化类型等所有场景。你只需要把宏的替换文本转换成Token流,交给它解析就能拿到对应的QualType。
具体步骤
- 在
PPCallbacks的MacroDefined回调里,拿到宏的MacroInfo,获取它的替换Token序列。 - 临时构造一个
Lexer,把宏的替换Token作为输入。 - 调用
Preprocessor::ParseType解析这个Token流,得到TypeSourceInfo,进而获取QualType。 - 如果解析成功,说明这是一个类型定义宏,触发警告建议用
typedef/using替代。
代码示例
void MacroDefined(const Token &MacroNameTok, const MacroDirective *MD) override { const MacroInfo *MI = MD->getMacroInfo(); if (!MI || MI->isObjectLike() || MI->getNumTokens() == 0) { return; // 只处理对象式宏(不带参数)且有替换内容的情况 } // 保存当前Preprocessor状态,解析后恢复,避免影响后续流程 Preprocessor::CleanupTemporaryPPState RAII(PP); // 构造临时Lexer,输入为宏的替换Token序列 TokenStreamLexer Lexer(MI->tokens(), PP.getSourceManager(), PP.getLangOpts()); PP.setTokenLexer(&Lexer); // 解析类型 TypeSourceInfo *TSI = PP.ParseType(); if (TSI) { QualType Type = TSI->getType(); if (!Type.isNull()) { // 确认是有效类型,触发警告并生成修复提示 diag(MacroNameTok.getLocation(), "macro used as type alias; prefer 'typedef' or 'using'") << FixItHint::CreateReplacement( MacroNameTok.getLocation(), "using " + MacroNameTok.getIdentifierInfo()->getName().str() + " = ") << FixItHint::CreateReplacement( MI->getDefinitionRange(), Type.getAsString()); } } }
关键细节说明
- 覆盖所有类型场景:Preprocessor会自动处理
int这类内置类型、std::string这类命名空间类型、std::array<int>这类模板实例化类型,甚至const MyTemplateClass<int>&这种复杂类型,完全不需要手动适配。 - 状态保护:用
Preprocessor::CleanupTemporaryPPState保存解析前的状态,避免临时修改Lexer影响后续预处理流程。 - 自动过滤非类型宏:如果宏的替换文本是
int + 1这类非类型内容,ParseType会直接失败,不会误触发警告。
内容的提问来源于stack exchange,提问作者dif
相关产品推荐
相关产品推荐

