clang无法替换包含宏的语句 [英] clang fails replacing a statement if it contains a macro

查看:71
本文介绍了clang无法替换包含宏的语句的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用clang尝试(使用C ++ API)解析一些C ++文件,并使其全部使用-中断对使用特定样式。



示例

  **原始** 

开关(... )
{
情况1:
{
< code>
}休息;

情况2:
{
< code>
休息时间;
}
}

**更换后**

开关(...)
{
情况1:
{
< code>
休息时间;
}

情况2:
{
< code>
休息时间;
}
}

如果到目前为止,我所拥有的正是我想要的代码部分不包含任何宏。
我的问题是:clang是否以不同的方式对待扩展(如果我对有问题的语句进行转储,它将显示扩展版本)宏?如果可以,我该如何使用它?



其他信息可能会有所帮助:



我正在使用 true 返回值(这意味着失败-请参阅文档)。



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



为了成功获取宏的范围扩展s方式,我们需要退后一步,并与 SourceManager 是所有拼写位置实例位置的查询网关(如果您不记得这些术语,请退后一步)。除了文档中提供的

$ b之外,我还不清楚。
$ b


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




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



回到我建议的示例,这是实现该目标的代码:

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

if(startLoc.isMacroID()){
//获取开始/结束扩展位置
std :: pair< SourceLocation,SourceLocation> expandRange =
rewriter.getSourceMgr()。getImmediateExpansionRange(startLoc);

//我们只是对起始位置感兴趣
startLoc = expandRange.first;
}

if(endLoc.isMacroID()){
//将不会执行
}

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

if(!failure)
std :: cout<< 如果操作正确,它将被打印出来!

declaration_statement 是两者之一

  MACROTEST newvar; 

replacer_statement 是用于替换

  int var = 2; 

上面的代码将为您提供:

  #define MACROTEST bool 

int main(){

int var = 2;
switch(var)
{
情况1:
{
int var = 2;
}休息;

情况2:
{
int var = 2;
休息时间;
}
}

返回0;
}






参考文献:




I'm using clang to try and parse (with the C++ API) some C++ files and make all the case - break pairs use a specific style.

Example:

**Original**

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

   case 2:
   {
      <code>
      break;
   }
}

**After replacement**

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

   case 2:
   {
      <code>
      break;
   }
}

What I have so far does exactly what I want if the code parts don't contain any macros. My question is: does clang treat expanded ( if I do a dump of a problematic statement it will show the expanded version ) macros differently? if so how can I get this to work?

Additional info that may help:

I'm using Rewriter::ReplaceStmt to replace the sub-statements of each case with a newly created CompoundStmt and I noticed ReplaceStmt returns true if the "from" parameter contains a macro and the only way that method returns true is if

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

returns -1

解决方案

Your problem is caused by the design of SourceLocation.

An article follows:


Macro expansion with clang's SourceLocation

SourceLocation is designed to be flexible enough to handle both unexpanded locations and macro expanded locations at the same time.

If the token is the result of an expansion, then there are two different locations to be kept into account: the spelling location (the location of the characters corresponding to the token) and the instantiation location (the location where the token was used - the macro instantiation point).

Let's take the following simple source file as an example:

#define MACROTEST bool

int main() {

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

       case 2:
       {
          MACROTEST newvar;
          break;
       }
    }

    return 0;
}

and suppose we want to replace the two declarations statements

MACROTEST newvar;

with the declaration statement

int var = 2;

in order to get something like this

#define MACROTEST bool

int main() {

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

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

    return 0;
}

if we output the AST (-ast-dump) we get the following (I'm including an image since it's more intuitive than just uncolored text):

as you can see the location reported for the first DeclStmt we're interested in, spans from line 1 to 10: that means clang is reporting in the dump the interval spanning from the macro's line to the point where the macro is used:

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

(notice that the count of characters might not be the same with normal spaces since my text editor used tabs)

Ultimately, this is triggering the Rewriter::getRangeSize failure (-1) and the subsequent Rewriter::ReplaceStmt true return value (which means failure - see documentation).

What is happening is the following: you're receiving a couple of SourceLocation markers where the first is a macro ID (isMacroID() would return true) while the latter isn't.

In order to successfully get the extent of the macro-expanded statement we need to take a step back and communicate with the SourceManager which is the query-gateway for all your spelling locations and instantiation locations (take a step back if you don't remember these terms) needs. I can't be more clear than the detailed description provided in the documentation:

The SourceManager can be queried for information about SourceLocation objects, turning them into either spelling or expansion locations. Spelling locations represent where the bytes corresponding to a token came from and expansion locations represent where the location is in the user's view. In the case of a macro expansion, for example, the spelling location indicates where the expanded token came from and the expansion location specifies where it was expanded.

At this point you should be getting why I explained all this stuff in the first place: if you intend to use source ranges for your substitution, you need to use the appropriate expansion interval.

Back to the sample I proposed, this is the code to achieve it:

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!";

The declaration_statement is either one of the two

MACROTEST newvar;

while replacer_statement is the statement used for the replacement

int var = 2;

The above code will get you this:

#define MACROTEST bool

int main() {

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

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

    return 0;
}

i.e. a complete and successful substitution of the macro-expanded statement.


References:

这篇关于clang无法替换包含宏的语句的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