Clang AST Matchers: как найти тело функции из объявления функции? - PullRequest
1 голос
/ 27 февраля 2020

Я пытался написать простую программу проверки clang-tidy, которая будет проверять конструктор, который вызывает fopen() более одного раза. Я хочу найти потенциальную утечку памяти в случае, если во втором вызове fopen() произойдет какое-либо исключение.

class Dummy_file
{
  FILE *f1_;
  FILE *f2_;
  public:
    Dummy_file(const char* f1_name, const char* f2_name, const char * mode){
        f1_ = fopen(f1_name, mode);
        f2_ = fopen(f2_name, mode);
    }
    ~Dummy_file(){
        fclose(f1_);
        fclose(f2_);
    }
};

С помощью этого

callExpr(callee(functionDecl(hasName("fopen")))).bind("fopencalls")

удалось найти все fopen() звонки.

Но я не смог найти cxxConstructorDecl, используя это.

cxxConstructorDecl(has(callExpr(callee(functionDecl(hasName("fopen")))))).bind("ctr")

Я сомневаюсь, так как я использую cxxConstructorDecl, мой фильтр не применяется к телу конструктора. Итак, как найти тело функции из объявления функции?

1 Ответ

1 голос
/ 27 февраля 2020

Краткое объяснение

Вы должны использовать hasDescendant matcher вместо has matcher. В то время как has проверяет только непосредственные дочерние элементы проверенного узла на совпадение, hasDescendant соответствует любому потомку.

Здесь вы можете видеть это для вашего примера:

  |-CXXConstructorDecl <line:8:3, line:11:3> line:8:3 Dummy_file 'void (const char *, const char *, const char *)'
  | |-ParmVarDecl <col:14, col:26> col:26 used f1_name 'const char *'
  | |-ParmVarDecl <col:35, col:47> col:47 used f2_name 'const char *'
  | |-ParmVarDecl <col:56, col:68> col:68 used mode 'const char *'
  | `-CompoundStmt <col:74, line:11:3>
  |   |-BinaryOperator <line:9:5, col:30> 'FILE *' lvalue '='
  |   | |-MemberExpr <col:5> 'FILE *' lvalue ->f1_ 0x55d36491a230
  |   | | `-CXXThisExpr <col:5> 'Dummy_file *' this
  |   | `-CallExpr <col:11, col:30> 'FILE *'
  |   |   |-ImplicitCastExpr <col:11> 'FILE *(*)(const char *__restrict, const char *__restrict)' <FunctionToPointerDecay>
  |   |   | `-DeclRefExpr <col:11> 'FILE *(const char *__restrict, const char *__restrict)' lvalue Function 0x55d3648fa220 'fopen' 'FILE *(const char *__restrict, const char *__restrict)'
  |   |   |-ImplicitCastExpr <col:17> 'const char *' <LValueToRValue>
  |   |   | `-DeclRefExpr <col:17> 'const char *' lvalue ParmVar 0x55d36491a310 'f1_name' 'const char *'
  |   |   `-ImplicitCastExpr <col:26> 'const char *' <LValueToRValue>
  |   |     `-DeclRefExpr <col:26> 'const char *' lvalue ParmVar 0x55d36491a400 'mode' 'const char *'

CallExpr не является потомком CXXConstructorDecl, но BinaryOperator.

Решение

Ниже я завершил ваш матчер и проверил его в clang-query.

clang-query> match cxxConstructorDecl(hasDescendant(callExpr(callee(functionDecl(hasName("fopen")))).bind("fopencall"))).bind("ctr")

Match #1:

$TEST_DIR/test.cpp:8:3: note: "ctr" binds here
  Dummy_file(const char *f1_name, const char *f2_name, const char *mode) {
  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
$TEST_DIR/test.cpp:9:11: note: "fopencall" binds here
    f1_ = fopen(f1_name, mode);
          ^~~~~~~~~~~~~~~~~~~~
$TEST_DIR/test.cpp:8:3: note: "root" binds here
  Dummy_file(const char *f1_name, const char *f2_name, const char *mode) {
  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 match.

Надеюсь, это ответит на ваш вопрос!

...