我正在使用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
答案 0 :(得分:24)
您的问题是由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 ),我们得到以下内容(我包含一个图像,因为它比未着色的文本更直观):
你可以看到我们感兴趣的第一个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;
}
即。完全成功地取代宏观扩展声明。
参考文献:
答案 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;
}