Skip to content

Commit 80b1cd9

Browse files
committed
Handle more cases where builtins.int isn't ready yet
1 parent f3f285e commit 80b1cd9

File tree

3 files changed

+81
-39
lines changed

3 files changed

+81
-39
lines changed

mypy/semanal.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3181,7 +3181,7 @@ def visit_assignment_stmt(self, s: AssignmentStmt) -> None:
31813181
else:
31823182
s.rvalue.accept(self)
31833183

3184-
if self.found_incomplete_ref(tag) or self.should_wait_rhs(s.rvalue):
3184+
if self.found_incomplete_ref(tag) or self.should_wait_rhs(s.rvalue, s):
31853185
# Initializer couldn't be fully analyzed. Defer the current node and give up.
31863186
# Make sure that if we skip the definition of some local names, they can't be
31873187
# added later in this scope, since an earlier definition should take precedence.
@@ -3280,7 +3280,7 @@ def analyze_identity_global_assignment(self, s: AssignmentStmt) -> bool:
32803280
node.fullname = sym.node.fullname
32813281
return True
32823282

3283-
def should_wait_rhs(self, rv: Expression) -> bool:
3283+
def should_wait_rhs(self, rv: Expression, s: AssignmentStmt) -> bool:
32843284
"""Can we already classify this r.h.s. of an assignment or should we wait?
32853285
32863286
This returns True if we don't have enough information to decide whether
@@ -3291,6 +3291,10 @@ def should_wait_rhs(self, rv: Expression) -> bool:
32913291
if self.final_iteration:
32923292
# No chance, nothing has changed.
32933293
return False
3294+
if isinstance(s.type, UnboundType):
3295+
lookup = self.lookup_qualified(s.type.name, s, suppress_errors=True)
3296+
if lookup and isinstance(lookup.node, PlaceholderNode):
3297+
return True
32943298
if isinstance(rv, NameExpr):
32953299
n = self.lookup(rv.name, rv)
32963300
if n and isinstance(n.node, PlaceholderNode) and not n.node.becomes_typeinfo:
@@ -3302,11 +3306,11 @@ def should_wait_rhs(self, rv: Expression) -> bool:
33023306
if n and isinstance(n.node, PlaceholderNode) and not n.node.becomes_typeinfo:
33033307
return True
33043308
elif isinstance(rv, IndexExpr) and isinstance(rv.base, RefExpr):
3305-
return self.should_wait_rhs(rv.base)
3309+
return self.should_wait_rhs(rv.base, s)
33063310
elif isinstance(rv, CallExpr) and isinstance(rv.callee, RefExpr):
33073311
# This is only relevant for builtin SCC where things like 'TypeVar'
33083312
# may be not ready.
3309-
return self.should_wait_rhs(rv.callee)
3313+
return self.should_wait_rhs(rv.callee, s)
33103314
return False
33113315

33123316
def can_be_type_alias(self, rv: Expression, allow_none: bool = False) -> bool:

mypy/typeanal.py

Lines changed: 37 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -297,43 +297,40 @@ def not_declared_in_type_params(self, tvar_name: str) -> bool:
297297
and tvar_name not in self.alias_type_params_names
298298
)
299299

300-
def handle_placeholder_node(self, node: PlaceholderNode, t: UnboundType) -> Type:
301-
if node.becomes_typeinfo:
302-
# Reference to placeholder type.
303-
if self.api.final_iteration:
304-
self.cannot_resolve_type(t)
305-
return AnyType(TypeOfAny.from_error)
306-
elif self.allow_placeholder:
307-
self.api.defer()
308-
else:
309-
self.api.record_incomplete_ref()
310-
# Always allow ParamSpec for placeholders, if they are actually not valid,
311-
# they will be reported later, after we resolve placeholders.
312-
return PlaceholderType(
313-
node.fullname,
314-
self.anal_array(
315-
t.args,
316-
allow_param_spec=True,
317-
allow_param_spec_literals=True,
318-
allow_unpack=True,
319-
),
320-
t.line,
321-
)
322-
else:
323-
if self.api.final_iteration:
324-
self.cannot_resolve_type(t)
325-
return AnyType(TypeOfAny.from_error)
326-
else:
327-
# Reference to an unknown placeholder node.
328-
self.api.record_incomplete_ref()
329-
return AnyType(TypeOfAny.special_form)
330-
331300
def visit_unbound_type_nonoptional(self, t: UnboundType, defining_literal: bool) -> Type:
332301
sym = self.lookup_qualified(t.name, t)
333302
if sym is not None:
334303
node = sym.node
335304
if isinstance(node, PlaceholderNode):
336-
return self.handle_placeholder_node(node, t)
305+
if node.becomes_typeinfo:
306+
# Reference to placeholder type.
307+
if self.api.final_iteration:
308+
self.cannot_resolve_type(t)
309+
return AnyType(TypeOfAny.from_error)
310+
elif self.allow_placeholder:
311+
self.api.defer()
312+
else:
313+
self.api.record_incomplete_ref()
314+
# Always allow ParamSpec for placeholders, if they are actually not valid,
315+
# they will be reported later, after we resolve placeholders.
316+
return PlaceholderType(
317+
node.fullname,
318+
self.anal_array(
319+
t.args,
320+
allow_param_spec=True,
321+
allow_param_spec_literals=True,
322+
allow_unpack=True,
323+
),
324+
t.line,
325+
)
326+
else:
327+
if self.api.final_iteration:
328+
self.cannot_resolve_type(t)
329+
return AnyType(TypeOfAny.from_error)
330+
else:
331+
# Reference to an unknown placeholder node.
332+
self.api.record_incomplete_ref()
333+
return AnyType(TypeOfAny.special_form)
337334
if node is None:
338335
self.fail(f"Internal error (node is None, kind={sym.kind})", t)
339336
return AnyType(TypeOfAny.special_form)
@@ -1705,9 +1702,14 @@ def analyze_literal_param(self, idx: int, arg: Type, ctx: Context) -> list[Type]
17051702
return None
17061703

17071704
# Make sure the literal's class is ready
1708-
sym = self.lookup_fully_qualified(arg.base_type_name)
1709-
if isinstance(sym.node, PlaceholderNode):
1710-
return [self.handle_placeholder_node(sym.node, UnboundType(arg.base_type_name))]
1705+
sym = self.api.lookup_fully_qualified_or_none(arg.base_type_name)
1706+
if sym is None or isinstance(sym.node, PlaceholderNode):
1707+
assert arg.base_type_name.startswith("builtins.")
1708+
if self.api.is_incomplete_namespace("builtins"):
1709+
self.api.record_incomplete_ref()
1710+
else:
1711+
self.fail(f'Name "{arg.base_type_name}" is not defined', arg)
1712+
return [AnyType(TypeOfAny.special_form)]
17111713

17121714
# Remap bytes and unicode into the appropriate type for the correct Python version
17131715
fallback = self.named_type(arg.base_type_name)

test-data/unit/check-literal.test

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3021,3 +3021,39 @@ class str: ...
30213021
class list: ...
30223022
class dict: ...
30233023
class ellipsis: ...
3024+
3025+
3026+
-- In this version, looking up the node for builtins.int
3027+
-- during type analysis of _LiteralInteger returns None
3028+
[case testDeferIntLiteral2]
3029+
[file _collections_abc.pyi]
3030+
[file abc.pyi]
3031+
[file collections/__init__.pyi]
3032+
[file collections/abc.pyi]
3033+
[file types.pyi]
3034+
class UnionType: ...
3035+
3036+
[file typing_extensions.pyi]
3037+
from typing import Any
3038+
TypeAlias: Any
3039+
3040+
[file typing.pyi]
3041+
Any = object()
3042+
Literal: Any
3043+
3044+
[file builtins.pyi]
3045+
import _collections_abc
3046+
import abc
3047+
import collections.abc
3048+
import types
3049+
from typing import Literal
3050+
from typing_extensions import TypeAlias
3051+
3052+
class int: ...
3053+
class ellipsis: ...
3054+
class str: ...
3055+
class list: ...
3056+
class dict: ...
3057+
3058+
_PositiveInteger: TypeAlias = Literal[1]
3059+
_LiteralInteger = _PositiveInteger | Literal[0]

0 commit comments

Comments
 (0)