Skip to content

Commit 02a06a1

Browse files
authored
Update attrs plugin to work with new semantic analyzer (#6435)
1 parent 0d61a5b commit 02a06a1

File tree

5 files changed

+39
-25
lines changed

5 files changed

+39
-25
lines changed

mypy/checker.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
ComparisonExpr, StarExpr, EllipsisExpr, RefExpr, PromoteExpr,
2323
Import, ImportFrom, ImportAll, ImportBase, TypeAlias,
2424
ARG_POS, ARG_STAR, LITERAL_TYPE, MDEF, GDEF,
25-
CONTRAVARIANT, COVARIANT, INVARIANT,
25+
CONTRAVARIANT, COVARIANT, INVARIANT, TypeVarExpr
2626
)
2727
from mypy import nodes
2828
from mypy.literals import literal, literal_hash
@@ -1614,6 +1614,10 @@ def determine_type_of_class_member(self, sym: SymbolTableNode) -> Optional[Type]
16141614
if isinstance(sym.node, TypeInfo):
16151615
# nested class
16161616
return type_object_type(sym.node, self.named_type)
1617+
if isinstance(sym.node, TypeVarExpr):
1618+
# Use of TypeVars is rejected in an expression/runtime context, so
1619+
# we don't need to check supertype compatibility for them.
1620+
return AnyType(TypeOfAny.special_form)
16171621
return None
16181622

16191623
def check_compatibility(self, name: str, base1: TypeInfo,

mypy/plugins/attrs.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
Context, Argument, Var, ARG_OPT, ARG_POS, TypeInfo, AssignmentStmt,
1010
TupleExpr, ListExpr, NameExpr, CallExpr, RefExpr, FuncBase,
1111
is_class_var, TempNode, Decorator, MemberExpr, Expression,
12-
SymbolTableNode, MDEF, JsonDict, OverloadedFuncDef, ARG_NAMED_OPT, ARG_NAMED
12+
SymbolTableNode, MDEF, JsonDict, OverloadedFuncDef, ARG_NAMED_OPT, ARG_NAMED,
13+
TypeVarExpr
1314
)
1415
from mypy.plugins.common import (
1516
_get_argument, _get_bool_argument, _get_decorator_bool_argument, add_method
@@ -43,6 +44,8 @@
4344
'attr.attr',
4445
} # type: Final
4546

47+
SELF_TVAR_NAME = '_AT' # type: Final
48+
4649

4750
class Converter:
4851
"""Holds information about a `converter=` argument"""
@@ -112,7 +115,7 @@ def argument(self, ctx: 'mypy.plugin.ClassDefContext') -> Argument:
112115
if self.converter.is_attr_converters_optional and init_type:
113116
# If the converter was attr.converter.optional(type) then add None to
114117
# the allowed init_type.
115-
init_type = UnionType.make_simplified_union([init_type, NoneTyp()])
118+
init_type = UnionType.make_union([init_type, NoneTyp()])
116119

117120
if not init_type:
118121
ctx.api.fail("Cannot determine __init__ type from converter", self.context)
@@ -512,8 +515,13 @@ def _add_cmp(ctx: 'mypy.plugin.ClassDefContext', adder: 'MethodAdder') -> None:
512515
# AT = TypeVar('AT')
513516
# def __lt__(self: AT, other: AT) -> bool
514517
# This way comparisons with subclasses will work correctly.
515-
tvd = TypeVarDef('AT', 'AT', -1, [], object_type)
518+
tvd = TypeVarDef(SELF_TVAR_NAME, ctx.cls.info.fullname() + '.' + SELF_TVAR_NAME,
519+
-1, [], object_type)
516520
tvd_type = TypeVarType(tvd)
521+
self_tvar_expr = TypeVarExpr(SELF_TVAR_NAME, ctx.cls.info.fullname() + '.' + SELF_TVAR_NAME,
522+
[], object_type)
523+
ctx.cls.info.names[SELF_TVAR_NAME] = SymbolTableNode(MDEF, self_tvar_expr)
524+
517525
args = [Argument(Var('other', tvd_type), tvd_type, None, ARG_POS)]
518526
for method in ['__lt__', '__le__', '__gt__', '__ge__']:
519527
adder.add_method(method, args, bool_type, self_type=tvd_type, tvd=tvd)

mypy/test/hacks.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
# Files to not run with new semantic analyzer.
99
new_semanal_blacklist = [
1010
'check-async-await.test',
11-
'check-attr.test',
1211
'check-bound.test',
1312
'check-callable.test',
1413
'check-classes.test',

test-data/unit/check-attr.test

Lines changed: 15 additions & 12 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)
@@ -577,7 +577,10 @@ class C:
577577

578578
[builtins fixtures/list.pyi]
579579

580+
-- This is tricky with new analyzer:
581+
-- We need to know the analyzed type of a function while still processing top-level.
580582
[case testAttrsUsingBadConverter]
583+
# flags: --no-new-semantic-analyzer --no-strict-optional
581584
import attr
582585
from typing import overload
583586
@overload
@@ -596,11 +599,11 @@ class A:
596599
bad_overloaded: int = attr.ib(converter=bad_overloaded_converter)
597600
reveal_type(A)
598601
[out]
599-
main:15: error: Cannot determine __init__ type from converter
600-
main:15: error: Argument "converter" has incompatible type "Callable[[], str]"; expected "Callable[[Any], str]"
601602
main:16: error: Cannot determine __init__ type from converter
602-
main:16: error: Argument "converter" has incompatible type overloaded function; expected "Callable[[Any], int]"
603-
main:17: error: Revealed type is 'def (bad: Any, bad_overloaded: Any) -> __main__.A'
603+
main:16: error: Argument "converter" has incompatible type "Callable[[], str]"; expected "Callable[[Any], str]"
604+
main:17: error: Cannot determine __init__ type from converter
605+
main:17: error: Argument "converter" has incompatible type overloaded function; expected "Callable[[Any], int]"
606+
main:18: error: Revealed type is 'def (bad: Any, bad_overloaded: Any) -> __main__.A'
604607
[builtins fixtures/list.pyi]
605608

