Определите, является ли вызов участника виртуальным в Clang AST - PullRequest
1 голос
/ 28 мая 2020

Я хотел бы написать анализатор, который подсчитывает virtual вызовы функций, глядя на C ++ AST (вывод -ast-dump), но мне трудно определить, какие вызовы функций являются virtual, а какие нет. . Вот пример фрагмента кода:

struct A {
    A() {}
    virtual int foo() { return 0; }
};
struct B : public A {
    B() {}
    //virtual int foo() { return 3; }
};

struct C : public B {
    C() {}
    virtual int foo() { return 1; }
};

int test(C* c) { 
    return c->foo() + c->B::foo() + c->A::foo();
}

В моей первоначальной реализации я просто проверяю, является ли вызываемая функция virtual, используя expr->getMethodDecl()->isVirtual(), но (насколько я понимаю) вызовы в c->B::foo() и c->A::foo() на самом деле не virtual, поэтому недостаточно спросить, является ли вызываемая функция virtual.

Когда я выгружаю AST из этого фрагмента кода, я получаю следующее дерево для test:

`-FunctionDecl 0x1d33620 <line:19:1, line:21:1> line:19:5 test 'int (C *)'
  |-ParmVarDecl 0x1d33558 <col:10, col:13> col:13 used c 'C *'
  `-CompoundStmt 0x1d339f0 <col:16, line:21:1>
    `-ReturnStmt 0x1d339e0 <line:20:5, col:47>
      `-BinaryOperator 0x1d339c0 <col:12, col:47> 'int' '+'
        |-BinaryOperator 0x1d338a8 <col:12, col:33> 'int' '+'
        | |-CXXMemberCallExpr 0x1d33778 <col:12, col:19> 'int'
        | | `-MemberExpr 0x1d33748 <col:12, col:15> '<bound member function type>' ->foo 0x1d31c80
        | |   `-ImplicitCastExpr 0x1d33730 <col:12> 'C *' <LValueToRValue>
        | |     `-DeclRefExpr 0x1d33710 <col:12> 'C *' lvalue ParmVar 0x1d33558 'c' 'C *'
        | `-CXXMemberCallExpr 0x1d33848 <col:23, col:33> 'int'
        |   `-MemberExpr 0x1d33800 <col:23, col:29> '<bound member function type>' ->foo 0x1d02400
        |     `-ImplicitCastExpr 0x1d33888 <col:23> 'A *' <UncheckedDerivedToBase (A)>
        |       `-ImplicitCastExpr 0x1d33868 <col:23> 'B *' <UncheckedDerivedToBase (B)>
        |         `-ImplicitCastExpr 0x1d337d8 <col:23> 'C *' <LValueToRValue>
        |           `-DeclRefExpr 0x1d33798 <col:23> 'C *' lvalue ParmVar 0x1d33558 'c' 'C *'
        `-CXXMemberCallExpr 0x1d33978 <col:37, col:47> 'int'
          `-MemberExpr 0x1d33930 <col:37, col:43> '<bound member function type>' ->foo 0x1d02400
            `-ImplicitCastExpr 0x1d33998 <col:37> 'A *' <UncheckedDerivedToBase (B -> A)>
              `-ImplicitCastExpr 0x1d33908 <col:37> 'C *' <LValueToRValue>
                `-DeclRefExpr 0x1d338c8 <col:37> 'C *' lvalue ParmVar 0x1d33558 'c' 'C *'

Глядя на это, кажется, что приведение UncheckedDerivedToBase отмечает места, где вызов функции не virtual. Так всегда бывает? Должен ли я всегда рассматривать вызов формы CXXMemberCallExpr (MemberExpr (ImplicitCastExpr<UncheckedDerivedToBase> e))) как вызов, отличный от virtual? Существуют ли другие шаблоны, указывающие на вызовы функций, отличных от virtual? Есть ли более надежный способ определить этот факт?

РЕДАКТИРОВАТЬ : Еще одно исследование предполагает, что вышеприведенной гипотезы о том, что UncheckedDerivedToBase недостаточно. Этот код:

struct A {
    virtual int foo() { return 100; }
};
struct B : public A {
    virtual int foo() { return 10; }
};

int test(B* b) {
    return b->foo() + b->B::foo();
}

, похоже, создает точно такой же узел AST (по крайней мере, неразличимый на консоли) для обоих вызовов, но семантика должна отличаться в соответствии со стандартом, если b на самом деле является производным класс, например C выше.

1 Ответ

0 голосов
/ 29 мая 2020

Отличительный фактор - hasQualifier на MemberExpr вызываемого объекта. Если hasQualifier равно true, то вызов функции не virtual.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...