def parseIdentifier(node: Cursor, regex: str = None) -> str: # TODO: Check this while node.kind == CursorKind.CSTYLE_CAST_EXPR: node = list(node.get_children())[1] # Descend to child node if node.kind == CursorKind.UNEXPOSED_EXPR: node = descend(node) # Descend to child node if node.kind == CursorKind.UNARY_OPERATOR and next( node.get_tokens()).spelling in ('&', '*'): node = descend(node) # Check for null if node.kind == CursorKind.GNU_NULL_EXPR: return '' # Validate the node if node.kind != CursorKind.DECL_REF_EXPR: raise ParseError('expected identifier') value = node.spelling # Apply conditional regex constraint if regex and not re.match(regex, value): raise ParseError(f'expected identifier matching {regex}') return value
def parse_clang_node(cls, cursor: clang.Cursor, parent: typing.Optional[ir.Node] = None) -> ir.Node: """ Parses a Clang AST node identified by a cursor into an IR node. :param cursor: The cursor pointing to the node. :param parent: The parent IR node. :return: The corresponding IR node. """ type_str = getattr(cursor.type, 'spelling', '') # Build label label = f"{type_str}: {cursor.spelling}" if cursor.is_definition() and type_str else cursor.spelling # Default node data kwargs = dict( typ=cursor.kind.name, label=label, ref=cursor.get_usr(), parent=parent, source_range=cls.get_range(cursor), ) # Overrides for root node if parent is None and cursor.kind == clang.CursorKind.TRANSLATION_UNIT: kwargs['label'] = os.path.basename(kwargs['label']) # Overrides for specific kinds of nodes if cursor.kind in cls._customizers: cls._customizers[cursor.kind](cursor, kwargs) # Build node node = ir.Node(**kwargs) return node
def traverse(c: cindex.Cursor, indent='') -> None: # skip if c.location.file.name != str(path): # exclude included file return if c.hash in used: # avoid show twice return used.add(c.hash) ref = '' if c.referenced and c.referenced.hash != c.hash: ref = f' => {c.referenced.hash:#010x}' canonical = '' if c.canonical and c.canonical.hash != c.hash: canonical = f' => {c.canonical.hash:#010x} (forward decl)' value = f'{c.hash:#010x}:{indent} {c.kind}: {c.spelling}{ref}{canonical}' print(value) if c.kind == cindex.CursorKind.UNEXPOSED_DECL: tokens = [t for t in c.get_tokens()] if tokens and tokens[0].spelling == 'extern': # extern "C" block for child in c.get_children(): traverse(child) return for child in c.get_children(): traverse(child, indent + ' ')
def get_binary_op(cursor: cindex.Cursor) -> str: try: children_list = [i for i in cursor.get_children()] left_offset = len([i for i in children_list[0].get_tokens()]) op = [i for i in cursor.get_tokens()][left_offset].spelling return op except Exception: return ''
def match_element(parent: Cursor, element: CursorPathElement): if isinstance(element, str): for child in parent.get_children(): if child.displayname == element: yield child else: children_of_kind = [child for child in parent.get_children() if child.kind == element[0]] if len(children_of_kind) > element[1]: yield children_of_kind[element[1]]
def test_location(): tu = get_tu(baseInput) one = get_cursor(tu, 'one') two = get_cursor(tu, 'two') assert one is not None assert two is not None assert_location(one.location, line=1, column=5, offset=4) assert_location(two.location, line=2, column=5, offset=13) # adding a linebreak at top should keep columns same tu = get_tu('\n' + baseInput) one = get_cursor(tu, 'one') two = get_cursor(tu, 'two') assert one is not None assert two is not None assert_location(one.location, line=2, column=5, offset=5) assert_location(two.location, line=3, column=5, offset=14) # adding a space should affect column on first line only tu = get_tu(' ' + baseInput) one = get_cursor(tu, 'one') two = get_cursor(tu, 'two') assert_location(one.location, line=1, column=6, offset=5) assert_location(two.location, line=2, column=5, offset=14) # define the expected location ourselves and see if it matches # the returned location tu = get_tu(baseInput) file = File.from_name(tu, 't.c') location = SourceLocation.from_position(tu, file, 1, 5) cursor = Cursor.from_location(tu, location) one = get_cursor(tu, 'one') assert one is not None assert one == cursor # Ensure locations referring to the same entity are equivalent. location2 = SourceLocation.from_position(tu, file, 1, 5) assert location == location2 location3 = SourceLocation.from_position(tu, file, 1, 4) assert location2 != location3 offset_location = SourceLocation.from_offset(tu, file, 5) cursor = Cursor.from_location(tu, offset_location) verified = False for n in [n for n in tu.cursor.get_children() if n.spelling == 'one']: assert n == cursor verified = True assert verified
def generateDoxygenForSourceLocation(line, col): filename = vim.current.buffer.name index = Index.create() tu = index.parse(filename, vim.eval("g:clang_doxygen_clang_args"), [(filename, "\n".join(vim.current.buffer[:]))]) # Skip whitespace at beginning of line indent = re.match(r'^\s*', vim.current.buffer[line - 1]).span()[1] col = max(col, indent + 1) c = Cursor.from_location(tu, SourceLocation.from_position(tu, File.from_name(tu, filename), line, col)) # If there is no declaration at the source location try to find the nearest one. while c is not None: # If cursor is on a TypeRef in a FunctionTemplate, manually go backward in the source. if c.kind == CursorKind.TYPE_REF: pLine, pCol = previousSourceLocation(c.extent.start.line, c.extent.start.column) c = Cursor.from_location(tu, SourceLocation.from_position(tu, File.from_name(tu, filename), pLine, pCol)) continue # If cursor is on a NamespaceRef, manually go forward in the source. elif c.kind == CursorKind.NAMESPACE_REF: nLine, nCol = nextSourceLocation(c.extent.end.line, c.extent.end.column) c = Cursor.from_location(tu, SourceLocation.from_position(tu, File.from_name(tu, filename), nLine, nCol)) continue elif c.kind == CursorKind.FUNCTION_DECL: return handleFunctionDecl(c) elif c.kind == CursorKind.CXX_METHOD: return handleFunctionDecl(c) elif c.kind == CursorKind.CONSTRUCTOR: return handleFunctionDecl(c) elif c.kind == CursorKind.DESTRUCTOR: return handleFunctionDecl(c) elif c.kind == CursorKind.FUNCTION_TEMPLATE: return handleFunctionTemplate(c) elif c.kind == CursorKind.CLASS_DECL: return handleClassDecl(c) elif c.kind == CursorKind.CLASS_TEMPLATE: return handleClassDecl(c) elif c.kind == CursorKind.OBJC_INSTANCE_METHOD_DECL: return handleFunctionDecl(c) elif c.kind == CursorKind.OBJC_INTERFACE_DECL: return handleClassDecl(c) elif c.kind == CursorKind.OBJC_CATEGORY_DECL: return handleClassDecl(c) elif c.kind == CursorKind.OBJC_IMPLEMENTATION_DECL: return handleClassDecl(c) # Cursor is not on a supported type, go to the lexical parent else: c = c.lexical_parent if c is None: print "Error: No supported declaration found at %s:%i,%i.\n" % (filename, line, col) return (None, None)
def get_arguments(cursor: clang.Cursor) -> typing.Generator[clang.Cursor, None, None]: """ Retrieves the argument dependencies from the AST node pointed to by the given cursor. :param cursor: The cursor pointing to the node. :return: The cursors of the nodes of the arguments of the node. """ # If the cursor has arguments, yield them yield from cursor.get_arguments() # If the cursor is an enum, yield all children if cursor.kind == clang.CursorKind.ENUM_DECL: for arg_cursor in cursor.walk_preorder(): yield arg_cursor
def parse_ENUM_DECL(cursor: Cursor) -> ast.Node: if not cursor.is_definition(): return ast.EhEnum(0, ast.Identifier(0, cursor.spelling), None) fields: List[ast.Identifier] = [] expect: bool = False for t in cursor.get_tokens(): if t.spelling == '{' or t.spelling == ',': expect = True elif t.spelling == '}': break elif expect: fields.append(ast.Identifier(0, t.spelling)) expect = False return ast.EhEnum(0, ast.Identifier(0, cursor.spelling), fields)
def local_references(libclang, translation_unit, path, line, column): source_location = translation_unit.get_location(path, (line, column)) cursor = Cursor.from_location(translation_unit, source_location) if cursor is None: return None if cursor.referenced is not None: cursor = cursor.referenced if cursor.kind == CursorKind.NO_DECL_FOUND: return None references = [] def visitor(_context, cursor, source_range): range_dict = range_dict_relative(source_range) # The end location is non-inclusive. range_dict['end']['column'] -= 1 references.append(range_dict) return 1 # continue visitor_obj = CXCursorAndRangeVisitor() visitor_obj.visit = CFUNCTYPE(c_int, c_void_p, Cursor, SourceRange)(visitor) libclang.clang_findReferencesInFile(cursor, source_location.file, visitor_obj) return { 'cursor_name': cursor.spelling, 'cursor_kind': cursor.kind.name, 'references': references, }
def get_file_comment(cursor: Cursor, child: Optional[Cursor]) -> str: """ Attempts to get the comment at the top of the file. Args: cursor (:class:`cindex.Cursor`): The root cursor of the file. child (:class:`cindex.Cursor`): The first child node in the file. This can be None. Returns: str: The file level comment. """ try: token = next(cursor.get_tokens()) except StopIteration: # Only happens with a completely empty file return "" if token.kind == cindex.TokenKind.COMMENT: if child is not None: child_comment = child.raw_comment else: child_comment = "" # if the first comment is not the documentation comment for the first # child then assume it is the file comment. if child_comment != token.spelling: return parse_comment(token) return ""
def gotoDeclaration(preview=True): global debug debug = int(vim.eval("g:clang_debug")) == 1 params = getCompileParams(vim.current.buffer.name) line, col = vim.current.window.cursor timer = CodeCompleteTimer(debug, vim.current.buffer.name, line, col, params) with libclangLock: tu = getCurrentTranslationUnit(params['args'], getCurrentFile(), vim.current.buffer.name, timer, update = True) if tu is None: print("Couldn't get the TranslationUnit") return f = File.from_name(tu, vim.current.buffer.name) loc = SourceLocation.from_position(tu, f, line, col + 1) cursor = Cursor.from_location(tu, loc) defs = [cursor.get_definition(), cursor.referenced] for d in defs: if d is not None and loc != d.location: loc = d.location if loc.file is not None: jumpToLocation(loc.file.name, loc.line, loc.column, preview) break timer.finish()
def gotoDeclaration(preview=True): global debug debug = int(vim.eval("g:clang_debug")) == 1 params = getCompileParams(vim.current.buffer.name) line, col = vim.current.window.cursor timer = CodeCompleteTimer(debug, vim.current.buffer.name, line, col, params) with libclangLock: tu = getCurrentTranslationUnit(params['args'], getCurrentFile(), vim.current.buffer.name, timer, update=True) if tu is None: print("Couldn't get the TranslationUnit") return f = File.from_name(tu, vim.current.buffer.name) loc = SourceLocation.from_position(tu, f, line, col + 1) cursor = Cursor.from_location(tu, loc) defs = [cursor.get_definition(), cursor.referenced] for d in defs: if d is not None and loc != d.location: loc = d.location if loc.file is not None: jumpToLocation(loc.file.name, loc.line, loc.column, preview) break timer.finish()
def create(cursor: cindex.Cursor) -> Optional['ClangInterface']: name = cursor.spelling if name[0] != 'I': return None base = 'IUnknown' methods: List[ClangMethod] = [] iid = '' if name == 'ID2D1Factory': a = 0 for x in cursor.get_children(): if x.kind == cindex.CursorKind.CXX_BASE_SPECIFIER: ref = next(y for y in x.get_children() if y.kind == cindex.CursorKind.TYPE_REF) base = ref.spelling if base.startswith('struct '): base = base[7:] elif x.kind == cindex.CursorKind.CXX_METHOD: if not any(y for y in methods if y.name == x.spelling): methods.append(ClangMethod(x)) elif x.kind == cindex.CursorKind.CXX_ACCESS_SPEC_DECL: pass elif x.kind == cindex.CursorKind.UNEXPOSED_ATTR: try: iid = extract(x).split('"')[1].strip().split('-') iid = f'0x{iid[0]}, 0x{iid[1]}, 0x{iid[2]}, [0x{iid[3][0:2]}, 0x{iid[3][2:4]}, 0x{iid[4][0:2]}, 0x{iid[4][2:4]}, 0x{iid[4][4:6]}, 0x{iid[4][6:8]}, 0x{iid[4][8:10]}, 0x{iid[4][10:12]}]' except: pass else: print(x.kind) return ClangInterface(name, base, methods, iid)
def get_usr_under_cursor(self, file_name, line, col): with self.c_parse_lock: if file_name not in self.current_file_tus: return "" tu = self.current_file_tus[file_name] f = File.from_name(tu, file_name) loc = SourceLocation.from_position(tu, f, int(line), int(col)) cursor = Cursor.from_location(tu, loc) while cursor is not None and (not cursor.referenced or not cursor.referenced.get_usr()): nextCursor = cursor.lexical_parent if nextCursor is not None and nextCursor == cursor: return "" cursor = nextCursor if cursor is None: return "" cursor = cursor.referenced if cursor is None: return "" return { 'usr': cursor.get_usr(), 'file': str(cursor.location.file), 'line': cursor.location.line, 'column': cursor.location.column }
def __init__(self, cursor: cindex.Cursor): super().__init__(cursor) self.template_arguments = [] for x in cursor.get_children(): param = CursorAdapter.create(x) if param is not None: self.template_arguments.append(param)
def parse_field(self, c: cindex.Cursor) -> Field: ''' structのfieldを処理する。 配列の処理に注意。 ''' offset = c.get_field_offsetof() // 8 # if not decl.template_parameters and offset < 0: # # parseに失敗(特定のheaderが見つからないなど) # # clang 環境が壊れているかも # # VCのプレビュー版とか原因かも # # プレビュー版をアンインストールして LLVM を入れたり消したらなおった # raise Exception(f'struct {c.spelling}.{child.spelling}: offset error') field_type, stack = strip_nest_type(c.type) primitive = get_primitive_type(field_type) if primitive: # field return Field(restore_nest_type(primitive, stack), c.spelling, offset) decl = self.get_type_from_hash(field_type, c) if decl: return Field(restore_nest_type(decl, stack), c.spelling) raise Exception()
def _find_callsites( node: Cursor, closure: List[Cursor], in_function: bool) -> Tuple[List[Cursor], List[List[Cursor]]]: """ Macro invocations are not aligned with the remaining ast. - We need the macro invocation to get source line, where INT3 was included - To get the position in the AST, we look for a our dummy header, this gives us the closure with all defined variables and symbols We can combine both information with a simple zip(a,b) """ include_locations = [] closures = [] # reset closure outside of functions if not in_function: closure = [] if node.kind == CursorKind.MACRO_INSTANTIATION and node.spelling == "INT3": include_locations.append(node.location) elif in_function: if (node.kind == CursorKind.COMPOUND_STMT and node.location is not None and node.location.file.name.endswith("int3/dummy.h")): closures.append(closure) elif node.kind == CursorKind.VAR_DECL or node.kind == CursorKind.PARM_DECL: closure.append(node) for c in node.get_children(): in_function = in_function or node.kind == CursorKind.FUNCTION_DECL include_locations_, closures_ = _find_callsites( c, closure, in_function) include_locations.extend(include_locations_) closures.extend(closures_) return include_locations, closures
def traverse(c: cindex.Cursor) -> None: if not c.location.file: return current = get_or_create_header(c) if current.name in include: pass else: return if c.hash in used: # already processed return if c.kind not in kinds: # skip return if c.kind == cindex.CursorKind.UNEXPOSED_DECL: for child in c.get_children(): traverse(child) return node = get_node(current, c) if not node: return used[c.hash] = node current.nodes.append(node)
def __subproc_same_func_in_file_check(cursor: cl.Cursor, error_an1, error_an2): for child in cursor.get_children(): if child.kind == cl.CursorKind.FUNCTION_DECL: if child.extent.start.line <= error_an1.main_line <= child.extent.end.line and \ child.extent.start.line <= error_an2.main_line <= child.extent.end.line: return True
def __init__(self, cursor: cindex.Cursor) -> None: self.name = cursor.spelling self.result = cursor.result_type.spelling self.args: List[ClangNamedType] = [] for x in cursor.get_children(): if x.kind == cindex.CursorKind.PARM_DECL: self.args.append(ClangNamedType(x.spelling, x.type.spelling))
def get_string_literal(c: Cursor) -> Optional[str]: if c.kind == CursorKind.STRING_LITERAL: # TODO: This parses python literals which are not the same as C. Ideally this would use clang to parse C # literals to bytes. return ast.literal_eval(c.spelling) else: for ch in c.get_children(): return get_string_literal(ch)
def get_arguments(cursor: cindex.Cursor) -> Iterator[cindex.Cursor]: for child in cursor.get_arguments(): args = [] if child.kind != cindex.CursorKind.UNEXPOSED_EXPR: args.append(child) else: walk_ast(child, _get_arguments_helper, args) yield args[0] if len(args) > 0 else None
def __init__(self, cursor: cindex.Cursor): super().__init__(cursor) self.parent = CursorAdapter.create(cursor.semantic_parent) self.arguments = [(TypeAdapter.create(x.type), x.spelling) for x in cursor.get_arguments()] self.result_type = TypeAdapter.create(cursor.result_type) logger.info(f'FuncType: {self.cursor.kind}, ' f'ResultType: {self.cursor.result_type.kind}')
def _parse(self, c: cindex.Cursor) -> None: for child in c.get_children(): if child.kind == cindex.CursorKind.FIELD_DECL: # print( # f'{child.spelling}: {int(self.t.get_offset(child.spelling)/8)}' # ) field = StructNode(self.path, child, False) if child.type == cindex.TypeKind.TYPEDEF: field_type = cdeclare.parse_declare( get_typedef_type(child).spelling) else: field_type = cdeclare.parse_declare(child.type.spelling) field.field_type = field_type self.fields.append(field) elif child.kind == cindex.CursorKind.STRUCT_DECL: struct = StructNode(self.path, child) struct.field_type = 'struct' self.fields.append(struct) elif child.kind == cindex.CursorKind.UNION_DECL: union = StructNode(self.path, child) union.field_type = 'union' self.fields.append(union) elif child.kind == cindex.CursorKind.UNEXPOSED_ATTR: value = extract(child) d3d11_key = 'MIDL_INTERFACE("' d2d1_key = 'DX_DECLARE_INTERFACE("' dwrite_key = 'DWRITE_DECLARE_INTERFACE("' if value.startswith(d3d11_key): self.iid = uuid.UUID(value[len(d3d11_key):-2]) elif value.startswith(d2d1_key): self.iid = uuid.UUID(value[len(d2d1_key):-2]) elif value.startswith(dwrite_key): self.iid = uuid.UUID(value[len(dwrite_key):-2]) else: print(value) elif child.kind == cindex.CursorKind.CXX_BASE_SPECIFIER: if child.type == cindex.TypeKind.TYPEDEF: self.base = get_typedef_type(child).spelling else: self.base = child.type.spelling elif child.kind == cindex.CursorKind.CXX_METHOD: method = FunctionNode(self.path, child) if not method.has_body: self.methods.append(method) elif child.kind == cindex.CursorKind.CONSTRUCTOR: pass elif child.kind == cindex.CursorKind.DESTRUCTOR: pass elif child.kind == cindex.CursorKind.CONVERSION_FUNCTION: pass elif child.kind == cindex.CursorKind.CXX_ACCESS_SPEC_DECL: pass elif child.kind == cindex.CursorKind.FUNCTION_TEMPLATE: pass elif child.kind == cindex.CursorKind.USING_DECLARATION: pass else: raise Exception(child.kind)
def get_unary_op(self, cursor: Cursor): # libclang does not expose the unary operation in the C API... arg: Cursor = next(cursor.get_children()) ext: SourceRange = arg.extent op_text = self.content[cursor.extent.start.offset:ext.start.offset].lstrip().rstrip() return op_text
def parseClass(self, cursor: ci.Cursor) -> parsed.Klass: methods = [] for child in cursor.get_children(): if child.kind == ci.CursorKind.CXX_METHOD: methods.append(self.parseMethod(child)) return parsed.Klass(name=cursor.spelling, decl_file=self.file_path, methods=methods)
def parse(c: cindex.Cursor): if c.kind == cindex.CursorKind.UNEXPOSED_DECL: tokens = [t.spelling for t in c.get_tokens()] elif c.kind == cindex.CursorKind.STRUCT_DECL: return StructDecl.parse(c) elif c.kind == cindex.CursorKind.TYPEDEF_DECL: return TypedefDecl.parse(c) else: print(c.kind)
def walk_ast(cursor: cindex.Cursor, callback: Callable[[cindex.Cursor, T], WalkResult], data: T = None): for child in cursor.get_children(): result = callback(child, data) if result == WalkResult.BREAK: break elif result == WalkResult.RECURSE: walk_ast(child, callback, data)
def from_cindex(cls, cursor: Cursor, namespace: Text): """Create a Type object from cindex cursor.""" fq_name = '::'.join([namespace, cursor.spelling]) is_pure_virtual = cursor.is_pure_virtual_method() arguments = [Type.from_cindex(x) for x in cursor.type.argument_types()] if cursor.type == TypeKind.VOID: return_type = [Type.void()] else: return_type = [Type.from_cindex(cursor.type.get_result())] return cls(fq_name, is_pure_virtual, arguments, return_type)
def customize_literals(cursor: clang.Cursor, kwargs: NodeData) -> NodeData: """ Sets the label to the token value of the literal. Without this customization a literal would not have a label at all. """ try: kwargs['label'] = ''.join(map(lambda x: x.spelling, cursor.get_tokens())) except AttributeError: raise parse.ParseError("Unexpected error: literal does not have any tokens.") return kwargs
def __init__(self, cursor: cindex.Cursor) -> None: self.name = cursor.spelling self.fields: List[ClangNamedType] = [] for x in cursor.get_children(): if x.kind == cindex.CursorKind.FIELD_DECL: self.fields.append(ClangNamedType(x.spelling, x.type.spelling)) for y in x.get_children(): a = 0 else: a = 0
def name_of(node): spelling = node.spelling.decode('utf-8') if spelling: return spelling typedef_node = Cursor.from_location(node.translation_unit, node.extent.end) if typedef_node.kind == CursorKind.TYPEDEF_DECL: return name_of(typedef_node) else: return ''
def dump(cursor: Cursor): #line_count = cursor.get_tokens[0].location.line line_count = 1 for t in cursor.get_tokens(): current_line = t.location.line if current_line > line_count: for _ in range(current_line - line_count): print('') line_count = current_line print(t.spelling, end=' ') print('')
def GetCurrentUsrCursor(tu): line, col = vim.current.window.cursor col = col + 1 f = File.from_name(tu, vim.current.buffer.name) loc = SourceLocation.from_position(tu, f, line, col) cursor = Cursor.from_location(tu, loc) while cursor is not None and (not cursor.referenced or not cursor.referenced.get_usr()): nextCursor = cursor.lexical_parent if nextCursor is not None and nextCursor == cursor: return None cursor = nextCursor if cursor is None: return None return cursor.referenced
def get_declaration_info(self, request): contents = request['contents'] line = request['line'] column = request['column'] # Update the translation unit with the latest contents. translation_unit = self._update_translation_unit(contents) if not translation_unit: return None location = translation_unit.get_location(self.src, (line + 1, column + 1)) cursor = Cursor.from_location(translation_unit, location) cursor = cursor.referenced if cursor is None: return None return self.get_declaration_info_for_cursor(cursor)
def convert_canonical(typ): decl = typ.get_declaration() struct_name = decl.displayname.decode('utf-8') if not struct_name: typedef_node = Cursor.from_location(decl.translation_unit, decl.extent.end) if typedef_node.kind == CursorKind.TYPEDEF_DECL: struct_name = typedef_node.displayname.decode('utf-8') if struct_name: sb = [struct_name] else: sb = [RECORD_KEYWORD[decl.kind], ' { '] for node in decl.get_children(): name = node.spelling.decode('utf-8') sb.append(to_cc(node.type, name)) sb.append('; ') sb.append('}') return (''.join(sb), '', '')
def get_usr_under_cursor(self, file_name, line, col): with self.c_parse_lock: if file_name not in self.current_file_tus: return "" tu = self.current_file_tus[file_name] f = File.from_name(tu, file_name) loc = SourceLocation.from_position(tu, f, int(line), int(col)) cursor = Cursor.from_location(tu, loc) while cursor is not None and (not cursor.referenced or not cursor.referenced.get_usr()): nextCursor = cursor.lexical_parent if nextCursor is not None and nextCursor == cursor: return "" cursor = nextCursor if cursor is None: return "" cursor = cursor.referenced if cursor is None: return "" return {'usr': cursor.get_usr(), 'file': str(cursor.location.file), 'line': cursor.location.line, 'column': cursor.location.column}
def test_location(): index = Index.create() tu = index.parse('t.c', unsaved_files = [('t.c',baseInput)]) for n in tu.cursor.get_children(): if n.spelling == 'one': assert_location(n.location,line=1,column=5,offset=4) if n.spelling == 'two': assert_location(n.location,line=2,column=5,offset=13) # adding a linebreak at top should keep columns same tu = index.parse('t.c', unsaved_files = [('t.c',"\n"+baseInput)]) for n in tu.cursor.get_children(): if n.spelling == 'one': assert_location(n.location,line=2,column=5,offset=5) if n.spelling == 'two': assert_location(n.location,line=3,column=5,offset=14) # adding a space should affect column on first line only tu = index.parse('t.c', unsaved_files = [('t.c'," "+baseInput)]) for n in tu.cursor.get_children(): if n.spelling == 'one': assert_location(n.location,line=1,column=6,offset=5) if n.spelling == 'two': assert_location(n.location,line=2,column=5,offset=14) # define the expected location ourselves and see if it matches # the returned location tu = index.parse('t.c', unsaved_files = [('t.c',baseInput)]) file = File.from_name(tu, 't.c') location = SourceLocation.from_position(tu, file, 1, 5) cursor = Cursor.from_location(tu, location) for n in tu.cursor.get_children(): if n.spelling == 'one': assert n == cursor
def local_references(libclang, translation_unit, path, line, column): source_location = translation_unit.get_location(path, (line, column)) cursor = Cursor.from_location(translation_unit, source_location) if cursor is None: return None if cursor.referenced is not None: cursor = cursor.referenced if cursor.kind == CursorKind.NO_DECL_FOUND: return None references = [] def visitor(_context, cursor, source_range): references.append(range_dict_relative(source_range)) return 1 # continue visitor_obj = CXCursorAndRangeVisitor() visitor_obj.visit = CFUNCTYPE(c_int, c_void_p, Cursor, SourceRange)(visitor) libclang.clang_findReferencesInFile(cursor, source_location.file, visitor_obj) return { 'cursor_name': cursor.spelling, 'cursor_kind': cursor.kind.name, 'references': references, }
def print_all_of_kind(parent: Cursor, kind: CursorKind): for node in parent.get_children(): #if kind == node.kind: debug_cursor(node) print_all_of_kind(node, kind)
def find_child(parent: Cursor, kind: CursorKind, spelling: str): for node in parent.get_children(): if kind == node.kind and spelling == node.spelling: return node
def get_declaration_location_and_spelling(translation_unit, contents, flags, absolute_path, line, column): def log(s): logger.info('%s:%d:%d - %s', os.path.basename(absolute_path), line, column, s) source_location = translation_unit.get_location( absolute_path, (line, column)) cursor = Cursor.from_location(translation_unit, source_location) if cursor is None: log('No cursor') return None # Don't allow clicks/tooltips on most declarations, as their content is usually obvious. # Make an exception for variable declarations, as these can often have auto types. if cursor.kind != CursorKind.VAR_DECL and cursor.kind.is_declaration(): log('Ignoring declaration') return None referenced = cursor.referenced if referenced is None or referenced.location is None or referenced.location.file is None: # If cursor is over an include statement, attempt to resolve the location # of the included file. line_text = get_line(contents, line) bounds = get_include_path_extent(line_text) if bounds is not None: start_col, end_col = bounds # Don't allow hyperclick if cursor is not over the include name, i.e.: # #include "header.h" # ^^^^^^^^ if column < start_col or column > end_col: return None filename = resolve_include(absolute_path, line_text, flags) if filename is None: return None # Point location to beginning of the found included file (line 0, column 0) location = { 'file': filename, 'point': { 'row': 0, 'column': 0, }, # Show destination file of hyperclick in hover popover 'type': filename, 'spelling': None, 'extent': { 'start': {'row': line - 1, 'column': start_col}, 'end': {'row': line - 1, 'column': end_col} } } return location else: log('No referenced information') return None loc = referenced.location log('Returning {0}:{1}:{2}'.format( os.path.basename(loc.file.name), loc.line, loc.column)) # An extent has a `start` and `end` property, each of which have a `line` # and `column` property. extent = cursor.extent type = None try: type = cursor.type and cursor.type.spelling except: logger.warn('Was not able to get cursor type') pass location = location_dict(loc) location['spelling'] = cursor.spelling location['type'] = type location['extent'] = range_dict_relative(extent) return location
def cursor(self): """ Get a Cursor object for TextMate's current cursor point (file,line,column). """ return Cursor.from_location(self.tu, self.tu.get_location(env['TM_FILEPATH'], ( int(env['TM_LINE_NUMBER']), int(env['TM_COLUMN_NUMBER'])-1 ) ))