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

时间:2014-06-05 14:33:10

标签: 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;
   }
}

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

可能有用的其他信息:

我正在使用Rewriter::ReplaceStmt用新创建的 CompoundStmt 替换每个案例的子语句,我注意到 ReplaceStmt 如果“来自”,则返回true “参数包含一个宏,方法返回 true 的唯一方法是

  

Rewriter::getRangeSize(从 - &GT; getSourceRange())

返回-1

2 个答案:

答案 0 :(得分:24)

您的问题是由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;
}

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

MACROTEST newvar;

声明声明

int var = 2;

为了得到这样的东西

#define MACROTEST bool

int main() {

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

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

    return 0;
}

如果我们输出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;
}

(请注意,自我的文本编辑器使用标签后,字符数可能与普通空格不同)

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

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

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

  

可以查询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!";

declaration_statement是两个中的一个

MACROTEST newvar;

replacer_statement是用于替换的语句

int var = 2;

上面的代码会告诉你:

#define MACROTEST bool

int main() {

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

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

    return 0;
}

即。完全成功地取代宏观扩展声明。


参考文献:

  • {{3}}
  • clang doxygen API
  • clang源代码

答案 1 :(得分:3)

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

SourceLocation startLoc = rewriter.getSourceMgr().getFileLoc(
    declaration_statement->getLocStart());
SourceLocation endLoc = rewriter.getSourceMgr().getFileLoc(
    declaration_statement->getLocEnd());

此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;
}