| 
4 | 4 | from typing import cast, Dict, Set, List, Tuple, Callable, Union, Optional, Iterable, Sequence, Any  | 
5 | 5 | 
 
  | 
6 | 6 | from mypy.errors import report_internal_error  | 
7 |  | -from mypy.typeanal import has_any_from_unimported_type, check_for_explicit_any, set_any_tvars  | 
 | 7 | +from mypy.typeanal import (  | 
 | 8 | +    has_any_from_unimported_type, check_for_explicit_any, set_any_tvars, expand_type_alias  | 
 | 9 | +)  | 
8 | 10 | from mypy.types import (  | 
9 | 11 |     Type, AnyType, CallableType, Overloaded, NoneTyp, TypeVarDef,  | 
10 | 12 |     TupleType, TypedDictType, Instance, TypeVarType, ErasedType, UnionType,  | 
 | 
21 | 23 |     ConditionalExpr, ComparisonExpr, TempNode, SetComprehension,  | 
22 | 24 |     DictionaryComprehension, ComplexExpr, EllipsisExpr, StarExpr, AwaitExpr, YieldExpr,  | 
23 | 25 |     YieldFromExpr, TypedDictExpr, PromoteExpr, NewTypeExpr, NamedTupleExpr, TypeVarExpr,  | 
24 |  | -    TypeAliasExpr, BackquoteExpr, EnumCallExpr,  | 
25 |  | -    ARG_POS, ARG_OPT, ARG_NAMED, ARG_STAR, ARG_STAR2, MODULE_REF, TVAR, LITERAL_TYPE, REVEAL_TYPE  | 
 | 26 | +    TypeAliasExpr, BackquoteExpr, EnumCallExpr, TypeAlias, ClassDef, Block, SymbolTable,  | 
 | 27 | +    ARG_POS, ARG_OPT, ARG_NAMED, ARG_STAR, ARG_STAR2, MODULE_REF, LITERAL_TYPE, REVEAL_TYPE  | 
26 | 28 | )  | 
27 | 29 | from mypy.literals import literal  | 
28 | 30 | from mypy import nodes  | 
@@ -181,6 +183,14 @@ def analyze_ref_expr(self, e: RefExpr, lvalue: bool = False) -> Type:  | 
181 | 183 |                 result = self.named_type('builtins.object')  | 
182 | 184 |         elif isinstance(node, Decorator):  | 
183 | 185 |             result = self.analyze_var_ref(node.var, e)  | 
 | 186 | +        elif isinstance(node, TypeAlias):  | 
 | 187 | +            # Something that refers to a type alias appears in runtime context.  | 
 | 188 | +            # Note that we suppress bogus errors for alias redefinitions,  | 
 | 189 | +            # they are already reported in semanal.py.  | 
 | 190 | +            result = self.alias_type_in_runtime_context(node.target, node.alias_tvars,  | 
 | 191 | +                                                        node.no_args, e,  | 
 | 192 | +                                                        alias_definition=e.is_alias_rvalue  | 
 | 193 | +                                                        or lvalue)  | 
184 | 194 |         else:  | 
185 | 195 |             # Unknown reference; use any type implicitly to avoid  | 
186 | 196 |             # generating extra type errors.  | 
@@ -220,8 +230,8 @@ def visit_call_expr(self, e: CallExpr, allow_none_return: bool = False) -> Type:  | 
220 | 230 |                         pass  | 
221 | 231 |                 if ((isinstance(typ, IndexExpr)  | 
222 | 232 |                         and isinstance(typ.analyzed, (TypeApplication, TypeAliasExpr)))  | 
223 |  | -                        # node.kind == TYPE_ALIAS only for aliases like It = Iterable[int].  | 
224 |  | -                        or (isinstance(typ, NameExpr) and node and node.kind == nodes.TYPE_ALIAS)):  | 
 | 233 | +                        or (isinstance(typ, NameExpr) and node and  | 
 | 234 | +                            isinstance(node.node, TypeAlias) and not node.node.no_args)):  | 
225 | 235 |                     self.msg.type_arguments_not_allowed(e)  | 
226 | 236 |                 if isinstance(typ, RefExpr) and isinstance(typ.node, TypeInfo):  | 
227 | 237 |                     if typ.node.typeddict_type:  | 
@@ -2094,60 +2104,119 @@ def visit_reveal_expr(self, expr: RevealExpr) -> Type:  | 
2094 | 2104 |             return NoneTyp()  | 
2095 | 2105 | 
 
  | 
