使用clang解析命名空间:在另一个源文件中包含头文件或直接解析头文件时,AST存在差异。

10

很抱歉问题描述可能有些冗长,但我没有其他更清晰的方式来表达。我正在编写一个工具,将C ++头文件转换为SWIG接口文件,作为进一步微调的起点。

在这个过程中,我注意到clang(版本3.0)有一些奇怪的行为。如果我解析头文件,得到的AST与解析包含头文件的源文件时得到的AST明显不同。

为了举例说明,这里是一些示例源文件:

源文件:

// example.cpp: Test case for nsbug.py
//
#include "example.h"

页眉:

// example.h: Test case for nsbug.py
//
namespace Geom {

struct Location
{
    double x, y;
};

class Shape
{
public:
    Shape();

    void set_location(const Location &where)
    {
        m_pos = where;
    };

    const Location &get_location() const

    // Draw it...
    virtual void draw() const = 0;

protected:
    Location m_pos;
};

class Circle : public Shape
{
    Circle();

    virtual void draw() const;
};
} // namespace Geom
我使用以下Python代码来解析并输出AST:
# Usage: python nsbug.py <file>

import sys
import clang.cindex

def indent(level):
    """ Indentation string for pretty-printing
    """ 
    return '  '*level

def output_cursor(cursor, level):
    """ Low level cursor output
    """
    spelling = ''
    displayname = ''

    if cursor.spelling:
        spelling = cursor.spelling
    if cursor.displayname:
        displayname = cursor.displayname
    kind = cursor.kind;

    print indent(level) + spelling, '<' + str(kind) + '>'
    print indent(level+1) + '"'  + displayname + '"'

def output_cursor_and_children(cursor, level=0):
    """ Output this cursor and its children with minimal formatting.
    """
    output_cursor(cursor, level)
    if cursor.kind.is_reference():
        print indent(level) + 'reference to:'
        output_cursor(clang.cindex.Cursor_ref(cursor), level+1)

    # Recurse for children of this cursor
    has_children = False;
    for c in cursor.get_children():
        if not has_children:
            print indent(level) + '{'
            has_children = True
        output_cursor_and_children(c, level+1)

    if has_children:
        print indent(level) + '}'

index = clang.cindex.Index.create()
tu = index.parse(sys.argv[1], options=1)

output_cursor_and_children(tu.cursor)
當我在 example.cpp 上運行此代碼時,我得到了(我認為是正確的)結果:
 <CursorKind.TRANSLATION_UNIT>
  "example.cpp"
{

  (Deleted lots of clang-generated declarations such as __VERSION__)

  Geom <CursorKind.NAMESPACE>
    "Geom"
  {
    Location <CursorKind.STRUCT_DECL>
      "Location"
    {
      x <CursorKind.FIELD_DECL>
        "x"
      y <CursorKind.FIELD_DECL>
        "y"
    }
    Shape <CursorKind.CLASS_DECL>
      "Shape"
    {
       <CursorKind.CXX_ACCESS_SPEC_DECL>
        ""
       <CursorKind.CXX_ACCESS_SPEC_DECL>
        ""
      Shape <CursorKind.CONSTRUCTOR>
        "Shape()"
      set_location <CursorKind.CXX_METHOD>
        "set_location(const Geom::Location &)"
      {
        where <CursorKind.PARM_DECL>
          "where"
        {
           <CursorKind.TYPE_REF>
            "struct Geom::Location"
          reference to:
            Location <CursorKind.STRUCT_DECL>
              "Location"
        }
         <CursorKind.COMPOUND_STMT>
          ""
        {
           <CursorKind.CALL_EXPR>
            "operator="
          {
             <CursorKind.MEMBER_REF_EXPR>
              "m_pos"
             <CursorKind.UNEXPOSED_EXPR>
              "operator="
            {
               <CursorKind.DECL_REF_EXPR>
                "operator="
            }
             <CursorKind.DECL_REF_EXPR>
              "where"
          }
        }
      }
      get_location <CursorKind.CXX_METHOD>
        "get_location()"
      {
         <CursorKind.TYPE_REF>
          "struct Geom::Location"
        reference to:
          Location <CursorKind.STRUCT_DECL>
            "Location"
      }
       <CursorKind.CXX_ACCESS_SPEC_DECL>
        ""
       <CursorKind.CXX_ACCESS_SPEC_DECL>
        ""
      m_pos <CursorKind.FIELD_DECL>
        "m_pos"
      {
         <CursorKind.TYPE_REF>
          "struct Geom::Location"
        reference to:
          Location <CursorKind.STRUCT_DECL>
            "Location"
      }
    }
    Circle <CursorKind.CLASS_DECL>
      "Circle"
    {
       <CursorKind.CXX_BASE_SPECIFIER>
        "class Geom::Shape"
      reference to:
        Shape <CursorKind.CLASS_DECL>
          "Shape"
      {
         <CursorKind.TYPE_REF>
          "class Geom::Shape"
        reference to:
          Shape <CursorKind.CLASS_DECL>
            "Shape"
      }
      Circle <CursorKind.CONSTRUCTOR>
        "Circle()"
      draw <CursorKind.CXX_METHOD>
        "draw()"
    }
  }
}

但是当我尝试在头文件上使用python nsbug.py example.py时,只得到:

 <CursorKind.TRANSLATION_UNIT>
  "example.h"
{

  (deleted lots of clang-generated definitions such as __VERSION__)

  Geom <CursorKind.VAR_DECL>
    "Geom"
}
为什么在AST中,Geom名称空间作为VAR_DECL?我希望它与预处理器光标中没有区别。
解决方法很明显 - 只需在内存中创建一个包含头文件的临时文件即可。但这并不是很令人满意。有人能启发我吗?

3
小鬼们啊,他们无处不在。 - Thomas Eding
2个回答

13

因为你没有明确指定编程语言,Clang会根据文件扩展名来确定编程语言,这导致 "example.h" 被解析为C而不是C++。 因此,该文件在很大程度上是格式错误的,索引器尽可能地进行恢复。namespace Geom 被视为具有未知类型namespaceGeom变量声明,随后的意外{ ... }块被跳过。

尝试:

tu = index.parse(sys.argv[1], args=['-x', 'c++'])

1
好的,谢谢!奇怪的是它没有生成错误。 - Codie CodeMonkey

4
虽然Richard的答案在这种情况下是可行的,但我遇到了同样的问题,而这个方法对我并没有起作用。原来 Python clang 绑定隐藏了错误消息。如果你运行以下命令:
clang -Xclang -ast-dump -fsyntax-only yourfile.cpp

这将打印出AST信息。在我的情况下,它找不到头文件因为它在另一个目录中。因此,在传入的args中添加-I和额外的包含路径后就可以正常工作了。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接