Извлечение пользовательских атрибутов исходного кода с помощью инструмента Clang

#clang

#clang

Вопрос:

Я разрабатываю инструмент, который анализирует и проверяет код с помощью clang, и я пытаюсь выяснить, есть ли способ получить нестандартные атрибуты clang из исходного кода. Например, я хотел бы аннотировать цикл инвариантом цикла или выражением с [[not_null]] . Возможно ли это? Может быть, есть какой-то вариант, который бы говорил

Вот конкретный пример:

 struct custom { };
int test() {
  int x;
  switch (1) {
  case 1:
    x = 1;
    [[fallthrough]] ;
  case 2:
    x = 2;
    break;
  }
  [[custom()]]
  if (1) { return 1; } else { return 0; }
}
  

Результирующий AST выглядит следующим образом:

 `-FunctionDecl 0x55a9780ebba0 <line:2:1, line:14:1> line:2:5 test 'int ()'
  `-CompoundStmt 0x55a9780ec060 <col:12, line:14:1>
    |-DeclStmt 0x55a9780ebd08 <line:3:3, col:8>
    | `-VarDecl 0x55a9780ebca0 <col:3, col:7> col:7 used x 'int'
    |-SwitchStmt 0x55a9780ebd40 <line:4:3, line:11:3>
    | |-IntegerLiteral 0x55a9780ebd20 <line:4:11> 'int' 1
    | `-CompoundStmt 0x55a9780ebf40 <col:14, line:11:3>
    |   |-CaseStmt 0x55a9780ebda0 <line:5:3, line:6:9>
    |   | |-ConstantExpr 0x55a9780ebd80 <line:5:8> 'int' Int: 1
    |   | | `-IntegerLiteral 0x55a9780ebd60 <col:8> 'int' 1
    |   | `-BinaryOperator 0x55a9780ebe08 <line:6:5, col:9> 'int' lvalue '='
    |   |   |-DeclRefExpr 0x55a9780ebdc8 <col:5> 'int' lvalue Var 0x55a9780ebca0 'x' 'int'
    |   |   `-IntegerLiteral 0x55a9780ebde8 <col:9> 'int' 1
    |   |-AttributedStmt 0x55a9780ebe58 <line:7:5, col:21>  <<<<<<<<<<<<<<<<<<<<<<<<<<< GOT Fallthrough
    |   | |-FallThroughAttr 0x55a9780ebe30 <col:7>
    |   | `-NullStmt 0x55a9780ebe28 <col:21>
    |   |-CaseStmt 0x55a9780ebeb0 <line:8:3, line:9:9>
    |   | |-ConstantExpr 0x55a9780ebe90 <line:8:8> 'int' Int: 2
    |   | | `-IntegerLiteral 0x55a9780ebe70 <col:8> 'int' 2
    |   | `-BinaryOperator 0x55a9780ebf18 <line:9:5, col:9> 'int' lvalue '='
    |   |   |-DeclRefExpr 0x55a9780ebed8 <col:5> 'int' lvalue Var 0x55a9780ebca0 'x' 'int'
    |   |   `-IntegerLiteral 0x55a9780ebef8 <col:9> 'int' 2
    |   `-BreakStmt 0x55a9780ebf38 <line:10:5>
    `-IfStmt 0x55a9780ec038 <line:13:3, col:41> has_else  <<<<<<<<<<<<<<<<<<<<<<<<<<< MISSING custom
      |-ImplicitCastExpr 0x55a9780ebf90 <col:7> 'bool' <IntegralToBoolean>
      | `-IntegerLiteral 0x55a9780ebf70 <col:7> 'int' 1
      |-CompoundStmt 0x55a9780ebfd8 <col:10, col:22>
      | `-ReturnStmt 0x55a9780ebfc8 <col:12, col:19>
      |   `-IntegerLiteral 0x55a9780ebfa8 <col:19> 'int' 1
      `-CompoundStmt 0x55a9780ec020 <col:29, col:41>
        `-ReturnStmt 0x55a9780ec010 <col:31, col:38>
          `-IntegerLiteral 0x55a9780ebff0 <col:38> 'int' 0
  

Ответ №1:

Да, это возможно. есть два способа AFAIK.

Сначала нужно добавить ваше новое определение в Clang. Вы можете изменить исходный код Clang в tools/clang/include/clang/Basic/Attr.td . Затем Clang узнает ваш новый атрибут. Недостатки могут заключаться в том, что вам нужно каждый раз перестраивать весь исходный код для внесения изменений.

Второе — использовать доступный атрибут annotate . Например [[clang::annotate("not_null")]] . Лично я предпочитаю гибкость этого подхода.

Здесь мы предполагаем, что я хочу выполнить поиск функции decl с пользовательским атрибутом:

 #define MY_ATTR [[clang::annotate("my_attr")]]

...

// i want to extract this attribute
MY_ATTR void foo(...) { ... }
  

я могу реализовать функцию посетителя, например:

 bool VisitFunctionDecl(FunctionDecl *D) {                                                                                                                                                                                 
   if (D->hasAttrs()) {                                                                                                                                                                                                             
     for (auto attr : D->getAttrs()) {                                                                                                                                                                                              
       std::string attr_name(attr->getSpelling());                                                                                                                                                                                  
       std::string attr_annotate("annotate");                                                                                                                                                                                       
       std::string attr_my("annotate("my_attr")");                                                                                                                                                                     
       if (attr_name == attr_annotate) {                                                                                                                                                                                            
         std::string SS; llvm::raw_string_ostream S(SS);                                                                                                                                                                            
         attr->printPretty(S, Policy);                                                                                                                                                                                              
         std::string attr_string(S.str());                                                                                                                                                                                          
         if (attr_string.find(attr_my) != std::string::npos) { 
           // found it! do stuff here!
           // ...
         }
       }                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               
     }                                                                                                                                                                                                                              
   }
  return true;
}          
  

я думаю, вы можете легко адаптировать его к аннотации вашего цикла.

Комментарии:

1. Спасибо! Просматривая документы, похоже, что мне в основном нужно реализовать свой собственный анализатор для аннотации. В приведенном выше примере вы просите clang распечатать его, есть ли какой-либо способ получить представление, из которого он печатается?

2. да, вам нужно реализовать свой собственный анализатор. Извлечение строк, подобных приведенному в ответе, — это всего лишь одна из возможностей, которая сработала для меня. Загляните в ссылку clang::Attr , я не знаю, как отличить два clang::annotate атрибута с разными аннотациями. Вот почему я печатаю строки и сравниваю их в анализаторе.