正如回答的评论所指出的那样,所有其他答案似乎都有缺陷,因此我将发布自己的代码,该代码似乎涵盖了评论中提到的所有缺陷。
我认为getSourceRange()
将语句视为标记序列,而不是字符序列。这意味着,如果我们有一个对应于FOO + BAR
的clang::Stmt
,那么标记FOO
在第1个字符处,标记+
在第5个字符处,标记BAR
在第7个字符处。getSourceRange()
因此返回一个SourceRange
,其本质上意味着"这段代码从1处的标记开始,到7处的标记结束"。 因此,我们必须使用clang::Lexer::getLocForEndOfToken(stmt.getSourceRange().getEnd())
来获得BAR
标记的实际字符位置,并将那个作为clang::Lexer::getSourceText
的结束位置参数传递。如果不这样做,则clang::Lexer::getSourceText
将返回"FOO + "
,而不是我们想要的"FOO + BAR"
。
我不认为我的实现有@Steven Lu在评论中提到的问题,因为这个代码使用了clang::Lexer::getSourceText
函数,根据Clang的源代码文档,这个函数专门设计用于从范围内获取源文本。
此实现还考虑了@Ramin Halavati的备注;我已经在一些代码上进行了测试,它确实返回了宏展开的字符串。
这是我的实现:
std::string get_source_text(clang::SourceRange range, const clang::SourceManager& sm) {
clang::LangOptions lo;
auto start_loc = sm.getSpellingLoc(range.getBegin());
auto last_token_loc = sm.getSpellingLoc(range.getEnd());
auto end_loc = clang::Lexer::getLocForEndOfToken(last_token_loc, 0, sm, lo);
auto printable_range = clang::SourceRange{start_loc, end_loc};
return get_source_text_raw(printable_range, sm);
}
std::string get_source_text_raw(clang::SourceRange range, const clang::SourceManager& sm) {
return clang::Lexer::getSourceText(clang::CharSourceRange::getCharRange(range), sm, clang::LangOptions());
}