Как найти все поля для чтения / записи с помощью Clang?

Учитывая исходный код C++, я хочу найти поля класса, которые каждая функция пишет и читает. Каков наилучший способ сделать это с помощью внешнего интерфейса Clang?

(Я не прошу подробного объяснения всех шагов; однако отправная точка для эффективного решения была бы отличной.)

До сих пор я пытался выполнить синтаксический анализ операторов с помощью RecursiveASTVisitor, но отслеживать соединения узлов сложно. Кроме того, я не могу понять, как отслеживать что-то вроде ниже:

int& x = m_int_field;
x++;

Это явно изменяет m_int_field; но с учетом одного Stmt невозможно знать это; поэтому обход AST сам по себе кажется недостаточным.

Бонусом для меня является возможность отдельного подсчета полей и подполей (например, доступ к трем полям структуры члена).

Пример:

typedef struct Y {
    int m_structfield1;
    float m_structfield2;
    Y () {
        m_structfield1 = 0;
        m_structfield2 = 1.0f;
    }
} Y;
class X {
    int m_field1;
    std::string m_field2;
    Y m_field3;
public:
    X () : m_field2("lel") {}
    virtual ~X() {}
    void func1 (std::string s) {
        m_field1 += 2;
        m_field2 = s;
    }
    int func2 () {
        return m_field1 + 5;
    }
    void func3 (Y& y) {
        int& i = m_field1;
        y.m_structfield2 = 1.2f + i++;
    }
    int func4 () {
        func3 (m_field3);
        return m_field3.m_structfield1;
    }
};

должен вернуться

X::X() -> m_field1 (w), m_field3.m_structfield1 (w), m_field3.m_structfield2 (w)
X::func1(std::string) -> m_field1 (r+w), m_field2 (w)
X::func2() -> m_field1 (r)
X::func3(Y&) -> m_field1 (r+w)
X::func4() -> m_field1 (r+w), m_field3.m_structfield2 (w), m_field3.m_structfield1 (r)

Для простоты можно предположить, что наследования нет.

1 ответ

Решение

Я собирал несколько примеров анализа кода с помощью совпадений AST Clang. Там есть пример приложения StructFieldUser, которое сообщает, какие поля структуры читаются или записываются, и функцию, в которой происходит каждый доступ. Это отличается от того, что вы ищете, но это может быть полезным ориентиром. Это демонстрирует извлечение и запись такого рода информации, и это показывает, как собрать все части воедино.

Хорошее место для начала с матчами AST в целом - это сообщение Эли Бендерски.

Чтобы почувствовать тех, кто решит вашу проблему, вы можете попрактиковаться с clang-query:

$ clang-query example.cpp --    # the two dashes mean no compilation db
clang-query> let m1 memberExpr()
clang-query> m m1

Match #1:

/path/example.cpp:9:9: note: "root" binds here
        m_structfield1 = 0;
        ^~~~~~~~~~~~~~

Match #2:

/path/example.cpp:10:9: note: "root" binds here
        m_structfield2 = 1.0f;
        ^~~~~~~~~~~~~~
...
11 matches.

Затем вы можете начать подключаться к другим узлам, используя средства поиска обхода. Это позволяет вам захватывать связанный контекст, такой как метод функции или класса, в котором делается ссылка. Добавление bind выражения для сопоставителей узлов помогут вам увидеть, что именно сопоставляется. Связывающие узлы также дадут доступ к узлам в обратных вызовах.

clang-query> let m2 memberExpr(hasAncestor(functionDecl().bind("fdecl"))).bind("mexpr")
clang-query> m m2

Match #1:

/path/example.cpp/path/example.cpp:8:5: note: "fdecl" binds here
    Y () {
    ^~~~~~
/path/example.cpp:9:9: note: "mexpr" binds here
        m_structfield1 = 0;
        ^~~~~~~~~~~~~~
/path/example.cpp:9:9: note: "root" binds here
        m_structfield1 = 0;
        ^~~~~~~~~~~~~~

Match #2:

/path/example.cpp:8:5: note: "fdecl" binds here
    Y () {
    ^~~~~~
/path/example.cpp:10:9: note: "mexpr" binds here
        m_structfield2 = 1.0f;
        ^~~~~~~~~~~~~~
/path/example.cpp:10:9: note: "root" binds here
        m_structfield2 = 1.0f;
        ^~~~~~~~~~~~~~
...

Может потребоваться некоторая работа, чтобы научиться подбирать именно те узлы, которые вам нужны. Обратите внимание, что приведенные выше совпадения не принимают инициализацию в X::X(), Глядя на АСТ от

clang-check -ast-dump example.cpp -- 

показывает, что эти узлы не являются MemberExpr узлы; они CXXCtorInitializer узлы, поэтому cxxCtorInitializer matcher необходим, чтобы получить эти узлы. Вероятно, для поиска всех разных узлов требуются несколько сопоставителей.

Другие вопросы по тегам