2096 | 2106 |     def visit_type_application(self, tapp: TypeApplication) -> Type:  | 
2097 |  | -        """Type check a type application (expr[type, ...])."""  | 
2098 |  | -        tp = self.accept(tapp.expr)  | 
2099 |  | -        if isinstance(tp, CallableType):  | 
2100 |  | -            if not tp.is_type_obj():  | 
 | 2107 | +        """Type check a type application (expr[type, ...]).  | 
 | 2108 | +
  | 
 | 2109 | +        There are two different options here, depending on whether expr refers  | 
 | 2110 | +        to a type alias or directly to a generic class. In the first case we need  | 
 | 2111 | +        to use a dedicated function typeanal.expand_type_aliases. This  | 
 | 2112 | +        is due to the fact that currently type aliases machinery uses  | 
 | 2113 | +        unbound type variables, while normal generics use bound ones;  | 
 | 2114 | +        see TypeAlias docstring for more details.  | 
 | 2115 | +        """  | 
 | 2116 | +        if isinstance(tapp.expr, RefExpr) and isinstance(tapp.expr.node, TypeAlias):  | 
 | 2117 | +            # Subscription of a (generic) alias in runtime context, expand the alias.  | 
 | 2118 | +            target = tapp.expr.node.target  | 
 | 2119 | +            all_vars = tapp.expr.node.alias_tvars  | 
 | 2120 | +            item = expand_type_alias(target, all_vars, tapp.types, self.chk.fail,  | 
 | 2121 | +                                     tapp.expr.node.no_args, tapp)  | 
 | 2122 | +            if isinstance(item, Instance):  | 
 | 2123 | +                tp = type_object_type(item.type, self.named_type)  | 
 | 2124 | +                return self.apply_type_arguments_to_callable(tp, item.args, tapp)  | 
 | 2125 | +            else:  | 
2101 | 2126 |                 self.chk.fail(messages.ONLY_CLASS_APPLICATION, tapp)  | 
2102 |  | -            if len(tp.variables) != len(tapp.types):  | 
2103 |  | -                self.msg.incompatible_type_application(len(tp.variables),  | 
2104 |  | -                                                       len(tapp.types), tapp)  | 
2105 | 2127 |                 return AnyType(TypeOfAny.from_error)  | 
2106 |  | -            return self.apply_generic_arguments(tp, tapp.types, tapp)  | 
2107 |  | -        elif isinstance(tp, Overloaded):  | 
 | 2128 | +        # Type application of a normal generic class in runtime context.  | 
 | 2129 | +        # This is typically used as `x = G[int]()`.  | 
 | 2130 | +        tp = self.accept(tapp.expr)  | 
 | 2131 | +        if isinstance(tp, (CallableType, Overloaded)):  | 
2108 | 2132 |             if not tp.is_type_obj():  | 
2109 | 2133 |                 self.chk.fail(messages.ONLY_CLASS_APPLICATION, tapp)  | 
2110 |  | -            for item in tp.items():  | 
2111 |  | -                if len(item.variables) != len(tapp.types):  | 
2112 |  | -                    self.msg.incompatible_type_application(len(item.variables),  | 
2113 |  | -                                                           len(tapp.types), tapp)  | 
2114 |  | -                    return AnyType(TypeOfAny.from_error)  | 
2115 |  | -            return Overloaded([self.apply_generic_arguments(item, tapp.types, tapp)  | 
2116 |  | -                               for item in tp.items()])  | 
 | 2134 | +            return self.apply_type_arguments_to_callable(tp, tapp.types, tapp)  | 
2117 | 2135 |         if isinstance(tp, AnyType):  | 
2118 | 2136 |             return AnyType(TypeOfAny.from_another_any, source_any=tp)  | 
2119 | 2137 |         return AnyType(TypeOfAny.special_form)  | 
2120 | 2138 | 
 
  | 
2121 | 2139 |     def visit_type_alias_expr(self, alias: TypeAliasExpr) -> Type:  | 
2122 |  | -        """Get type of a type alias (could be generic) in a runtime expression."""  | 
2123 |  | -        if isinstance(alias.type, Instance) and alias.type.invalid:  | 
 | 2140 | +        """Right hand side of a type alias definition.  | 
 | 2141 | +
  | 
 | 2142 | +        It has the same type as if the alias itself was used in a runtime context.  | 
 | 2143 | +        For example, here:  | 
 | 2144 | +
  | 
 | 2145 | +            A = reveal_type(List[T])  | 
 | 2146 | +            reveal_type(A)  | 
 | 2147 | +
  | 
 | 2148 | +        both `reveal_type` instances will reveal the same type `def (...) -> builtins.list[Any]`.  | 
 | 2149 | +        Note that type variables are implicitly substituted with `Any`.  | 
 | 2150 | +        """  | 
 | 2151 | +        return self.alias_type_in_runtime_context(alias.type, alias.tvars, alias.no_args,  | 
 | 2152 | +                                                  alias, alias_definition=True)  | 
 | 2153 | + | 
 | 2154 | +    def alias_type_in_runtime_context(self, target: Type, alias_tvars: List[str],  | 
 | 2155 | +                                      no_args: bool, ctx: Context,  | 
 | 2156 | +                                      *,  | 
 | 2157 | +                                      alias_definition: bool = False) -> Type:  | 
 | 2158 | +        """Get type of a type alias (could be generic) in a runtime expression.  | 
 | 2159 | +
  | 
 | 2160 | +        Note that this function can be called only if the alias appears _not_  | 
 | 2161 | +        as a target of type application, which is treated separately in the  | 
 | 2162 | +        visit_type_application method. Some examples where this method is called are  | 
 | 2163 | +        casts and instantiation:  | 
 | 2164 | +
  | 
 | 2165 | +            class LongName(Generic[T]): ...  | 
 | 2166 | +            A = LongName[int]  | 
 | 2167 | +
  | 
 | 2168 | +            x = A()  | 
 | 2169 | +            y = cast(A, ...)  | 
 | 2170 | +        """  | 
 | 2171 | +        if isinstance(target, Instance) and target.invalid:  | 
