2015-01-18 3 views
3

Я пишу инструмент Clang для статического анализа исходного файла и для сопоставления и переименования всех частных членов класса.Сопоставление частных членов класса с использованием clang AST Matcher

Рассмотрим пример:

class AClass { // problem: my matcher modifies AST node here too 
private: 
    int a; // <- I know how to rename this 'a' using other matcher 
public: 
    AClass() { 
     AClass cl; 
     this->a = 1; // <- rename this 'a' 
     cl.a = 2; // <- rename this 'a' 
    } 
}; 
void bar(AClass); 
void foo() { 
    //bar(AClass()); 
} 

Я использую следующий Искателя, чтобы получить доступ к AST узлов, которые я хотел бы изменить. Он работает, как я ожидаю.

clang-query> match memberExpr(hasDeclaration(namedDecl(isPrivate()))) 

Match #1: 

sum.cpp:7:9: note: "root" binds here 
    this->a = 1; 
    ^~~~~~~ 

Match #2: 

sum.cpp:8:9: note: "root" binds here 
    cl.a = 2; 
    ^~~~ 
2 matches. 

Если в примере я раскомментировать строку с bar(AClass());, возникает проблема. Существует дополнительный матч, точно

Match #3: 

sum.cpp:1:7: note: "root" binds here 
class AClass { 
^~~~~~ 
3 matches. 

что приводит к перезаписи class AClass декларации в странном образе. Я хочу избавиться от этого матча.

Матчи возвращают указатель на объект MemberExpr. Я попытался отфильтровать третий матч, проверив предикат isArrow(), и это помогло, но затем я не могу сопоставить выражения с точками, например cl.a.

Я ищу другое выражение для определения AST или код, который работает с объектами MemberExpr, и получает доступ ко всем закрытым переменным в исходном файле и не более того.

+0

это мог быть местом, которое звон дает в неявном определен копировать конструктор? Я не знаком с тем, как это делает clang, но я знаком с подобными инструментами. Общий подход заключается в том, чтобы неявно объявленные/определенные функции занимали местоположение конструктора. Может быть, вы можете добавить фильтр на закрывающей функции, которая не определена неявно? –

+0

@RichardCorden, я попытался использовать 'memberExpr (hasDeclaration (namedDecl (isPrivate(), если только (isImplicit()))))' Соответствие expresion и ничего не сказалось. –

+2

Я вообще не знаком с Clang API. Вы не хотите спрашивать, является ли 'a' неявным, вы хотите спросить, неявно ли определена функция, охватывающая выражение. Вы можете получить эту информацию? Один из способов проверить мою теорию - явно определить конструктор копирования. Если вы больше не получаете совпадение в строке 1 исходного файла, то это показывает, что это было место конструктора копирования. –

ответ

4

Как вы заметили, проблема связана с неявным образом сконструированным конструктором . Вот дамп AST,

% clang-check -ast-dump -ast-dump-filter=AClass s.cpp -- 
Dumping AClass: 
CXXRecordDecl 0x2cb3700 </tmp/s.cpp:1:1, line:10:1> line:1:7 referenced class AClass definition 
|-CXXRecordDecl 0x2cb3810 <col:1, col:7> col:7 implicit referenced class AClass 
|-AccessSpecDecl 0x2cb38a0 <line:2:1, col:8> col:1 private 
|-FieldDecl 0x2cb38e0 <line:3:3, col:7> col:7 referenced a 'int' 
|-AccessSpecDecl 0x2cb3930 <line:4:1, col:7> col:1 public 
|-CXXConstructorDecl 0x2cfaee0 <line:5:3, line:9:3> line:5:3 used AClass 'void (void)' 
| `-CompoundStmt 0x2cfb440 <col:12, line:9:3> 
| |-DeclStmt 0x2cfb240 <line:6:5, col:14> 
| | `-VarDecl 0x2cfafe0 <col:5, col:12> col:12 used cl 'class AClass' callinit 
| | `-CXXConstructExpr 0x2cfb210 <col:12> 'class AClass' 'void (void)' 
| |-BinaryOperator 0x2cfb2c0 <line:7:5, col:15> 'int' lvalue '=' 
| | |-MemberExpr 0x2cfb270 <col:5, col:11> 'int' lvalue ->a 0x2cb38e0 
| | | `-CXXThisExpr 0x2cfb258 <col:5> 'class AClass *' this 
| | `-IntegerLiteral 0x2cfb2a0 <col:15> 'int' 1 
| `-BinaryOperator 0x2cfb360 <line:8:5, col:12> 'int' lvalue '=' 
|  |-MemberExpr 0x2cfb310 <col:5, col:8> 'int' lvalue .a 0x2cb38e0 
|  | `-DeclRefExpr 0x2cfb2e8 <col:5> 'class AClass' lvalue Var 0x2cfafe0 'cl' 'class AClass' 
|  `-IntegerLiteral 0x2cfb340 <col:12> 'int' 2 
|-CXXConstructorDecl 0x2cfb070 <line:1:7> col:7 implicit used AClass 'void (const class AClass &) throw()' inline 
| |-ParmVarDecl 0x2cfb1b0 <col:7> col:7 used 'const class AClass &' 
| |-CXXCtorInitializer Field 0x2cb38e0 'a' 'int' 
| | `-ImplicitCastExpr 0x2cfb9e0 <col:7> 'int' <LValueToRValue> 
| | `-MemberExpr 0x2cfb998 <col:7> 'const int' lvalue .a 0x2cb38e0 
| |  `-DeclRefExpr 0x2cfb970 <col:7> 'const class AClass' lvalue ParmVar 0x2cfb1b0 '' 'const class AClass &' 
| `-CompoundStmt 0x2cfba28 <col:7> 
`-CXXDestructorDecl 0x2cfb770 <col:7> col:7 implicit used ~AClass 'void (void) throw()' inline 
    `-CompoundStmt 0x2cfb890 <col:7> 

где конструктор копии присваивается определение в <line:1:7>. Здесь MemberExpr вы случайно попадаете в неявный экземпляр . Как писал Ричард Коэн в своем комментарии, это не MemberExpr, то есть неявный, но его предок, конструктор копирования. Вы можете удалить эти нежелательные матчей путем фильтрации на содержащем, как предложено,

match memberExpr(hasDeclaration(namedDecl(isPrivate())), 
       unless(hasAncestor(isImplicit()))) 

, который оставляет вас с матчами, которые вы хотите,

Match #1: 

/tmp/s.cpp:7:5: note: "root" binds here 
    this->a = 1; // <- rename this 'a' 
    ^~~~~~~ 

Match #2: 

/tmp/s.cpp:8:5: note: "root" binds here 
    cl.a = 2; // <- rename this 'a' 
    ^~~~ 
2 matches. 
Смежные вопросы