Clang LibTooling:如何获取函数式宏调用的结束位置?
解决Clang-Tidy中获取宏调用右括号外侧位置的问题
要处理这种由函数式宏生成的VarDecl,关键是要从宏展开后的变量声明追溯到原代码中的宏调用位置,再定位到宏调用的右括号结束点。以下是具体实现步骤:
核心思路
VarDecl的getBeginLoc()和getEndLoc()返回的是宏展开后的位置,我们需要通过Clang的SourceManager和Lexer回溯到原代码中的宏调用语句,再扫描找到右括号的位置。
具体代码实现
假设你在Clang-Tidy的Check类中,已经拿到了目标VarDecl* vd:
#include "clang/AST/ASTContext.h" #include "clang/Lex/Lexer.h" #include "clang/Lex/Preprocessor.h" #include "clang/Tooling/FixIt.h" // 获取SourceManager和Preprocessor引用 const auto& SM = getASTContext().getSourceManager(); const auto& PP = getASTContext().getPreprocessor(); // 1. 确认当前VarDecl来自宏体展开 if (!SM.isMacroBodyExpansion(vd->getBeginLoc())) { // 非宏生成的变量,按原有逻辑处理 return; } // 2. 获取原代码中宏调用的起始位置(比如VAR_DECL的开头) SourceLocation macroCallStart = SM.getExpansionLoc(vd->getBeginLoc()); // 3. 用Lexer扫描宏调用,找到右括号的结束位置 Lexer lexer(SM.getLocForStartOfFile(SM.getFileID(macroCallStart)), SM.getBufferData(SM.getFileID(macroCallStart)), SM, PP.getLangOpts()); lexer.SetKeepWhitespaceMode(true); // 保留空格,避免跳过宏调用中的空白字符 Token tok; SourceLocation macroCallEnd; while (lexer.LexFromRawLexer(tok)) { if (tok.is(tok::r_paren)) { // 右括号的结束位置,即宏调用的末尾 macroCallEnd = tok.getEndLoc(); break; } } // 4. 生成FixItHint,替换整个宏调用 auto fixIt = FixItHint::CreateReplacement( SourceRange(macroCallStart, macroCallEnd), "float your_new_variable;" // 替换成你需要的新声明 );
关键细节说明
SM.isMacroBodyExpansion():用来判断变量声明是否由宏展开生成,过滤非宏场景。SM.getExpansionLoc():将宏展开后的位置转换为原代码中宏调用的起始位置。Lexer扫描:通过逐Token扫描,精准定位到宏调用的右括号,确保替换范围覆盖整个VAR_DECL(int, test)语句。
内容的提问来源于stack exchange,提问作者Umrikhin Vasiliy
相关产品推荐
相关产品推荐

