Skip to content

Commit 64ba2a4

Browse files
committed
Delay errors in semanal for proper unreachability information
1 parent eb144ce commit 64ba2a4

6 files changed

Lines changed: 57 additions & 12 deletions

File tree

mypy/build.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2554,6 +2554,7 @@ def type_checker(self) -> TypeChecker:
25542554
self.xpath,
25552555
manager.plugin,
25562556
self.per_line_checking_time_ns,
2557+
manager.semantic_analyzer.delayed_errors,
25572558
)
25582559
return self._type_checker
25592560

mypy/checker.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,7 @@ def __init__(
409409
path: str,
410410
plugin: Plugin,
411411
per_line_checking_time_ns: dict[int, int],
412+
semanal_delayed_errors: dict[tuple[str, int, int], list[ErrorInfo]],
412413
) -> None:
413414
"""Construct a type checker.
414415
@@ -442,6 +443,7 @@ def __init__(
442443
self.inferred_attribute_types = None
443444
self.allow_constructor_cache = True
444445
self.local_type_map = LocalTypeMap(self)
446+
self.semanal_delayed_errors = semanal_delayed_errors
445447

446448
# If True, process function definitions. If False, don't. This is used
447449
# for processing module top levels in fine-grained incremental mode.
@@ -637,6 +639,12 @@ def handle_cannot_determine_type(self, name: str, context: Context) -> None:
637639

638640
def accept(self, stmt: Statement) -> None:
639641
"""Type check a node in the given type context."""
642+
curr_module = self.scope.stack[0]
643+
if isinstance(curr_module, MypyFile):
644+
key = (curr_module.fullname, stmt.line, stmt.column)
645+
if key in self.semanal_delayed_errors:
646+
self.msg.add_errors(self.semanal_delayed_errors[key])
647+
640648
try:
641649
stmt.accept(self)
642650
except Exception as err:
@@ -1227,6 +1235,16 @@ def check_func_item(
12271235
"""
12281236
self.dynamic_funcs.append(defn.is_dynamic() and not type_override)
12291237

1238+
# top-level function definitions are one of the main
1239+
# things errors can be associated with, and are sometimes masked
1240+
# e.g. by a decorator. it's better to make sure to flush any errors
1241+
# just in case.
1242+
curr_module = self.scope.stack[0]
1243+
if isinstance(curr_module, MypyFile):
1244+
key = (curr_module.fullname, defn.line, defn.column)
1245+
if key in self.semanal_delayed_errors:
1246+
self.msg.add_errors(self.semanal_delayed_errors[key])
1247+
12301248
enclosing_node_deferred = self.current_node_deferred
12311249
with self.enter_partial_types(is_function=True):
12321250
typ = self.function_type(defn)

mypy/checkexpr.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5495,8 +5495,8 @@ def visit_lambda_expr(self, e: LambdaExpr) -> Type:
54955495
# Lambdas can have more than one element in body,
54965496
# when we add "fictional" AssignmentStatement nodes, like in:
54975497
# `lambda (a, b): a`
5498-
for stmt in e.body.body[:-1]:
5499-
stmt.accept(self.chk)
5498+
for stmt in e.body.body:
5499+
self.chk.accept(stmt)
55005500
# Only type check the return expression, not the return statement.
55015501
# There's no useful type context.
55025502
ret_type = self.accept(e.expr(), allow_none_return=True)

