Rah*_*ahn 8 c++ clang clang-ast-matchers clang-tidy
我希望将所有 for 循环与在其主体中声明的向量相匹配(稍后可能会扩展到 while 循环和 do-while 循环):
\n#include <vector>\n\nint main() {\n for (int i = 0; i < 10; i++) {\n std::vector<int> foo;\n }\n\n return 0;\n}\nRun Code Online (Sandbox Code Playgroud)\n我从这个程序中得到的 AST 是
\n`-FunctionDecl 0x55c1d33c8bc8 <main.cpp:3:1, line:9:1> line:3:5 main \'int ()\'\n `-CompoundStmt 0x55c1d3402a48 <col:12, line:9:1>\n |-ForStmt 0x55c1d34029e0 <line:4:3, line:6:3>\n | |-DeclStmt 0x55c1d33c8d40 <line:4:8, col:17>\n | | `-VarDecl 0x55c1d33c8cb8 <col:8, col:16> col:12 used i \'int\' cinit\n | | `-IntegerLiteral 0x55c1d33c8d20 <col:16> \'int\' 0\n | |-<<<NULL>>>\n | |-BinaryOperator 0x55c1d33c8db0 <col:19, col:23> \'bool\' \'<\'\n | | |-ImplicitCastExpr 0x55c1d33c8d98 <col:19> \'int\' <LValueToRValue>\n | | | `-DeclRefExpr 0x55c1d33c8d58 <col:19> \'int\' lvalue Var 0x55c1d33c8cb8 \'i\' \'int\'\n | | `-IntegerLiteral 0x55c1d33c8d78 <col:23> \'int\' 10\n | |-UnaryOperator 0x55c1d33c8df0 <col:27, col:28> \'int\' postfix \'++\'\n | | `-DeclRefExpr 0x55c1d33c8dd0 <col:27> \'int\' lvalue Var 0x55c1d33c8cb8 \'i\' \'int\'\n | `-CompoundStmt 0x55c1d34029c8 <col:32, line:6:3>\n | `-DeclStmt 0x55c1d34029b0 <line:5:5, col:25>\n | `-VarDecl 0x55c1d33cd388 <col:5, col:22> col:22 foo \'std::vector<int>\' callinit destroyed\n | `-CXXConstructExpr 0x55c1d3402988 <col:22> \'std::vector<int>\' \'void () noexcept\'\n `-ReturnStmt 0x55c1d3402a38 <line:8:3, col:10>\n `-IntegerLiteral 0x55c1d3402a18 <col:10> \'int\' 0\nRun Code Online (Sandbox Code Playgroud)\n现在我如何编写一个匹配器(在 clang-tidy 定制检查中)来匹配这种模式?
\n我阅读了有关自定义 clang-tidy 检查和匹配 Clang AST的文档,但它们似乎没有提供足够的信息来说明我应该如何实际组合每个 API。
\n更新:根据 @Scott McPeak\'s 的回答,我可以在终端中将 for 循环与 clang-query 进行匹配,但我无法将此匹配器移植到我的 clang-tidy 检查中:
\n`-FunctionDecl 0x55c1d33c8bc8 <main.cpp:3:1, line:9:1> line:3:5 main \'int ()\'\n `-CompoundStmt 0x55c1d3402a48 <col:12, line:9:1>\n |-ForStmt 0x55c1d34029e0 <line:4:3, line:6:3>\n | |-DeclStmt 0x55c1d33c8d40 <line:4:8, col:17>\n | | `-VarDecl 0x55c1d33c8cb8 <col:8, col:16> col:12 used i \'int\' cinit\n | | `-IntegerLiteral 0x55c1d33c8d20 <col:16> \'int\' 0\n | |-<<<NULL>>>\n | |-BinaryOperator 0x55c1d33c8db0 <col:19, col:23> \'bool\' \'<\'\n | | |-ImplicitCastExpr 0x55c1d33c8d98 <col:19> \'int\' <LValueToRValue>\n | | | `-DeclRefExpr 0x55c1d33c8d58 <col:19> \'int\' lvalue Var 0x55c1d33c8cb8 \'i\' \'int\'\n | | `-IntegerLiteral 0x55c1d33c8d78 <col:23> \'int\' 10\n | |-UnaryOperator 0x55c1d33c8df0 <col:27, col:28> \'int\' postfix \'++\'\n | | `-DeclRefExpr 0x55c1d33c8dd0 <col:27> \'int\' lvalue Var 0x55c1d33c8cb8 \'i\' \'int\'\n | `-CompoundStmt 0x55c1d34029c8 <col:32, line:6:3>\n | `-DeclStmt 0x55c1d34029b0 <line:5:5, col:25>\n | `-VarDecl 0x55c1d33cd388 <col:5, col:22> col:22 foo \'std::vector<int>\' callinit destroyed\n | `-CXXConstructExpr 0x55c1d3402988 <col:22> \'std::vector<int>\' \'void () noexcept\'\n `-ReturnStmt 0x55c1d3402a38 <line:8:3, col:10>\n `-IntegerLiteral 0x55c1d3402a18 <col:10> \'int\' 0\nRun Code Online (Sandbox Code Playgroud)\n当构建 clang-tidy 时,它说call of overloaded \'hasType()\' is ambiguous:
llvm-project/clang-tools-extra/clang-tidy/misc/LowPerfLoopCheck.cpp: In member function \xe2\x80\x98virtual void clang::tidy::misc::LowPerfLoopCheck::registerMatchers(clang::ast_matchers::MatchFinder*)\xe2\x80\x99:\n/home/wentao/Desktop/llvm-project/clang-tools-extra/clang-tidy/misc/LowPerfLoopCheck.cpp:19:44: error: call of overloaded \xe2\x80\x98hasType(clang::ast_matchers::internal::PolymorphicMatcher<clang::ast_matchers::internal::HasDeclarationMatcher, void(clang::ast_matchers::internal::TypeList<clang::CallExpr, clang::CXXConstructExpr, clang::CXXNewExpr, clang::DeclRefExpr, clang::EnumType, clang::ElaboratedType, clang::InjectedClassNameType, clang::LabelStmt, clang::AddrLabelExpr, clang::MemberExpr, clang::QualType, clang::RecordType, clang::TagType, clang::TemplateSpecializationType, clang::TemplateTypeParmType, clang::TypedefType, clang::UnresolvedUsingType, clang::ObjCIvarRefExpr>), clang::ast_matchers::internal::Matcher<clang::Decl> >)\xe2\x80\x99 is ambiguous\n 19 | forStmt(hasDescendant(varDecl(hasType(hasDeclaration(cxxRecordDecl(\n | ~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n 20 | matchesName("^::std::vector$")))))\n | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nRun Code Online (Sandbox Code Playgroud)\n为什么相同的代码适用于 clang-query 而不适用于 clang-tidy 检查?
\n您可以使用此 AST 匹配器找到for在某处包含变量声明的所有循环:std::vector
#!/bin/sh
PATH=$HOME/opt/clang+llvm-16.0.0-x86_64-linux-gnu-ubuntu-18.04/bin:$PATH
# In this query, the comments are ignored because clang-query (not the
# shell) recognizes and discards them.
query='m
forStmt( # Report any "for" statement
hasDescendant( # where a descendant
varDecl( # is a variable declaration
hasType( # whose type
cxxRecordDecl( # is a struct/class/union (possibly templatized)
matchesName( # whose name matches the regex
"^::std::vector$" # given here.
)
)
)
).bind("vector_var_decl") # Bind to the declaration AST node.
)
)
'
clang-query \
-c="$query" \
test.cc -- -w
# EOF
Run Code Online (Sandbox Code Playgroud)
clang-query
为了便于实验,该匹配器被表示为
shell 脚本,但匹配器表达式可以直接复制到 C++clang-tidy检查器实现中,如下所示:
void HelloCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(
forStmt( // Report any "for" statement
hasDescendant( // where a descendant
varDecl( // is a variable declaration
hasType( // whose type
cxxRecordDecl( // is a struct/class/union (possibly templatized)
matchesName( // whose name matches the regex
"^::std::vector$" // given here.
)
)
)
).bind("vector_var_decl") // Bind to the declaration AST node.
)
),
this
);
}
Run Code Online (Sandbox Code Playgroud)
为了创建这个匹配器,我使用了AST Matcher Reference中的匹配器列表
。需要反复试验才能找到正确的组合;我希望文档更好。我可以提供的一个小技巧是,您可以使用
anything()匹配器作为占位符来匹配任何内容,同时逐步开发匹配器。
在这种情况下,第一个困难的部分(对我来说,尽管有一些先前的经验)是意识到我需要cxxRecordDecl先使用匹配器matchesName才能使用。最初,我尝试过
hasDeclaration,但它匹配不一定有名称的声明,因此matchesName只会默默地失败(clang-query在这种情况下可能会提供更多信息)。通过使用cxxRecordDecl,我们保证使用具有匹配名称的声明,因此它可以工作。
第二个问题是,当将我的原始clang-query匹配器转换为 C++clang-tidy检查时,出现了关于对
hasType. 原始匹配器有:
varDecl(hasType(hasDeclaration(cxxRecordDecl(...))))
Run Code Online (Sandbox Code Playgroud)
显然,hasDeclaration在这个地方会在 C++ 上下文中引起问题(尽管我不太明白为什么),我的解决方案就是删除它:
varDecl(hasType(cxxRecordDecl(...)))
Run Code Online (Sandbox Code Playgroud)
要扩展到其他类型的循环,可以将whileStmt和doStmt匹配器替换为forStmt。
输入示例:
#if 1
#include <vector>
#else
// Simplified stand-in for easier AST inspection.
namespace std {
template <class T>
class vector {};
}
#endif
class SomethingElse {};
int main() {
for (int i = 0; i < 10; i++) { // Report.
std::vector<int> foo; // Original example.
}
for (int i = 0; i < 10; i++) { // Do not report.
SomethingElse se; // Not a std::vector.
}
for (;;) { // Report.
{
std::vector<int> foo; // Not an immediate child.
}
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
对应输出:
$ ./cmd.sh
Match #1:
$PWD/test.cc:14:3: note: "root" binds here
for (int i = 0; i < 10; i++) { // Report.
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
$PWD/test.cc:15:5: note: "vector_var_decl" binds here
std::vector<int> foo; // Original example.
^~~~~~~~~~~~~~~~~~~~
Match #2:
$PWD/test.cc:22:3: note: "root" binds here
for (;;) { // Report.
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
$PWD/test.cc:24:7: note: "vector_var_decl" binds here
std::vector<int> foo; // Not an immediate child.
^~~~~~~~~~~~~~~~~~~~
2 matches.
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
130 次 |
| 最近记录: |