Run DCE after a LoopFlatten test to reduce spurious output [nfc]
[llvm-project.git] / clang / bindings / python / tests / cindex / test_translation_unit.py
blobff7213c69dd0f17a6f7fedc3bf0a46d52d6801b1
1 import os
2 from clang.cindex import Config
4 if "CLANG_LIBRARY_PATH" in os.environ:
5 Config.set_library_path(os.environ["CLANG_LIBRARY_PATH"])
7 from contextlib import contextmanager
8 import gc
9 import os
10 import sys
11 import tempfile
12 import unittest
14 from clang.cindex import CursorKind
15 from clang.cindex import Cursor
16 from clang.cindex import File
17 from clang.cindex import Index
18 from clang.cindex import SourceLocation
19 from clang.cindex import SourceRange
20 from clang.cindex import TranslationUnitSaveError
21 from clang.cindex import TranslationUnitLoadError
22 from clang.cindex import TranslationUnit
23 from .util import get_cursor
24 from .util import get_tu
25 from .util import skip_if_no_fspath
26 from .util import str_to_path
29 kInputsDir = os.path.join(os.path.dirname(__file__), "INPUTS")
32 @contextmanager
33 def save_tu(tu):
34 """Convenience API to save a TranslationUnit to a file.
36 Returns the filename it was saved to.
37 """
38 with tempfile.NamedTemporaryFile() as t:
39 tu.save(t.name)
40 yield t.name
43 @contextmanager
44 def save_tu_pathlike(tu):
45 """Convenience API to save a TranslationUnit to a file.
47 Returns the filename it was saved to.
48 """
49 with tempfile.NamedTemporaryFile() as t:
50 tu.save(str_to_path(t.name))
51 yield t.name
54 class TestTranslationUnit(unittest.TestCase):
55 def test_spelling(self):
56 path = os.path.join(kInputsDir, "hello.cpp")
57 tu = TranslationUnit.from_source(path)
58 self.assertEqual(tu.spelling, path)
60 def test_cursor(self):
61 path = os.path.join(kInputsDir, "hello.cpp")
62 tu = get_tu(path)
63 c = tu.cursor
64 self.assertIsInstance(c, Cursor)
65 self.assertIs(c.kind, CursorKind.TRANSLATION_UNIT)
67 def test_parse_arguments(self):
68 path = os.path.join(kInputsDir, "parse_arguments.c")
69 tu = TranslationUnit.from_source(path, ["-DDECL_ONE=hello", "-DDECL_TWO=hi"])
70 spellings = [c.spelling for c in tu.cursor.get_children()]
71 self.assertEqual(spellings[-2], "hello")
72 self.assertEqual(spellings[-1], "hi")
74 def test_reparse_arguments(self):
75 path = os.path.join(kInputsDir, "parse_arguments.c")
76 tu = TranslationUnit.from_source(path, ["-DDECL_ONE=hello", "-DDECL_TWO=hi"])
77 tu.reparse()
78 spellings = [c.spelling for c in tu.cursor.get_children()]
79 self.assertEqual(spellings[-2], "hello")
80 self.assertEqual(spellings[-1], "hi")
82 def test_unsaved_files(self):
83 tu = TranslationUnit.from_source(
84 "fake.c",
85 ["-I./"],
86 unsaved_files=[
88 "fake.c",
89 """
90 #include "fake.h"
91 int x;
92 int SOME_DEFINE;
93 """,
96 "./fake.h",
97 """
98 #define SOME_DEFINE y
99 """,
103 spellings = [c.spelling for c in tu.cursor.get_children()]
104 self.assertEqual(spellings[-2], "x")
105 self.assertEqual(spellings[-1], "y")
107 def test_unsaved_files_2(self):
108 if sys.version_info.major >= 3:
109 from io import StringIO
110 else:
111 from io import BytesIO as StringIO
112 tu = TranslationUnit.from_source(
113 "fake.c", unsaved_files=[("fake.c", StringIO("int x;"))]
115 spellings = [c.spelling for c in tu.cursor.get_children()]
116 self.assertEqual(spellings[-1], "x")
118 @skip_if_no_fspath
119 def test_from_source_accepts_pathlike(self):
120 tu = TranslationUnit.from_source(
121 str_to_path("fake.c"),
122 ["-Iincludes"],
123 unsaved_files=[
125 str_to_path("fake.c"),
127 #include "fake.h"
128 int x;
129 int SOME_DEFINE;
130 """,
133 str_to_path("includes/fake.h"),
135 #define SOME_DEFINE y
136 """,
140 spellings = [c.spelling for c in tu.cursor.get_children()]
141 self.assertEqual(spellings[-2], "x")
142 self.assertEqual(spellings[-1], "y")
144 def assert_normpaths_equal(self, path1, path2):
145 """Compares two paths for equality after normalizing them with
146 os.path.normpath
148 self.assertEqual(os.path.normpath(path1), os.path.normpath(path2))
150 def test_includes(self):
151 def eq(expected, actual):
152 if not actual.is_input_file:
153 self.assert_normpaths_equal(expected[0], actual.source.name)
154 self.assert_normpaths_equal(expected[1], actual.include.name)
155 else:
156 self.assert_normpaths_equal(expected[1], actual.include.name)
158 src = os.path.join(kInputsDir, "include.cpp")
159 h1 = os.path.join(kInputsDir, "header1.h")
160 h2 = os.path.join(kInputsDir, "header2.h")
161 h3 = os.path.join(kInputsDir, "header3.h")
162 inc = [(src, h1), (h1, h3), (src, h2), (h2, h3)]
164 tu = TranslationUnit.from_source(src)
165 for i in zip(inc, tu.get_includes()):
166 eq(i[0], i[1])
168 def test_inclusion_directive(self):
169 src = os.path.join(kInputsDir, "include.cpp")
170 h1 = os.path.join(kInputsDir, "header1.h")
171 h2 = os.path.join(kInputsDir, "header2.h")
172 h3 = os.path.join(kInputsDir, "header3.h")
173 inc = [h1, h3, h2, h3, h1]
175 tu = TranslationUnit.from_source(
176 src, options=TranslationUnit.PARSE_DETAILED_PROCESSING_RECORD
178 inclusion_directive_files = [
179 c.get_included_file().name
180 for c in tu.cursor.get_children()
181 if c.kind == CursorKind.INCLUSION_DIRECTIVE
183 for i in zip(inc, inclusion_directive_files):
184 self.assert_normpaths_equal(i[0], i[1])
186 def test_save(self):
187 """Ensure TranslationUnit.save() works."""
189 tu = get_tu("int foo();")
191 with save_tu(tu) as path:
192 self.assertTrue(os.path.exists(path))
193 self.assertGreater(os.path.getsize(path), 0)
195 @skip_if_no_fspath
196 def test_save_pathlike(self):
197 """Ensure TranslationUnit.save() works with PathLike filename."""
199 tu = get_tu("int foo();")
201 with save_tu_pathlike(tu) as path:
202 self.assertTrue(os.path.exists(path))
203 self.assertGreater(os.path.getsize(path), 0)
205 def test_save_translation_errors(self):
206 """Ensure that saving to an invalid directory raises."""
208 tu = get_tu("int foo();")
210 path = "/does/not/exist/llvm-test.ast"
211 self.assertFalse(os.path.exists(os.path.dirname(path)))
213 with self.assertRaises(TranslationUnitSaveError) as cm:
214 tu.save(path)
215 ex = cm.exception
216 expected = TranslationUnitSaveError.ERROR_UNKNOWN
217 self.assertEqual(ex.save_error, expected)
219 def test_load(self):
220 """Ensure TranslationUnits can be constructed from saved files."""
222 tu = get_tu("int foo();")
223 self.assertEqual(len(tu.diagnostics), 0)
224 with save_tu(tu) as path:
225 self.assertTrue(os.path.exists(path))
226 self.assertGreater(os.path.getsize(path), 0)
228 tu2 = TranslationUnit.from_ast_file(filename=path)
229 self.assertEqual(len(tu2.diagnostics), 0)
231 foo = get_cursor(tu2, "foo")
232 self.assertIsNotNone(foo)
234 # Just in case there is an open file descriptor somewhere.
235 del tu2
237 @skip_if_no_fspath
238 def test_load_pathlike(self):
239 """Ensure TranslationUnits can be constructed from saved files -
240 PathLike variant."""
241 tu = get_tu("int foo();")
242 self.assertEqual(len(tu.diagnostics), 0)
243 with save_tu(tu) as path:
244 tu2 = TranslationUnit.from_ast_file(filename=str_to_path(path))
245 self.assertEqual(len(tu2.diagnostics), 0)
247 foo = get_cursor(tu2, "foo")
248 self.assertIsNotNone(foo)
250 # Just in case there is an open file descriptor somewhere.
251 del tu2
253 def test_index_parse(self):
254 path = os.path.join(kInputsDir, "hello.cpp")
255 index = Index.create()
256 tu = index.parse(path)
257 self.assertIsInstance(tu, TranslationUnit)
259 def test_get_file(self):
260 """Ensure tu.get_file() works appropriately."""
262 tu = get_tu("int foo();")
264 f = tu.get_file("t.c")
265 self.assertIsInstance(f, File)
266 self.assertEqual(f.name, "t.c")
268 with self.assertRaises(Exception):
269 f = tu.get_file("foobar.cpp")
271 @skip_if_no_fspath
272 def test_get_file_pathlike(self):
273 """Ensure tu.get_file() works appropriately with PathLike filenames."""
275 tu = get_tu("int foo();")
277 f = tu.get_file(str_to_path("t.c"))
278 self.assertIsInstance(f, File)
279 self.assertEqual(f.name, "t.c")
281 with self.assertRaises(Exception):
282 f = tu.get_file(str_to_path("foobar.cpp"))
284 def test_get_source_location(self):
285 """Ensure tu.get_source_location() works."""
287 tu = get_tu("int foo();")
289 location = tu.get_location("t.c", 2)
290 self.assertIsInstance(location, SourceLocation)
291 self.assertEqual(location.offset, 2)
292 self.assertEqual(location.file.name, "t.c")
294 location = tu.get_location("t.c", (1, 3))
295 self.assertIsInstance(location, SourceLocation)
296 self.assertEqual(location.line, 1)
297 self.assertEqual(location.column, 3)
298 self.assertEqual(location.file.name, "t.c")
300 def test_get_source_range(self):
301 """Ensure tu.get_source_range() works."""
303 tu = get_tu("int foo();")
305 r = tu.get_extent("t.c", (1, 4))
306 self.assertIsInstance(r, SourceRange)
307 self.assertEqual(r.start.offset, 1)
308 self.assertEqual(r.end.offset, 4)
309 self.assertEqual(r.start.file.name, "t.c")
310 self.assertEqual(r.end.file.name, "t.c")
312 r = tu.get_extent("t.c", ((1, 2), (1, 3)))
313 self.assertIsInstance(r, SourceRange)
314 self.assertEqual(r.start.line, 1)
315 self.assertEqual(r.start.column, 2)
316 self.assertEqual(r.end.line, 1)
317 self.assertEqual(r.end.column, 3)
318 self.assertEqual(r.start.file.name, "t.c")
319 self.assertEqual(r.end.file.name, "t.c")
321 start = tu.get_location("t.c", 0)
322 end = tu.get_location("t.c", 5)
324 r = tu.get_extent("t.c", (start, end))
325 self.assertIsInstance(r, SourceRange)
326 self.assertEqual(r.start.offset, 0)
327 self.assertEqual(r.end.offset, 5)
328 self.assertEqual(r.start.file.name, "t.c")
329 self.assertEqual(r.end.file.name, "t.c")
331 def test_get_tokens_gc(self):
332 """Ensures get_tokens() works properly with garbage collection."""
334 tu = get_tu("int foo();")
335 r = tu.get_extent("t.c", (0, 10))
336 tokens = list(tu.get_tokens(extent=r))
338 self.assertEqual(tokens[0].spelling, "int")
339 gc.collect()
340 self.assertEqual(tokens[0].spelling, "int")
342 del tokens[1]
343 gc.collect()
344 self.assertEqual(tokens[0].spelling, "int")
346 # May trigger segfault if we don't do our job properly.
347 del tokens
348 gc.collect()
349 gc.collect() # Just in case.
351 def test_fail_from_source(self):
352 path = os.path.join(kInputsDir, "non-existent.cpp")
353 try:
354 tu = TranslationUnit.from_source(path)
355 except TranslationUnitLoadError:
356 tu = None
357 self.assertEqual(tu, None)
359 def test_fail_from_ast_file(self):
360 path = os.path.join(kInputsDir, "non-existent.ast")
361 try:
362 tu = TranslationUnit.from_ast_file(path)
363 except TranslationUnitLoadError:
364 tu = None
365 self.assertEqual(tu, None)