606609
[case testAttrsUsingUnsupportedConverter]
@@ -667,10 +670,10 @@ class C(A, B): pass
667670
@attr.s
668671
class D(A): pass
669672

670-
reveal_type(A.__lt__) # E: Revealed type is 'def [AT] (self: AT`-1, other: AT`-1) -> builtins.bool'
671-
reveal_type(B.__lt__) # E: Revealed type is 'def [AT] (self: AT`-1, other: AT`-1) -> builtins.bool'
672-
reveal_type(C.__lt__) # E: Revealed type is 'def [AT] (self: AT`-1, other: AT`-1) -> builtins.bool'
673-
reveal_type(D.__lt__) # E: Revealed type is 'def [AT] (self: AT`-1, other: AT`-1) -> builtins.bool'
673+
reveal_type(A.__lt__) # E: Revealed type is 'def [_AT] (self: _AT`-1, other: _AT`-1) -> builtins.bool'
674+
reveal_type(B.__lt__) # E: Revealed type is 'def [_AT] (self: _AT`-1, other: _AT`-1) -> builtins.bool'
675+
reveal_type(C.__lt__) # E: Revealed type is 'def [_AT] (self: _AT`-1, other: _AT`-1) -> builtins.bool'
676+
reveal_type(D.__lt__) # E: Revealed type is 'def [_AT] (self: _AT`-1, other: _AT`-1) -> builtins.bool'
674677

675678
A() < A()
676679
B() < B()

test-data/unit/check-incremental.test

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2951,10 +2951,10 @@ from a import A
29512951
reveal_type(A) # E: Revealed type is 'def (a: builtins.int) -> a.A'
29522952
reveal_type(A.__eq__) # E: Revealed type is 'def (self: a.A, other: builtins.object) -> builtins.bool'
29532953
reveal_type(A.__ne__) # E: Revealed type is 'def (self: a.A, other: builtins.object) -> builtins.bool'
2954-
reveal_type(A.__lt__) # E: Revealed type is 'def [AT] (self: AT`-1, other: AT`-1) -> builtins.bool'
2955-
reveal_type(A.__le__) # E: Revealed type is 'def [AT] (self: AT`-1, other: AT`-1) -> builtins.bool'
2956-
reveal_type(A.__gt__) # E: Revealed type is 'def [AT] (self: AT`-1, other: AT`-1) -> builtins.bool'
2957-
reveal_type(A.__ge__) # E: Revealed type is 'def [AT] (self: AT`-1, other: AT`-1) -> builtins.bool'
2954+
reveal_type(A.__lt__) # E: Revealed type is 'def [_AT] (self: _AT`-1, other: _AT`-1) -> builtins.bool'
2955+
reveal_type(A.__le__) # E: Revealed type is 'def [_AT] (self: _AT`-1, other: _AT`-1) -> builtins.bool'
2956+
reveal_type(A.__gt__) # E: Revealed type is 'def [_AT] (self: _AT`-1, other: _AT`-1) -> builtins.bool'
2957+
reveal_type(A.__ge__) # E: Revealed type is 'def [_AT] (self: _AT`-1, other: _AT`-1) -> builtins.bool'
29582958

29592959
A(1) < A(2)
29602960
A(1) <= A(2)
@@ -2990,10 +2990,10 @@ class A:
29902990
main:2: error: Revealed type is 'def (a: builtins.int) -> a.A'
29912991
main:3: error: Revealed type is 'def (self: a.A, other: builtins.object) -> builtins.bool'
29922992
main:4: error: Revealed type is 'def (self: a.A, other: builtins.object) -> builtins.bool'
2993-
main:5: error: Revealed type is 'def [AT] (self: AT`-1, other: AT`-1) -> builtins.bool'
2994-
main:6: error: Revealed type is 'def [AT] (self: AT`-1, other: AT`-1) -> builtins.bool'
2995-
main:7: error: Revealed type is 'def [AT] (self: AT`-1, other: AT`-1) -> builtins.bool'
2996-
main:8: error: Revealed type is 'def [AT] (self: AT`-1, other: AT`-1) -> builtins.bool'
2993+
main:5: error: Revealed type is 'def [_AT] (self: _AT`-1, other: _AT`-1) -> builtins.bool'
2994+
main:6: error: Revealed type is 'def [_AT] (self: _AT`-1, other: _AT`-1) -> builtins.bool'
2995+
main:7: error: Revealed type is 'def [_AT] (self: _AT`-1, other: _AT`-1) -> builtins.bool'
2996+
main:8: error: Revealed type is 'def [_AT] (self: _AT`-1, other: _AT`-1) -> builtins.bool'
29972997
main:17: error: Unsupported operand types for < ("A" and "int")
29982998
main:18: error: Unsupported operand types for <= ("A" and "int")
29992999
main:19: error: Unsupported operand types for > ("A" and "int")

0 commit comments

Comments
 (0)