目标代码块:
int age = 5;
std::stringstream q;
q << "my name "
<< "is Tom, "
<< "my age is " << age;
我正在尝试创建一个匹配器来匹配第二行中的整个块。
我尝试了一个非常具体的匹配器,但它不起作用。 我的目标是匹配类似的代码块,其中 LHS 和 RHS 编号不固定。
cxxOperatorCallExpr(hasOverloadedOperatorName("<<"),
hasLHS(ignoringParenImpCasts(expr(anyOf(
stringLiteral().bind("firstString"),
stringLiteral().bind("secondString"),
stringLiteral().bind("thirdString"))))),
hasRHS(ignoringParenImpCasts(expr(anyOf(
declRefExpr(to(varDecl(hasType(cxxRecordDecl(hasName("std::stringstream")))))),
declRefExpr(hasType(isInteger())))))))
我尝试通过找到
std::stringstream q;
和 varDecl(hasType(cxxRecordDecl(hasName("std::stringstream"))))
行进行测试,但它也返回空。
我应该如何开始,甚至如何根据ast转储日志构建匹配器?
感谢任何帮助。
std::stringstream
从第二个问题开始,匹配一个变量声明 输入
std::stringstream
,使用
Clang AST 匹配器
像这样:
varDecl(
hasType(
asString("std::stringstream")
)
)
asString
匹配器将类型转换为字符串,使其成为
相当容易使用。
问题中尝试的匹配器:
varDecl(hasType(cxxRecordDecl(hasName("std::stringstream"))))
由于以下几个原因失败:
序列
hasType(cxxRecordDecl(...))
永远不会匹配,因为
cxxRecordDecl
匹配 声明(一段语法),而
hasType
匹配 types (抽象语义概念)。 你会
至少需要在两者之间插入hasDeclaration
,但是
在类型进一步确定之前不能直接应用
精炼,因为并非所有类型都有声明。
ElaboratedType
,
Clang 似乎在其 Type
结构中散布着
方式我觉得有些不可预测。 这使得一般来说很难
匹配 Type
s。
名称
std::stringstream
所指的类型是
TypedefType
对于模板专业化,而不是独立的类,所以
cxxRecordDecl
不匹配。 (注:TypedefType
用于类型
使用 typedef
或 using
创建的别名。)
如果您想匹配
std::stringstream
变量的声明
不使用 asString
,使用这个更复杂的匹配器:
varDecl(
hasType(
elaboratedType(
namesType(
typedefType(
hasDeclaration(
namedDecl(
hasName("std::stringstream")
)
)
)
)
)
)
)
operator<<
应用于 stringstream
有了上述内容,我们可以尝试匹配
operator<<
的使用,其中
左侧是 stringstream
。 问题有这个例子
表达:
q << "my name " << "is Tom, " << "my age is " << age;
这被解析为二元运算符的嵌套树:
(((q << "my name ") << "is Tom, ") << "my age is ") << age;
所以我们需要寻找
operator<<
的用法,其中 somewhere 在
左侧是 stringstream
,右侧是
字符串文字或整数值表达式(我部分猜测
基于问题中尝试的匹配器的意图)。 那可以是
像这样完成:
cxxOperatorCallExpr(
hasOverloadedOperatorName("<<"),
hasLHS(
hasDescendant(
expr(
hasType(
asString("std::stringstream")
)
).bind("stringStreamExpr")
)
),
hasRHS(
ignoringParenImpCasts(
expr(
anyOf(
stringLiteral(
).bind("stringLiteral"),
expr(
hasType(
isInteger()
)
).bind("intExpr")
)
)
)
)
)
这将单独报告每次出现
operator<<
的匹配项。
它不会尝试报告整个化合物的单个匹配项
同时表达所有不同的参数; AST 匹配器
语言还不够强大,不足以稳健地做到这一点。
clang-query
与上面的匹配器:
#!/bin/sh
PATH=/d/opt/clang+llvm-18.1.8-msvc/bin:$PATH
matcher='
cxxOperatorCallExpr(
hasOverloadedOperatorName("<<"),
hasLHS(
hasDescendant(
expr(
hasType(
asString("std::stringstream")
)
).bind("stringStreamExpr")
)
),
hasRHS(
ignoringParenImpCasts(
expr(
anyOf(
stringLiteral(
).bind("stringLiteral"),
expr(
hasType(
isInteger()
)
).bind("intExpr")
)
)
)
)
)
'
clang-query \
-c "set bind-root false" \
-c "m $matcher" \
test.cc --
# EOF
测试输入文件
test.cc
:
// test.cc
// Match an `operator<<` expression involving `std::stringstream`.
#include <sstream> // std::stringstream
void f()
{
int age = 5;
std::stringstream q;
q << "my name "
<< "is Tom, "
<< "my age is " << age;
}
// EOF
脚本的输出:
Match #1:
$PWD\test.cc:12:22: note:
"intExpr" binds here
12 | << "my age is " << age;
| ^~~
$PWD\test.cc:10:3: note:
"stringStreamExpr" binds here
10 | q << "my name "
| ^
Match #2:
$PWD\test.cc:12:6: note:
"stringLiteral" binds here
12 | << "my age is " << age;
| ^~~~~~~~~~~~
$PWD\test.cc:10:3: note:
"stringStreamExpr" binds here
10 | q << "my name "
| ^
Match #3:
$PWD\test.cc:11:6: note:
"stringLiteral" binds here
11 | << "is Tom, "
| ^~~~~~~~~~
$PWD\test.cc:10:3: note:
"stringStreamExpr" binds here
10 | q << "my name "
| ^
Match #4:
$PWD\test.cc:10:8: note:
"stringLiteral" binds here
10 | q << "my name "
| ^~~~~~~~~~
$PWD\test.cc:10:3: note:
"stringStreamExpr" binds here
10 | q << "my name "
| ^
4 matches.
问题有这个尝试的匹配器:
cxxOperatorCallExpr(hasOverloadedOperatorName("<<"),
hasLHS(ignoringParenImpCasts(expr(anyOf(
stringLiteral().bind("firstString"),
stringLiteral().bind("secondString"),
stringLiteral().bind("thirdString"))))),
hasRHS(ignoringParenImpCasts(expr(anyOf(
declRefExpr(to(varDecl(hasType(cxxRecordDecl(hasName("std::stringstream")))))),
declRefExpr(hasType(isInteger())))))))
这有几个问题值得确定,作为澄清如何 AST 的结构以及匹配器的工作原理:
它似乎正在寻找left手边的字符串文字 和
right侧的
stringstream
变量;那些应该
被交换了。
它试图匹配
anyOf
内的多个字符串文字,但是
anyOf
将在第一个分支成功后停止。 如果你改变这一点
到 allOf
,那么所有的绑定都会转到同一个
表达。 在某些情况下,forEachDescendant
可以用来做
像这样的东西,但我发现很难稳健地使用,而且它
在这里不适用(或者至少我不知道如何让它在
这种情况)。
似乎期望整个声明是一个
cxxOperatorCallExpr
,但它实际上是它们的一棵树。
它在识别
stringstream
解释的变量方面存在问题
在这个答案的第一部分。