Skip to content

Commit 2c9d11e

Browse files
eurestiilevkivskyi
authored andcommitted
Avoid type-var collisions in attrs and dataclasses (#5552)
The issue was caused because we were using 1 for the ids of the generated types. Since by convention TypeVars for methods should have negative numbers and we're creating these methods we're guaranteed to have -1 free. So use that instead. Fixes #5542
1 parent 940c9b9 commit 2c9d11e

File tree

5 files changed

+36
-24
lines changed

5 files changed

+36
-24
lines changed

mypy/plugins/attrs.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -458,7 +458,7 @@ def _add_cmp(ctx: 'mypy.plugin.ClassDefContext', adder: 'MethodAdder') -> None:
458458
# AT = TypeVar('AT')
459459
# def __lt__(self: AT, other: AT) -> bool
460460
# This way comparisons with subclasses will work correctly.
461-
tvd = TypeVarDef('AT', 'AT', 1, [], object_type)
461+
tvd = TypeVarDef('AT', 'AT', -1, [], object_type)
462462
tvd_type = TypeVarType(tvd)
463463
args = [Argument(Var('other', tvd_type), tvd_type, None, ARG_POS)]
464464
for method in ['__lt__', '__le__', '__gt__', '__ge__']:

mypy/plugins/dataclasses.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ def transform(self) -> None:
113113
# the same type as self (covariant). Note the
114114
# "self_type" parameter to _add_method.
115115
obj_type = ctx.api.named_type('__builtins__.object')
116-
cmp_tvar_def = TypeVarDef('T', 'T', 1, [], obj_type)
116+
cmp_tvar_def = TypeVarDef('T', 'T', -1, [], obj_type)
117117
cmp_other_type = TypeVarType(cmp_tvar_def)
118118
cmp_return_type = ctx.api.named_type('__builtins__.bool')
119119

@@ -135,7 +135,7 @@ def transform(self) -> None:
135135
# Like for __eq__ and __ne__, we want "other" to match
136136
# the self type.
137137
obj_type = ctx.api.named_type('__builtins__.object')
138-
order_tvar_def = TypeVarDef('T', 'T', 1, [], obj_type)
138+
order_tvar_def = TypeVarDef('T', 'T', -1, [], obj_type)
139139
order_other_type = TypeVarType(order_tvar_def)
140140
order_return_type = ctx.api.named_type('__builtins__.bool')
141141
order_args = [

mypy/semanal_namedtuple.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,7 @@ def add_field(var: Var, is_initialized_in_class: bool = False,
333333
add_field(Var('__annotations__', ordereddictype), is_initialized_in_class=True)
334334
add_field(Var('__doc__', strtype), is_initialized_in_class=True)
335335

336-
tvd = TypeVarDef('NT', 'NT', 1, [], info.tuple_type)
336+
tvd = TypeVarDef('NT', 'NT', -1, [], info.tuple_type)
337337
selftype = TypeVarType(tvd)
338338

339339
def add_method(funcname: str,

test-data/unit/check-attr.test

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -187,10 +187,10 @@ class A:
187187
reveal_type(A) # E: Revealed type is 'def (a: builtins.int) -> __main__.A'
188188
reveal_type(A.__eq__) # E: Revealed type is 'def (self: __main__.A, other: builtins.object) -> builtins.bool'
189189
reveal_type(A.__ne__) # E: Revealed type is 'def (self: __main__.A, other: builtins.object) -> builtins.bool'
190-
reveal_type(A.__lt__) # E: Revealed type is 'def [AT] (self: AT`1, other: AT`1) -> builtins.bool'
191-
reveal_type(A.__le__) # E: Revealed type is 'def [AT] (self: AT`1, other: AT`1) -> builtins.bool'
192-
reveal_type(A.__gt__) # E: Revealed type is 'def [AT] (self: AT`1, other: AT`1) -> builtins.bool'
193-
reveal_type(A.__ge__) # E: Revealed type is 'def [AT] (self: AT`1, other: AT`1) -> builtins.bool'
190+
reveal_type(A.__lt__) # E: Revealed type is 'def [AT] (self: AT`-1, other: AT`-1) -> builtins.bool'
191+
reveal_type(A.__le__) # E: Revealed type is 'def [AT] (self: AT`-1, other: AT`-1) -> builtins.bool'
192+
reveal_type(A.__gt__) # E: Revealed type is 'def [AT] (self: AT`-1, other: AT`-1) -> builtins.bool'
193+
reveal_type(A.__ge__) # E: Revealed type is 'def [AT] (self: AT`-1, other: AT`-1) -> builtins.bool'
194194

195195
A(1) < A(2)
196196
A(1) <= A(2)
@@ -654,10 +654,10 @@ class C(A, B): pass
654654
@attr.s
655655
class D(A): pass
656656

657-
reveal_type(A.__lt__) # E: Revealed type is 'def [AT] (self: AT`1, other: AT`1) -> builtins.bool'
658-
reveal_type(B.__lt__) # E: Revealed type is 'def [AT] (self: AT`1, other: AT`1) -> builtins.bool'
659-
reveal_type(C.__lt__) # E: Revealed type is 'def [AT] (self: AT`1, other: AT`1) -> builtins.bool'
660-
reveal_type(D.__lt__) # E: Revealed type is 'def [AT] (self: AT`1, other: AT`1) -> builtins.bool'
657+
reveal_type(A.__lt__) # E: Revealed type is 'def [AT] (self: AT`-1, other: AT`-1) -> builtins.bool'
658+
reveal_type(B.__lt__) # E: Revealed type is 'def [AT] (self: AT`-1, other: AT`-1) -> builtins.bool'
659+
reveal_type(C.__lt__) # E: Revealed type is 'def [AT] (self: AT`-1, other: AT`-1) -> builtins.bool'
660+
reveal_type(D.__lt__) # E: Revealed type is 'def [AT] (self: AT`-1, other: AT`-1) -> builtins.bool'
661661

662662
A() < A()
663663
B() < B()
@@ -909,3 +909,15 @@ class A:
909909
A(None, None)
910910

911911
[builtins fixtures/attr.pyi]
912+
913+
[case testAttrsTypeVarNoCollision]
914+
from typing import TypeVar, Generic
915+
import attr
916+
917+
T = TypeVar("T", bytes, str)
918+
919+
# Make sure the generated __le__ (and friends) don't use T for their arguments.
920+
@attr.s(auto_attribs=True)
921+
class A(Generic[T]):
922+
v: T
923+
[builtins fixtures/attr.pyi]

test-data/unit/check-incremental.test

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3642,10 +3642,10 @@ from a import A
36423642
reveal_type(A) # E: Revealed type is 'def (a: builtins.int) -> a.A'
36433643
reveal_type(A.__eq__) # E: Revealed type is 'def (self: a.A, other: builtins.object) -> builtins.bool'
36443644
reveal_type(A.__ne__) # E: Revealed type is 'def (self: a.A, other: builtins.object) -> builtins.bool'
3645-
reveal_type(A.__lt__) # E: Revealed type is 'def [AT] (self: AT`1, other: AT`1) -> builtins.bool'
3646-
reveal_type(A.__le__) # E: Revealed type is 'def [AT] (self: AT`1, other: AT`1) -> builtins.bool'
3647-
reveal_type(A.__gt__) # E: Revealed type is 'def [AT] (self: AT`1, other: AT`1) -> builtins.bool'
3648-
reveal_type(A.__ge__) # E: Revealed type is 'def [AT] (self: AT`1, other: AT`1) -> builtins.bool'
3645+
reveal_type(A.__lt__) # E: Revealed type is 'def [AT] (self: AT`-1, other: AT`-1) -> builtins.bool'
3646+
reveal_type(A.__le__) # E: Revealed type is 'def [AT] (self: AT`-1, other: AT`-1) -> builtins.bool'
3647+
reveal_type(A.__gt__) # E: Revealed type is 'def [AT] (self: AT`-1, other: AT`-1) -> builtins.bool'
3648+
reveal_type(A.__ge__) # E: Revealed type is 'def [AT] (self: AT`-1, other: AT`-1) -> builtins.bool'
36493649

36503650
A(1) < A(2)
36513651
A(1) <= A(2)
@@ -3681,10 +3681,10 @@ class A:
36813681
main:2: error: Revealed type is 'def (a: builtins.int) -> a.A'
36823682
main:3: error: Revealed type is 'def (self: a.A, other: builtins.object) -> builtins.bool'
36833683
main:4: error: Revealed type is 'def (self: a.A, other: builtins.object) -> builtins.bool'
3684-
main:5: error: Revealed type is 'def [AT] (self: AT`1, other: AT`1) -> builtins.bool'
3685-
main:6: error: Revealed type is 'def [AT] (self: AT`1, other: AT`1) -> builtins.bool'
3686-
main:7: error: Revealed type is 'def [AT] (self: AT`1, other: AT`1) -> builtins.bool'
3687-
main:8: error: Revealed type is 'def [AT] (self: AT`1, other: AT`1) -> builtins.bool'
3684+
main:5: error: Revealed type is 'def [AT] (self: AT`-1, other: AT`-1) -> builtins.bool'
3685+
main:6: error: Revealed type is 'def [AT] (self: AT`-1, other: AT`-1) -> builtins.bool'
3686+
main:7: error: Revealed type is 'def [AT] (self: AT`-1, other: AT`-1) -> builtins.bool'
3687+
main:8: error: Revealed type is 'def [AT] (self: AT`-1, other: AT`-1) -> builtins.bool'
36883688
main:17: error: Unsupported operand types for < ("A" and "int")
36893689
main:18: error: Unsupported operand types for <= ("A" and "int")
36903690
main:19: error: Unsupported operand types for > ("A" and "int")
@@ -4526,10 +4526,10 @@ class A:
45264526
tmp/b.py:3: error: Revealed type is 'def (a: builtins.int) -> a.A'
45274527
tmp/b.py:4: error: Revealed type is 'def (builtins.object, builtins.object) -> builtins.bool'
45284528
tmp/b.py:5: error: Revealed type is 'def (builtins.object, builtins.object) -> builtins.bool'
4529-
tmp/b.py:6: error: Revealed type is 'def [T] (self: T`1, other: T`1) -> builtins.bool'
4530-
tmp/b.py:7: error: Revealed type is 'def [T] (self: T`1, other: T`1) -> builtins.bool'
4531-
tmp/b.py:8: error: Revealed type is 'def [T] (self: T`1, other: T`1) -> builtins.bool'
4532-
tmp/b.py:9: error: Revealed type is 'def [T] (self: T`1, other: T`1) -> builtins.bool'
4529+
tmp/b.py:6: error: Revealed type is 'def [T] (self: T`-1, other: T`-1) -> builtins.bool'
4530+
tmp/b.py:7: error: Revealed type is 'def [T] (self: T`-1, other: T`-1) -> builtins.bool'
4531+
tmp/b.py:8: error: Revealed type is 'def [T] (self: T`-1, other: T`-1) -> builtins.bool'
4532+
tmp/b.py:9: error: Revealed type is 'def [T] (self: T`-1, other: T`-1) -> builtins.bool'
45334533
tmp/b.py:18: error: Unsupported operand types for < ("A" and "int")
45344534
tmp/b.py:19: error: Unsupported operand types for <= ("A" and "int")
45354535
tmp/b.py:20: error: Unsupported operand types for > ("A" and "int")

0 commit comments

Comments
 (0)