2014-06-05 46 views
16

我正在使用clang嘗試解析(使用C++ API)一些C++文件,並使所有case-break對使用特定的樣式。如果它包含宏,clang將無法替換語句

**Original** 

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

    case 2: 
    { 
     <code> 
     break; 
    } 
} 

**After replacement** 

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

    case 2: 
    { 
     <code> 
     break; 
    } 
} 

我至今不正是我想要的東西,如果代碼部分不包含任何的宏。 我的問題是:是否擴大(如果我做一個有問題的語句轉儲它將顯示擴展版本)擴展宏不同?如果是的話,我怎麼才能得到這個工作?

附加信息,可以幫助:

我使用Rewriter::ReplaceStmt與新創建的CompoundStmt更換各種情況下的子報表和我注意到ReplaceStmt返回true,如果「從」參數包含一個宏並且該方法返回的唯一途徑是如果

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

返回-1

回答

21

你的問題是由SourceLocation設計造成的。

的文章如下:


鏗鏘的SourceLocation

SourceLocation宏擴展的設計具有足夠的靈活性來處理雙方未展開的位置與宏觀擴張地點在同一時間

如果令牌是膨脹的結果,則存在要被保持到兩個不同的位置:在拼寫位置實例化位置(對應於所述令牌的字符的位置)(在使用令牌的位置 - 宏實例化點)。

讓我們以下面這個簡單的源文件爲例:

#define MACROTEST bool 

int main() { 

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

     case 2: 
     { 
      MACROTEST newvar; 
      break; 
     } 
    } 

    return 0; 
} 

,並假設我們想聲明語句

int var = 2; 

更換兩個聲明語句

MACROTEST newvar; 

爲了得到像這樣的東西

#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轉儲),我們得到以下(我包括圖像,因爲它不僅僅是無色文字更直觀):

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::ReplaceStmttrue返回值(這意味着失敗 - 請參閱文檔)。

發生的情況如下:您正在收到一對SourceLocation標記,其中第一個是宏ID(isMacroID()將返回true),而後者不是。

爲了順利拿到我們需要後退一步,並與SourceManager這是查詢網關爲所有拼寫地點實例位置溝通的宏擴展語句的程度(取退一步,如果你不記得這些條款)的需要。我不能比提供in the documentation的詳細描述中更加清楚:

的SourceManager可以查詢有關SourceLocation 對象的信息,把它們變成任一拼寫或膨脹位置。 拼寫位置表示與令牌 相對應的字節來自哪裏,擴展位置表示位置在用戶視圖中的位置 的位置。例如,對於宏擴展, 拼寫位置表示展開的令牌來自何處,而擴展位置則指定展開位置。

此時,你應該得到,爲什麼我首先解釋這一切的東西:如果你打算使用源量程爲你的替代,則需要使用適當的擴張區間

回到我提出的樣品,這是代碼來實現它:

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; 

一方而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; 
} 

即宏擴展語句的完整和成功替代。


參考文獻:

3

在奧德獲得有關宏擴展的文件位置,API函數可用於檢索信息:

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

這個API函數和Marco在他的代碼中寫的一樣,但是自動完成。

如果我們看一下該功能的實現getFileLoc():

這是功能的描述: 鑑於祿,如果它是一個宏位置返回擴展位置或拼寫位置,取決於它是否來自宏觀參數。

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