These are just convenience APIs to make obtaining File,
SourceLocation, and SourceRange instances easier.

Old way:

f = File.from_name(tu, 'foo.c')
location = SourceLocation.from_offset(tu, f, 10)

New way:

location = tu.get_source_location('foo.c', offset=10)
diff --git a/bindings/python/clang/cindex.py b/bindings/python/clang/cindex.py
index 75ab489..329e7ae 100644
--- a/bindings/python/clang/cindex.py
+++ b/bindings/python/clang/cindex.py
@@ -1890,16 +1890,66 @@ class TranslationUnit(ClangObject):
 
         # Automatically adapt CIndex/ctype pointers to python objects
         includes = []
         lib.clang_getInclusions(self,
                 callbacks['translation_unit_includes'](visitor), includes)
 
         return iter(includes)
 
+    def get_file(self, filename):
+        """Obtain a File from this translation unit."""
+
+        return File.from_name(self, filename)
+
+    def get_source_location(self, filename, offset=None, line=None,
+            column=None):
+        """Obtain a SourceLocation for a file in this translation unit.
+
+        The SourceLocation can be specified by either definining the file
+        offset or by a line-column pair.
+        """
+        f = self.get_file(filename)
+
+        if offset is not None:
+            return SourceLocation.from_offset(self, f, offset)
+        else:
+            return SourceLocation.from_position(self, f, line, column)
+
+    def get_source_range(self, filename, start_location=None, end_location=None,
+            start_offset=None, end_offset=None, start_linepos=None,
+            end_linepos=None):
+        """Obtain a SourceRange from this translation unit.
+
+        The bounds of the SourceRange must ultimately be defined by a start and
+        end SourceLocation. You can pass two instances directly via
+        start_location and end_location. You can pass two integer offsets via
+        start_offset and end_offset. Or, you can pass two line-column tuples
+        via start_linepos, end_linepos.
+
+        If multiple types of range bounds are defined, behavior is undefined.
+        """
+        f = self.get_file(filename)
+
+        if start_offset is not None:
+            start_location = SourceLocation.from_offset(self, f, start_offset)
+
+        if end_offset is not None:
+            end_location = SourceLocation.from_offset(self, f, end_offset)
+
+        if start_linepos is not None:
+            start_location = SourceLocation.from_position(self, f,
+                    start_linepos[0], start_linepos[1])
+
+        if end_linepos is not None:
+            end_location = SourceLocation.from_position(self, f,
+                    end_linepos[0], end_linepos[1])
+
+        return SourceRange.from_locations(start_location, end_location)
+
     @property
     def diagnostics(self):
         """
         Return an iterable (and indexable) object containing the diagnostics.
         """
         class DiagIterator:
             def __init__(self, tu):
                 self.tu = tu
diff --git a/bindings/python/tests/cindex/test_translation_unit.py b/bindings/python/tests/cindex/test_translation_unit.py
index 982a608..c883f39 100644
--- a/bindings/python/tests/cindex/test_translation_unit.py
+++ b/bindings/python/tests/cindex/test_translation_unit.py
@@ -1,11 +1,14 @@
 from clang.cindex import CursorKind
 from clang.cindex import Cursor
+from clang.cindex import File
 from clang.cindex import Index
+from clang.cindex import SourceLocation
+from clang.cindex import SourceRange
 from clang.cindex import TranslationUnitSaveError
 from clang.cindex import TranslationUnit
 from .util import get_cursor
 from .util import get_tu
 import os
 
 kInputsDir = os.path.join(os.path.dirname(__file__), 'INPUTS')
 
@@ -146,8 +149,71 @@ def test_load():
 
     os.unlink(path)
 
 def test_index_parse():
     path = os.path.join(kInputsDir, 'hello.cpp')
     index = Index.create()
     tu = index.parse(path)
     assert isinstance(tu, TranslationUnit)
+
+def test_get_file():
+    """Ensure tu.get_file() works appropriately."""
+
+    tu = get_tu('int foo();')
+
+    f = tu.get_file('t.c')
+    assert isinstance(f, File)
+    assert f.name == 't.c'
+
+    try:
+        f = tu.get_file('foobar.cpp')
+    except:
+        pass
+    else:
+        assert False
+
+def test_get_source_location():
+    """Ensure tu.get_source_location() works."""
+
+    tu = get_tu('int foo();')
+
+    location = tu.get_source_location('t.c', offset=2)
+    assert isinstance(location, SourceLocation)
+    assert location.offset == 2
+    assert location.file.name == 't.c'
+
+    location = tu.get_source_location('t.c', line=1, column=3)
+    assert isinstance(location, SourceLocation)
+    assert location.line == 1
+    assert location.column == 3
+    assert location.file.name == 't.c'
+
+def test_get_source_range():
+    """Ensure tu.get_source_range() works."""
+
+    tu = get_tu('int foo();')
+
+    r = tu.get_source_range('t.c', start_offset=1, end_offset=4)
+    assert isinstance(r, SourceRange)
+    assert r.start.offset == 1
+    assert r.end.offset == 4
+    assert r.start.file.name == 't.c'
+    assert r.end.file.name == 't.c'
+
+    r = tu.get_source_range('t.c', start_linepos=(1,2), end_linepos=(1,3))
+    assert isinstance(r, SourceRange)
+    assert r.start.line == 1
+    assert r.start.column == 2
+    assert r.end.line == 1
+    assert r.end.column == 3
+    assert r.start.file.name == 't.c'
+    assert r.end.file.name == 't.c'
+
+    start = tu.get_source_location('t.c', offset=0)
+    end = tu.get_source_location('t.c', offset=5)
+
+    r = tu.get_source_range('t.c', start_location=start, end_location=end)
+    assert isinstance(r, SourceRange)
+    assert r.start.offset == 0
+    assert r.end.offset == 5
+    assert r.start.file.name == 't.c'
+    assert r.end.file.name == 't.c'
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to