testsemanal.py 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. """Semantic analyzer test cases"""
  2. from __future__ import annotations
  3. import sys
  4. from typing import Dict
  5. from mypy import build
  6. from mypy.defaults import PYTHON3_VERSION
  7. from mypy.errors import CompileError
  8. from mypy.modulefinder import BuildSource
  9. from mypy.nodes import TypeInfo
  10. from mypy.options import TYPE_VAR_TUPLE, UNPACK, Options
  11. from mypy.test.config import test_temp_dir
  12. from mypy.test.data import DataDrivenTestCase, DataSuite
  13. from mypy.test.helpers import (
  14. assert_string_arrays_equal,
  15. find_test_files,
  16. normalize_error_messages,
  17. parse_options,
  18. testfile_pyversion,
  19. )
  20. # Semantic analyzer test cases: dump parse tree
  21. # Semantic analysis test case description files.
  22. semanal_files = find_test_files(
  23. pattern="semanal-*.test",
  24. exclude=[
  25. "semanal-errors-python310.test",
  26. "semanal-errors.test",
  27. "semanal-typeinfo.test",
  28. "semanal-symtable.test",
  29. ],
  30. )
  31. if sys.version_info < (3, 10):
  32. semanal_files.remove("semanal-python310.test")
  33. def get_semanal_options(program_text: str, testcase: DataDrivenTestCase) -> Options:
  34. options = parse_options(program_text, testcase, 1)
  35. options.use_builtins_fixtures = True
  36. options.semantic_analysis_only = True
  37. options.show_traceback = True
  38. options.python_version = PYTHON3_VERSION
  39. options.enable_incomplete_feature = [TYPE_VAR_TUPLE, UNPACK]
  40. options.force_uppercase_builtins = True
  41. return options
  42. class SemAnalSuite(DataSuite):
  43. files = semanal_files
  44. native_sep = True
  45. def run_case(self, testcase: DataDrivenTestCase) -> None:
  46. test_semanal(testcase)
  47. def test_semanal(testcase: DataDrivenTestCase) -> None:
  48. """Perform a semantic analysis test case.
  49. The testcase argument contains a description of the test case
  50. (inputs and output).
  51. """
  52. try:
  53. src = "\n".join(testcase.input)
  54. options = get_semanal_options(src, testcase)
  55. options.python_version = testfile_pyversion(testcase.file)
  56. result = build.build(
  57. sources=[BuildSource("main", None, src)], options=options, alt_lib_path=test_temp_dir
  58. )
  59. a = result.errors
  60. if a:
  61. raise CompileError(a)
  62. # Include string representations of the source files in the actual
  63. # output.
  64. for module in sorted(result.files.keys()):
  65. if module in testcase.test_modules:
  66. a += result.files[module].str_with_options(options).split("\n")
  67. except CompileError as e:
  68. a = e.messages
  69. if testcase.normalize_output:
  70. a = normalize_error_messages(a)
  71. assert_string_arrays_equal(
  72. testcase.output,
  73. a,
  74. f"Invalid semantic analyzer output ({testcase.file}, line {testcase.line})",
  75. )
  76. # Semantic analyzer error test cases
  77. class SemAnalErrorSuite(DataSuite):
  78. files = ["semanal-errors.test"]
  79. if sys.version_info >= (3, 10):
  80. semanal_files.append("semanal-errors-python310.test")
  81. def run_case(self, testcase: DataDrivenTestCase) -> None:
  82. test_semanal_error(testcase)
  83. def test_semanal_error(testcase: DataDrivenTestCase) -> None:
  84. """Perform a test case."""
  85. try:
  86. src = "\n".join(testcase.input)
  87. res = build.build(
  88. sources=[BuildSource("main", None, src)],
  89. options=get_semanal_options(src, testcase),
  90. alt_lib_path=test_temp_dir,
  91. )
  92. a = res.errors
  93. except CompileError as e:
  94. # Verify that there was a compile error and that the error messages
  95. # are equivalent.
  96. a = e.messages
  97. if testcase.normalize_output:
  98. a = normalize_error_messages(a)
  99. assert_string_arrays_equal(
  100. testcase.output, a, f"Invalid compiler output ({testcase.file}, line {testcase.line})"
  101. )
  102. # SymbolNode table export test cases
  103. class SemAnalSymtableSuite(DataSuite):
  104. required_out_section = True
  105. files = ["semanal-symtable.test"]
  106. def run_case(self, testcase: DataDrivenTestCase) -> None:
  107. """Perform a test case."""
  108. try:
  109. # Build test case input.
  110. src = "\n".join(testcase.input)
  111. result = build.build(
  112. sources=[BuildSource("main", None, src)],
  113. options=get_semanal_options(src, testcase),
  114. alt_lib_path=test_temp_dir,
  115. )
  116. # The output is the symbol table converted into a string.
  117. a = result.errors
  118. if a:
  119. raise CompileError(a)
  120. for module in sorted(result.files.keys()):
  121. if module in testcase.test_modules:
  122. a.append(f"{module}:")
  123. for s in str(result.files[module].names).split("\n"):
  124. a.append(" " + s)
  125. except CompileError as e:
  126. a = e.messages
  127. assert_string_arrays_equal(
  128. testcase.output,
  129. a,
  130. f"Invalid semantic analyzer output ({testcase.file}, line {testcase.line})",
  131. )
  132. # Type info export test cases
  133. class SemAnalTypeInfoSuite(DataSuite):
  134. required_out_section = True
  135. files = ["semanal-typeinfo.test"]
  136. def run_case(self, testcase: DataDrivenTestCase) -> None:
  137. """Perform a test case."""
  138. try:
  139. # Build test case input.
  140. src = "\n".join(testcase.input)
  141. result = build.build(
  142. sources=[BuildSource("main", None, src)],
  143. options=get_semanal_options(src, testcase),
  144. alt_lib_path=test_temp_dir,
  145. )
  146. a = result.errors
  147. if a:
  148. raise CompileError(a)
  149. # Collect all TypeInfos in top-level modules.
  150. typeinfos = TypeInfoMap()
  151. for module, file in result.files.items():
  152. if module in testcase.test_modules:
  153. for n in file.names.values():
  154. if isinstance(n.node, TypeInfo):
  155. assert n.fullname
  156. if any(n.fullname.startswith(m + ".") for m in testcase.test_modules):
  157. typeinfos[n.fullname] = n.node
  158. # The output is the symbol table converted into a string.
  159. a = str(typeinfos).split("\n")
  160. except CompileError as e:
  161. a = e.messages
  162. assert_string_arrays_equal(
  163. testcase.output,
  164. a,
  165. f"Invalid semantic analyzer output ({testcase.file}, line {testcase.line})",
  166. )
  167. class TypeInfoMap(Dict[str, TypeInfo]):
  168. def __str__(self) -> str:
  169. a: list[str] = ["TypeInfoMap("]
  170. for x, y in sorted(self.items()):
  171. ti = ("\n" + " ").join(str(y).split("\n"))
  172. a.append(f" {x} : {ti}")
  173. a[-1] += ")"
  174. return "\n".join(a)