2

C++ 소스 코드가 주어지면 모든 함수가 쓰고 읽는 클래스 필드를 찾고 싶습니다. Clang 프론트 엔드를 사용하여이 작업을 수행하는 가장 좋은 방법은 무엇입니까? 지금까지 내가 RecursiveASTVisitor를 사용하여 문을 구문 분석을 시도Clang을 사용하여 모든 회원 필드의 읽기/쓰기를 어떻게 찾을 수 있습니까?

, 그러나 추적하는,

(그러나 효율적인 솔루션을위한 출발점이 좋은 것입니다. 나는 모든 단계에 대한 자세한 설명을 요구하고 있지 않다) 노드 연결은 어렵습니다. 또한, 나는 다음과 같은 것을 추적하는 방법을 알아낼 수 없습니다 :

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) 

우리는 상속 재산이 없다는 것을 단순화를 위해 가정 할 수있다 반환해야합니다.

답변

3

나는 examples of analyzing code with Clang's AST matchers을 수집 중입니다. StructFieldUser는 구조체의 어느 필드가 읽히거나 쓰여지는지와 각 액세스가 발생하는 함수를보고하는 예제 응용 프로그램입니다. 그것은 당신이 찾고있는 것과는 다르지만, 유용한 참조 점이 될 수 있습니다. 그것은 이런 종류의 정보를 추출하고 기록하는 것을 보여 주며, 모든 조각들을 모으는 방법을 보여줍니다.

일반적으로 AST matchers로 시작하는 좋은 장소는 this post by Eli Bendersky입니다.

, 당신은 clang-query 연습 수도 문제를 해결할 것 매처 (matcher)에 대한 느낌을 얻으려면 :

$ 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. 

은 그럼 당신은 탐색 매처 (matcher)를 사용하여 다른 노드에 연결을 시작할 수 있습니다. 이를 통해 참조가 작성된 함수 또는 클래스 메소드와 같은 관련 컨텍스트를 캡처 할 수 있습니다. 노드 합치기에 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; 
     ^~~~~~~~~~~~~~ 
... 

필요한 정확한 노드를 선택하는 방법을 배우려면 약간의 작업이 필요할 수 있습니다. 위의 matchers는 X::X()에서 초기화를 선택하지 않습니다.

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

에서 AST를 보면 해당 노드가 MemberExpr 노드가 아님을 알 수 있습니다. 노드가 CXXCtorInitializer이므로 노드를 얻으려면 cxxCtorInitializer 정규 표현식이 필요합니다. 모든 다른 노드를 찾으려면 여러 matcher가 필요할 것입니다.