【问题标题】:Getting the source behind clang's AST获取 clang 的 AST 背后的源代码
【发布时间】:2012-06-18 12:38:02
【问题描述】:

给定一个clang中的AST对象,我怎样才能得到它背后的代码?我尝试编辑tutorial中的代码,并添加:

clang::SourceLocation _b = d->getLocStart(), _e = d->getLocEnd();
char *b = sourceManager->getCharacterData(_b),
      e = sourceManager->getCharacterData(_E);
llvm:errs() << std::string(b, e-b) << "\n";

但是很遗憾,它没有打印出整个 typedef 声明,只打印了一半!打印Expr时也出现了同样的现象。

如何打印并查看构成声明的整个原始字符串?

【问题讨论】:

  • 我认为最终源位置指向该范围内的最后一个标记(不是最后一个),因此您会错过最后一个标记。
  • @bames53 看起来你是对的!那么我如何获得最后一个令牌呢?
  • 除了第三行应该是_e而不是_w之外,最后一行的区别是不是弄错了? (即e - b 不是b - e
  • @KonradRudolph 已修复,谢谢。

标签: c++ clang


【解决方案1】:

使用Lexer 模块:

clang::SourceManager *sm;
clang::LangOptions lopt;

std::string decl2str(clang::Decl *d) {
    clang::SourceLocation b(d->getLocStart()), _e(d->getLocEnd());
    clang::SourceLocation e(clang::Lexer::getLocForEndOfToken(_e, 0, *sm, lopt));
    return std::string(sm->getCharacterData(b),
        sm->getCharacterData(e)-sm->getCharacterData(b));
}

【讨论】:

  • 这是一个非常好的答案,但是当我尝试使用此函数std::string fullComment2str(comments::FullComment* comment, clang::SourceManager *sm, clang::LangOptions lopt) { if (!comment) { return std::string(); } clang::SourceLocation b(comment-&gt;getLocStart()), _e(comment-&gt;getLocEnd()); clang::SourceLocation e(clang::Lexer::getLocForEndOfToken(_e, 0, *sm, lopt)); return std::string(sm-&gt;getCharacterData(b), sm-&gt;getCharacterData(e)-sm-&gt;getCharacterData(b)); } 打印段落注释std::string fullComment2str(comments::FullComment* comment, clang::SourceManager *sm, clang::LangOptions lopt) { if (!comment) { return std::string(); } clang::SourceLocation b(comment-&gt;getLocStart()), _e(comment-&gt;getLocEnd()); clang::SourceLocation e(clang::Lexer::getLocForEndOfToken(_e, 0, *sm, lopt)); return std::string(sm-&gt;getCharacterData(b), sm-&gt;getCharacterData(e)-sm-&gt;getCharacterData(b)); } 时,输出不包含开始+结束标记。
  • 请注意,我用这种方法做了一些测试,有时对于非常大量的代码,getCharacterData() 的结果不会从同一个缓冲区产生 char 指针......我有“结束”指针落在堆栈上,而“开始”指针指向堆中的某处......这会导致工具崩溃或用垃圾内存淹没您的终端。
  • @StevenLu 你明白这些方法有什么问题吗?我该如何解决?
  • TBH 我不确定是否有一种简单的方法可以做到这一点,但我确实有一种稳定的方法来获取开始和结束位置的 FileID 和 offsets/line+col(SourceManager 方法),考虑到 Rewriter 工作正常,这对于我自己的目的来说确实足够了。我将不得不再看看它,看看如何可靠地获取大 decls 的完整字符串。
  • @StevenLu 我遇到了同样的问题,发现下面@LucasWang 的解决方案完美无缺。关键似乎是用Lexer组装StringRef
【解决方案2】:

以下代码适用于我。

std::string decl2str(clang::Decl *d, SourceManager &sm) {
    // (T, U) => "T,,"
    string text = Lexer::getSourceText(CharSourceRange::getTokenRange(d->getSourceRange()), sm, LangOptions(), 0);
    if (text.size() > 0 && (text.at(text.size()-1) == ',')) //the text can be ""
        return Lexer::getSourceText(CharSourceRange::getCharRange(d->getSourceRange()), sm, LangOptions(), 0);
    return text;
}

【讨论】:

    【解决方案3】:

    正如答案的 cmets 所指出的,所有其他答案似乎都有缺陷,所以我将发布我自己的代码,似乎涵盖了 cmets 中提到的所有缺陷。

    我相信getSourceRange() 将语句视为标记序列,而不是字符序列。这意味着,如果我们有一个对应于FOO + BARclang::Stmt,那么令牌FOO 位于字符1,令牌+ 位于字符5,令牌BAR 位于字符7。@987654328 @因此返回一个SourceRange,这实质上意味着“此代码以1 处的标记开始,以7 处的标记结束”。所以我们必须使用clang::Lexer::getLocForEndOfToken(stmt.getSourceRange().getEnd()) 来获取BAR 标记的结束字符的实际字符位置,并将that 作为结束位置传递给clang::Lexer::getSourceText。如果我们不这样做,那么clang::Lexer::getSourceText 将返回"FOO + ",而不是我们可能想要的"FOO + BAR"

    我认为我的实现不存在 cmets 中提到的 @Steven Lu 的问题,因为这段代码使用了 clang::Lexer::getSourceText 函数,该函数 according to Clang's source documentation 专门用于从范围中获取源文本。

    这个实现还考虑了@Ramin Halavati 的评论;我已经在一些代码上对其进行了测试,它确实返回了宏扩展的字符串。

    这是我的实现:

    /**
     * Gets the portion of the code that corresponds to given SourceRange, including the
     * last token. Returns expanded macros.
     * 
     * @see get_source_text_raw()
     */
    std::string get_source_text(clang::SourceRange range, const clang::SourceManager& sm) {
        clang::LangOptions lo;
    
        // NOTE: sm.getSpellingLoc() used in case the range corresponds to a macro/preprocessed source.
        auto start_loc = sm.getSpellingLoc(range.getBegin());
        auto last_token_loc = sm.getSpellingLoc(range.getEnd());
        auto end_loc = clang::Lexer::getLocForEndOfToken(last_token_loc, 0, sm, lo);
        auto printable_range = clang::SourceRange{start_loc, end_loc};
        return get_source_text_raw(printable_range, sm);
    }
    
    /**
     * Gets the portion of the code that corresponds to given SourceRange exactly as
     * the range is given.
     *
     * @warning The end location of the SourceRange returned by some Clang functions 
     * (such as clang::Expr::getSourceRange) might actually point to the first character
     * (the "location") of the last token of the expression, rather than the character
     * past-the-end of the expression like clang::Lexer::getSourceText expects.
     * get_source_text_raw() does not take this into account. Use get_source_text()
     * instead if you want to get the source text including the last token.
     *
     * @warning This function does not obtain the source of a macro/preprocessor expansion.
     * Use get_source_text() for that.
     */
    std::string get_source_text_raw(clang::SourceRange range, const clang::SourceManager& sm) {
        return clang::Lexer::getSourceText(clang::CharSourceRange::getCharRange(range), sm, clang::LangOptions());
    }
    

    【讨论】:

      【解决方案4】:

      Elazar 的方法对我有用,除非涉及到宏。以下更正解决了它:

      std::string decl2str(clang::Decl *d) {
          clang::SourceLocation b(d->getLocStart()), _e(d->getLocEnd());
          if (b.isMacroID())
              b = sm->getSpellingLoc(b);
          if (e.isMacroID())
              e = sm->getSpellingLoc(e);
          clang::SourceLocation e(clang::Lexer::getLocForEndOfToken(_e, 0, *sm, lopt));
          return std::string(sm->getCharacterData(b),
              sm->getCharacterData(e)-sm->getCharacterData(b));
      }
      

      【讨论】:

      • 我尝试了 Expression like lhs of the binary operator 。如果它包含宏,我得到的是 Identifier 而不是 token 。为什么会这样。例如,我的程序 #define abc ab int ab; int main() { abc = 0; } 中有一个宏,当我将代码作为输入时,它的预处理代码将会使用。 abc 应该替换为 ab ,当我打印字符串时,我将左侧作为 abc 。为什么会这样?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-01-16
      • 2023-03-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多