|
163 | 163 | '_make', '_replace', '_asdict', '_source',
|
164 | 164 | '__annotations__')
|
165 | 165 |
|
| 166 | +# Map from the full name of a missing definition to the test fixture (under |
| 167 | +# test-data/unit/fixtures/) that provides the definition. This is used for |
| 168 | +# generating better error messages when running mypy tests only. |
| 169 | +SUGGESTED_TEST_FIXTURES = { |
| 170 | + 'typing.List': 'list.pyi', |
| 171 | + 'typing.Dict': 'dict.pyi', |
| 172 | + 'typing.Set': 'set.pyi', |
| 173 | + 'builtins.bool': 'bool.pyi', |
| 174 | + 'builtins.Exception': 'exception.pyi', |
| 175 | + 'builtins.BaseException': 'exception.pyi', |
| 176 | + 'builtins.isinstance': 'isinstancelist.pyi', |
| 177 | + 'builtins.property': 'property.pyi', |
| 178 | + 'builtins.classmethod': 'classmethod.pyi', |
| 179 | +} |
| 180 | + |
166 | 181 |
|
167 | 182 | class SemanticAnalyzer(NodeVisitor):
|
168 | 183 | """Semantically analyze parsed mypy files.
|
@@ -1373,20 +1388,31 @@ def process_import_over_existing_name(self,
|
1373 | 1388 | def normalize_type_alias(self, node: SymbolTableNode,
|
1374 | 1389 | ctx: Context) -> SymbolTableNode:
|
1375 | 1390 | normalized = False
|
1376 |
| - if node.fullname in type_aliases: |
| 1391 | + fullname = node.fullname |
| 1392 | + if fullname in type_aliases: |
1377 | 1393 | # Node refers to an aliased type such as typing.List; normalize.
|
1378 |
| - node = self.lookup_qualified(type_aliases[node.fullname], ctx) |
| 1394 | + node = self.lookup_qualified(type_aliases[fullname], ctx) |
| 1395 | + if node is None: |
| 1396 | + self.add_fixture_note(fullname, ctx) |
| 1397 | + return None |
1379 | 1398 | normalized = True
|
1380 |
| - if node.fullname in collections_type_aliases: |
| 1399 | + if fullname in collections_type_aliases: |
1381 | 1400 | # Similar, but for types from the collections module like typing.DefaultDict
|
1382 | 1401 | self.add_module_symbol('collections', '__mypy_collections__', False, ctx)
|
1383 |
| - node = self.lookup_qualified(collections_type_aliases[node.fullname], ctx) |
| 1402 | + node = self.lookup_qualified(collections_type_aliases[fullname], ctx) |
1384 | 1403 | normalized = True
|
1385 | 1404 | if normalized:
|
1386 | 1405 | node = SymbolTableNode(node.kind, node.node,
|
1387 | 1406 | node.mod_id, node.type_override, normalized=True)
|
1388 | 1407 | return node
|
1389 | 1408 |
|
| 1409 | + def add_fixture_note(self, fullname: str, ctx: Context) -> None: |
| 1410 | + self.note('Maybe your test fixture does not define "{}"?'.format(fullname), ctx) |
| 1411 | + if fullname in SUGGESTED_TEST_FIXTURES: |
| 1412 | + self.note( |
| 1413 | + 'Consider adding [builtins fixtures/{}] to your test description'.format( |
| 1414 | + SUGGESTED_TEST_FIXTURES[fullname]), ctx) |
| 1415 | + |
1390 | 1416 | def correct_relative_import(self, node: Union[ImportFrom, ImportAll]) -> str:
|
1391 | 1417 | if node.relative == 0:
|
1392 | 1418 | return node.id
|
@@ -3396,6 +3422,12 @@ def name_not_defined(self, name: str, ctx: Context) -> None:
|
3396 | 3422 | if extra:
|
3397 | 3423 | message += ' {}'.format(extra)
|
3398 | 3424 | self.fail(message, ctx)
|
| 3425 | + if 'builtins.{}'.format(name) in SUGGESTED_TEST_FIXTURES: |
| 3426 | + # The user probably has a missing definition in a test fixture. Let's verify. |
| 3427 | + fullname = 'builtins.{}'.format(name) |
| 3428 | + if self.lookup_fully_qualified_or_none(fullname) is None: |
| 3429 | + # Yes. Generate a helpful note. |
| 3430 | + self.add_fixture_note(fullname, ctx) |
3399 | 3431 |
|
3400 | 3432 | def name_already_defined(self, name: str, ctx: Context) -> None:
|
3401 | 3433 | self.fail("Name '{}' already defined".format(name), ctx)
|
|
0 commit comments