Skip to content

Commit 6181b0f

Browse files
authored
Fix line number for slices, clean up old logic (#18397)
Fixes #17655 The decorator cleanup moves a type ignore, but so does the bug fix for decorators in #18392 , so might as well batch into a single release
1 parent b96a3f1 commit 6181b0f

File tree

7 files changed

+44
-44
lines changed

7 files changed

+44
-44
lines changed

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
## Next release
44

5+
### Performance improvements
6+
7+
TODO
8+
59
### Drop Support for Python 3.8
610

711
Mypy no longer supports running with Python 3.8, which has reached end-of-life.
@@ -40,6 +44,14 @@ Contributed by Christoph Tyralla (PR [18180](https://github.com/python/mypy/pull
4044
(Speaking of partial types, another reminder that mypy plans on enabling `--local-partial-types`
4145
by default in **mypy 2.0**).
4246

47+
### Better line numbers for decorators and slice expressions
48+
49+
Mypy now uses more correct line numbers for decorators and slice expressions. In some cases, this
50+
may necessitate changing the location of a `# type: ignore` comment.
51+
52+
Contributed by Shantanu Jain (PR [18392](https://github.com/python/mypy/pull/18392),
53+
PR [18397](https://github.com/python/mypy/pull/18397)).
54+
4355
## Mypy 1.14
4456

4557
We’ve just uploaded mypy 1.14 to the Python Package Index ([PyPI](https://pypi.org/project/mypy/)).

mypy/fastparse.py

Lines changed: 8 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1009,28 +1009,22 @@ def do_func_def(
10091009
func_def.is_coroutine = True
10101010
if func_type is not None:
10111011
func_type.definition = func_def
1012-
func_type.line = lineno
1012+
func_type.set_line(lineno)
10131013

10141014
if n.decorator_list:
1015-
# Set deco_line to the old pre-3.8 lineno, in order to keep
1016-
# existing "# type: ignore" comments working:
1017-
deco_line = n.decorator_list[0].lineno
1018-
10191015
var = Var(func_def.name)
10201016
var.is_ready = False
10211017
var.set_line(lineno)
10221018

10231019
func_def.is_decorated = True
1024-
func_def.deco_line = deco_line
1025-
func_def.set_line(lineno, n.col_offset, end_line, end_column)
1020+
self.set_line(func_def, n)
10261021

10271022
deco = Decorator(func_def, self.translate_expr_list(n.decorator_list), var)
10281023
first = n.decorator_list[0]
10291024
deco.set_line(first.lineno, first.col_offset, end_line, end_column)
10301025
retval: FuncDef | Decorator = deco
10311026
else:
1032-
# FuncDef overrides set_line -- can't use self.set_line
1033-
func_def.set_line(lineno, n.col_offset, end_line, end_column)
1027+
self.set_line(func_def, n)
10341028
retval = func_def
10351029
if self.options.include_docstrings:
10361030
func_def.docstring = ast3.get_docstring(n, clean=False)
@@ -1149,10 +1143,7 @@ def visit_ClassDef(self, n: ast3.ClassDef) -> ClassDef:
11491143
type_args=explicit_type_params,
11501144
)
11511145
cdef.decorators = self.translate_expr_list(n.decorator_list)
1152-
# Set lines to match the old mypy 0.700 lines, in order to keep
1153-
# existing "# type: ignore" comments working:
1154-
cdef.line = n.lineno
1155-
cdef.deco_line = n.decorator_list[0].lineno if n.decorator_list else None
1146+
self.set_line(cdef, n)
11561147

11571148
if self.options.include_docstrings:
11581149
cdef.docstring = ast3.get_docstring(n, clean=False)
@@ -1247,8 +1238,7 @@ def visit_AnnAssign(self, n: ast3.AnnAssign) -> AssignmentStmt:
12471238
line = n.lineno
12481239
if n.value is None: # always allow 'x: int'
12491240
rvalue: Expression = TempNode(AnyType(TypeOfAny.special_form), no_rhs=True)
1250-
rvalue.line = line
1251-
rvalue.column = n.col_offset
1241+
self.set_line(rvalue, n)
12521242
else:
12531243
rvalue = self.visit(n.value)
12541244
typ = TypeConverter(self.errors, line=line).visit(n.annotation)
@@ -1675,19 +1665,7 @@ def visit_Attribute(self, n: Attribute) -> MemberExpr | SuperExpr:
16751665
# Subscript(expr value, slice slice, expr_context ctx)
16761666
def visit_Subscript(self, n: ast3.Subscript) -> IndexExpr:
16771667
e = IndexExpr(self.visit(n.value), self.visit(n.slice))
1678-
self.set_line(e, n)
1679-
# alias to please mypyc
1680-
is_py38_or_earlier = sys.version_info < (3, 9)
1681-
if isinstance(n.slice, ast3.Slice) or (
1682-
is_py38_or_earlier and isinstance(n.slice, ast3.ExtSlice)
1683-
):
1684-
# Before Python 3.9, Slice has no line/column in the raw ast. To avoid incompatibility
1685-
# visit_Slice doesn't set_line, even in Python 3.9 on.
1686-
# ExtSlice also has no line/column info. In Python 3.9 on, line/column is set for
1687-
# e.index when visiting n.slice.
1688-
e.index.line = e.line
1689-
e.index.column = e.column
1690-
return e
1668+
return self.set_line(e, n)
16911669

16921670
# Starred(expr value, expr_context ctx)
16931671
def visit_Starred(self, n: Starred) -> StarExpr:
@@ -1718,7 +1696,8 @@ def visit_Tuple(self, n: ast3.Tuple) -> TupleExpr:
17181696

17191697
# Slice(expr? lower, expr? upper, expr? step)
17201698
def visit_Slice(self, n: ast3.Slice) -> SliceExpr:
1721-
return SliceExpr(self.visit(n.lower), self.visit(n.upper), self.visit(n.step))
1699+
e = SliceExpr(self.visit(n.lower), self.visit(n.upper), self.visit(n.step))
1700+
return self.set_line(e, n)
17221701

17231702
# ExtSlice(slice* dims)
17241703
def visit_ExtSlice(self, n: ast3.ExtSlice) -> TupleExpr:

mypy/messages.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ def span_from_context(ctx: Context) -> Iterable[int]:
244244
TODO: address this in follow up PR
245245
"""
246246
if isinstance(ctx, (ClassDef, FuncDef)):
247-
return range(ctx.deco_line or ctx.line, ctx.line + 1)
247+
return range(ctx.line, ctx.line + 1)
248248
elif not isinstance(ctx, Expression):
249249
return [ctx.line]
250250
else:

mypy/nodes.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -768,7 +768,6 @@ class FuncDef(FuncItem, SymbolNode, Statement):
768768
"is_conditional",
769769
"abstract_status",
770770
"original_def",
771-
"deco_line",
772771
"is_trivial_body",
773772
"is_mypy_only",
774773
# Present only when a function is decorated with @typing.dataclass_transform or similar
@@ -798,8 +797,6 @@ def __init__(
798797
self.is_trivial_body = False
799798
# Original conditional definition
800799
self.original_def: None | FuncDef | Var | Decorator = None
801-
# Used for error reporting (to keep backward compatibility with pre-3.8)
802-
self.deco_line: int | None = None
803800
# Definitions that appear in if TYPE_CHECKING are marked with this flag.
804801
self.is_mypy_only = False
805802
self.dataclass_transform_spec: DataclassTransformSpec | None = None
@@ -1115,7 +1112,6 @@ class ClassDef(Statement):
11151112
"keywords",
11161113
"analyzed",
11171114
"has_incompatible_baseclass",
1118-
"deco_line",
11191115
"docstring",
11201116
"removed_statements",
11211117
)
@@ -1166,8 +1162,6 @@ def __init__(
11661162
self.keywords = dict(keywords) if keywords else {}
11671163
self.analyzed = None
11681164
self.has_incompatible_baseclass = False
1169-
# Used for error reporting (to keep backward compatibility with pre-3.8)
1170-
self.deco_line: int | None = None
11711165
self.docstring: str | None = None
11721166
self.removed_statements = []
11731167

mypy/plugins/common.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,6 @@ def add_overloaded_method_to_class(
282282
var = Var(func.name, func.type)
283283
var.set_line(func.line)
284284
func.is_decorated = True
285-
func.deco_line = func.line
286285

287286
deco = Decorator(func, [], var)
288287
else:

test-data/unit/check-python38.test

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ def f(): ... # E: Function is missing a return type annotation \
1717
# flags: --disallow-untyped-defs --warn-unused-ignores
1818
def d(f): ... # type: ignore
1919
@d
20-
# type: ignore
21-
def f(): ... # type: ignore # E: Unused "type: ignore" comment
20+
# type: ignore # E: Unused "type: ignore" comment
21+
def f(): ... # type: ignore
2222

2323
[case testIgnoreDecoratedFunction2]
2424
# flags: --disallow-untyped-defs

test-data/unit/parse.test

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3171,10 +3171,10 @@ MypyFile:1(
31713171
IndexExpr:1(
31723172
NameExpr(a)
31733173
TupleExpr:1(
3174-
SliceExpr:-1(
3174+
SliceExpr:1(
31753175
<empty>
31763176
<empty>)
3177-
SliceExpr:-1(
3177+
SliceExpr:1(
31783178
<empty>
31793179
<empty>)))))
31803180

@@ -3186,10 +3186,10 @@ MypyFile:1(
31863186
IndexExpr:1(
31873187
NameExpr(a)
31883188
TupleExpr:1(
3189-
SliceExpr:-1(
3189+
SliceExpr:1(
31903190
IntExpr(1)
31913191
IntExpr(2))
3192-
SliceExpr:-1(
3192+
SliceExpr:1(
31933193
<empty>
31943194
<empty>)))))
31953195

@@ -3201,13 +3201,29 @@ MypyFile:1(
32013201
IndexExpr:1(
32023202
NameExpr(a)
32033203
TupleExpr:1(
3204-
SliceExpr:-1(
3204+
SliceExpr:1(
32053205
IntExpr(1)
32063206
IntExpr(2)
32073207
IntExpr(3))
32083208
Ellipsis
32093209
IntExpr(1)))))
32103210

3211+
[case testParseExtendedSlicing4]
3212+
m[*index, :]
3213+
[out]
3214+
main:1: error: invalid syntax
3215+
[out version>=3.11]
3216+
MypyFile:1(
3217+
ExpressionStmt:1(
3218+
IndexExpr:1(
3219+
NameExpr(m)
3220+
TupleExpr:1(
3221+
StarExpr:1(
3222+
NameExpr(index))
3223+
SliceExpr:1(
3224+
<empty>
3225+
<empty>)))))
3226+
32113227
[case testParseIfExprInDictExpr]
32123228
test = { 'spam': 'eggs' if True else 'bacon' }
32133229
[out]

0 commit comments

Comments
 (0)