2012-05-12 5 views
8

Disculpe la pregunta prolija, pero no veo otra forma de dejarlo en claro. Estoy escribiendo una herramienta para transformar archivos de cabecera C++ a archivos de interfaz SWIG como un iniciador para un ajuste más detallado.Análisis de espacios de nombres con clang: diferencias de AST cuando se incluye un encabezado en otro archivo fuente o se analiza directamente

En el proceso de hacer esto, he notado un comportamiento extraño por clang (v3.0). Si analizo el archivo de encabezado obtengo un AST significativamente diferente que si analizo un archivo fuente que incluye el encabezado.

Para fines de ilustración, aquí hay algunos archivos de origen de la muestra: archivo

Fuente:

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

Cabecera:

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

He utilizado el siguiente código Python para analizarlo y volcar el 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) 

Cuando ejecuto esto en example.cpp consigo (correctamente creo):

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

Pero cuando lo intento en el archivo de cabecera junto con python nsbug.py example.py sólo consigo:

<CursorKind.TRANSLATION_UNIT> 
    "example.h" 
{ 

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

    Geom <CursorKind.VAR_DECL> 
    "Geom" 
} 

¿Por qué el espacio de nombre Geom en AST es VAR_DECL? No esperaba ninguna diferencia, excepto en los cursores del preprocesador.

La solución temporal es obvia: simplemente haga un archivo temporal en la memoria que incluya el encabezado, pero eso no es muy satisfactorio. ¿Alguien me puede iluminar?

+3

The gremlins man. Están por todas partes. –

Respuesta

10

Dado que no especifica un idioma explícitamente, Clang determina el idioma de la extensión de archivo, lo que da como resultado "example.h" que se analiza como C, no como C++. Por lo tanto, el archivo está en gran medida mal formado, y el indexador intenta recuperarse tan bien como puede. namespace Geom se está tratando como una declaración de variable para Geom con un tipo desconocido namespace, y se omite el siguiente { ... } bloque inesperado.

Probar:

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

¡Eso funcionó, gracias! Es extraño que no haya generado un error. –

4

Si bien la respuesta de Richard funciona en este caso, no puedo con el mismo problema y esto no funcionó para mí. Resultó que los enlaces clang pitón ocultaban el mensaje de error. Si ejecuta lo siguiente:

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

Esto imprimirá la información de AST. En mi caso, no pudo encontrar el archivo de encabezado porque estaba en un directorio diferente. Entonces, tuve que agregar -I y la ruta de inclusión adicional al args pasó y funcionó.

Cuestiones relacionadas