2012-05-12 3 views
8

Sorry für die ausführliche Frage, aber ich kann keine andere Möglichkeit, um es klar zu sehen. Ich schreibe ein Werkzeug, um C++ - Header-Dateien in SWIG-Interface-Dateien als Starter für die weitere Feinabstimmung zu transformieren.Parsing Namespaces mit Clang: AST Unterschiede in, wenn Sie einen Header in einer anderen Quelldatei oder Parsing es direkt

In diesem Prozess habe ich ein seltsames Verhalten von Clang bemerkt (v3.0). Wenn ich die Headerdatei analysiere, erhalte ich eine wesentlich andere AST als wenn ich eine Quelldatei analysiere, die den Header enthält.

Zum Zwecke der Darstellung, sind hier einige Beispieldateien,:

Quelldatei:

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

Rubrik:

// 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 

ich folgende Python-Code verwendet haben, um es zu analysieren und die AST entladen:

# 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) 

Wenn ich laufe dies auf example.cpp ich (richtig glaube ich):

<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()" 
    } 
    } 
} 

Aber wenn ich es auf der Header-Datei zusammen mit python nsbug.py example.py versuchen erhalte ich nur:

<CursorKind.TRANSLATION_UNIT> 
    "example.h" 
{ 

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

    Geom <CursorKind.VAR_DECL> 
    "Geom" 
} 

Warum ist der Geom Namensraum im AST als VAR_DECL? Ich hätte keinen Unterschied erwartet, außer in den Präprozessorcursors.

Die Umgehung ist offensichtlich - machen Sie einfach eine temporäre Datei im Speicher, die die Kopfzeile enthält - aber das ist nicht sehr befriedigend. Kann mich jemand aufklären?

+3

Der Gremlins Mann. Sie sind überall. –

Antwort

10

Da Sie keine Sprache explizit angeben, bestimmt Clang die Sprache aus der Dateierweiterung, was dazu führt, dass "example.h" als C, nicht C++ geparst wird. Daher ist die Datei weitgehend fehlerhaft, und der Indexer versucht, so gut wie möglich wiederherzustellen. namespace Geom wird als eine Variablendeklaration für Geom mit einem unbekannten Typ namespace behandelt, und der folgende unerwartete { ... } Block wird übersprungen.

Versuchen:

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

Das hat funktioniert, danke! Seltsam, dass es keinen Fehler erzeugt hat. –

4

Während Richard Antwort Arbeit in diesem Fall tut, ich auf das gleiche Problem und das ist für mich nicht funktioniert hat. Es stellte sich heraus, dass die Python Clang Bindings die Fehlermeldung versteckten. Wenn Sie Folgendes ausführen:

clang -Xclang -ast-dump -fsyntax-only yourfile.cpp 

Dies wird die AST-Informationen ausdrucken. In meinem Fall konnte die Header-Datei nicht gefunden werden, da sie sich in einem anderen Verzeichnis befand. Also musste ich -I hinzufügen und der zusätzliche Include-Pfad zum args ging rein und es funktionierte.