From d76172204213da0f78cd6395a8922018cfa20c89 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ilinskiy Date: Thu, 29 Jun 2017 21:56:31 -0700 Subject: [PATCH 1/3] Option to disallow omitting type parameters of generic types (--disallow-any=generics) This code is based on #3141 by pkch. This option disallows implicit Anys from omitted type parameters to generic types. For instance, `def x() -> List` would produce an error while `def x() -> List[Any]` is allowed. Note that with the flag enabled builtin generic types such as `list` and `set` are also forbidden. --- mypy/main.py | 2 +- mypy/messages.py | 2 + mypy/semanal.py | 31 ++++-- mypy/typeanal.py | 85 ++++++++++++---- mypy/types.py | 16 +-- test-data/unit/cmdline.test | 188 ++++++++++++++++++++++++++++++++++++ 6 files changed, 291 insertions(+), 33 deletions(-) diff --git a/mypy/main.py b/mypy/main.py index d9740c78e532..69861fd39d87 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -97,7 +97,7 @@ def type_check_only(sources: List[BuildSource], bin_dir: str, options: Options) options=options) -disallow_any_options = ['unimported', 'expr', 'unannotated', 'decorated'] +disallow_any_options = ['unimported', 'expr', 'unannotated', 'decorated', 'generics'] def disallow_any_argument_type(raw_options: str) -> List[str]: diff --git a/mypy/messages.py b/mypy/messages.py index 3abeb97eb4d4..e0eef81bc307 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -88,6 +88,8 @@ NON_BOOLEAN_IN_CONDITIONAL = 'Condition must be a boolean' DUPLICATE_TYPE_SIGNATURES = 'Function has duplicate type signatures' GENERIC_INSTANCE_VAR_CLASS_ACCESS = 'Access to generic instance variables via class is ambiguous' +BARE_GENERIC = 'Missing type parameters for generic type' +GENERIC_BUILTIN_TYPES_DISALLOWED = "Builtin generic types are disallowed. Use '{}' instead" ARG_CONSTRUCTOR_NAMES = { ARG_POS: "Arg", diff --git a/mypy/semanal.py b/mypy/semanal.py index c236b7c19b9d..4dc80a78dbf5 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -77,20 +77,18 @@ from mypy.errors import Errors, report_internal_error from mypy.messages import CANNOT_ASSIGN_TO_TYPE, MessageBuilder from mypy.types import ( - NoneTyp, CallableType, Overloaded, Instance, Type, TypeVarType, AnyType, - FunctionLike, UnboundType, TypeList, TypeVarDef, TypeType, - TupleType, UnionType, StarType, EllipsisType, function_type, TypedDictType, - TypeQuery + NoneTyp, CallableType, Overloaded, Instance, Type, TypeVarType, AnyType, FunctionLike, + UnboundType, TypeVarDef, TypeType, TupleType, UnionType, StarType, function_type, TypedDictType ) from mypy.nodes import implicit_module_attrs from mypy.typeanal import ( TypeAnalyser, TypeAnalyserPass3, analyze_type_alias, no_subscript_builtin_alias, - TypeVariableQuery, TypeVarList, remove_dups, has_any_from_unimported_type + TypeVariableQuery, TypeVarList, remove_dups, has_any_from_unimported_type, collect_any_types ) from mypy.exprtotype import expr_to_unanalyzed_type, TypeTranslationError from mypy.sametypes import is_same_type from mypy.options import Options -from mypy import experiments +from mypy import experiments, messages from mypy.plugin import Plugin from mypy import join @@ -230,6 +228,7 @@ class SemanticAnalyzer(NodeVisitor): loop_depth = 0 # Depth of breakable loops cur_mod_id = '' # Current module id (or None) (phase 2) is_stub_file = False # Are we analyzing a stub file? + is_typeshed_stub_file = False # Are we analyzing a typeshed stub file? imports = None # type: Set[str] # Imported modules (during phase 2 analysis) errors = None # type: Errors # Keeps track of generated errors plugin = None # type: Plugin # Mypy plugin for special casing of library features @@ -274,6 +273,7 @@ def visit_file(self, file_node: MypyFile, fnam: str, options: Options, self.cur_mod_node = file_node self.cur_mod_id = file_node.fullname() self.is_stub_file = fnam.lower().endswith('.pyi') + self.is_typeshed_stub_file = self.errors.is_typeshed_file(file_node.path) self.globals = file_node.names self.patches = patches @@ -339,6 +339,7 @@ def file_context(self, file_node: MypyFile, fnam: str, options: Options, self.cur_mod_node = file_node self.cur_mod_id = file_node.fullname() self.is_stub_file = fnam.lower().endswith('.pyi') + self.is_typeshed_stub_file = self.errors.is_typeshed_file(file_node.path) self.globals = file_node.names if active_type: self.enter_class(active_type.defn.info) @@ -1529,6 +1530,8 @@ def type_analyzer(self, *, tvar_scope, self.fail, self.plugin, + self.options, + self.is_typeshed_stub_file, aliasing=aliasing, allow_tuple_literal=allow_tuple_literal, allow_unnormalized=self.is_stub_file) @@ -1568,6 +1571,8 @@ def visit_assignment_stmt(self, s: AssignmentStmt) -> None: self.tvar_scope, self.fail, self.plugin, + self.options, + self.is_typeshed_stub_file, allow_unnormalized=True) if res and (not isinstance(res, Instance) or res.args): # TODO: What if this gets reassigned? @@ -3176,6 +3181,8 @@ def visit_index_expr(self, expr: IndexExpr) -> None: self.tvar_scope, self.fail, self.plugin, + self.options, + self.is_typeshed_stub_file, allow_unnormalized=self.is_stub_file) expr.analyzed = TypeAliasExpr(res, fallback=self.alias_fallback(res), in_runtime=True) @@ -3863,6 +3870,7 @@ def __init__(self, modules: Dict[str, MypyFile], errors: Errors) -> None: def visit_file(self, file_node: MypyFile, fnam: str, options: Options) -> None: self.errors.set_file(fnam, file_node.fullname()) self.options = options + self.is_typeshed_file = self.errors.is_typeshed_file(fnam) with experiments.strict_optional_set(options.strict_optional): self.accept(file_node) @@ -3994,8 +4002,17 @@ def visit_type_application(self, e: TypeApplication) -> None: def analyze(self, type: Optional[Type]) -> None: if type: - analyzer = TypeAnalyserPass3(self.fail) + analyzer = TypeAnalyserPass3(self.fail, self.options, self.is_typeshed_file) type.accept(analyzer) + self.check_for_omitted_generics(type) + + def check_for_omitted_generics(self, typ: Type) -> None: + if 'generics' not in self.options.disallow_any or self.is_typeshed_file: + return + + for t in collect_any_types(typ): + if t.from_omitted_generics: + self.fail(messages.BARE_GENERIC, t) def fail(self, msg: str, ctx: Context, *, blocker: bool = False) -> None: self.errors.report(ctx.get_line(), ctx.get_column(), msg) diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 96047129a15b..ec8f845afefb 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -6,6 +6,7 @@ from contextlib import contextmanager +from mypy.options import Options from mypy.types import ( Type, UnboundType, TypeVarType, TupleType, TypedDictType, UnionType, Instance, AnyType, CallableType, NoneTyp, DeletedType, TypeList, TypeVarDef, TypeVisitor, @@ -15,18 +16,16 @@ ) from mypy.nodes import ( - TVAR, TYPE_ALIAS, UNBOUND_IMPORTED, - TypeInfo, Context, SymbolTableNode, Var, Expression, - IndexExpr, RefExpr, nongen_builtins, check_arg_names, check_arg_kinds, - ARG_POS, ARG_NAMED, ARG_OPT, ARG_NAMED_OPT, ARG_STAR, ARG_STAR2, TypeVarExpr + TVAR, TYPE_ALIAS, UNBOUND_IMPORTED, TypeInfo, Context, SymbolTableNode, Var, Expression, + IndexExpr, RefExpr, nongen_builtins, check_arg_names, check_arg_kinds, ARG_POS, ARG_NAMED, + ARG_OPT, ARG_NAMED_OPT, ARG_STAR, ARG_STAR2, TypeVarExpr ) from mypy.tvar_scope import TypeVarScope from mypy.sametypes import is_same_type from mypy.exprtotype import expr_to_unanalyzed_type, TypeTranslationError from mypy.subtypes import is_subtype from mypy.plugin import Plugin, AnalyzerPluginInterface, AnalyzeTypeContext -from mypy import nodes -from mypy import experiments +from mypy import nodes, messages T = TypeVar('T') @@ -56,6 +55,8 @@ def analyze_type_alias(node: Expression, tvar_scope: TypeVarScope, fail_func: Callable[[str, Context], None], plugin: Plugin, + options: Options, + is_typeshed_stub: bool, allow_unnormalized: bool = False) -> Optional[Type]: """Return type if node is valid as a type alias rvalue. @@ -98,8 +99,8 @@ def analyze_type_alias(node: Expression, except TypeTranslationError: fail_func('Invalid type alias', node) return None - analyzer = TypeAnalyser(lookup_func, lookup_fqn_func, tvar_scope, fail_func, plugin, - aliasing=True, allow_unnormalized=allow_unnormalized) + analyzer = TypeAnalyser(lookup_func, lookup_fqn_func, tvar_scope, fail_func, plugin, options, + is_typeshed_stub, aliasing=True, allow_unnormalized=allow_unnormalized) return type.accept(analyzer) @@ -122,7 +123,9 @@ def __init__(self, lookup_fqn_func: Callable[[str], SymbolTableNode], tvar_scope: TypeVarScope, fail_func: Callable[[str, Context], None], - plugin: Plugin, *, + plugin: Plugin, + options: Options, + is_typeshed_stub: bool, *, aliasing: bool = False, allow_tuple_literal: bool = False, allow_unnormalized: bool = False) -> None: @@ -136,6 +139,8 @@ def __init__(self, self.nesting_level = 0 self.allow_unnormalized = allow_unnormalized self.plugin = plugin + self.options = options + self.is_typeshed_stub = is_typeshed_stub def visit_unbound_type(self, t: UnboundType) -> Type: if t.optional: @@ -170,7 +175,11 @@ def visit_unbound_type(self, t: UnboundType) -> Type: elif fullname == 'typing.Tuple': if len(t.args) == 0 and not t.empty_tuple_index: # Bare 'Tuple' is same as 'tuple' - return self.named_type('builtins.tuple') + if 'generics' in self.options.disallow_any and not self.is_typeshed_stub: + self.fail(messages.BARE_GENERIC, t) + typ = self.named_type('builtins.tuple', line=t.line, column=t.column) + typ.from_generic_builtin = True + return typ if len(t.args) == 2 and isinstance(t.args[1], EllipsisType): # Tuple[T, ...] (uniform, variable-length tuple) instance = self.named_type('builtins.tuple', [self.anal_type(t.args[0])]) @@ -190,7 +199,8 @@ def visit_unbound_type(self, t: UnboundType) -> Type: return self.analyze_callable_type(t) elif fullname == 'typing.Type': if len(t.args) == 0: - return TypeType(AnyType(), line=t.line) + any_type = AnyType(from_omitted_generics=True, line=t.line, column=t.column) + return TypeType(any_type, line=t.line, column=t.column) if len(t.args) != 1: self.fail('Type[...] must have exactly one type argument', t) item = self.anal_type(t.args[0]) @@ -219,7 +229,8 @@ def visit_unbound_type(self, t: UnboundType) -> Type: act_len = len(an_args) if exp_len > 0 and act_len == 0: # Interpret bare Alias same as normal generic, i.e., Alias[Any, Any, ...] - return self.replace_alias_tvars(override, all_vars, [AnyType()] * exp_len, + any_type = AnyType(from_omitted_generics=True, line=t.line, column=t.column) + return self.replace_alias_tvars(override, all_vars, [any_type] * exp_len, t.line, t.column) if exp_len == 0 and act_len == 0: return override @@ -255,6 +266,7 @@ def visit_unbound_type(self, t: UnboundType) -> Type: # valid count at this point. Thus we may construct an # Instance with an invalid number of type arguments. instance = Instance(info, self.anal_array(t.args), t.line, t.column) + instance.from_generic_builtin = sym.normalized tup = info.tuple_type if tup is not None: # The class has a Tuple[...] base class so it will be @@ -395,10 +407,11 @@ def analyze_callable_type(self, t: UnboundType) -> Type: fallback = self.named_type('builtins.function') if len(t.args) == 0: # Callable (bare). Treat as Callable[..., Any]. - ret = CallableType([AnyType(), AnyType()], + any_type = AnyType(from_omitted_generics=True, line=t.line, column=t.column) + ret = CallableType([any_type, any_type], [nodes.ARG_STAR, nodes.ARG_STAR2], [None, None], - ret_type=AnyType(), + ret_type=any_type, fallback=fallback, is_ellipsis_args=True) elif len(t.args) == 2: @@ -552,10 +565,13 @@ def anal_var_defs(self, var_defs: List[TypeVarDef]) -> List[TypeVarDef]: vd.line)) return a - def named_type(self, fully_qualified_name: str, args: List[Type] = None) -> Instance: + def named_type(self, fully_qualified_name: str, + args: List[Type] = None, + line: int = -1, + column: int = -1) -> Instance: node = self.lookup_fqn_func(fully_qualified_name) assert isinstance(node.node, TypeInfo) - return Instance(node.node, args or []) + return Instance(node.node, args or [], line=line, column=column) def tuple_type(self, items: List[Type]) -> TupleType: return TupleType(items, fallback=self.named_type('builtins.tuple', [AnyType()])) @@ -581,16 +597,29 @@ class TypeAnalyserPass3(TypeVisitor[None]): to types. """ - def __init__(self, fail_func: Callable[[str, Context], None]) -> None: + def __init__(self, + fail_func: Callable[[str, Context], None], + options: Options, + is_typeshed_stub: bool) -> None: self.fail = fail_func + self.options = options + self.is_typeshed_stub = is_typeshed_stub def visit_instance(self, t: Instance) -> None: info = t.type # Check type argument count. if len(t.args) != len(info.type_vars): if len(t.args) == 0: + from_builtins = t.type.fullname() in nongen_builtins and not t.from_generic_builtin + if ('generics' in self.options.disallow_any and + not self.is_typeshed_stub and + from_builtins): + alternative = nongen_builtins[t.type.fullname()] + self.fail(messages.GENERIC_BUILTIN_TYPES_DISALLOWED.format(alternative), t) # Insert implicit 'Any' type arguments. - t.args = [AnyType()] * len(info.type_vars) + any_type = AnyType(from_omitted_generics=not from_builtins, line=t.line, + column=t.line) + t.args = [any_type] * len(info.type_vars) return # Invalid number of type parameters. n = len(info.type_vars) @@ -775,6 +804,26 @@ def visit_typeddict_type(self, t: TypedDictType) -> bool: return False +def collect_any_types(t: Type) -> List[AnyType]: + """Return all inner `AnyType`s of type t""" + return t.accept(CollectAnyTypesQuery()) + + +class CollectAnyTypesQuery(TypeQuery[List[AnyType]]): + def __init__(self) -> None: + super().__init__(self.combine_lists_strategy) + + def visit_any(self, t: AnyType) -> List[AnyType]: + return [t] + + @classmethod + def combine_lists_strategy(cls, it: Iterable[List[AnyType]]) -> List[AnyType]: + result = [] # type: List[AnyType] + for l in it: + result.extend(l) + return result + + def make_optional_type(t: Type) -> Type: """Return the type corresponding to Optional[t]. diff --git a/mypy/types.py b/mypy/types.py index da6ddba5207d..9151fa918f34 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -1,22 +1,20 @@ """Classes for representing mypy types.""" -from abc import abstractmethod import copy +from abc import abstractmethod from collections import OrderedDict from typing import ( - Any, TypeVar, Dict, List, Tuple, cast, Generic, Set, Sequence, Optional, Union, Iterable, - NamedTuple, Callable, + Any, TypeVar, Dict, List, Tuple, cast, Generic, Set, Optional, Union, Iterable, NamedTuple, + Callable, ) import mypy.nodes +from mypy import experiments from mypy.nodes import ( - INVARIANT, SymbolNode, - ARG_POS, ARG_OPT, ARG_STAR, ARG_STAR2, ARG_NAMED, ARG_NAMED_OPT, + INVARIANT, SymbolNode, ARG_POS, ARG_OPT, ARG_STAR, ARG_STAR2, ARG_NAMED, ARG_NAMED_OPT, ) from mypy.sharedparse import argument_elide_name from mypy.util import IdMapper -from mypy import experiments - T = TypeVar('T') @@ -255,6 +253,7 @@ class AnyType(Type): def __init__(self, implicit: bool = False, from_unimported_type: bool = False, + from_omitted_generics: bool = False, line: int = -1, column: int = -1) -> None: super().__init__(line, column) @@ -262,6 +261,8 @@ def __init__(self, self.implicit = implicit # Does this come from an unfollowed import? See --disallow-any=unimported option self.from_unimported_type = from_unimported_type + # Does this type come from omitted generics? + self.from_omitted_generics = from_omitted_generics def accept(self, visitor: 'TypeVisitor[T]') -> T: return visitor.visit_any(self) @@ -385,6 +386,7 @@ class Instance(Type): args = None # type: List[Type] erased = False # True if result of type variable substitution invalid = False # True if recovered after incorrect number of type arguments error + from_generic_builtin = False # True if created from a generic builtin (e.g. list() or set()) def __init__(self, typ: mypy.nodes.TypeInfo, args: List[Type], line: int = -1, column: int = -1, erased: bool = False) -> None: diff --git a/test-data/unit/cmdline.test b/test-data/unit/cmdline.test index 294c0a107e5e..9a44bc8d8578 100644 --- a/test-data/unit/cmdline.test +++ b/test-data/unit/cmdline.test @@ -681,3 +681,191 @@ from unreal import F def f(x: F) -> None: pass [out] + +[case testDisallowAnyGenericsTupleNoTypeParams] +# cmd: mypy m.py +[file mypy.ini] +[[mypy] +[[mypy-m] +disallow_any = generics + +[file m.py] +from typing import Tuple + +def f(s: Tuple) -> None: pass # error +def g(s) -> Tuple: # error + return 'a', 'b' +def h(s) -> Tuple[str, str]: # no error + return 'a', 'b' +x: Tuple = () # error +[out] +m.py:3: error: Missing type parameters for generic type +m.py:4: error: Missing type parameters for generic type +m.py:8: error: Missing type parameters for generic type + +[case testDisallowAnyGenericsTupleWithNoTypeParamsGeneric] +# cmd: mypy m.py +[file mypy.ini] +[[mypy] +[[mypy-m] +disallow_any = generics + +[file m.py] +from typing import Tuple, List + +def f(s: List[Tuple]) -> None: pass # error +def g(s: List[Tuple[str, str]]) -> None: pass # no error +[out] +m.py:3: error: Missing type parameters for generic type + +[case testDisallowAnyGenericsTypeType] +# cmd: mypy m.py +[file mypy.ini] +[[mypy] +[[mypy-m] +disallow_any = generics + +[file m.py] +from typing import Type, Any + +def f(s: Type[Any]) -> None: pass # no error +def g(s) -> Type: # error + return s +def h(s) -> Type[str]: # no error + return s +x: Type = g(0) # error +[out] +m.py:4: error: Missing type parameters for generic type +m.py:8: error: Missing type parameters for generic type + +[case testDisallowAnyGenericsAliasGenericType] +# cmd: mypy m.py +[file mypy.ini] +[[mypy] +[[mypy-m] +disallow_any = generics + +[file m.py] +from typing import List + +L = List # no error + +def f(l: L) -> None: pass # error +def g(l: L[str]) -> None: pass # no error +[out] +m.py:5: error: Missing type parameters for generic type + +[case testDisallowAnyGenericsGenericAlias] +# cmd: mypy m.py +[file mypy.ini] +[[mypy] +[[mypy-m] +disallow_any = generics + +[file m.py] +from typing import List, TypeVar, Tuple + +T = TypeVar('T') +A = Tuple[T, str, T] + +def f(s: A) -> None: pass # error +def g(s) -> A: # error + return 'a', 'b', 1 +def h(s) -> A[str]: # no error + return 'a', 'b', 'c' +x: A = ('a', 'b', 1) # error +[out] +m.py:6: error: Missing type parameters for generic type +m.py:7: error: Missing type parameters for generic type +m.py:11: error: Missing type parameters for generic type + +[case testDisallowAnyGenericsPlainList] +# cmd: mypy m.py +[file mypy.ini] +[[mypy] +[[mypy-m] +disallow_any = generics + +[file m.py] +from typing import List + +def f(l: List) -> None: pass # error +def g(l: List[str]) -> None: pass # no error +def h(l: List[List]) -> None: pass # error +def i(l: List[List[List[List]]]) -> None: pass # error + +x = [] # error: need type annotation +y: List = [] # error +[out] +m.py:3: error: Missing type parameters for generic type +m.py:5: error: Missing type parameters for generic type +m.py:6: error: Missing type parameters for generic type +m.py:8: error: Need type annotation for variable +m.py:9: error: Missing type parameters for generic type + +[case testDisallowAnyGenericsCustomGenericClass] +# cmd: mypy m.py +[file mypy.ini] +[[mypy] +[[mypy-m] +disallow_any = generics + +[file m.py] +from typing import Generic, TypeVar, Any + +T = TypeVar('T') +class G(Generic[T]): pass + +def f() -> G: # error + return G() + +x: G[Any] = G() # no error +y: G = x # error + +[out] +m.py:6: error: Missing type parameters for generic type +m.py:10: error: Missing type parameters for generic type + +[case testDisallowAnyGenericsBuiltinCollections] +# cmd: mypy m.py +[file mypy.ini] +[[mypy] +[[mypy-m] +disallow_any = generics + +[file m.py] +s = tuple([1, 2, 3]) # no error + +def f(t: tuple) -> None: pass +def g() -> list: pass +def h(s: dict) -> None: pass +def i(s: set) -> None: pass +def j(s: frozenset) -> None: pass +[out] +m.py:3: error: Builtin generic types are disallowed. Use 'typing.Tuple' instead +m.py:4: error: Builtin generic types are disallowed. Use 'typing.List' instead +m.py:5: error: Builtin generic types are disallowed. Use 'typing.Dict' instead +m.py:6: error: Builtin generic types are disallowed. Use 'typing.Set' instead +m.py:7: error: Builtin generic types are disallowed. Use 'typing.FrozenSet' instead + +[case testDisallowAnyGenericsTypingCollections] +# cmd: mypy m.py +[file mypy.ini] +[[mypy] +[[mypy-m] +disallow_any = generics + +[file m.py] +from typing import Tuple, List, Dict, Set, FrozenSet + +def f(t: Tuple) -> None: pass +def g() -> List: pass +def h(s: Dict) -> None: pass +def i(s: Set) -> None: pass +def j(s: FrozenSet) -> None: pass +[out] +m.py:3: error: Missing type parameters for generic type +m.py:4: error: Missing type parameters for generic type +m.py:5: error: Missing type parameters for generic type +m.py:6: error: Missing type parameters for generic type +m.py:7: error: Missing type parameters for generic type From c2727b60af83cccab9c6b6e09e86b6c3149700ef Mon Sep 17 00:00:00 2001 From: Svyatoslav Ilinskiy Date: Wed, 5 Jul 2017 13:24:36 -0700 Subject: [PATCH 2/3] Create appropriate number of type arguments for fallback --- mypy/semanal.py | 5 ++--- mypy/typeanal.py | 3 ++- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 53182b32a258..3d44a2aed84e 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -77,9 +77,8 @@ from mypy.errors import Errors, report_internal_error from mypy.messages import CANNOT_ASSIGN_TO_TYPE, MessageBuilder from mypy.types import ( - NoneTyp, CallableType, Overloaded, Instance, Type, TypeVarType, AnyType, FunctionLike, - UnboundType, TypeVarDef, TypeType, TupleType, UnionType, StarType, function_type, TypedDictType, - NoneTyp, CallableType, Overloaded, Instance, Type, TypeVarType, AnyType, + FunctionLike, UnboundType, TypeVarDef, TypeType, TupleType, UnionType, StarType, function_type, + TypedDictType, NoneTyp, CallableType, Overloaded, Instance, Type, TypeVarType, AnyType, TypeTranslator, ) from mypy.nodes import implicit_module_attrs diff --git a/mypy/typeanal.py b/mypy/typeanal.py index d4766fdbceef..20dba539fb60 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -573,7 +573,8 @@ def named_type(self, fully_qualified_name: str, column: int = -1) -> Instance: node = self.lookup_fqn_func(fully_qualified_name) assert isinstance(node.node, TypeInfo) - return Instance(node.node, args or [], line=line, column=column) + return Instance(node.node, args or [AnyType()] * len(node.node.defn.type_vars), + line=line, column=column) def tuple_type(self, items: List[Type]) -> TupleType: return TupleType(items, fallback=self.named_type('builtins.tuple', [AnyType()])) From 7729fde79ed75485808a3c4be4197820806f6437 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ilinskiy Date: Wed, 5 Jul 2017 13:31:33 -0700 Subject: [PATCH 3/3] Change error message for using builtin collection types --- mypy/messages.py | 2 +- mypy/typeanal.py | 2 +- test-data/unit/cmdline.test | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/mypy/messages.py b/mypy/messages.py index b4ff5344ddef..6375f4d0ba7b 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -91,7 +91,7 @@ CANNOT_ISINSTANCE_TYPEDDICT = 'Cannot use isinstance() with a TypedDict type' CANNOT_ISINSTANCE_NEWTYPE = 'Cannot use isinstance() with a NewType type' BARE_GENERIC = 'Missing type parameters for generic type' -GENERIC_BUILTIN_TYPES_DISALLOWED = "Builtin generic types are disallowed. Use '{}' instead" +IMPLICIT_GENERIC_ANY_BUILTIN = 'Implicit generic "Any". Use \'{}\' and specify generic parameters' ARG_CONSTRUCTOR_NAMES = { ARG_POS: "Arg", diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 20dba539fb60..a087ed54e4fc 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -618,7 +618,7 @@ def visit_instance(self, t: Instance) -> None: not self.is_typeshed_stub and from_builtins): alternative = nongen_builtins[t.type.fullname()] - self.fail(messages.GENERIC_BUILTIN_TYPES_DISALLOWED.format(alternative), t) + self.fail(messages.IMPLICIT_GENERIC_ANY_BUILTIN.format(alternative), t) # Insert implicit 'Any' type arguments. any_type = AnyType(from_omitted_generics=not from_builtins, line=t.line, column=t.line) diff --git a/test-data/unit/cmdline.test b/test-data/unit/cmdline.test index 4c3d353606b0..667a75c900ad 100644 --- a/test-data/unit/cmdline.test +++ b/test-data/unit/cmdline.test @@ -1070,11 +1070,11 @@ def h(s: dict) -> None: pass def i(s: set) -> None: pass def j(s: frozenset) -> None: pass [out] -m.py:3: error: Builtin generic types are disallowed. Use 'typing.Tuple' instead -m.py:4: error: Builtin generic types are disallowed. Use 'typing.List' instead -m.py:5: error: Builtin generic types are disallowed. Use 'typing.Dict' instead -m.py:6: error: Builtin generic types are disallowed. Use 'typing.Set' instead -m.py:7: error: Builtin generic types are disallowed. Use 'typing.FrozenSet' instead +m.py:3: error: Implicit generic "Any". Use 'typing.Tuple' and specify generic parameters +m.py:4: error: Implicit generic "Any". Use 'typing.List' and specify generic parameters +m.py:5: error: Implicit generic "Any". Use 'typing.Dict' and specify generic parameters +m.py:6: error: Implicit generic "Any". Use 'typing.Set' and specify generic parameters +m.py:7: error: Implicit generic "Any". Use 'typing.FrozenSet' and specify generic parameters [case testDisallowAnyGenericsTypingCollections] # cmd: mypy m.py