|
99 | 99 | Plugin, ClassDefContext, SemanticAnalyzerPluginInterface,
|
100 | 100 | DynamicClassDefContext
|
101 | 101 | )
|
102 |
| -from mypy.util import get_prefix, correct_relative_import, unmangle, split_module_names |
| 102 | +from mypy.util import ( |
| 103 | + get_prefix, correct_relative_import, unmangle, module_prefix |
| 104 | +) |
103 | 105 | from mypy.scope import Scope
|
104 | 106 | from mypy.newsemanal.semanal_shared import (
|
105 | 107 | SemanticAnalyzerInterface, set_callable_name, calculate_tuple_fallback, PRIORITY_FALLBACKS
|
@@ -3752,20 +3754,7 @@ def lookup(self, name: str, ctx: Context,
|
3752 | 3754 | if self.type and not self.is_func_scope() and name in self.type.names:
|
3753 | 3755 | node = self.type.names[name]
|
3754 | 3756 | if not node.implicit:
|
3755 |
| - # Only allow access to class attributes textually after |
3756 |
| - # the definition, so that it's possible to fall back to the |
3757 |
| - # outer scope. Example: |
3758 |
| - # |
3759 |
| - # class X: ... |
3760 |
| - # |
3761 |
| - # class C: |
3762 |
| - # X = X # Initializer refers to outer scope |
3763 |
| - # |
3764 |
| - # Nested classes are an exception, since we want to support |
3765 |
| - # arbitrary forward references in type annotations. |
3766 |
| - if (node.node is None |
3767 |
| - or node.node.line < self.statement.line |
3768 |
| - or isinstance(node.node, TypeInfo)): |
| 3757 | + if self.is_active_symbol_in_class_body(node.node): |
3769 | 3758 | return node
|
3770 | 3759 | else:
|
3771 | 3760 | # Defined through self.x assignment
|
@@ -3798,6 +3787,33 @@ def lookup(self, name: str, ctx: Context,
|
3798 | 3787 | return implicit_node
|
3799 | 3788 | return None
|
3800 | 3789 |
|
| 3790 | + def is_active_symbol_in_class_body(self, node: Optional[SymbolNode]) -> bool: |
| 3791 | + """Can a symbol defined in class body accessed at current statement? |
| 3792 | +
|
| 3793 | + Only allow access to class attributes textually after |
| 3794 | + the definition, so that it's possible to fall back to the |
| 3795 | + outer scope. Example: |
| 3796 | +
|
| 3797 | + class X: ... |
| 3798 | +
|
| 3799 | + class C: |
| 3800 | + X = X # Initializer refers to outer scope |
| 3801 | +
|
| 3802 | + Nested classes are an exception, since we want to support |
| 3803 | + arbitrary forward references in type annotations. |
| 3804 | + """ |
| 3805 | + # TODO: Forward reference to name imported in class body is not |
| 3806 | + # caught. |
| 3807 | + return (node is None |
| 3808 | + or node.line < self.statement.line |
| 3809 | + or not self.is_defined_in_current_module(node.fullname()) |
| 3810 | + or isinstance(node, TypeInfo)) |
| 3811 | + |
| 3812 | + def is_defined_in_current_module(self, fullname: Optional[str]) -> bool: |
| 3813 | + if fullname is None: |
| 3814 | + return False |
| 3815 | + return module_prefix(self.modules, fullname) == self.cur_mod_id |
| 3816 | + |
3801 | 3817 | def lookup_qualified(self, name: str, ctx: Context,
|
3802 | 3818 | suppress_errors: bool = False) -> Optional[SymbolTableNode]:
|
3803 | 3819 | if '.' not in name:
|
@@ -4455,7 +4471,7 @@ def attribute_already_defined(self,
|
4455 | 4471 |
|
4456 | 4472 | def is_local_name(self, name: str) -> bool:
|
4457 | 4473 | """Does name look like reference to a definition in the current module?"""
|
4458 |
| - return self.cur_mod_id in split_module_names(name) or '.' not in name |
| 4474 | + return self.is_defined_in_current_module(name) or '.' not in name |
4459 | 4475 |
|
4460 | 4476 | def fail(self,
|
4461 | 4477 | msg: str,
|
|
0 commit comments