diff --git a/clang/bindings/python/clang/cindex.py b/clang/bindings/python/clang/cindex.py --- a/clang/bindings/python/clang/cindex.py +++ b/clang/bindings/python/clang/cindex.py @@ -1419,6 +1419,20 @@ """ _fields_ = [("_kind_id", c_int), ("xdata", c_int), ("data", c_void_p * 3)] + # Include the nested-name-specifier, e.g. Foo:: in x.Foo::y, in the range. + NAME_RANGE_WANT_QUALIFIER = 0x1 + + # Include the explicit template arguments, e.g. in x.f, in the range. + NAME_RANGE_WANT_TEMPLATE_ARGS = 0x2 + + # If the name is non-contiguous, return the full spanning range. + # Non-contiguous names occur in Objective-C when a selector with two or + # more parameters is used, or in C++ when using an operator: + # + # [object doSomething:here withValue:there]; // Objective-C + # return some_vector[1]; // C++ + NAME_RANGE_WANT_SINGLE_PIECE = 0x4 + @staticmethod def from_location(tu, location): # We store a reference to the TU in the instance so the TU won't get @@ -1824,6 +1838,20 @@ """Returns the value of the indicated arg as an unsigned 64b integer.""" return conf.lib.clang_Cursor_getTemplateArgumentUnsignedValue(self, num) + def get_reference_name_range(self, name_flags, piece_index): + """Return the source range covering the reference at this cursor, if applicable. + + name_flags is a bitset of the three independent flags: NAME_RANGE_WANT_QUALIFIER, + NAME_RANGE_WANT_TEMPLATE_ARGS, NAME_RANGE_WANT_SINGLE_PIECE. + + piece_index identifies a piece of the overall reference. For contiguous names + or when passing the flag NAME_RANGE_WANT_SINGLE_PIECE, only one piece with index + 0 is available. When the NAME_RANGE_WANT_SINGLE_PIECE flag is not passed for a + non-contiguous names, this index can be used to retrieve the individual pieces + of the name. + """ + return conf.lib.clang_getCursorReferenceNameRange(self, name_flags, piece_index) + def get_children(self): """Return an iterator for accessing the children of this cursor.""" diff --git a/clang/bindings/python/tests/cindex/test_cursor.py b/clang/bindings/python/tests/cindex/test_cursor.py --- a/clang/bindings/python/tests/cindex/test_cursor.py +++ b/clang/bindings/python/tests/cindex/test_cursor.py @@ -9,10 +9,14 @@ from clang.cindex import AvailabilityKind from clang.cindex import CursorKind +from clang.cindex import Cursor from clang.cindex import TemplateArgumentKind from clang.cindex import TranslationUnit from clang.cindex import TypeKind +from clang.cindex import SourceRange +from clang.cindex import SourceLocation from .util import get_cursor +from .util import get_subcursor_by_path from .util import get_cursors from .util import get_tu @@ -53,6 +57,37 @@ void foo<-7, float, true>(); """ +kCPPReferenceNameRangeTest = """\ +void baz(); + +class C { +public: + void foo(); + template + void bar(T v) { } + int operator[](int i) { return i; } +}; + +void ref1() { + baz(); +} + +void ref2() { + C x; + x.C::foo(); +} + +void ref3() { + C x; + x.bar("str"); +} + +void ref4() { + C x; + x[1]; +} + """ + class TestCursor(unittest.TestCase): def test_get_children(self): tu = get_tu(kInput) @@ -554,6 +589,30 @@ self.assertEqual(c.referenced.spelling, foo.spelling) break + def test_get_reference_name_range(self): + tu = get_tu(kCPPReferenceNameRangeTest, lang='cpp') + + c = get_subcursor_by_path(get_cursor(tu, 'ref1'), 0, 0) + self.assertEqual(c.get_reference_name_range(0, 0), + tu.get_extent("t.cpp", ((12, 5), (12, 10)))) + + c = get_subcursor_by_path(get_cursor(tu, 'ref2'), 0, 1) + self.assertEqual(c.get_reference_name_range(Cursor.NAME_RANGE_WANT_QUALIFIER, 0), + tu.get_extent("t.cpp", ((17, 5), (17, 15)))) + + c = get_subcursor_by_path(get_cursor(tu, 'ref3'), 0, 1) + self.assertEqual(c.get_reference_name_range(Cursor.NAME_RANGE_WANT_TEMPLATE_ARGS, 0), + tu.get_extent("t.cpp", ((22, 5), (22, 30)))) + + c = get_subcursor_by_path(get_cursor(tu, 'ref4'), 0, 1) + self.assertEqual(c.get_reference_name_range(0, 0), + tu.get_extent("t.cpp", ((27, 6), (27, 7)))) + self.assertEqual(c.get_reference_name_range(0, 1), + tu.get_extent("t.cpp", ((27, 8), (27, 9)))) + self.assertEqual(c.get_reference_name_range(Cursor.NAME_RANGE_WANT_SINGLE_PIECE, 0), + tu.get_extent("t.cpp", ((27, 6), (27, 9)))) + + def test_mangled_name(self): kInputForMangling = """\ int foo(int, int); diff --git a/clang/bindings/python/tests/cindex/util.py b/clang/bindings/python/tests/cindex/util.py --- a/clang/bindings/python/tests/cindex/util.py +++ b/clang/bindings/python/tests/cindex/util.py @@ -58,6 +58,18 @@ return None + +def get_subcursor_by_path(cursor, *path): + """Obtain a cursor by following the path provided from cursor. + + path is a series of ints specifying which child to select. + """ + + if path: + return get_subcursor_by_path(list(cursor.get_children())[path[0]], *path[1:]) + else: + return cursor + def get_cursors(source, spelling): """Obtain all cursors from a source object with a specific spelling. @@ -84,6 +96,7 @@ __all__ = [ 'get_cursor', 'get_cursors', + 'get_subcursor_by_path', 'get_tu', 'skip_if_no_fspath', 'str_to_path',