[cfe-commits] [PATCH 1/2] [clang.py] Add TranslationUnit.get_{file, source_location, source_range}
Gregory Szorc
gregory.szorc at gmail.com
Sun Jul 8 23:27:07 PDT 2012
On 7/1/12 11:03 PM, Manuel Klimek wrote:
> High-level remark:
> The idea of having multiple different parameters of which only one
> must ever be set seems strange to me. Why not instead have multiple
> methods with different names? I'd also prefer having 1 parameter and
> finding out what methods it provides.
>
Changed API to single argument with type detection. I don't like having
multiple methods with different names because that feels too "Java-y"
for me. I prefer a single method that just works and accepts whatever is
thrown at it.
>
> On Sat, Jun 30, 2012 at 4:15 AM, Gregory Szorc
> <gregory.szorc at gmail.com <mailto:gregory.szorc at gmail.com>> wrote:
>
> Updated patch.
>
> On Fri, Jun 29, 2012 at 8:47 AM, Gregory Szorc
> <gregory.szorc at gmail.com <mailto:gregory.szorc at gmail.com>> wrote:
> > Having thought about this in my sleep, I may want to rescind this
> > review request and refactor things a little.
> >
> > 1) I may change "range" and "source_range" names to "extent" since
> > that is what is used elsewhere.
> > 2) I may combine the bounds to obtain ranges/extents from two
> > arguments to 2-tuples.
> > 3) I may remove the "_source" from get_source_location and
> > get_source_extent. I don't think that's any less clear.
> >
> > This will of course invalidate the token API patch that followed,
> > albeit trivially.
> >
> > On Fri, Jun 29, 2012 at 12:13 AM, Gregory Szorc
> <gregory.szorc at gmail.com <mailto:gregory.szorc at gmail.com>> wrote:
> >> 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)
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20120708/e7127fb9/attachment.html>
-------------- next part --------------
>From 9caaae34db916a7ef8a4ea6017ec25dc9a1a4c84 Mon Sep 17 00:00:00 2001
From: Gregory Szorc <gregory.szorc at gmail.com>
Date: Thu, 28 Jun 2012 23:48:12 -0700
Subject: [PATCH 2/3] [clang.py] Add
TranslationUnit.get_{file,source_location,source_range}
---
bindings/python/clang/cindex.py | 61 ++++++++++++++++++++
.../python/tests/cindex/test_translation_unit.py | 66 ++++++++++++++++++++++
2 files changed, 127 insertions(+)
diff --git a/bindings/python/clang/cindex.py b/bindings/python/clang/cindex.py
index b21eedb..2d0c677 100644
--- a/bindings/python/clang/cindex.py
+++ b/bindings/python/clang/cindex.py
@@ -1890,16 +1890,77 @@ 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_location(self, filename, position):
+ """Obtain a SourceLocation for a file in this translation unit.
+
+ The position can be specified by passing:
+
+ - Integer file offset. Initial file offset is 0.
+ - 2-tuple of (line number, column number). Initial file position is
+ (0, 0)
+ """
+ f = self.get_file(filename)
+
+ if isinstance(position, int):
+ return SourceLocation.from_offset(self, f, position)
+
+ return SourceLocation.from_position(self, f, position[0], position[1])
+
+ def get_extent(self, filename, locations):
+ """Obtain a SourceRange from this translation unit.
+
+ The bounds of the SourceRange must ultimately be defined by a start and
+ end SourceLocation. For the locations argument, you can pass:
+
+ - 2 SourceLocation instances in a 2-tuple or list.
+ - 2 int file offsets via a 2-tuple or list.
+ - 2 2-tuple or lists of (line, column) pairs in a 2-tuple or list.
+
+ e.g.
+
+ get_extent('foo.c', (5, 10))
+ get_extent('foo.c', ((1, 1), (1, 15)))
+ """
+ f = self.get_file(filename)
+
+ if len(locations) < 2:
+ raise Exception('Must pass object with at least 2 elements')
+
+ start_location, end_location = locations
+
+ if hasattr(start_location, '__len__'):
+ start_location = SourceLocation.from_position(self, f,
+ start_location[0], start_location[1])
+ elif isinstance(start_location, int):
+ start_location = SourceLocation.from_offset(self, f,
+ start_location)
+
+ if hasattr(end_location, '__len__'):
+ end_location = SourceLocation.from_position(self, f,
+ end_location[0], end_location[1])
+ elif isinstance(end_location, int):
+ end_location = SourceLocation.from_offset(self, f, end_location)
+
+ assert isinstance(start_location, SourceLocation)
+ assert isinstance(end_location, SourceLocation)
+
+ 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..9de12ad 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_location('t.c', 2)
+ assert isinstance(location, SourceLocation)
+ assert location.offset == 2
+ assert location.file.name == 't.c'
+
+ location = tu.get_location('t.c', (1, 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_extent('t.c', (1,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_extent('t.c', ((1,2), (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_location('t.c', 0)
+ end = tu.get_location('t.c', 5)
+
+ r = tu.get_extent('t.c', (start, 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'
--
1.7.11.1
More information about the cfe-commits
mailing list