diff --git a/mypy/fastparse.py b/mypy/fastparse.py index d9e2d5df8f4c..1a273f9412ca 100644 --- a/mypy/fastparse.py +++ b/mypy/fastparse.py @@ -323,7 +323,7 @@ def parse_type_comment( ignored = None assert isinstance(typ, ast3.Expression) converted = TypeConverter( - errors, line=line, override_column=column, is_evaluated=False + errors, line=line, override_column=column, line_offset=line - 1, is_evaluated=False ).visit(typ.body) return ignored, converted @@ -966,11 +966,16 @@ def do_func_def( blocker=False, ) translated_args: list[Type] = TypeConverter( - self.errors, line=lineno, override_column=n.col_offset + self.errors, + line=lineno, + override_column=n.col_offset, + line_offset=lineno - 1, ).translate_expr_list(func_type_ast.argtypes) # Use a cast to work around `list` invariance arg_types = cast(list[Type | None], translated_args) - return_type = TypeConverter(self.errors, line=lineno).visit(func_type_ast.returns) + return_type = TypeConverter( + self.errors, line=lineno, line_offset=lineno - 1 + ).visit(func_type_ast.returns) # add implicit self type in_method_scope = self.class_and_function_stack[-2:] == ["C", "D"] @@ -1877,11 +1882,13 @@ def __init__( errors: Errors | None, line: int = -1, override_column: int = -1, + line_offset: int = 0, is_evaluated: bool = True, ) -> None: self.errors = errors self.line = line self.override_column = override_column + self.line_offset = line_offset self.node_stack: list[AST] = [] self.is_evaluated = is_evaluated @@ -1896,6 +1903,10 @@ def convert_column(self, column: int) -> int: else: return self.override_column + def convert_lineno(self, line: int) -> int: + """Map relative line numbers from synthetic parses back to source lines.""" + return line + self.line_offset + def invalid_type(self, node: AST, note: str | None = None) -> RawExpressionType: """Constructs a type representing some expression that normally forms an invalid type. For example, if we see a type hint that says "3 + 4", we would transform that @@ -2112,12 +2123,13 @@ def visit_Subscript(self, n: ast3.Subscript) -> Type: result = UnboundType( value.name, params, - line=self.line, + line=self.convert_lineno(n.lineno), column=value.column, empty_tuple_index=empty_tuple_index, ) result.end_column = n.end_col_offset - result.end_line = n.end_lineno + if n.end_lineno is not None: + result.end_line = self.convert_lineno(n.end_lineno) return result else: return self.invalid_type(n) diff --git a/test-data/unit/check-errorcodes.test b/test-data/unit/check-errorcodes.test index 85a2264c2088..c11f615bb4f6 100644 --- a/test-data/unit/check-errorcodes.test +++ b/test-data/unit/check-errorcodes.test @@ -1087,6 +1087,21 @@ def f(arg: int) -> int: def f(arg: str) -> str: ... +[case testNestedSubscriptWrongArityLineNumber] +# flags: --python-version 3.12 +def foo() -> dict[ + str, + dict[int, int, int], # E: "dict" expects 2 type arguments, but 3 given [type-arg] +]: + return {} +[builtins fixtures/dict.pyi] + +[case testTypeCommentNestedSubscriptWrongArityLineNumber] +# flags: --python-version 3.12 +x = 0 +y = {} # type: dict[int, int, int] # E: "dict" expects 2 type arguments, but 3 given [type-arg] +[builtins fixtures/dict.pyi] + [case testSliceInDictBuiltin_no_native_parse] # flags: --show-column-numbers b: dict[int, x:y]