mypy/semanal.py

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@
5959
from mypy import errorcodes as codes, message_registry
6060
from mypy.constant_fold import constant_fold_expr
6161
from mypy.errorcodes import PROPERTY_DECORATOR, ErrorCode
62-
from mypy.errors import Errors, report_internal_error
62+
from mypy.errors import ErrorInfo, Errors, report_internal_error
6363
from mypy.exprtotype import TypeTranslationError, expr_to_unanalyzed_type
6464
from mypy.message_registry import ErrorMessage
6565
from mypy.messages import (
@@ -546,6 +546,8 @@ def __init__(
546546
# import foo.bar
547547
self.transitive_submodule_imports: dict[str, set[str]] = {}
548548

549+
self.delayed_errors: dict[tuple[str, int, int], list[ErrorInfo]] = {}
550+
549551
# mypyc doesn't properly handle implementing an abstractproperty
550552
# with a regular attribute so we make them properties
551553
@property
@@ -7093,6 +7095,7 @@ def _get_node_for_class_scoped_import(
70937095
) -> SymbolNode | None:
70947096
if symbol_node is None:
70957097
return None
7098+
# TODO: remove supposedly unnecessary `f`
70967099
# I promise this type checks; I'm just making mypyc issues go away.
70977100
# mypyc is absolutely convinced that `symbol_node` narrows to a Var in the following,
70987101
# when it can also be a FuncBase. Once fixed, `f` in the following can be removed.
@@ -7577,10 +7580,25 @@ def incomplete_feature_enabled(self, feature: str, ctx: Context) -> bool:
75777580
return True
75787581

75797582
def accept(self, node: Node) -> None:
7580-
try:
7581-
node.accept(self)
7582-
except Exception as err:
7583-
report_internal_error(err, self.errors.file, node.line, self.errors, self.options)
7583+
should_filter = isinstance(node, Statement) and not self.options.semantic_analysis_only
7584+
if should_filter:
7585+
filter_errors: bool | Callable[[str, ErrorInfo], bool] = lambda _, e: not e.blocker
7586+
else:
7587+
filter_errors = False
7588+
with self.msg.filter_errors(filter_errors=filter_errors, save_filtered_errors=True) as msg:
7589+
try:
7590+
node.accept(self)
7591+
except Exception as err:
7592+
report_internal_error(err, self.errors.file, node.line, self.errors, self.options)
7593+
7594+
errors = msg.filtered_errors()
7595+
if errors:
7596+
# since nodes aren't hashable, carry things through values
7597+
assign_to = (self.cur_mod_id, node.line, node.column)
7598+
self.delayed_errors.setdefault(assign_to, [])
7599+
self.delayed_errors[assign_to].extend(errors)
7600+
7601+
# print(node, [e.message for e in self.delayed_errors[node]])
75847602

75857603
def expr_to_analyzed_type(
75867604
self,

test-data/unit/check-generics.test

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3100,11 +3100,11 @@ def dec4_bound(f: Callable[[I], List[T]]) -> Callable[[I], T]:
31003100
reveal_type(dec1(lambda x: x)) # N: Revealed type is "def [T] (T`3) -> builtins.list[T`3]"
31013101
reveal_type(dec2(lambda x: x)) # N: Revealed type is "def [S] (S`5) -> builtins.list[S`5]"
31023102
reveal_type(dec3(lambda x: x[0])) # N: Revealed type is "def [S] (S`8) -> S`8"
3103-
reveal_type(dec4(lambda x: [x])) # N: Revealed type is "def [S] (S`11) -> S`11"
3103+
reveal_type(dec4(lambda x: [x])) # N: Revealed type is "def [S] (S`12) -> S`12"
31043104
reveal_type(dec1(lambda x: 1)) # N: Revealed type is "def (builtins.int) -> builtins.list[builtins.int]"
31053105
reveal_type(dec5(lambda x: x)) # N: Revealed type is "def (builtins.int) -> builtins.list[builtins.int]"
3106-
reveal_type(dec3(lambda x: x)) # N: Revealed type is "def [S] (S`19) -> builtins.list[S`19]"
3107-
reveal_type(dec4(lambda x: x)) # N: Revealed type is "def [T] (builtins.list[T`23]) -> T`23"
3106+
reveal_type(dec3(lambda x: x)) # N: Revealed type is "def [S] (S`20) -> builtins.list[S`20]"
3107+
reveal_type(dec4(lambda x: x)) # N: Revealed type is "def [T] (builtins.list[T`24]) -> T`24"
31083108
dec4_bound(lambda x: x) # E: Value of type variable "I" of "dec4_bound" cannot be "list[T]"
31093109
[builtins fixtures/list.pyi]
31103110

test-data/unit/check-type-aliases.test

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1216,14 +1216,22 @@ reveal_type(x) # N: Revealed type is "builtins.dict[builtins.str, Any]"
12161216
[builtins fixtures/dict.pyi]
12171217
[typing fixtures/typing-full.pyi]
12181218

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

1223-
T = TypeVar("T")
12241223
Ts = TypeVarTuple("Ts")
12251224

1225+
# note that the following is a blocker, so the assignment after isn't checked
12261226
Ta1 = TypeAliasType("Ta1", None, type_params=(*Ts,)) # E: can't use starred expression here
1227+
[builtins fixtures/tuple.pyi]
1228+
1229+
[case testTypeAliasTypeNoUnpackInTypeParams2_311]
1230+
# flags: --python-version 3.11
1231+
from typing_extensions import TypeAliasType, TypeVar, TypeVarTuple, Unpack
1232+
1233+
Ts = TypeVarTuple("Ts")
1234+
12271235
Ta2 = TypeAliasType("Ta2", None, type_params=(Unpack[Ts],)) # E: Free type variable expected in type_params argument to TypeAliasType \
12281236
# N: Don't Unpack type variables in type_params
12291237

0 commit comments

Comments
 (0)