diff --git a/mypy/checkmember.py b/mypy/checkmember.py index 2b944769fa1f..2117194e7fba 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -1,6 +1,6 @@ """Type checking of attribute access""" -from typing import cast, Callable, List, Optional, TypeVar, Any +from typing import cast, Callable, List, Optional, TypeVar from mypy.types import ( Type, Instance, AnyType, TupleType, TypedDictType, CallableType, FunctionLike, TypeVarDef, @@ -10,7 +10,7 @@ from mypy.nodes import ( TypeInfo, FuncBase, Var, FuncDef, SymbolNode, Context, MypyFile, TypeVarExpr, ARG_POS, ARG_STAR, ARG_STAR2, Decorator, OverloadedFuncDef, TypeAlias, TempNode, - is_final_node + is_final_node, SYMBOL_FUNCBASE_TYPES, ) from mypy.messages import MessageBuilder from mypy.maptype import map_instance_to_supertype @@ -624,12 +624,10 @@ def analyze_class_attribute_access(itype: Instance, return mx.chk.handle_partial_var_type(t, mx.is_lvalue, symnode, mx.context) # Find the class where method/variable was defined. - # mypyc hack to workaround mypy misunderstanding multiple inheritance (#3603) - node_node = node.node # type: Any - if isinstance(node_node, Decorator): - super_info = node_node.var.info # type: Optional[TypeInfo] - elif isinstance(node_node, (Var, FuncBase)): - super_info = node_node.info + if isinstance(node.node, Decorator): + super_info = node.node.var.info # type: Optional[TypeInfo] + elif isinstance(node.node, (Var, SYMBOL_FUNCBASE_TYPES)): + super_info = node.node.info else: super_info = None diff --git a/mypy/nodes.py b/mypy/nodes.py index 73c82774f1e2..c9e27d09fa9f 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -403,7 +403,18 @@ def __str__(self) -> str: class FuncBase(Node): - """Abstract base class for function-like nodes""" + """Abstract base class for function-like nodes. + + N.B: Although this has SymbolNode subclasses (FuncDef, + OverloadedFuncDef), avoid calling isinstance(..., FuncBase) on + something that is typed as SymbolNode. This is to work around + mypy bug #3603, in which mypy doesn't understand multiple + inheritance very well, and will assume that a SymbolNode + cannot be a FuncBase. + + Instead, test against SYMBOL_FUNCBASE_TYPES, which enumerates + SymbolNode subclasses that are also FuncBase subclasses. + """ __slots__ = ('type', 'unanalyzed_type', @@ -668,6 +679,11 @@ def deserialize(cls, data: JsonDict) -> 'FuncDef': return ret +# All types that are both SymbolNodes and FuncBases. See the FuncBase +# docstring for the rationale. +SYMBOL_FUNCBASE_TYPES = (OverloadedFuncDef, FuncDef) + + class Decorator(SymbolNode, Statement): """A decorated function. @@ -2857,10 +2873,7 @@ def fullname(self) -> Optional[str]: @property def type(self) -> 'Optional[mypy.types.Type]': node = self.node - if (isinstance(node, Var) and node.type is not None): - return node.type - # mypy thinks this branch is unreachable but it is wrong (#3603) - elif (isinstance(node, FuncBase) and node.type is not None): + if isinstance(node, (Var, SYMBOL_FUNCBASE_TYPES)) and node.type is not None: return node.type elif isinstance(node, Decorator): return node.var.type diff --git a/mypy/plugins/common.py b/mypy/plugins/common.py index e95634152ab0..efc0fd9878f8 100644 --- a/mypy/plugins/common.py +++ b/mypy/plugins/common.py @@ -1,7 +1,7 @@ from typing import List, Optional, Any from mypy.nodes import ( - ARG_POS, MDEF, Argument, Block, CallExpr, Expression, FuncBase, + ARG_POS, MDEF, Argument, Block, CallExpr, Expression, FuncBase, SYMBOL_FUNCBASE_TYPES, FuncDef, PassStmt, RefExpr, SymbolTableNode, Var, StrExpr, ) from mypy.plugin import ClassDefContext @@ -51,9 +51,8 @@ def _get_argument(call: CallExpr, name: str) -> Optional[Expression]: return None callee_type = None - # mypyc hack to workaround mypy misunderstanding multiple inheritance (#3603) - callee_node = call.callee.node # type: Any - if (isinstance(callee_node, (Var, FuncBase)) + callee_node = call.callee.node + if (isinstance(callee_node, (Var, SYMBOL_FUNCBASE_TYPES)) and callee_node.type): callee_node_type = callee_node.type if isinstance(callee_node_type, Overloaded):