2014-06-05 17 views
16

Używam clang, aby spróbować i parsować (z C++ API) niektóre pliki C++ i sprawić, że wszystkie pary break case będą miały określony styl.clang nie powiedzie się zastępując instrukcję jeśli zawiera makro

Przykład:

**Original** 

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

    case 2: 
    { 
     <code> 
     break; 
    } 
} 

**After replacement** 

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

    case 2: 
    { 
     <code> 
     break; 
    } 
} 

Co mam tak daleko robi dokładnie to, co chcę, jeśli części kodu nie zawierają żadnych makr. Moje pytanie brzmi: czy polecenie "clang treat" (jeśli zrobię zrzut problematycznej instrukcji spowoduje wyświetlenie wersji rozszerzonej) w inny sposób? jeśli tak, to jak mogę to uruchomić?

Dodatkowe informacje, które mogą pomóc:

Używam Rewriter::ReplaceStmt zastąpić sub-wypowiedzi każdorazowo z nowo utworzonej CompoundStmt i zauważyłem ReplaceStmt Zwraca TRUE jeśli „od” zawiera parametr makro, a jedynym sposobem, że metoda zwraca prawda jest jeśli

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

zwraca -1

Odpowiedz

21

Twój problem jest spowodowany przez konstrukcję SourceLocation.

Wyrób następująco:


makr ze szczęk jest SourceLocation

SourceLocation ma być wystarczająco elastyczna, aby obsługiwać zarówno nieporowate położenia i makro rozszerzone położenie jednocześnie.

Jeżeli znacznik jest wynikiem rozszerzania, to istnieją dwa różne miejsca być utrzymywane pod uwagę: położenie pisowni (lokalizacja znaków odpowiadających tokena) i położenie tworzenia instancji (The lokalizacja, w której użyto tokena - punkt inicjowania makr).

Weźmy następujący prosty plik źródłowy jako przykład:

#define MACROTEST bool 

int main() { 

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

     case 2: 
     { 
      MACROTEST newvar; 
      break; 
     } 
    } 

    return 0; 
} 

i przypuśćmy, że chcemy wymienić dwie deklaracje sprawozdania

MACROTEST newvar; 

ze stwierdzeniem deklaracji

int var = 2; 

aby uzyskać coś takiego,

#define MACROTEST bool 

int main() { 

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

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

    return 0; 
} 

jeśli wyjście AST (-ast-dump) otrzymujemy następujące (jestem w tym obrazie, ponieważ jest to bardziej intuicyjne niż tekst tylko Niepokolorowany):

clang AST

jako możesz zobaczyć lokalizację zgłoszoną dla pierwszego DeclStmt, które nas interesuje, obejmuje zakres od 1 do 10: oznacza to, że klang zgłasza w zrzucie przedział rozciągający się od linii makra do punktu, w którym makro jest używane:

#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; 
} 

(zauważyć, że liczba znaków nie może być taka sama, z przestrzeni normalnych, ponieważ mój edytor tekstu używanych zakładek)

Ostatecznie to wyzwalających awarię Rewriter::getRangeSize (-1), oraz kolejne Rewriter::ReplaceStmt wartość true zwrotny (co oznacza awarię - patrz dokumentacja).

To, co się dzieje, jest następujące: otrzymujesz kilka znaczników SourceLocation, gdzie pierwszym jest identyfikator makra (isMacroID() zwróci wartość true), podczas gdy drugie nie.

aby skutecznie pozbyć zakresu rachunku makro rozszerzone musimy zrobić krok do tyłu i komunikować się z SourceManager który jest kwerenda Bramka dla wszystkich lokalizacjach pisowni i miejscach instancji (potrwać cofnij, jeśli nie pamiętasz tych terminów) potrzeb. Nie mogę być bardziej jasne niż szczegółowym opisem zawartym in the documentation:

SourceManager można przeszukiwać informacje na temat SourceLocation obiektów, zamieniając je w zarówno ortograficzne lub rozszerzeń lokalizacji. Miejsca sprawdzania pisowni reprezentują miejsce, w którym przybył bajt odpowiadający tokenowi , a lokalizacje rozszerzeń reprezentują położenie użytkownika w widoku . Na przykład w przypadku rozszerzenia makra, lokalizacja pisowni wskazuje, skąd pochodzi rozszerzony token, a lokalizacja rozwinięcia określa, gdzie została rozwinięta.

W tym momencie powinno być coraz dlaczego wyjaśniłem wszystkie te rzeczy w pierwszej kolejności: jeśli masz zamiar używać zakresów źródłowych do zastąpienia, trzeba użyć odpowiedniego odstępu ekspansji.

Powrót do próbki zaproponowałem, jest to kod, aby osiągnąć go:

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 jest jedno z dwóch

MACROTEST newvar; 

podczas replacer_statement jest stwierdzenie wykorzystane do zastąpienia

int var = 2; 

Powyższy kod zapewnia:

#define MACROTEST bool 

int main() { 

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

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

    return 0; 
} 

tj. Pełne i pomyślne zastąpienie makro-rozwiniętego wyciągu.


Referencje:

3

W oder pobrać plik lokalizację związane z makr, funkcja API może być użyty do odzyskania informacji:

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

Ta funkcja API działa tak samo, jak to, co Marco napisał w swoim kodzie, ale automatycznie.

Jeśli spojrzymy na realizację funkcji getFileLoc():

Jest to opis funkcji: Biorąc Loc, jeśli jest to makro lokalizacja powrócić lokalizację rozszerzeń lub lokalizację pisowni , w zależności od tego, czy pochodzi z argumentu makro, czy nie.

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; 
} 
Powiązane problemy