如何通过AST匹配std::stringstream?

问题描述 投票:0回答:1

目标代码块:

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转储日志构建匹配器?

感谢任何帮助。

c++ clang abstract-syntax-tree llvm-c++-api clang-ast-matchers
1个回答
0
投票

匹配类型为
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 匹配器 语言还不够强大,不足以稳健地做到这一点。

完整示例

这是一个运行的shell脚本

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
    解释的变量方面存在问题 在这个答案的第一部分。

© www.soinside.com 2019 - 2024. All rights reserved.