如果语句包含宏,则clang无法替换语句

use*_*963 20 c++ clang clang++

我正在使用clang尝试解析(使用C++ API)一些C++文件并使所有case-break对使用特定的样式.

示例:

**Original**

switch(...)
{
   case 1:
   {
      <code>
   }break;

   case 2:
   {
      <code>
      break;
   }
}

**After replacement**

switch(...)
{
   case 1:
   {
      <code>
      break;
   }

   case 2:
   {
      <code>
      break;
   }
}
Run Code Online (Sandbox Code Playgroud)

到目前为止,如果代码部分不包含任何宏,我到底做了什么.我的问题是:clang处理扩展(如果我转换一个有问题的语句,它将显示扩展版本)宏是否有所不同?如果是这样我怎么能让它工作?

可能有用的其他信息:

我正在使用Rewriter :: ReplaceStmt用新创建的CompoundStmt替换每个案例的子语句,我注意到如果"from"参数包含一个宏,则replaceStmt返回true,并且该方法返回true的唯一方法是if

Rewriter :: getRangeSize(from-> getSourceRange())

返回-1

Mar*_* A. 25

您的问题是由SourceLocation的设计引起的.

一篇文章如下:


使用clang的SourceLocation进行宏扩展

SourceLocation设计灵活,足以同时处理未扩展位置和宏扩展位置.

如果令牌是扩展的结果,那么有两个不同的位置需要考虑:拼写位置(对应于令牌的字符的位置)和实例化位置(令牌使用的位置 -宏实例化点).

我们以下面的简单源文件为例:

#define MACROTEST bool

int main() {

    int var = 2;
    switch(var)
    {
       case 1:
       {
          MACROTEST newvar;
       }break;

       case 2:
       {
          MACROTEST newvar;
          break;
       }
    }

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

并假设我们要替换两个声明语句

MACROTEST newvar;
Run Code Online (Sandbox Code Playgroud)

声明声明

int var = 2;
Run Code Online (Sandbox Code Playgroud)

为了得到这样的东西

#define MACROTEST bool

int main() {

    int var = 2;
    switch(var)
    {
       case 1:
       {
          int var = 2;
       }break;

       case 2:
       {
          int var = 2;
          break;
       }
    }

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

如果我们输出AST(-ast-dump),我们得到以下内容(我包含一个图像,因为它比未着色的文本更直观):

clang AST

你可以看到DeclStmt我们感兴趣的第一个报告的位置,从第1行到第10行:这意味着clang在转储中报告从宏的行到使用宏的点的间隔:

#define MACROTEST [from_here]bool

int main() {

    int var = 2;
    switch(var)
    {
       case 1:
       {
          MACROTEST newvar[to_here];
       }break;

       case 2:
       {
          MACROTEST newvar;
          break;
       }
    }

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

(请注意,由于我的文本编辑器使用了制表符,因此字符数可能与普通空格不同)

最终,这会触发Rewriter::getRangeSizefailure(-1)和后续返回值(这意味着失败 - 请参阅文档).Rewriter::ReplaceStmt true

发生的事情如下:你收到了几个SourceLocation标记,其中第一个是宏ID(isMacroID()将返回true),而后者则不是.

为了成功获得宏扩展语句的范围,我们需要退后一步,与SourceManager所有拼写位置实例化位置的查询网关进行通信(如果你不记得这些,请退后一步)条款)需求.我不能比文档中提供的详细描述更清楚:

可以查询SourceManager以获取有关SourceLocation对象的信息,将它们转换为拼写或扩展位置.拼写位置表示对应于令牌的字节来自何处,而扩展位置表示位置在用户视图中的位置.例如,在宏扩展的情况下,拼写位置指示扩展的令牌来自何处,扩展位置指定扩展的位置.

在这一点上,您应该首先解释为什么我首先解释了所有这些内容:如果您打算使用源范围进行替换,则需要使用适当的扩展间隔.

回到我提出的示例,这是实现它的代码:

SourceLocation startLoc = declaration_statement->getLocStart();
SourceLocation endLoc = declaration_statement->getLocEnd();

if( startLoc.isMacroID() ) {
    // Get the start/end expansion locations
    std::pair< SourceLocation, SourceLocation > expansionRange = 
             rewriter.getSourceMgr().getImmediateExpansionRange( startLoc );

    // We're just interested in the start location
    startLoc = expansionRange.first;
}

if( endLoc.isMacroID() ) {
  // will not be executed
}

SourceRange expandedLoc( startLoc, endLoc );
bool failure = rewriter.ReplaceText( expandedLoc, 
                                     replacer_statement->getSourceRange() );

if( !failure )
    std::cout << "This will get printed if you did it correctly!";
Run Code Online (Sandbox Code Playgroud)

declaration_statement是两者中的任何一个

MACROTEST newvar;
Run Code Online (Sandbox Code Playgroud)

虽然replacer_statement是用于替换的声明

int var = 2;
Run Code Online (Sandbox Code Playgroud)

上面的代码将为您提供:

#define MACROTEST bool

int main() {

    int var = 2;
    switch(var)
    {
       case 1:
       {
          int var = 2;
       }break;

       case 2:
       {
          int var = 2;
          break;
       }
    }

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

即完全和成功地替代宏观扩展的陈述.


参考文献:


ken*_*liu 5

为了获取与宏扩展相关的文件位置,可以使用 API 函数来检索信息:

SourceLocation startLoc = rewriter.getSourceMgr().getFileLoc(
    declaration_statement->getLocStart());
SourceLocation endLoc = rewriter.getSourceMgr().getFileLoc(
    declaration_statement->getLocEnd());
Run Code Online (Sandbox Code Playgroud)

这个 API 函数的作用与 Marco 在他的代码中编写的相同,但是是自动执行的。

如果我们看一下函数getFileLoc ()的实现:

这是函数的描述: 给定 Loc,如果它是宏位置,则返回扩展位置或拼写位置,具体取决于它是否来自宏参数。

 SourceLocation getFileLoc(SourceLocation Loc) const {
     if (Loc.isFileID()) return Loc;
         return getFileLocSlowCase(Loc);
 }

SourceLocation SourceManager::getFileLocSlowCase(SourceLocation Loc) const {
    do {
        if (isMacroArgExpansion(Loc))
            Loc = getImmediateSpellingLoc(Loc);
        else
            Loc = getImmediateExpansionRange(Loc).first;
    } while (!Loc.isFileID());
    return Loc;
}
Run Code Online (Sandbox Code Playgroud)