|
15 | 15 | SymbolTableNode, MDEF, JsonDict, OverloadedFuncDef, ARG_NAMED_OPT, ARG_NAMED,
|
16 | 16 | TypeVarExpr, PlaceholderNode
|
17 | 17 | )
|
| 18 | +from mypy.plugin import SemanticAnalyzerPluginInterface |
18 | 19 | from mypy.plugins.common import (
|
19 |
| - _get_argument, _get_bool_argument, _get_decorator_bool_argument, add_method |
| 20 | + _get_argument, _get_bool_argument, _get_decorator_bool_argument, add_method, |
| 21 | + deserialize_and_fixup_type |
20 | 22 | )
|
21 | 23 | from mypy.types import (
|
22 | 24 | Type, AnyType, TypeOfAny, CallableType, NoneType, TypeVarDef, TypeVarType,
|
23 | 25 | Overloaded, UnionType, FunctionLike, get_proper_type
|
24 | 26 | )
|
25 |
| -from mypy.typeops import make_simplified_union |
| 27 | +from mypy.typeops import make_simplified_union, map_type_from_supertype |
26 | 28 | from mypy.typevars import fill_typevars
|
27 | 29 | from mypy.util import unmangle
|
28 | 30 | from mypy.server.trigger import make_wildcard_trigger
|
@@ -70,19 +72,22 @@ class Attribute:
|
70 | 72 |
|
71 | 73 | def __init__(self, name: str, info: TypeInfo,
|
72 | 74 | has_default: bool, init: bool, kw_only: bool, converter: Converter,
|
73 |
| - context: Context) -> None: |
| 75 | + context: Context, |
| 76 | + init_type: Optional[Type]) -> None: |
74 | 77 | self.name = name
|
75 | 78 | self.info = info
|
76 | 79 | self.has_default = has_default
|
77 | 80 | self.init = init
|
78 | 81 | self.kw_only = kw_only
|
79 | 82 | self.converter = converter
|
80 | 83 | self.context = context
|
| 84 | + self.init_type = init_type |
81 | 85 |
|
82 | 86 | def argument(self, ctx: 'mypy.plugin.ClassDefContext') -> Argument:
|
83 | 87 | """Return this attribute as an argument to __init__."""
|
84 | 88 | assert self.init
|
85 |
| - init_type = self.info[self.name].type |
| 89 | + |
| 90 | + init_type = self.init_type or self.info[self.name].type |
86 | 91 |
|
87 | 92 | if self.converter.name:
|
88 | 93 | # When a converter is set the init_type is overridden by the first argument
|
@@ -168,20 +173,33 @@ def serialize(self) -> JsonDict:
|
168 | 173 | 'converter_is_attr_converters_optional': self.converter.is_attr_converters_optional,
|
169 | 174 | 'context_line': self.context.line,
|
170 | 175 | 'context_column': self.context.column,
|
| 176 | + 'init_type': self.init_type.serialize() if self.init_type else None, |
171 | 177 | }
|
172 | 178 |
|
173 | 179 | @classmethod
|
174 |
| - def deserialize(cls, info: TypeInfo, data: JsonDict) -> 'Attribute': |
| 180 | + def deserialize(cls, info: TypeInfo, |
| 181 | + data: JsonDict, |
| 182 | + api: SemanticAnalyzerPluginInterface) -> 'Attribute': |
175 | 183 | """Return the Attribute that was serialized."""
|
176 |
| - return Attribute( |
177 |
| - data['name'], |
| 184 | + raw_init_type = data['init_type'] |
| 185 | + init_type = deserialize_and_fixup_type(raw_init_type, api) if raw_init_type else None |
| 186 | + |
| 187 | + return Attribute(data['name'], |
178 | 188 | info,
|
179 | 189 | data['has_default'],
|
180 | 190 | data['init'],
|
181 | 191 | data['kw_only'],
|
182 | 192 | Converter(data['converter_name'], data['converter_is_attr_converters_optional']),
|
183 |
| - Context(line=data['context_line'], column=data['context_column']) |
184 |
| - ) |
| 193 | + Context(line=data['context_line'], column=data['context_column']), |
| 194 | + init_type) |
| 195 | + |
| 196 | + def expand_typevar_from_subtype(self, sub_type: TypeInfo) -> None: |
| 197 | + """Expands type vars in the context of a subtype when an attribute is inherited |
| 198 | + from a generic super type.""" |
| 199 | + if not isinstance(self.init_type, TypeVarType): |
| 200 | + return |
| 201 | + |
| 202 | + self.init_type = map_type_from_supertype(self.init_type, sub_type, self.info) |
185 | 203 |
|
186 | 204 |
|
187 | 205 | def _determine_eq_order(ctx: 'mypy.plugin.ClassDefContext') -> bool:
|
@@ -363,7 +381,8 @@ def _analyze_class(ctx: 'mypy.plugin.ClassDefContext',
|
363 | 381 | # Only add an attribute if it hasn't been defined before. This
|
364 | 382 | # allows for overwriting attribute definitions by subclassing.
|
365 | 383 | if data['name'] not in taken_attr_names:
|
366 |
| - a = Attribute.deserialize(super_info, data) |
| 384 | + a = Attribute.deserialize(super_info, data, ctx.api) |
| 385 | + a.expand_typevar_from_subtype(ctx.cls.info) |
367 | 386 | super_attrs.append(a)
|
368 | 387 | taken_attr_names.add(a.name)
|
369 | 388 | attributes = super_attrs + list(own_attrs.values())
|
@@ -491,7 +510,9 @@ def _attribute_from_auto_attrib(ctx: 'mypy.plugin.ClassDefContext',
|
491 | 510 | name = unmangle(lhs.name)
|
492 | 511 | # `x: int` (without equal sign) assigns rvalue to TempNode(AnyType())
|
493 | 512 | has_rhs = not isinstance(rvalue, TempNode)
|
494 |
| - return Attribute(name, ctx.cls.info, has_rhs, True, kw_only, Converter(), stmt) |
| 513 | + sym = ctx.cls.info.names.get(name) |
| 514 | + init_type = sym.type if sym else None |
| 515 | + return Attribute(name, ctx.cls.info, has_rhs, True, kw_only, Converter(), stmt, init_type) |
495 | 516 |
|
496 | 517 |
|
497 | 518 | def _attribute_from_attrib_maker(ctx: 'mypy.plugin.ClassDefContext',
|
@@ -557,7 +578,8 @@ def _attribute_from_attrib_maker(ctx: 'mypy.plugin.ClassDefContext',
|
557 | 578 | converter_info = _parse_converter(ctx, converter)
|
558 | 579 |
|
559 | 580 | name = unmangle(lhs.name)
|
560 |
| - return Attribute(name, ctx.cls.info, attr_has_default, init, kw_only, converter_info, stmt) |
| 581 | + return Attribute(name, ctx.cls.info, attr_has_default, init, |
| 582 | + kw_only, converter_info, stmt, init_type) |
561 | 583 |
|
562 | 584 |
|
563 | 585 | def _parse_converter(ctx: 'mypy.plugin.ClassDefContext',
|
|
0 commit comments