cindex.py revision de3b8e525a876d6c25554aeb782c368afec00db1
1# -*- coding: utf-8 -*- 2 3from ctypes import * 4 5def get_cindex_library(): 6 # FIXME: It's probably not the case that the library is actually found in 7 # this location. We need a better system of identifying and loading the 8 # CIndex library. It could be on path or elsewhere, or versioned, etc. 9 import platform 10 name = platform.system() 11 if name == 'Darwin': 12 return cdll.LoadLibrary('libCIndex.dylib') 13 elif name == 'Windows': 14 return cdll.LoadLibrary('libCIndex.dll') 15 else: 16 return cdll.LoadLibrary('libCIndex.so') 17 18## Utility Types and Functions ## 19def alloc_string_vector(strs): 20 """ 21 Allocate a string buffer large enough to accommodate the given list of 22 python strings. 23 """ 24 n = 0 25 for i in strs: n += len(i) + 1 26 return create_string_buffer(n) 27 28def copy_string_vector(vec, strs): 29 """ 30 Copy the contents of each string into the vector, preserving null 31 terminated elements. 32 """ 33 n = 0 34 for i in strs: 35 # This is terribly inefficient, but I can't figure out how to copy a 36 # chunk of characters into the resultant vector. t should be: something 37 # like this: vec[n:n + len(i)] = i[:]; n += len(i) + 1 38 for j in i: 39 vec[n] = j 40 n += 1 41 n += 1 42 43def create_string_vector(strs): 44 """ 45 Create a string vector (char *[]) from the given list of strings. 46 """ 47 vec = alloc_string_vector(strs) 48 copy_string_vector(vec, strs) 49 return vec 50 51# Aliases for convenience 52c_int_p = POINTER(c_int) 53c_uint_p = POINTER(c_uint) 54c_bool = c_uint 55 56# ctypes doesn't implicitly convert c_void_p to the appropriate wrapper 57# object. This is a problem, because it means that from_parameter will see an 58# integer and pass the wrong value on platforms where int != void*. Work around 59# this by marshalling object arguments as void**. 60c_object_p = POINTER(c_void_p) 61 62lib = get_cindex_library() 63 64## Typedefs ## 65CursorKind = c_int 66 67### Structures and Utility Classes ### 68 69class String(Structure): 70 """ 71 The String class is a simple wrapper around constant string data returned 72 from functions in the CIndex library. 73 74 String objects do not provide any of the operations that Python strings 75 support. However, these objects can be explicitly cast using the str() 76 function. 77 """ 78 _fields_ = [("spelling", c_char_p), ("free", c_int)] 79 80 def __del__(self): 81 if self.free: 82 String_dispose(self) 83 84 def __str__(self): 85 return self.spelling 86 87class SourceLocation(Structure): 88 """ 89 A SourceLocation represents a particular location within a source file. 90 """ 91 _fields_ = [("ptr_data", c_void_p), ("int_data", c_uint)] 92 93 def init(self): 94 """ 95 Initialize the source location, setting its file, line and column. 96 """ 97 f, l, c = c_object_p(), c_uint(), c_uint() 98 SourceLocation_loc(self, byref(f), byref(l), byref(c)) 99 f = File(f) if f else None 100 self.file, self.line, self.column = f, int(l.value), int(c.value) 101 return self 102 103 def __repr__(self): 104 return "<SourceLocation file %r, line %r, column %r>" % ( 105 self.file.name if self.file else None, self.line, self.column) 106 107class SourceRange(Structure): 108 """ 109 A SourceRange describes a range of source locations within the source 110 code. 111 """ 112 _fields_ = [ 113 ("ptr_data", c_void_p), 114 ("begin_int_data", c_uint), 115 ("end_int_data", c_uint)] 116 117 @property 118 def start(self): 119 """ 120 Return a SourceLocation representing the first character within a 121 source range. 122 """ 123 return SourceRange_start(self).init() 124 125 @property 126 def end(self): 127 """ 128 Return a SourceLocation representing the last character within a 129 source range. 130 """ 131 return SourceRange_end(self).init() 132 133class Cursor(Structure): 134 """ 135 The Cursor class represents a reference to an element within the AST. It 136 acts as a kind of iterator. 137 """ 138 _fields_ = [("kind", c_int), ("data", c_void_p * 3)] 139 140 def __eq__(self, other): 141 return Cursor_eq(self, other) 142 143 def __ne__(self, other): 144 return not Cursor_eq(self, other) 145 146 @staticmethod 147 def null(): 148 """Return the null cursor object.""" 149 return Cursor_null() 150 151 def is_declaration(self): 152 """Return True if the cursor points to a declaration.""" 153 return Cursor_is_decl(self.kind) 154 155 def is_reference(self): 156 """Return True if the cursor points to a reference.""" 157 return Cursor_is_ref(self.kind) 158 159 def is_expression(self): 160 """Return True if the cursor points to an expression.""" 161 return Cursor_is_expr(self.kind) 162 163 def is_statement(self): 164 """Return True if the cursor points to a statement.""" 165 return Cursor_is_stmt(self.kind) 166 167 def is_translation_unit(self): 168 """Return True if the cursor points to a translation unit.""" 169 return Cursor_is_tu(self.kind) 170 171 def is_invalid(self): 172 """Return True if the cursor points to an invalid entity.""" 173 return Cursor_is_inv(self.kind) 174 175 def is_definition(self): 176 """ 177 Returns true if the declaration pointed at by the cursor is also a 178 definition of that entity. 179 """ 180 return Cursor_is_def(self) 181 182 def get_definition(self): 183 """ 184 If the cursor is a reference to a declaration or a declaration of 185 some entity, return a cursor that points to the definition of that 186 entity. 187 """ 188 # TODO: Should probably check that this is either a reference or 189 # declaration prior to issuing the lookup. 190 return Cursor_def(self) 191 192 @property 193 def spelling(self): 194 """Return the spelling of the entity pointed at by the cursor.""" 195 if not self.is_declaration(): 196 # FIXME: This should be documented in Index.h 197 raise ValueError("Cursor does not refer to a Declaration") 198 return Cursor_spelling(self) 199 200 @property 201 def location(self): 202 """ 203 Return the source location (the starting character) of the entity 204 pointed at by the cursor. 205 """ 206 return Cursor_loc(self).init() 207 208 @property 209 def extent(self): 210 """ 211 Return the source range (the range of text) occupied by the entity 212 pointed at by the cursor. 213 """ 214 return Cursor_extent(self) 215 216 @property 217 def file(self): 218 """ 219 Return the file containing the pointed-at entity. This is an alias for 220 location.file. 221 """ 222 return self.location.file 223 224 def get_children(self): 225 """Return an iterator for the accessing children of this cursor.""" 226 227 # FIXME: Expose iteration from CIndex, PR6125. 228 def visitor(child, parent, children): 229 children.append(child) 230 return 1 # continue 231 children = [] 232 Cursor_visit(self, Callback(visitor), children) 233 return iter(children) 234 235## CIndex Objects ## 236 237# CIndex objects (derived from ClangObject) are essentially lightweight 238# wrappers attached to some underlying object, which is exposed via CIndex as 239# a void*. 240 241class ClangObject(object): 242 """ 243 A helper for Clang objects. This class helps act as an intermediary for 244 the ctypes library and the Clang CIndex library. 245 """ 246 def __init__(self, obj): 247 assert isinstance(obj, c_object_p) and obj 248 self.obj = self._as_parameter_ = obj 249 250 def from_param(self): 251 return self._as_parameter_ 252 253class Index(ClangObject): 254 """ 255 The Index type provides the primary interface to the Clang CIndex library, 256 primarily by providing an interface for reading and parsing translation 257 units. 258 """ 259 260 @staticmethod 261 def create(excludeDecls=False, displayDiags=False): 262 """ 263 Create a new Index. 264 Parameters: 265 excludeDecls -- Exclude local declarations from translation units. 266 displayDiags -- Display diagnostics during translation unit creation. 267 """ 268 return Index(Index_create(excludeDecls, displayDiags)) 269 270 def __del__(self): 271 Index_dispose(self) 272 273 def read(self, path): 274 """Load the translation unit from the given AST file.""" 275 return TranslationUnit.read(self, path) 276 277 def parse(self, path, args = []): 278 """ 279 Load the translation unit from the given source code file by running 280 clang and generating the AST before loading. Additional command line 281 parameters can be passed to clang via the args parameter. 282 """ 283 return TranslationUnit.parse(self, path, args) 284 285 286class TranslationUnit(ClangObject): 287 """ 288 The TranslationUnit class represents a source code translation unit and 289 provides read-only access to its top-level declarations. 290 """ 291 292 def __del__(self): 293 TranslationUnit_dispose(self) 294 295 @property 296 def cursor(self): 297 """Retrieve the cursor that represents the given translation unit.""" 298 return TranslationUnit_cursor(self) 299 300 @property 301 def spelling(self): 302 """Get the original translation unit source file name.""" 303 return TranslationUnit_spelling(self) 304 305 @staticmethod 306 def read(ix, path): 307 """Create a translation unit from the given AST file.""" 308 ptr = TranslationUnit_read(ix, path) 309 return TranslationUnit(ptr) if ptr else None 310 311 @staticmethod 312 def parse(ix, path, args = []): 313 """ 314 Construct a translation unit from the given source file, applying 315 the given command line argument. 316 """ 317 # TODO: Support unsaved files. 318 argc, argv = len(args), create_string_vector(args) 319 ptr = TranslationUnit_parse(ix, path, argc, byref(argv), 0, 0) 320 return TranslationUnit(ptr) if ptr else None 321 322class File(ClangObject): 323 """ 324 The File class represents a particular source file that is part of a 325 translation unit. 326 """ 327 328 @property 329 def name(self): 330 """Return the complete file and path name of the file, if valid.""" 331 return File_name(self) 332 333 @property 334 def time(self): 335 """Return the last modification time of the file, if valid.""" 336 return File_time(self) 337 338# Additional Functions and Types 339 340# Wrap calls to TranslationUnit._load and Decl._load. 341Callback = CFUNCTYPE(c_int, Cursor, Cursor, py_object) 342 343# String Functions 344String_dispose = lib.clang_disposeString 345String_dispose.argtypes = [String] 346 347# Source Location Functions 348SourceLocation_loc = lib.clang_getInstantiationLocation 349SourceLocation_loc.argtypes = [SourceLocation, POINTER(c_object_p), c_uint_p, 350 c_uint_p] 351 352# Source Range Functions 353SourceRange_start = lib.clang_getRangeStart 354SourceRange_start.argtypes = [SourceRange] 355SourceRange_start.restype = SourceLocation 356 357SourceRange_end = lib.clang_getRangeEnd 358SourceRange_end.argtypes = [SourceRange] 359SourceRange_end.restype = SourceLocation 360 361# Cursor Functions 362# TODO: Implement this function 363Cursor_get = lib.clang_getCursor 364Cursor_get.argtypes = [TranslationUnit, SourceLocation] 365Cursor.restype = Cursor 366 367Cursor_null = lib.clang_getNullCursor 368Cursor_null.restype = Cursor 369 370Cursor_kind = lib.clang_getCursorKind 371Cursor_kind.argtypes = [Cursor] 372Cursor_kind.res = c_int 373 374# FIXME: Not really sure what a USR is or what this function actually does... 375Cursor_usr = lib.clang_getCursorUSR 376 377Cursor_is_decl = lib.clang_isDeclaration 378Cursor_is_decl.argtypes = [CursorKind] 379Cursor_is_decl.restype = c_bool 380 381Cursor_is_ref = lib.clang_isReference 382Cursor_is_ref.argtypes = [CursorKind] 383Cursor_is_ref.restype = c_bool 384 385Cursor_is_expr = lib.clang_isExpression 386Cursor_is_expr.argtypes = [CursorKind] 387Cursor_is_expr.restype = c_bool 388 389Cursor_is_stmt = lib.clang_isStatement 390Cursor_is_stmt.argtypes = [CursorKind] 391Cursor_is_stmt.restype = c_bool 392 393Cursor_is_inv = lib.clang_isInvalid 394Cursor_is_inv.argtypes = [CursorKind] 395Cursor_is_inv.restype = c_bool 396 397Cursor_is_tu = lib.clang_isTranslationUnit 398Cursor_is_tu.argtypes = [CursorKind] 399Cursor_is_tu.restype = c_bool 400 401Cursor_is_def = lib.clang_isCursorDefinition 402Cursor_is_def.argtypes = [Cursor] 403Cursor_is_def.restype = c_bool 404 405Cursor_def = lib.clang_getCursorDefinition 406Cursor_def.argtypes = [Cursor] 407Cursor_def.restype = Cursor 408 409Cursor_eq = lib.clang_equalCursors 410Cursor_eq.argtypes = [Cursor, Cursor] 411Cursor_eq.restype = c_uint 412 413Cursor_spelling = lib.clang_getCursorSpelling 414Cursor_spelling.argtypes = [Cursor] 415Cursor_spelling.restype = String 416 417Cursor_loc = lib.clang_getCursorLocation 418Cursor_loc.argtypes = [Cursor] 419Cursor_loc.restype = SourceLocation 420 421Cursor_extent = lib.clang_getCursorExtent 422Cursor_extent.argtypes = [Cursor] 423Cursor_extent.restype = SourceRange 424 425Cursor_ref = lib.clang_getCursorReferenced 426Cursor_ref.argtypes = [Cursor] 427Cursor_ref.restype = Cursor 428 429Cursor_visit = lib.clang_visitChildren 430Cursor_visit.argtypes = [Cursor, Callback, py_object] 431Cursor_visit.restype = c_uint 432 433# Index Functions 434Index_create = lib.clang_createIndex 435Index_create.argtypes = [c_int, c_int] 436Index_create.restype = c_object_p 437 438Index_dispose = lib.clang_disposeIndex 439Index_dispose.argtypes = [Index] 440 441# Translation Unit Functions 442TranslationUnit_read = lib.clang_createTranslationUnit 443TranslationUnit_read.argtypes = [Index, c_char_p] 444TranslationUnit_read.restype = c_object_p 445 446TranslationUnit_parse = lib.clang_createTranslationUnitFromSourceFile 447TranslationUnit_parse.argtypes = [Index, c_char_p, c_int, c_void_p, 448 c_int, c_void_p] 449TranslationUnit_parse.restype = c_object_p 450 451TranslationUnit_cursor = lib.clang_getTranslationUnitCursor 452TranslationUnit_cursor.argtypes = [TranslationUnit] 453TranslationUnit_cursor.restype = Cursor 454 455TranslationUnit_spelling = lib.clang_getTranslationUnitSpelling 456TranslationUnit_spelling.argtypes = [TranslationUnit] 457TranslationUnit_spelling.restype = String 458 459TranslationUnit_dispose = lib.clang_disposeTranslationUnit 460TranslationUnit_dispose.argtypes = [TranslationUnit] 461 462# File Functions 463File_name = lib.clang_getFileName 464File_name.argtypes = [File] 465File_name.restype = c_char_p 466 467File_time = lib.clang_getFileTime 468File_time.argtypes = [File] 469File_time.restype = c_uint 470