2124 | 2172 |             # An invalid alias, error already has been reported  | 
2125 | 2173 |             return AnyType(TypeOfAny.from_error)  | 
2126 |  | -        item = alias.type  | 
2127 |  | -        if not alias.in_runtime:  | 
2128 |  | -            # We don't replace TypeVar's with Any for alias used as Alias[T](42).  | 
2129 |  | -            item = set_any_tvars(item, alias.tvars, alias.line, alias.column)  | 
 | 2174 | +        # If this is a generic alias, we set all variables to `Any`.  | 
 | 2175 | +        # For example:  | 
 | 2176 | +        #     A = List[Tuple[T, T]]  | 
 | 2177 | +        #     x = A() <- same as List[Tuple[Any, Any]], see PEP 484.  | 
 | 2178 | +        item = set_any_tvars(target, alias_tvars, ctx.line, ctx.column)  | 
2130 | 2179 |         if isinstance(item, Instance):  | 
2131 | 2180 |             # Normally we get a callable type (or overloaded) with .is_type_obj() true  | 
2132 | 2181 |             # representing the class's constructor  | 
2133 | 2182 |             tp = type_object_type(item.type, self.named_type)  | 
 | 2183 | +            if no_args:  | 
 | 2184 | +                return tp  | 
 | 2185 | +            return self.apply_type_arguments_to_callable(tp, item.args, ctx)  | 
 | 2186 | +        elif (isinstance(item, TupleType) and  | 
 | 2187 | +              # Tuple[str, int]() fails at runtime, only named tuples and subclasses work.  | 
 | 2188 | +              item.fallback.type.fullname() != 'builtins.tuple'):  | 
 | 2189 | +            return type_object_type(item.fallback.type, self.named_type)  | 
 | 2190 | +        elif isinstance(item, AnyType):  | 
 | 2191 | +            return AnyType(TypeOfAny.from_another_any, source_any=item)  | 
2134 | 2192 |         else:  | 
2135 |  | -            # This type is invalid in most runtime contexts  | 
2136 |  | -            # and corresponding an error will be reported.  | 
2137 |  | -            return alias.fallback  | 
 | 2193 | +            if alias_definition:  | 
 | 2194 | +                return AnyType(TypeOfAny.special_form)  | 
 | 2195 | +            # This type is invalid in most runtime contexts.  | 
 | 2196 | +            self.msg.alias_invalid_in_runtime_context(item, ctx)  | 
 | 2197 | +            return AnyType(TypeOfAny.from_error)  | 
 | 2198 | + | 
 | 2199 | +    def apply_type_arguments_to_callable(self, tp: Type, args: List[Type], ctx: Context) -> Type:  | 
 | 2200 | +        """Apply type arguments to a generic callable type coming from a type object.  | 
 | 2201 | +
  | 
 | 2202 | +        This will first perform type arguments count checks, report the  | 
 | 2203 | +        error as needed, and return the correct kind of Any. As a special  | 
 | 2204 | +        case this returns Any for non-callable types, because if type object type  | 
 | 2205 | +        is not callable, then an error should be already reported.  | 
 | 2206 | +        """  | 
2138 | 2207 |         if isinstance(tp, CallableType):  | 
2139 |  | -            if len(tp.variables) != len(item.args):  | 
 | 2208 | +            if len(tp.variables) != len(args):  | 
2140 | 2209 |                 self.msg.incompatible_type_application(len(tp.variables),  | 
2141 |  | -                                                       len(item.args), item)  | 
 | 2210 | +                                                       len(args), ctx)  | 
2142 | 2211 |                 return AnyType(TypeOfAny.from_error)  | 
2143 |  | -            return self.apply_generic_arguments(tp, item.args, item)  | 
2144 |  | -        elif isinstance(tp, Overloaded):  | 
 | 2212 | +            return self.apply_generic_arguments(tp, args, ctx)  | 
 | 2213 | +        if isinstance(tp, Overloaded):  | 
2145 | 2214 |             for it in tp.items():  | 
2146 |  | -                if len(it.variables) != len(item.args):  | 
 | 2215 | +                if len(it.variables) != len(args):  | 
2147 | 2216 |                     self.msg.incompatible_type_application(len(it.variables),  | 
2148 |  | -                                                           len(item.args), item)  | 
 | 2217 | +                                                           len(args), ctx)  | 
2149 | 2218 |                     return AnyType(TypeOfAny.from_error)  | 
2150 |  | -            return Overloaded([self.apply_generic_arguments(it, item.args, item)  | 
 | 2219 | +            return Overloaded([self.apply_generic_arguments(it, args, ctx)  | 
2151 | 2220 |                                for it in tp.items()])  | 
2152 | 2221 |         return AnyType(TypeOfAny.special_form)  | 
2153 | 2222 | 
 
  | 
 | 
0 commit comments