Ответ 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), мы получим следующее (я включаю изображение, так как оно более интуитивно понятное, чем просто неокрашенный текст):
поскольку вы можете увидеть местоположение, указанное для первого 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;
}
то есть. полная и успешная замена оператора макрорасширения.
Литература:
- документация clang
- clang doxygen API
- clang исходный код