Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions mypy/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -2554,6 +2554,7 @@ def type_checker(self) -> TypeChecker:
self.xpath,
manager.plugin,
self.per_line_checking_time_ns,
manager.semantic_analyzer.delayed_errors,
)
return self._type_checker

Expand Down
15 changes: 13 additions & 2 deletions mypy/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,7 @@ def __init__(
path: str,
plugin: Plugin,
per_line_checking_time_ns: dict[int, int],
semanal_delayed_errors: dict[tuple[str, int, int], list[ErrorInfo]],
) -> None:
"""Construct a type checker.

Expand Down Expand Up @@ -442,6 +443,7 @@ def __init__(
self.inferred_attribute_types = None
self.allow_constructor_cache = True
self.local_type_map = LocalTypeMap(self)
self.semanal_delayed_errors = semanal_delayed_errors

# If True, process function definitions. If False, don't. This is used
# for processing module top levels in fine-grained incremental mode.
Expand Down Expand Up @@ -527,7 +529,7 @@ def check_first_pass(self) -> None:
self.msg.unreachable_statement(d)
break
else:
self.accept(d)
self.accept_with_delayed_errors(d)

assert not self.current_node_deferred

Expand Down Expand Up @@ -635,6 +637,15 @@ def handle_cannot_determine_type(self, name: str, context: Context) -> None:
else:
self.msg.cannot_determine_type(name, context)

def accept_with_delayed_errors(self, stmt: Statement) -> None:
curr_module = self.scope.stack[0]
if isinstance(curr_module, MypyFile):
key = (curr_module.fullname, stmt.line, stmt.column)
if key in self.semanal_delayed_errors:
self.msg.add_errors(self.semanal_delayed_errors[key])

self.accept(stmt)

def accept(self, stmt: Statement) -> None:
"""Type check a node in the given type context."""
try:
Expand Down Expand Up @@ -3156,7 +3167,7 @@ def visit_block(self, b: Block) -> None:
self.msg.unreachable_statement(s)
break
else:
self.accept(s)
self.accept_with_delayed_errors(s)
# Clear expression cache after each statement to avoid unlimited growth.
self.expr_checker.expr_cache.clear()

Expand Down
4 changes: 2 additions & 2 deletions mypy/checkexpr.py
Original file line number Diff line number Diff line change
Expand Up @@ -5495,8 +5495,8 @@ def visit_lambda_expr(self, e: LambdaExpr) -> Type:
# Lambdas can have more than one element in body,
# when we add "fictional" AssignmentStatement nodes, like in:
# `lambda (a, b): a`
for stmt in e.body.body[:-1]:
stmt.accept(self.chk)
self.chk.accept(e.body)

# Only type check the return expression, not the return statement.
# There's no useful type context.
ret_type = self.accept(e.expr(), allow_none_return=True)
Expand Down
25 changes: 22 additions & 3 deletions mypy/semanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
from mypy import errorcodes as codes, message_registry
from mypy.constant_fold import constant_fold_expr
from mypy.errorcodes import PROPERTY_DECORATOR, ErrorCode
from mypy.errors import Errors, report_internal_error
from mypy.errors import ErrorInfo, Errors, report_internal_error
from mypy.exprtotype import TypeTranslationError, expr_to_unanalyzed_type
from mypy.message_registry import ErrorMessage
from mypy.messages import (
Expand Down Expand Up @@ -546,6 +546,8 @@ def __init__(
# import foo.bar
self.transitive_submodule_imports: dict[str, set[str]] = {}

self.delayed_errors: dict[tuple[str, int, int], list[ErrorInfo]] = {}

# mypyc doesn't properly handle implementing an abstractproperty
# with a regular attribute so we make them properties
@property
Expand Down Expand Up @@ -716,7 +718,7 @@ def refresh_top_level(self, file_node: MypyFile) -> None:
self.recurse_into_functions = False
self.add_implicit_module_attrs(file_node)
for d in file_node.defs:
self.accept(d)
self.accept_delaying_errors(d)
if file_node.fullname == "typing":
self.add_builtin_aliases(file_node)
if file_node.fullname == "typing_extensions":
Expand Down Expand Up @@ -5383,7 +5385,7 @@ def visit_block(self, b: Block) -> None:
return
self.block_depth[-1] += 1
for s in b.body:
self.accept(s)
self.accept_delaying_errors(s)
self.block_depth[-1] -= 1

def visit_block_maybe(self, b: Block | None) -> None:
Expand Down Expand Up @@ -7093,6 +7095,7 @@ def _get_node_for_class_scoped_import(
) -> SymbolNode | None:
if symbol_node is None:
return None
# TODO: remove supposedly unnecessary `f`
# I promise this type checks; I'm just making mypyc issues go away.
# mypyc is absolutely convinced that `symbol_node` narrows to a Var in the following,
# when it can also be a FuncBase. Once fixed, `f` in the following can be removed.
Expand Down Expand Up @@ -7576,6 +7579,22 @@ def incomplete_feature_enabled(self, feature: str, ctx: Context) -> bool:
return False
return True

def accept_delaying_errors(self, node: Node) -> None:
should_filter = isinstance(node, Statement) and not self.options.semantic_analysis_only
if should_filter:
filter_errors: bool | Callable[[str, ErrorInfo], bool] = lambda _, e: not e.blocker
else:
filter_errors = False
with self.msg.filter_errors(filter_errors=filter_errors, save_filtered_errors=True) as msg:
self.accept(node)

errors = msg.filtered_errors()
if errors:
# since nodes don't implement hash(), carry things through values
assign_to = (self.cur_mod_id, node.line, node.column)
self.delayed_errors.setdefault(assign_to, [])
self.delayed_errors[assign_to].extend(errors)

def accept(self, node: Node) -> None:
try:
node.accept(self)
Expand Down
6 changes: 3 additions & 3 deletions test-data/unit/check-generics.test
Original file line number Diff line number Diff line change
Expand Up @@ -3100,11 +3100,11 @@ def dec4_bound(f: Callable[[I], List[T]]) -> Callable[[I], T]:
reveal_type(dec1(lambda x: x)) # N: Revealed type is "def [T] (T`3) -> builtins.list[T`3]"
reveal_type(dec2(lambda x: x)) # N: Revealed type is "def [S] (S`5) -> builtins.list[S`5]"
reveal_type(dec3(lambda x: x[0])) # N: Revealed type is "def [S] (S`8) -> S`8"
reveal_type(dec4(lambda x: [x])) # N: Revealed type is "def [S] (S`11) -> S`11"
reveal_type(dec4(lambda x: [x])) # N: Revealed type is "def [S] (S`12) -> S`12"
reveal_type(dec1(lambda x: 1)) # N: Revealed type is "def (builtins.int) -> builtins.list[builtins.int]"
reveal_type(dec5(lambda x: x)) # N: Revealed type is "def (builtins.int) -> builtins.list[builtins.int]"
reveal_type(dec3(lambda x: x)) # N: Revealed type is "def [S] (S`19) -> builtins.list[S`19]"
reveal_type(dec4(lambda x: x)) # N: Revealed type is "def [T] (builtins.list[T`23]) -> T`23"
reveal_type(dec3(lambda x: x)) # N: Revealed type is "def [S] (S`20) -> builtins.list[S`20]"
reveal_type(dec4(lambda x: x)) # N: Revealed type is "def [T] (builtins.list[T`24]) -> T`24"
dec4_bound(lambda x: x) # E: Value of type variable "I" of "dec4_bound" cannot be "list[T]"
[builtins fixtures/list.pyi]

Expand Down
12 changes: 10 additions & 2 deletions test-data/unit/check-type-aliases.test
Original file line number Diff line number Diff line change
Expand Up @@ -1216,14 +1216,22 @@ reveal_type(x) # N: Revealed type is "builtins.dict[builtins.str, Any]"
[builtins fixtures/dict.pyi]
[typing fixtures/typing-full.pyi]

[case testTypeAliasTypeNoUnpackInTypeParams311]
[case testTypeAliasTypeNoUnpackInTypeParams1_311]
# flags: --python-version 3.11
from typing_extensions import TypeAliasType, TypeVar, TypeVarTuple, Unpack

T = TypeVar("T")
Ts = TypeVarTuple("Ts")

# note that the following is a blocker, so the assignment after isn't checked
Ta1 = TypeAliasType("Ta1", None, type_params=(*Ts,)) # E: can't use starred expression here
[builtins fixtures/tuple.pyi]

[case testTypeAliasTypeNoUnpackInTypeParams2_311]
# flags: --python-version 3.11
from typing_extensions import TypeAliasType, TypeVar, TypeVarTuple, Unpack

Ts = TypeVarTuple("Ts")

Ta2 = TypeAliasType("Ta2", None, type_params=(Unpack[Ts],)) # E: Free type variable expected in type_params argument to TypeAliasType \
# N: Don't Unpack type variables in type_params

Expand Down
Loading