如何使用 libtooling 找到函数参数之间逗号的 SourceLocation?
How do I find the SourceLocation of the commas between function arguments using libtooling?
我的主要目标是尝试在函数参数之前获取宏(甚至只是文本)。例如:
void Foo(_In_ void* p, _Out_ int* x, _Out_cap_(2) int* y);
我需要优雅地处理声明参数的宏之类的事情(通过忽略它们)。
#define Example _In_ int x
void Foo(Example);
我查看了预处理器记录对象并使用 Lexer::getSourceText 获取宏名称 In、Out 等,但我没有看到将它们映射回函数参数的干净方法。
我目前的解决方案是记录文件中的所有宏展开,然后将它们的SourceLocation 与ParamVarDecl SourceLocation 进行比较。这主要是有效的,除了我不知道如何跳过参数后的东西。
void Foo(_In_ void* p _Other_, _In_ int y);
获取逗号的 SourceLocation 会起作用,但我无法在任何地方找到它。
问题的标题要求 libclang
,但当您使用 Lexer::getSourceText
时,我假设它是 libTooling
。我的其余答案仅在 libTooling
.
方面可行
解决方案 1
词法分析器在标记级别上工作。 逗号 也是一个标记,因此您可以获取参数的结束位置并使用 Lexer::findNextToken.
获取下一个标记
这里是ParmVarDecl
(对于函数参数)和CallExpr
(对于函数参数)访问函数,展示了如何使用它:
template <class T> void printNextTokenLocation(T *Node) {
auto NodeEndLocation = Node->getSourceRange().getEnd();
auto &SM = Context->getSourceManager();
auto &LO = Context->getLangOpts();
auto NextToken = Lexer::findNextToken(NodeEndLocation, SM, LO);
if (!NextToken) {
return;
}
auto NextTokenLocation = NextToken->getLocation();
llvm::errs() << NextTokenLocation.printToString(SM) << "\n";
}
bool VisitParmVarDecl(ParmVarDecl *Param) {
printNextTokenLocation(Param);
return true;
}
bool VisitCallExpr(CallExpr *Call) {
for (auto *Arg : Call->arguments()) {
printNextTokenLocation(Arg);
}
return true;
}
对于以下代码片段:
#define FOO(x) int x
#define BAR float d
#define MINUS -
#define BLANK
void foo(int a, double b ,
FOO(c) , BAR) {}
int main() {
foo( 42 ,
36.6 , MINUS 10 , BLANK 0.0 );
return 0;
}
它产生以下输出(逗号的六个位置和括号的两个位置):
test.cpp:6:15
test.cpp:6:30
test.cpp:7:19
test.cpp:7:24
test.cpp:10:17
test.cpp:11:12
test.cpp:11:28
test.cpp:11:43
不过,这是一种相当 low-level 和 error-prone 的方法。但是,您可以更改解决原始问题的方法。
解决方案 2
Clang 在其源位置存储有关扩展宏的信息。您可以在 SourceManager
中找到相关方法(例如 isMacroArgExpansion or isMacroBodyExpansion)。因此,您可以访问 ParmVarDecl
个节点并检查它们的位置以进行宏扩展。
我强烈建议朝第二个方向前进。
希望这些信息对您有所帮助。祝您使用 Clang 愉快!
UPD 说到属性,不幸的是,您没有太多选择。 Clang 会忽略 任何 未知属性,并且此行为 不可调整 。如果您不想修补 Clang 本身并将您的属性添加到 Attrs.td,那么您确实仅限于令牌和第一种方法。
我的主要目标是尝试在函数参数之前获取宏(甚至只是文本)。例如:
void Foo(_In_ void* p, _Out_ int* x, _Out_cap_(2) int* y);
我需要优雅地处理声明参数的宏之类的事情(通过忽略它们)。
#define Example _In_ int x
void Foo(Example);
我查看了预处理器记录对象并使用 Lexer::getSourceText 获取宏名称 In、Out 等,但我没有看到将它们映射回函数参数的干净方法。
我目前的解决方案是记录文件中的所有宏展开,然后将它们的SourceLocation 与ParamVarDecl SourceLocation 进行比较。这主要是有效的,除了我不知道如何跳过参数后的东西。
void Foo(_In_ void* p _Other_, _In_ int y);
获取逗号的 SourceLocation 会起作用,但我无法在任何地方找到它。
问题的标题要求 libclang
,但当您使用 Lexer::getSourceText
时,我假设它是 libTooling
。我的其余答案仅在 libTooling
.
解决方案 1
词法分析器在标记级别上工作。 逗号 也是一个标记,因此您可以获取参数的结束位置并使用 Lexer::findNextToken.
获取下一个标记这里是ParmVarDecl
(对于函数参数)和CallExpr
(对于函数参数)访问函数,展示了如何使用它:
template <class T> void printNextTokenLocation(T *Node) {
auto NodeEndLocation = Node->getSourceRange().getEnd();
auto &SM = Context->getSourceManager();
auto &LO = Context->getLangOpts();
auto NextToken = Lexer::findNextToken(NodeEndLocation, SM, LO);
if (!NextToken) {
return;
}
auto NextTokenLocation = NextToken->getLocation();
llvm::errs() << NextTokenLocation.printToString(SM) << "\n";
}
bool VisitParmVarDecl(ParmVarDecl *Param) {
printNextTokenLocation(Param);
return true;
}
bool VisitCallExpr(CallExpr *Call) {
for (auto *Arg : Call->arguments()) {
printNextTokenLocation(Arg);
}
return true;
}
对于以下代码片段:
#define FOO(x) int x
#define BAR float d
#define MINUS -
#define BLANK
void foo(int a, double b ,
FOO(c) , BAR) {}
int main() {
foo( 42 ,
36.6 , MINUS 10 , BLANK 0.0 );
return 0;
}
它产生以下输出(逗号的六个位置和括号的两个位置):
test.cpp:6:15
test.cpp:6:30
test.cpp:7:19
test.cpp:7:24
test.cpp:10:17
test.cpp:11:12
test.cpp:11:28
test.cpp:11:43
不过,这是一种相当 low-level 和 error-prone 的方法。但是,您可以更改解决原始问题的方法。
解决方案 2
Clang 在其源位置存储有关扩展宏的信息。您可以在 SourceManager
中找到相关方法(例如 isMacroArgExpansion or isMacroBodyExpansion)。因此,您可以访问 ParmVarDecl
个节点并检查它们的位置以进行宏扩展。
我强烈建议朝第二个方向前进。
希望这些信息对您有所帮助。祝您使用 Clang 愉快!
UPD 说到属性,不幸的是,您没有太多选择。 Clang 会忽略 任何 未知属性,并且此行为 不可调整 。如果您不想修补 Clang 本身并将您的属性添加到 Attrs.td,那么您确实仅限于令牌和第一种方法。