|
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
|
@@ -62,19 +64,22 @@ class Attribute:
|
62 | 64 |
|
63 | 65 | def __init__(self, name: str, info: TypeInfo,
|
64 | 66 | has_default: bool, init: bool, kw_only: bool, converter: Converter,
|
65 |
| - context: Context) -> None: |
| 67 | + context: Context, |
| 68 | + init_type: Optional[Type]) -> None: |
66 | 69 | self.name = name
|
67 | 70 | self.info = info
|
68 | 71 | self.has_default = has_default
|
69 | 72 | self.init = init
|
70 | 73 | self.kw_only = kw_only
|
71 | 74 | self.converter = converter
|
72 | 75 | self.context = context
|
| 76 | + self.init_type = init_type |
73 | 77 |
|
74 | 78 | def argument(self, ctx: 'mypy.plugin.ClassDefContext') -> Argument:
|
75 | 79 | """Return this attribute as an argument to __init__."""
|
76 | 80 | assert self.init
|
77 |
| - init_type = self.info[self.name].type |
| 81 | + |
| 82 | + init_type = self.init_type or self.info[self.name].type |
78 | 83 |
|
79 | 84 | if self.converter.name:
|
80 | 85 | # When a converter is set the init_type is overridden by the first argument
|
@@ -160,20 +165,33 @@ def serialize(self) -> JsonDict:
|
160 | 165 | 'converter_is_attr_converters_optional': self.converter.is_attr_converters_optional,
|
161 | 166 | 'context_line': self.context.line,
|
162 | 167 | 'context_column': self.context.column,
|
| 168 | + 'init_type': self.init_type.serialize() if self.init_type else None, |
163 | 169 | }
|
164 | 170 |
|
165 | 171 | @classmethod
|
166 |
| - def deserialize(cls, info: TypeInfo, data: JsonDict) -> 'Attribute': |
| 172 | + def deserialize(cls, info: TypeInfo, |
| 173 | + data: JsonDict, |
| 174 | + api: SemanticAnalyzerPluginInterface) -> 'Attribute': |
167 | 175 | """Return the Attribute that was serialized."""
|
168 |
| - return Attribute( |
169 |
| - data['name'], |
| 176 | + raw_init_type = data['init_type'] |
| 177 | + init_type = deserialize_and_fixup_type(raw_init_type, api) if raw_init_type else None |
| 178 | + |
| 179 | + return Attribute(data['name'], |
170 | 180 | info,
|
171 | 181 | data['has_default'],
|
172 | 182 | data['init'],
|
173 | 183 | data['kw_only'],
|
174 | 184 | Converter(data['converter_name'], data['converter_is_attr_converters_optional']),
|
175 |
| - Context(line=data['context_line'], column=data['context_column']) |
176 |
| - ) |
| 185 | + Context(line=data['context_line'], column=data['context_column']), |
| 186 | + init_type) |
| 187 | + |
| 188 | + def expand_typevar_from_subtype(self, sub_type: TypeInfo) -> None: |
| 189 | + """Expands type vars in the context of a subtype when an attribute is inherited |
| 190 | + from a generic super type.""" |
| 191 | + if not isinstance(self.init_type, TypeVarType): |
| 192 | + return |
| 193 | + |
| 194 | + self.init_type = map_type_from_supertype(self.init_type, sub_type, self.info) |
177 | 195 |
|
178 | 196 |
|
179 | 197 | def _determine_eq_order(ctx: 'mypy.plugin.ClassDefContext') -> bool:
|
@@ -350,7 +368,8 @@ def _analyze_class(ctx: 'mypy.plugin.ClassDefContext',
|
350 | 368 | # Only add an attribute if it hasn't been defined before. This
|
351 | 369 | # allows for overwriting attribute definitions by subclassing.
|
352 | 370 | if data['name'] not in taken_attr_names:
|
353 |
| - a = Attribute.deserialize(super_info, data) |
| 371 | + a = Attribute.deserialize(super_info, data, ctx.api) |
| 372 | + a.expand_typevar_from_subtype(ctx.cls.info) |
354 | 373 | super_attrs.append(a)
|
355 | 374 | taken_attr_names.add(a.name)
|
356 | 375 | attributes = super_attrs + list(own_attrs.values())
|
@@ -451,7 +470,9 @@ def _attribute_from_auto_attrib(ctx: 'mypy.plugin.ClassDefContext',
|
451 | 470 | name = unmangle(lhs.name)
|
452 | 471 | # `x: int` (without equal sign) assigns rvalue to TempNode(AnyType())
|
453 | 472 | has_rhs = not isinstance(rvalue, TempNode)
|
454 |
| - return Attribute(name, ctx.cls.info, has_rhs, True, kw_only, Converter(), stmt) |
| 473 | + sym = ctx.cls.info.names.get(name) |
| 474 | + init_type = sym.type if sym else None |
| 475 | + return Attribute(name, ctx.cls.info, has_rhs, True, kw_only, Converter(), stmt, init_type) |
455 | 476 |
|
456 | 477 |
|
457 | 478 | def _attribute_from_attrib_maker(ctx: 'mypy.plugin.ClassDefContext',
|
@@ -517,7 +538,8 @@ def _attribute_from_attrib_maker(ctx: 'mypy.plugin.ClassDefContext',
|
517 | 538 | converter_info = _parse_converter(ctx, converter)
|
518 | 539 |
|
519 | 540 | name = unmangle(lhs.name)
|
520 |
| - return Attribute(name, ctx.cls.info, attr_has_default, init, kw_only, converter_info, stmt) |
| 541 | + return Attribute(name, ctx.cls.info, attr_has_default, init, |
| 542 | + kw_only, converter_info, stmt, init_type) |
521 | 543 |
|
522 | 544 |
|
523 | 545 | def _parse_converter(ctx: 'mypy.plugin.ClassDefContext',
|
|
0 commit comments