Index: bindings/python/clang/cindex.py =================================================================== --- bindings/python/clang/cindex.py +++ bindings/python/clang/cindex.py @@ -211,25 +211,45 @@ assert isinstance(res, _CXString) return conf.lib.clang_getCString(res) +class Location(object): + """A Location is a specific kind of source location. A SourceLocation + refers to several kinds of locations (e.g. spelling location vs. expansion + location).""" + + def __init__(self, file, line, column, offset): + self._file = File(file) if file else None + self._line = int(line.value) + self._column = int(column.value) + self._offset = int(offset.value) + + + @property + def file(self): + """Get the file represented by this source location.""" + return self._file + + @property + def line(self): + """Get the line represented by this source location.""" + return self._line + + @property + def column(self): + """Get the column represented by this source location.""" + return self._column + + @property + def offset(self): + """Get the file offset represented by this source location.""" + return self._offset class SourceLocation(Structure): """ A SourceLocation represents a particular location within a source file. """ _fields_ = [("ptr_data", c_void_p * 2), ("int_data", c_uint)] - _data = None - - def _get_instantiation(self): - if self._data is None: - f, l, c, o = c_object_p(), c_uint(), c_uint(), c_uint() - conf.lib.clang_getInstantiationLocation(self, byref(f), byref(l), - byref(c), byref(o)) - if f: - f = File(f) - else: - f = None - self._data = (f, int(l.value), int(c.value), int(o.value)) - return self._data + _expansion = None + _spelling = None @staticmethod def from_position(tu, file, line, column): @@ -249,25 +269,73 @@ """ return conf.lib.clang_getLocationForOffset(tu, file, offset) + @property + def expansion(self): + """ + The source location where then entity this object is referring to is + expanded. + """ + if not self._expansion: + file = c_object_p() + line = c_uint() + column = c_uint() + offset = c_uint() + conf.lib.clang_getExpansionLocation(self, + byref(file), + byref(line), + byref(column), + byref(offset)) + + self._expansion = Location(file, line, column, offset) + return self._expansion + + @property + def spelling(self): + """ + The source location where then entity this object is referring to is + written. + """ + if not self._spelling: + file = c_object_p() + line = c_uint() + column = c_uint() + offset = c_uint() + conf.lib.clang_getSpellingLocation(self, + byref(file), + byref(line), + byref(column), + byref(offset)) + + self._spelling = Location(file, line, column, offset) + return self._spelling + @property def file(self): - """Get the file represented by this source location.""" - return self._get_instantiation()[0] + """Get the file represented by this source location. + + DEPRECATED: use expansion.file.""" + return self.expansion.file @property def line(self): - """Get the line represented by this source location.""" - return self._get_instantiation()[1] + """Get the line represented by this source location. + + DEPRECATED: use expansion.line.""" + return self.expansion.line @property def column(self): - """Get the column represented by this source location.""" - return self._get_instantiation()[2] + """Get the column represented by this source location. + + DEPRECATED: use expansion.column.""" + return self.expansion.column @property def offset(self): - """Get the file offset represented by this source location.""" - return self._get_instantiation()[3] + """Get the file offset represented by this source location. + + DEPRECATED: use expansion.offset.""" + return self.expansion.offset def __eq__(self, other): return conf.lib.clang_equalLocations(self, other) @@ -1540,8 +1608,7 @@ @property def location(self): """ - Return the source location (the starting character) of the entity - pointed at by the cursor. + Return the source locations of the entity pointed at by the cursor. """ if not hasattr(self, '_loc'): self._loc = conf.lib.clang_getCursorLocation(self) @@ -3629,7 +3696,11 @@ ("clang_getInclusions", [TranslationUnit, callbacks['translation_unit_includes'], py_object]), - ("clang_getInstantiationLocation", + ("clang_getExpansionLocation", + [SourceLocation, POINTER(c_object_p), POINTER(c_uint), POINTER(c_uint), + POINTER(c_uint)]), + + ("clang_getSpellingLocation", [SourceLocation, POINTER(c_object_p), POINTER(c_uint), POINTER(c_uint), POINTER(c_uint)]), Index: bindings/python/tests/cindex/test_location.py =================================================================== --- bindings/python/tests/cindex/test_location.py +++ bindings/python/tests/cindex/test_location.py @@ -93,3 +93,10 @@ location3 = SourceLocation.from_position(tu, file, 1, 6) range3 = SourceRange.from_locations(location1, location3) assert range1 != range3 + +def test_spelling_location(): + tu = get_tu('''#define ONE int one +ONE;''') + one = get_cursor(tu, 'one') + assert one.location.spelling.line == 1 + assert one.location.expansion.line == 2 Index: tools/libclang/CXSourceLocation.cpp =================================================================== --- tools/libclang/CXSourceLocation.cpp +++ tools/libclang/CXSourceLocation.cpp @@ -318,8 +318,7 @@ const SourceManager &SM = *static_cast(location.ptr_data[0]); - // FIXME: This should call SourceManager::getSpellingLoc(). - SourceLocation SpellLoc = SM.getFileLoc(Loc); + SourceLocation SpellLoc = SM.getSpellingLoc(Loc); std::pair LocInfo = SM.getDecomposedLoc(SpellLoc); FileID FID = LocInfo.first; unsigned FileOffset = LocInfo.second;