diff --git a/mypy/fastparse.py b/mypy/fastparse.py index cd7aab86daa0..14b30e5d7826 100644 --- a/mypy/fastparse.py +++ b/mypy/fastparse.py @@ -2052,13 +2052,16 @@ def visit_Subscript(self, n: ast3.Subscript) -> Type: value = self.visit(n.value) if isinstance(value, UnboundType) and not value.args: - return UnboundType( + result = UnboundType( value.name, params, line=self.line, column=value.column, empty_tuple_index=empty_tuple_index, ) + result.end_column = getattr(n, "end_col_offset", None) + result.end_line = getattr(n, "end_lineno", None) + return result else: return self.invalid_type(n) @@ -2092,7 +2095,7 @@ def visit_Attribute(self, n: Attribute) -> Type: before_dot = self.visit(n.value) if isinstance(before_dot, UnboundType) and not before_dot.args: - return UnboundType(f"{before_dot.name}.{n.attr}", line=self.line) + return UnboundType(f"{before_dot.name}.{n.attr}", line=self.line, column=n.col_offset) else: return self.invalid_type(n) diff --git a/mypy/semanal.py b/mypy/semanal.py index 034d8fb28b42..3c0bf35e1a30 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -7219,7 +7219,15 @@ def fail( if code is None: code = msg.code msg = msg.value - self.errors.report(ctx.line, ctx.column, msg, blocker=blocker, code=code) + self.errors.report( + ctx.line, + ctx.column, + msg, + blocker=blocker, + code=code, + end_line=ctx.end_line, + end_column=ctx.end_column, + ) def note(self, msg: str, ctx: Context, code: ErrorCode | None = None) -> None: if not self.in_checked_function(): diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 008e3c2477a1..b93c7ddd001a 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -862,6 +862,8 @@ def analyze_type_with_type_info( ctx.line, ctx.column, ) + instance.end_line = ctx.end_line + instance.end_column = ctx.end_column if len(info.type_vars) == 1 and info.has_param_spec_type: instance.args = tuple(self.pack_paramspec_args(instance.args)) @@ -2204,6 +2206,8 @@ def instantiate_type_alias( tp = Instance(node.target.type, args) tp.line = ctx.line tp.column = ctx.column + tp.end_line = ctx.end_line + tp.end_column = ctx.end_column return tp if node.tvar_tuple_index is None: if any(isinstance(a, UnpackType) for a in args): diff --git a/test-data/unit/check-columns.test b/test-data/unit/check-columns.test index 940e0846c959..8f91d99a0576 100644 --- a/test-data/unit/check-columns.test +++ b/test-data/unit/check-columns.test @@ -408,3 +408,17 @@ x[0] main:2:10:2:17: error: Incompatible types in assignment (expression has type "str", variable has type "int") main:6:3:7:1: error: Argument 1 to "f" has incompatible type "int"; expected "str" main:8:1:8:4: error: Value of type "int" is not indexable + +[case testEndColumnsWithTooManyTypeVars] +# flags: --pretty +import typing + +x1: typing.List[typing.List[int, int]] +x2: list[list[int, int]] +[out] +main:4:17: error: "list" expects 1 type argument, but 2 given + x1: typing.List[typing.List[int, int]] + ^~~~~~~~~~~~~~~~~~~~~ +main:5:10: error: "list" expects 1 type argument, but 2 given + x2: list[list[int, int]] + ^~~~~~~~~~~~~~ diff --git a/test-data/unit/check-parameter-specification.test b/test-data/unit/check-parameter-specification.test index fa3d98036ec3..352503023f97 100644 --- a/test-data/unit/check-parameter-specification.test +++ b/test-data/unit/check-parameter-specification.test @@ -1345,8 +1345,8 @@ from typing import Callable, ParamSpec P1 = ParamSpec('P1') P2 = ParamSpec('P2') -def f0(f: Callable[P1, int], *args: P1.args, **kwargs: P2.kwargs): ... # E: ParamSpec "P2" is unbound \ - # E: ParamSpec must have "*args" typed as "P1.args" and "**kwargs" typed as "P1.kwargs" +def f0(f: Callable[P1, int], *args: P1.args, **kwargs: P2.kwargs): ... # E: ParamSpec must have "*args" typed as "P1.args" and "**kwargs" typed as "P1.kwargs" \ + # E: ParamSpec "P2" is unbound def f1(*args: P1.args): ... # E: ParamSpec "P1" is unbound def f2(**kwargs: P1.kwargs): ... # E: ParamSpec "P1" is unbound