Clang не позволяет заменить оператор, если он содержит макрос

Я использую clang, чтобы попробовать и проанализировать (с С++ API) некоторые С++ файлы и сделать все пары разломов case использующими определенный стиль.

Пример:

**Original**

switch(...)
{
   case 1:
   {
      <code>
   }break;

   case 2:
   {
      <code>
      break;
   }
}

**After replacement**

switch(...)
{
   case 1:
   {
      <code>
      break;
   }

   case 2:
   {
      <code>
      break;
   }
}

То, что я до сих пор выполняю именно то, что хочу, если части кода не содержат макросов. Мой вопрос: делает ли clang treat расширенным (если я делаю дамп проблемного оператора, который покажет расширенную версию), макросы по-разному? если да, то как я могу заставить это работать?

Дополнительная информация, которая может помочь:

Я использую Rewriter:: ReplaceStmt, чтобы заменить подзапросы каждого случая вновь созданным CompoundStmt, и я заметил, что ReplaceStmt возвращает значение true, если параметр "from" содержит макрос и единственный способ вернуть этот метод true, если

Rewriter:: getRangeSize (from- > getSourceRange())

возвращает -1

Ответы

Ответ 1

Ваша проблема вызвана дизайном SourceLocation.

Далее следует статья:


Расширение макроса с помощью clang SourceLocation

SourceLocation разработан, чтобы быть достаточно гибким, чтобы обрабатывать как нерасширенные местоположения, так и макрораспределенные местоположения в то же время.

Если токен является результатом расширения, тогда необходимо учитывать два разных местоположения: расположение орфографии (расположение символов, соответствующих токену), и место создания экземпляра (место, где токен был used - точка создания макроса).

В качестве примера возьмем следующий простой исходный файл:

#define MACROTEST bool

int main() {

    int var = 2;
    switch(var)
    {
       case 1:
       {
          MACROTEST newvar;
       }break;

       case 2:
       {
          MACROTEST newvar;
          break;
       }
    }

    return 0;
}

и предположим, что мы хотим заменить два оператора декларации

MACROTEST newvar;

с выражением о декларации

int var = 2;

чтобы получить что-то вроде этого

#define MACROTEST bool

int main() {

    int var = 2;
    switch(var)
    {
       case 1:
       {
          int var = 2;
       }break;

       case 2:
       {
          int var = 2;
          break;
       }
    }

    return 0;
}

если вывести AST (-ast-dump), мы получим следующее (я включаю изображение, так как оно более интуитивно понятное, чем просто неокрашенный текст):

clang AST

поскольку вы можете увидеть местоположение, указанное для первого DeclStmt, которое нас интересует, охватывает от строки 1 до 10: это означает, что clang сообщает в дампе интервал, охватывающий от линии макроса, до точки, где макрос используется:

#define MACROTEST [from_here]bool

int main() {

    int var = 2;
    switch(var)
    {
       case 1:
       {
          MACROTEST newvar[to_here];
       }break;

       case 2:
       {
          MACROTEST newvar;
          break;
       }
    }

    return 0;
}

(обратите внимание, что количество символов может не совпадать с обычными пробелами, так как в моем текстовом редакторе были вкладки)

В конечном счете это вызывает отказ Rewriter::getRangeSize (-1) и последующее Rewriter::ReplaceStmt true возвращаемое значение (что означает сбой - см. документацию).

Что происходит, вы получаете пару маркеров SourceLocation, где первый - это идентификатор макроса (isMacroID() вернет true), а последнее не будет.

Чтобы успешно получить размер макро-расширенного оператора, нам нужно сделать шаг назад и связаться с SourceManager, который является шлюзом запросов для всех ваших мест написания и мест создания экземпляров (сделайте шаг назад, если вы не помните эти условия). Я не могу быть более ясным, чем подробное описание, представленное в документации:

С помощью SourceManager можно запросить информацию о SourceLocation объектов, превращая их в места орфографии или расширения. Описания орфографии представляют, где байты, соответствующие токену пришли, и места экспансии представляют, где это место находится в пользовательский вид. В случае макрорасширения, например, расположение орфографии указывает, откуда появился расширенный токен, и местоположение расширения указывает, где оно было расширено.

На этом этапе вы должны понять, почему я объяснил все это в первую очередь: , если вы намерены использовать диапазоны источников для вашей подстановки, вам нужно использовать соответствующий интервал расширения.. p >

Вернемся к образцу, который я предложил, это код для его достижения:

SourceLocation startLoc = declaration_statement->getLocStart();
SourceLocation endLoc = declaration_statement->getLocEnd();

if( startLoc.isMacroID() ) {
    // Get the start/end expansion locations
    std::pair< SourceLocation, SourceLocation > expansionRange = 
             rewriter.getSourceMgr().getImmediateExpansionRange( startLoc );

    // We're just interested in the start location
    startLoc = expansionRange.first;
}

if( endLoc.isMacroID() ) {
  // will not be executed
}

SourceRange expandedLoc( startLoc, endLoc );
bool failure = rewriter.ReplaceText( expandedLoc, 
                                     replacer_statement->getSourceRange() );

if( !failure )
    std::cout << "This will get printed if you did it correctly!";

declaration_statement является либо одним из двух

MACROTEST newvar;

while replacer_statement - это оператор, используемый для замены

int var = 2;

Вышеприведенный код предоставит вам следующее:

#define MACROTEST bool

int main() {

    int var = 2;
    switch(var)
    {
       case 1:
       {
          int var = 2;
       }break;

       case 2:
       {
          int var = 2;
          break;
       }
    }

    return 0;
}

то есть. полная и успешная замена оператора макрорасширения.


Литература:

Ответ 2

Чтобы получить местоположение файла, связанное с расширением макроса, для получения информации можно использовать функцию API:

SourceLocation startLoc = rewriter.getSourceMgr().getFileLoc(
    declaration_statement->getLocStart());
SourceLocation endLoc = rewriter.getSourceMgr().getFileLoc(
    declaration_statement->getLocEnd());

Эта функция API выполняет то же, что и Марко в своем коде, но автоматически.

Если мы рассмотрим реализацию функции getFileLoc():

Это описание функции: Если Loc, если это местоположение макроса, возвращает местоположение расширения или место орфографии, в зависимости от того, связано ли оно с аргументом макроса или нет.

 SourceLocation getFileLoc(SourceLocation Loc) const {
     if (Loc.isFileID()) return Loc;
         return getFileLocSlowCase(Loc);
 }

SourceLocation SourceManager::getFileLocSlowCase(SourceLocation Loc) const {
    do {
        if (isMacroArgExpansion(Loc))
            Loc = getImmediateSpellingLoc(Loc);
        else
            Loc = getImmediateExpansionRange(Loc).first;
    } while (!Loc.isFileID());
    return Loc;
}