You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 10:12:46