xref: /llvm-project/clang/bindings/python/tests/cindex/test_location.py (revision 71cfa381ef8c4fe659c67e8b2901d767e10f2aff)
1import os
2
3from clang.cindex import (
4    Config,
5    Cursor,
6    File,
7    SourceLocation,
8    SourceRange,
9    TranslationUnit,
10)
11
12if "CLANG_LIBRARY_PATH" in os.environ:
13    Config.set_library_path(os.environ["CLANG_LIBRARY_PATH"])
14
15import unittest
16
17from .util import get_cursor, get_tu
18
19baseInput = "int one;\nint two;\n"
20
21
22class TestLocation(unittest.TestCase):
23    def assert_location(self, loc, line, column, offset):
24        self.assertEqual(loc.line, line)
25        self.assertEqual(loc.column, column)
26        self.assertEqual(loc.offset, offset)
27
28    def test_location(self):
29        tu = get_tu(baseInput)
30        one = get_cursor(tu, "one")
31        two = get_cursor(tu, "two")
32
33        self.assertIsNotNone(one)
34        self.assertIsNotNone(two)
35
36        self.assert_location(one.location, line=1, column=5, offset=4)
37        self.assert_location(two.location, line=2, column=5, offset=13)
38
39        # adding a linebreak at top should keep columns same
40        tu = get_tu("\n" + baseInput)
41        one = get_cursor(tu, "one")
42        two = get_cursor(tu, "two")
43
44        self.assertIsNotNone(one)
45        self.assertIsNotNone(two)
46
47        self.assert_location(one.location, line=2, column=5, offset=5)
48        self.assert_location(two.location, line=3, column=5, offset=14)
49
50        # adding a space should affect column on first line only
51        tu = get_tu(" " + baseInput)
52        one = get_cursor(tu, "one")
53        two = get_cursor(tu, "two")
54
55        self.assert_location(one.location, line=1, column=6, offset=5)
56        self.assert_location(two.location, line=2, column=5, offset=14)
57
58        # define the expected location ourselves and see if it matches
59        # the returned location
60        tu = get_tu(baseInput)
61
62        file = File.from_name(tu, "t.c")
63        location = SourceLocation.from_position(tu, file, 1, 5)
64        cursor = Cursor.from_location(tu, location)
65
66        one = get_cursor(tu, "one")
67        self.assertIsNotNone(one)
68        self.assertEqual(one, cursor)
69
70        # Ensure locations referring to the same entity are equivalent.
71        location2 = SourceLocation.from_position(tu, file, 1, 5)
72        self.assertEqual(location, location2)
73        location3 = SourceLocation.from_position(tu, file, 1, 4)
74        self.assertNotEqual(location2, location3)
75
76        offset_location = SourceLocation.from_offset(tu, file, 5)
77        cursor = Cursor.from_location(tu, offset_location)
78        verified = False
79        for n in [n for n in tu.cursor.get_children() if n.spelling == "one"]:
80            self.assertEqual(n, cursor)
81            verified = True
82
83        self.assertTrue(verified)
84
85    def test_extent(self):
86        tu = get_tu(baseInput)
87        one = get_cursor(tu, "one")
88        two = get_cursor(tu, "two")
89
90        self.assert_location(one.extent.start, line=1, column=1, offset=0)
91        self.assert_location(one.extent.end, line=1, column=8, offset=7)
92        self.assertEqual(
93            baseInput[one.extent.start.offset : one.extent.end.offset], "int one"
94        )
95
96        self.assert_location(two.extent.start, line=2, column=1, offset=9)
97        self.assert_location(two.extent.end, line=2, column=8, offset=16)
98        self.assertEqual(
99            baseInput[two.extent.start.offset : two.extent.end.offset], "int two"
100        )
101
102        file = File.from_name(tu, "t.c")
103        location1 = SourceLocation.from_position(tu, file, 1, 1)
104        location2 = SourceLocation.from_position(tu, file, 1, 8)
105
106        range1 = SourceRange.from_locations(location1, location2)
107        range2 = SourceRange.from_locations(location1, location2)
108        self.assertEqual(range1, range2)
109
110        location3 = SourceLocation.from_position(tu, file, 1, 6)
111        range3 = SourceRange.from_locations(location1, location3)
112        self.assertNotEqual(range1, range3)
113
114    def test_is_system_location(self):
115        header = os.path.normpath("./fake/fake.h")
116        tu = TranslationUnit.from_source(
117            "fake.c",
118            [f"-isystem{os.path.dirname(header)}"],
119            unsaved_files=[
120                (
121                    "fake.c",
122                    """
123#include <fake.h>
124int one;
125""",
126                ),
127                (header, "int two();"),
128            ],
129        )
130        one = get_cursor(tu, "one")
131        two = get_cursor(tu, "two")
132        self.assertFalse(one.location.is_in_system_header)
133        self.assertTrue(two.location.is_in_system_header)
134
135    def test_operator_lt(self):
136        tu = get_tu("aaaaa")
137        t1_f = tu.get_file(tu.spelling)
138        tu2 = get_tu("aaaaa")
139
140        l_t1_12 = SourceLocation.from_position(tu, t1_f, 1, 2)
141        l_t1_13 = SourceLocation.from_position(tu, t1_f, 1, 3)
142        l_t1_14 = SourceLocation.from_position(tu, t1_f, 1, 4)
143
144        l_t2_13 = SourceLocation.from_position(tu2, tu2.get_file(tu2.spelling), 1, 3)
145
146        # In same file
147        assert l_t1_12 < l_t1_13 < l_t1_14
148        assert not l_t1_13 < l_t1_12
149
150        # In same file, different TU
151        assert l_t1_12 < l_t2_13 < l_t1_14
152        assert not l_t2_13 < l_t1_12
153        assert not l_t1_14 < l_t2_13
154