使用 clang 解析命名空间:在另一个源文件中包含标头或直接解析标头时,AST 差异
Parsing namespaces with clang: AST differences in when including a header in another source file or parsing it directly
很抱歉这个问题很冗长,但我看不出任何其他方法可以说清楚。 我正在编写一个工具,用于将C++头文件转换为 SWIG 接口文件,作为进一步微调的入门。
在这样做的过程中,我注意到 clang (v3.0( 的一些奇怪行为。 如果我解析头文件,得到的 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)
当我在示例中运行它时.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? 我本来期望没有区别,除了预处理器游标。
解决方法是显而易见的 - 只需在内存中创建一个包含标头的临时文件 - 但这不是很令人满意。 有人可以开导我吗?
由于您没有显式指定语言,因此 Clang 会根据文件扩展名确定语言,从而导致"example.h"
被解析为 C,而不是C++。因此,文件在很大程度上格式不正确,索引器会尝试尽可能好地恢复。 namespace Geom
被视为具有未知类型namespace
的Geom
的变量声明,以及以下意外{
... 跳过}
块。
尝试:
tu = index.parse(sys.argv[1], args=['-x', 'c++'])
虽然理查德的答案在这种情况下确实有效,但我可以遇到同样的问题,但这对我不起作用。 事实证明,python clang 绑定隐藏了错误消息。 如果运行以下命令:
clang -Xclang -ast-dump -fsyntax-only yourfile.cpp
这将打印出 AST 信息。 就我而言,它找不到头文件,因为它位于不同的目录中。 因此,我必须添加-I
和传入args
的附加包含路径,并且它起作用了。
相关文章:
- 将两个数组中的差异记录在第三个数组中
- 如何克服提升精神AST混乱
- 大小相等但成员数量不同的结构之间的性能差异
- C++constexpr实现差异
- 旧版c++中结构和类之间的差异
- 共享指针和具有自定义删除程序的唯一指针之间的语法差异背后的任何原因
- clang 插件:在编译过程中修改 AST
- 节点 *temp; 和节点 *tmp = 新节点之间的差异
- GCC 和 Clang 代码性能的巨大差异
- 类内部和外部静态 constexpr 元组之间的差异
- 平凡类型与非平凡类型的复制消除差异
- 了解算法的性能差异(如果以不同的编程语言实现)
- 以天C++为单位的两个时间戳之间的差异
- Boost.Spirit将表达转换为AST
- 如何以毫秒为单位获取开始时间和 now() 之间的毫秒差异(以 C++为单位?
- 方括号 [] 和括号 () 之间的犰狳库差异
- 不同语言中的模运算符差异
- 类继承,ENUM 与 AST 类实现的问题
- std::filesystem 和 std::experimental::filesystem 之间的路径差异
- 使用 clang 解析命名空间:在另一个源文件中包含标头或直接解析标头时,AST 差异