From fa8e7a3efb337c112961735c2835aab5a972ab17 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ilinskiy Date: Tue, 20 Jun 2017 16:44:42 -0700 Subject: [PATCH 01/13] Implement option to disallow explicit Any types (--disallow-any=explicit) Type aliases are a little tricky to deal with because they get "inlined". The solution was to unmark Anys as explicit right after we check a type alias for explicit Anys. --- mypy/checker.py | 12 ++- mypy/checkexpr.py | 10 ++- mypy/main.py | 2 +- mypy/messages.py | 3 + mypy/semanal.py | 46 +++++++++- mypy/typeanal.py | 17 +++- mypy/types.py | 3 + test-data/unit/cmdline.test | 175 ++++++++++++++++++++++++++++++++++++ 8 files changed, 262 insertions(+), 6 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 9562e5bc7fd8..4a98ce4fb2ea 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -29,7 +29,7 @@ ARG_POS, MDEF, CONTRAVARIANT, COVARIANT) from mypy import nodes -from mypy.typeanal import has_any_from_unimported_type +from mypy.typeanal import has_any_from_unimported_type, has_explicit_any from mypy.types import ( Type, AnyType, CallableType, FunctionLike, Overloaded, TupleType, TypedDictType, Instance, NoneTyp, strip_type, TypeType, @@ -627,6 +627,11 @@ def is_implicit_any(t: Type) -> bool: if has_any_from_unimported_type(arg_type): prefix = "Argument {} to \"{}\"".format(idx + 1, fdef.name()) self.msg.unimported_type_becomes_any(prefix, arg_type, fdef) + if ('explicit' in self.options.disallow_any and + not self.is_typeshed_stub and + fdef.type and + has_explicit_any(fdef.type)): + self.msg.explicit_any("type annotation", fdef) if name in nodes.reverse_op_method_set: self.check_reverse_op_method(item, typ, name) elif name in ('__getattr__', '__getattribute__'): @@ -1213,6 +1218,11 @@ def visit_assignment_stmt(self, s: AssignmentStmt) -> None: self.msg.unimported_type_becomes_any("A type on this line", AnyType(), s) else: self.msg.unimported_type_becomes_any("Type of variable", s.type, s) + if ('explicit' in self.options.disallow_any and + not self.is_typeshed_stub and + s.type and + has_explicit_any(s.type)): + self.msg.explicit_any("type annotation", s) if len(s.lvalues) > 1: # Chained assignment (e.g. x = y = ...). diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 9c8335b06c24..fd5f5e9b2959 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -4,7 +4,7 @@ from typing import cast, Dict, Set, List, Tuple, Callable, Union, Optional from mypy.errors import report_internal_error -from mypy.typeanal import has_any_from_unimported_type +from mypy.typeanal import has_any_from_unimported_type, has_explicit_any from mypy.types import ( Type, AnyType, CallableType, Overloaded, NoneTyp, TypeVarDef, TupleType, TypedDictType, Instance, TypeVarType, ErasedType, UnionType, @@ -1678,6 +1678,10 @@ def visit_cast_expr(self, expr: CastExpr) -> Type: self.msg.redundant_cast(target_type, expr) if 'unimported' in options.disallow_any and has_any_from_unimported_type(target_type): self.msg.unimported_type_becomes_any("Target type of cast", target_type, expr) + if ('explicit' in options.disallow_any and + not self.chk.is_typeshed_stub and + has_explicit_any(target_type)): + self.msg.explicit_any("cast", expr) return target_type def visit_reveal_type_expr(self, expr: RevealTypeExpr) -> Type: @@ -2398,6 +2402,10 @@ def visit_namedtuple_expr(self, e: NamedTupleExpr) -> Type: if ('unimported' in self.chk.options.disallow_any and has_any_from_unimported_type(tuple_type)): self.msg.unimported_type_becomes_any("NamedTuple type", tuple_type, e) + if ('explicit' in self.chk.options.disallow_any and + not self.chk.is_typeshed_stub and + has_explicit_any(tuple_type)): + self.msg.explicit_any("NamedTuple declaration", e) # TODO: Perhaps return a type object type? return AnyType() diff --git a/mypy/main.py b/mypy/main.py index b1c1529d0608..fa2537f8329b 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'] +disallow_any_options = ['unimported', 'expr', 'unannotated', 'explicit'] def disallow_any_argument_type(raw_options: str) -> List[str]: diff --git a/mypy/messages.py b/mypy/messages.py index 067cbd37f744..967cbd762854 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -872,6 +872,9 @@ def unimported_type_becomes_any(self, prefix: str, typ: Type, ctx: Context) -> N self.fail("{} becomes {} due to an unfollowed import".format(prefix, self.format(typ)), ctx) + def explicit_any(self, location: str, ctx: Context) -> None: + self.fail('Explicit "Any" is not allowed in a {}'.format(location), ctx) + def typeddict_instantiated_with_unexpected_items(self, expected_item_names: List[str], actual_item_names: List[str], diff --git a/mypy/semanal.py b/mypy/semanal.py index 14630d964b29..517842fc67fc 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -47,7 +47,7 @@ from contextlib import contextmanager from typing import ( - List, Dict, Set, Tuple, cast, TypeVar, Union, Optional, Callable, Iterator, + List, Dict, Set, Tuple, cast, TypeVar, Union, Optional, Callable, Iterator, Iterable ) from mypy.nodes import ( @@ -85,7 +85,7 @@ 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, has_explicit_any ) from mypy.exprtotype import expr_to_unanalyzed_type, TypeTranslationError from mypy.sametypes import is_same_type @@ -228,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 @@ -263,6 +264,7 @@ def visit_file(self, file_node: MypyFile, fnam: str, options: Options) -> None: 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 'builtins' in self.modules: @@ -325,6 +327,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) @@ -998,6 +1001,10 @@ def analyze_base_classes(self, defn: ClassDef) -> None: else: prefix = "Base type" self.msg.unimported_type_becomes_any(prefix, base, base_expr) + if ('explicit' in self.options.disallow_any and + not self.is_typeshed_stub_file and + has_explicit_any(base)): + self.msg.explicit_any('base type', base_expr) # Add 'object' as implicit base if there is no other base class. if (not base_types and defn.fullname != 'builtins.object'): @@ -1537,6 +1544,14 @@ def visit_assignment_stmt(self, s: AssignmentStmt) -> None: self.fail, allow_unnormalized=True) if res and (not isinstance(res, Instance) or res.args): # TODO: What if this gets reassigned? + if ('explicit' in self.options.disallow_any and + not self.is_typeshed_stub_file and + has_explicit_any(res)): + self.msg.explicit_any("type alias", s) + # when this type alias gets "inlined", the "Any" is not explicit anymore, + # so we need to mark it as such + unmark_any_as_explicit(res) + name = s.lvalues[0] node = self.lookup(name.name, name) node.kind = TYPE_ALIAS @@ -1801,6 +1816,11 @@ def process_newtype_declaration(self, s: AssignmentStmt) -> None: self.fail(message.format(old_type), s) return + if ('explicit' in self.options.disallow_any and + not self.is_typeshed_stub_file and + has_explicit_any(old_type)): + self.msg.explicit_any("NewType declaration", s) + # If so, add it to the symbol table. node = self.lookup(name, s) if node is None: @@ -1922,6 +1942,10 @@ def process_typevar_declaration(self, s: AssignmentStmt) -> None: prefix = "Upper bound of type variable" self.msg.unimported_type_becomes_any(prefix, upper_bound, s) + if ('explicit' in self.options.disallow_any and + not self.is_typeshed_stub_file and + any(has_explicit_any(t) for t in values + [upper_bound])): + self.msg.explicit_any("TypeVar declaration", s) # Yes, it's a valid type variable definition! Add it to the symbol table. node = self.lookup(name, s) node.kind = TVAR @@ -4310,3 +4334,21 @@ def find_fixed_callable_return(expr: Expression) -> Optional[CallableType]: if isinstance(t.ret_type, CallableType): return t.ret_type return None + + +def unmark_any_as_explicit(t: Type) -> None: + """For all types within t, if that type is Any, set explicit to False""" + t.accept(UnmarkAnyAsExplicit()) + + +class UnmarkAnyAsExplicit(TypeQuery[None]): + def __init__(self) -> None: + super().__init__(UnmarkAnyAsExplicit.do_nothing_strategy) + + def visit_any(self, t: AnyType) -> None: + t.explicit = False + + @classmethod + def do_nothing_strategy(cls, it: Iterable[None]) -> None: + # "it" can be a generator (which is lazy), force it to compute the value + list(it) diff --git a/mypy/typeanal.py b/mypy/typeanal.py index d36ecb98de6a..a1c910b36b9a 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -159,7 +159,7 @@ def visit_unbound_type(self, t: UnboundType) -> Type: elif fullname == 'builtins.None': return NoneTyp() elif fullname == 'typing.Any' or fullname == 'builtins.Any': - return AnyType() + return AnyType(explicit=True) elif fullname == 'typing.Tuple': if len(t.args) == 0 and not t.empty_tuple_index: # Bare 'Tuple' is same as 'tuple' @@ -734,6 +734,21 @@ def visit_callable_type(self, t: CallableType) -> TypeVarList: return [] +def has_explicit_any(t: Type) -> bool: + """ + Whether this type is or type it contains is an Any coming from explicit type annotation + """ + return t.accept(HasExplicitAny()) + + +class HasExplicitAny(TypeQuery[bool]): + def __init__(self) -> None: + super().__init__(any) + + def visit_any(self, t: AnyType) -> bool: + return t.explicit + + def has_any_from_unimported_type(t: Type) -> bool: """Return true if this type is Any because an import was not followed. diff --git a/mypy/types.py b/mypy/types.py index 339d2c5dd2c3..b8fbfb76db8b 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -254,6 +254,7 @@ class AnyType(Type): def __init__(self, implicit: bool = False, from_unimported_type: bool = False, + explicit: bool = False, line: int = -1, column: int = -1) -> None: super().__init__(line, column) @@ -261,6 +262,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 Any come from an explicit type annotation? + self.explicit = explicit def accept(self, visitor: 'TypeVisitor[T]') -> T: return visitor.visit_any(self) diff --git a/test-data/unit/cmdline.test b/test-data/unit/cmdline.test index a1333b69c5c1..f0f78893c756 100644 --- a/test-data/unit/cmdline.test +++ b/test-data/unit/cmdline.test @@ -681,3 +681,178 @@ from unreal import F def f(x: F) -> None: pass [out] + +[case testDisallowAnyExplicitDefSignature] +# cmd: mypy m.py +[file mypy.ini] +[[mypy] +[[mypy-m*] +disallow_any = explicit + +[file m.py] +from typing import Any, List + +def f(x: Any) -> None: + pass + +def g() -> Any: + pass + +def h() -> List[Any]: + pass + +[out] +m.py:3: error: Explicit "Any" is not allowed in a type annotation +m.py:6: error: Explicit "Any" is not allowed in a type annotation +m.py:9: error: Explicit "Any" is not allowed in a type annotation + +[case testDisallowAnyExplicitVarDeclaration] +# cmd: mypy m.py + +[file mypy.ini] +[[mypy] +[[mypy-m*] +disallow_any = explicit + +[file m.py] +from typing import Any, List +v: Any = '' +w = [''] # type: List[Any] +class X: + y = '' # type: Any + +[out] +m.py:2: error: Explicit "Any" is not allowed in a type annotation +m.py:3: error: Explicit "Any" is not allowed in a type annotation +m.py:5: error: Explicit "Any" is not allowed in a type annotation + +[case testDisallowAnyExplicitInheritance] +# cmd: mypy m.py + +[file mypy.ini] +[[mypy] +[[mypy-m*] +disallow_any = explicit + +[file m.py] +from typing import Any, List + +class C(Any): + pass + +class D(List[Any]): + pass +[out] +m.py:3: error: Explicit "Any" is not allowed in a base type +m.py:6: error: Explicit "Any" is not allowed in a base type + +[case testDisallowAnyExplicitAlias] +# cmd: mypy m.py + +[file mypy.ini] +[[mypy] +[[mypy-m*] +disallow_any = explicit + +[file m.py] +from typing import Any, List + +X = Any +Y = List[Any] + +def foo(x: X) -> Y: # no error + x.nonexistent() # no error + return x + +[out] +m.py:3: error: Explicit "Any" is not allowed in a type alias +m.py:4: error: Explicit "Any" is not allowed in a type alias + +[case testDisallowAnyExplicitGenericAlias] +# cmd: mypy m.py + +[file mypy.ini] +[[mypy] +[[mypy-m*] +disallow_any = explicit + +[file m.py] +from typing import Any, List, TypeVar, Tuple + +T = TypeVar('T') + +TupleAny = Tuple[Any, T] # error + +def foo(x: TupleAny[str]) -> None: # no error + pass + +[out] +m.py:5: error: Explicit "Any" is not allowed in a type alias + +[case testDisallowAnyExplicitCast] +# cmd: mypy m.py + +[file mypy.ini] +[[mypy] +[[mypy-m*] +disallow_any = explicit + +[file m.py] +from typing import Any, List, cast + +x = 1 +y = cast(Any, x) +z = cast(List[Any], x) +[out] +m.py:4: error: Explicit "Any" is not allowed in a cast +m.py:5: error: Explicit "Any" is not allowed in a cast + +[case testDisallowAnyExplicitNamedTuple] +# cmd: mypy m.py + +[file mypy.ini] +[[mypy] +[[mypy-m*] +disallow_any = explicit + +[file m.py] +from typing import Any, List, NamedTuple + +Point = NamedTuple('Point', [('x', List[Any]), + ('y', Any)]) + +[out] +m.py:3: error: Explicit "Any" is not allowed in a NamedTuple declaration + +[case testDisallowAnyExplicitTypeVarConstraint] +# cmd: mypy m.py + +[file mypy.ini] +[[mypy] +[[mypy-m*] +disallow_any = explicit + +[file m.py] +from typing import Any, List, TypeVar + +T = TypeVar('T', Any, List[Any]) +[out] +m.py:3: error: Explicit "Any" is not allowed in a TypeVar declaration + +[case testDisallowAnyExplicitNewType] +# cmd: mypy m.py + +[file mypy.ini] +[[mypy] +[[mypy-m*] +disallow_any = explicit + +[file m.py] +from typing import Any, List, NewType + +# The commented out line is covered without the flag enabled - it is tested elsewhere +# Baz = NewType('Baz', Any) +Bar = NewType('Bar', List[Any]) + +[out] +m.py:5: error: Explicit "Any" is not allowed in a NewType declaration From e3cd6ebf17bab68caf28998d3011e686cea48f03 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ilinskiy Date: Wed, 21 Jun 2017 15:30:03 -0700 Subject: [PATCH 02/13] Report errors for explicit `Any` in TypedDict declaration --- mypy/semanal.py | 5 +++++ mypy/typeanal.py | 4 ++++ test-data/unit/cmdline.test | 35 +++++++++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+) diff --git a/mypy/semanal.py b/mypy/semanal.py index b015f214e75f..551e7eb79b9a 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -2383,6 +2383,11 @@ def parse_typeddict_args(self, call: CallExpr, "TypedDict() expects a dictionary literal as the second argument", call) dictexpr = args[1] items, types, ok = self.parse_typeddict_fields_with_types(dictexpr.items, call) + if ('explicit' in self.options.disallow_any and + not self.is_typeshed_stub_file and + any(has_explicit_any(t) for t in types)): + self.msg.explicit_any("in TypedDict declaration", call) + return items, types, ok def parse_typeddict_fields_with_types(self, dict_items: List[Tuple[Expression, Expression]], diff --git a/mypy/typeanal.py b/mypy/typeanal.py index f044eab65f7e..d9ed93ee681d 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -770,6 +770,10 @@ def __init__(self) -> None: def visit_any(self, t: AnyType) -> bool: return t.explicit + def visit_typeddict_type(self, t: TypedDictType) -> bool: + # typedict is checked during TypedDict declaration + return False + def has_any_from_unimported_type(t: Type) -> bool: """Return true if this type is Any because an import was not followed. diff --git a/test-data/unit/cmdline.test b/test-data/unit/cmdline.test index f0f78893c756..fcffcaaf5bd5 100644 --- a/test-data/unit/cmdline.test +++ b/test-data/unit/cmdline.test @@ -856,3 +856,38 @@ Bar = NewType('Bar', List[Any]) [out] m.py:5: error: Explicit "Any" is not allowed in a NewType declaration + +[case testDisallowAnyExplicitTypedDictSimple] +# cmd: mypy m.py + +[file mypy.ini] +[[mypy] +[[mypy-m*] +disallow_any = explicit + +[file m.py] +from mypy_extensions import TypedDict +from typing import Any + +M = TypedDict('M', {'x': str, 'y': Any}) # error +M(x='x', y=2) # no error +def f(m: M) -> None: pass # no error +[out] +m.py:4: error: Explicit "Any" is not allowed in a in TypedDict declaration + +[case testDisallowAnyExplicitTypedDictGeneric] +# cmd: mypy m.py + +[file mypy.ini] +[[mypy] +[[mypy-m*] +disallow_any = explicit + +[file m.py] +from mypy_extensions import TypedDict +from typing import Any, List + +M = TypedDict('M', {'x': str, 'y': List[Any]}) # error +N = TypedDict('N', {'x': str, 'y': List}) # no error +[out] +m.py:4: error: Explicit "Any" is not allowed in a in TypedDict declaration From 1bf6c618ee6864fdb8d9746c30047cee9da1c0b1 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ilinskiy Date: Wed, 21 Jun 2017 15:49:46 -0700 Subject: [PATCH 03/13] Make error message simpler (just say that there's `Any` on this line) --- mypy/checker.py | 4 ++-- mypy/checkexpr.py | 4 ++-- mypy/messages.py | 4 ++-- mypy/semanal.py | 10 +++++----- test-data/unit/cmdline.test | 36 ++++++++++++++++++------------------ 5 files changed, 29 insertions(+), 29 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 5a953b8975e1..4ffe1b2fa8a6 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -631,7 +631,7 @@ def is_implicit_any(t: Type) -> bool: not self.is_typeshed_stub and fdef.type and has_explicit_any(fdef.type)): - self.msg.explicit_any("type annotation", fdef) + self.msg.explicit_any(fdef) if name in nodes.reverse_op_method_set: self.check_reverse_op_method(item, typ, name) elif name in ('__getattr__', '__getattribute__'): @@ -1222,7 +1222,7 @@ def visit_assignment_stmt(self, s: AssignmentStmt) -> None: not self.is_typeshed_stub and s.type and has_explicit_any(s.type)): - self.msg.explicit_any("type annotation", s) + self.msg.explicit_any(s) if len(s.lvalues) > 1: # Chained assignment (e.g. x = y = ...). diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 840dede70e50..6c2c1b4d1826 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -1693,7 +1693,7 @@ def visit_cast_expr(self, expr: CastExpr) -> Type: if ('explicit' in options.disallow_any and not self.chk.is_typeshed_stub and has_explicit_any(target_type)): - self.msg.explicit_any("cast", expr) + self.msg.explicit_any(expr) return target_type def visit_reveal_type_expr(self, expr: RevealTypeExpr) -> Type: @@ -2417,7 +2417,7 @@ def visit_namedtuple_expr(self, e: NamedTupleExpr) -> Type: if ('explicit' in self.chk.options.disallow_any and not self.chk.is_typeshed_stub and has_explicit_any(tuple_type)): - self.msg.explicit_any("NamedTuple declaration", e) + self.msg.explicit_any(e) # TODO: Perhaps return a type object type? return AnyType() diff --git a/mypy/messages.py b/mypy/messages.py index 2bf99ca1cd43..d2610f3983af 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -872,8 +872,8 @@ def unimported_type_becomes_any(self, prefix: str, typ: Type, ctx: Context) -> N self.fail("{} becomes {} due to an unfollowed import".format(prefix, self.format(typ)), ctx) - def explicit_any(self, location: str, ctx: Context) -> None: - self.fail('Explicit "Any" is not allowed in a {}'.format(location), ctx) + def explicit_any(self, ctx: Context) -> None: + self.fail('Explicit "Any" is not allowed', ctx) def typeddict_instantiated_with_unexpected_items(self, expected_item_names: List[str], diff --git a/mypy/semanal.py b/mypy/semanal.py index 551e7eb79b9a..617e720823d2 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -1016,7 +1016,7 @@ def analyze_base_classes(self, defn: ClassDef) -> None: if ('explicit' in self.options.disallow_any and not self.is_typeshed_stub_file and has_explicit_any(base)): - self.msg.explicit_any('base type', base_expr) + self.msg.explicit_any(base_expr) # Add 'object' as implicit base if there is no other base class. if (not base_types and defn.fullname != 'builtins.object'): @@ -1562,7 +1562,7 @@ def visit_assignment_stmt(self, s: AssignmentStmt) -> None: if ('explicit' in self.options.disallow_any and not self.is_typeshed_stub_file and has_explicit_any(res)): - self.msg.explicit_any("type alias", s) + self.msg.explicit_any(s) # when this type alias gets "inlined", the "Any" is not explicit anymore, # so we need to mark it as such unmark_any_as_explicit(res) @@ -1834,7 +1834,7 @@ def process_newtype_declaration(self, s: AssignmentStmt) -> None: if ('explicit' in self.options.disallow_any and not self.is_typeshed_stub_file and has_explicit_any(old_type)): - self.msg.explicit_any("NewType declaration", s) + self.msg.explicit_any(s) if 'unimported' in self.options.disallow_any and has_any_from_unimported_type(old_type): self.msg.unimported_type_becomes_any("Argument 2 to NewType(...)", old_type, s) @@ -1963,7 +1963,7 @@ def process_typevar_declaration(self, s: AssignmentStmt) -> None: if ('explicit' in self.options.disallow_any and not self.is_typeshed_stub_file and any(has_explicit_any(t) for t in values + [upper_bound])): - self.msg.explicit_any("TypeVar declaration", s) + self.msg.explicit_any(s) # Yes, it's a valid type variable definition! Add it to the symbol table. node = self.lookup(name, s) node.kind = TVAR @@ -2386,7 +2386,7 @@ def parse_typeddict_args(self, call: CallExpr, if ('explicit' in self.options.disallow_any and not self.is_typeshed_stub_file and any(has_explicit_any(t) for t in types)): - self.msg.explicit_any("in TypedDict declaration", call) + self.msg.explicit_any(call) return items, types, ok diff --git a/test-data/unit/cmdline.test b/test-data/unit/cmdline.test index fcffcaaf5bd5..3b41e5433434 100644 --- a/test-data/unit/cmdline.test +++ b/test-data/unit/cmdline.test @@ -702,9 +702,9 @@ def h() -> List[Any]: pass [out] -m.py:3: error: Explicit "Any" is not allowed in a type annotation -m.py:6: error: Explicit "Any" is not allowed in a type annotation -m.py:9: error: Explicit "Any" is not allowed in a type annotation +m.py:3: error: Explicit "Any" is not allowed +m.py:6: error: Explicit "Any" is not allowed +m.py:9: error: Explicit "Any" is not allowed [case testDisallowAnyExplicitVarDeclaration] # cmd: mypy m.py @@ -722,9 +722,9 @@ class X: y = '' # type: Any [out] -m.py:2: error: Explicit "Any" is not allowed in a type annotation -m.py:3: error: Explicit "Any" is not allowed in a type annotation -m.py:5: error: Explicit "Any" is not allowed in a type annotation +m.py:2: error: Explicit "Any" is not allowed +m.py:3: error: Explicit "Any" is not allowed +m.py:5: error: Explicit "Any" is not allowed [case testDisallowAnyExplicitInheritance] # cmd: mypy m.py @@ -743,8 +743,8 @@ class C(Any): class D(List[Any]): pass [out] -m.py:3: error: Explicit "Any" is not allowed in a base type -m.py:6: error: Explicit "Any" is not allowed in a base type +m.py:3: error: Explicit "Any" is not allowed +m.py:6: error: Explicit "Any" is not allowed [case testDisallowAnyExplicitAlias] # cmd: mypy m.py @@ -765,8 +765,8 @@ def foo(x: X) -> Y: # no error return x [out] -m.py:3: error: Explicit "Any" is not allowed in a type alias -m.py:4: error: Explicit "Any" is not allowed in a type alias +m.py:3: error: Explicit "Any" is not allowed +m.py:4: error: Explicit "Any" is not allowed [case testDisallowAnyExplicitGenericAlias] # cmd: mypy m.py @@ -787,7 +787,7 @@ def foo(x: TupleAny[str]) -> None: # no error pass [out] -m.py:5: error: Explicit "Any" is not allowed in a type alias +m.py:5: error: Explicit "Any" is not allowed [case testDisallowAnyExplicitCast] # cmd: mypy m.py @@ -804,8 +804,8 @@ x = 1 y = cast(Any, x) z = cast(List[Any], x) [out] -m.py:4: error: Explicit "Any" is not allowed in a cast -m.py:5: error: Explicit "Any" is not allowed in a cast +m.py:4: error: Explicit "Any" is not allowed +m.py:5: error: Explicit "Any" is not allowed [case testDisallowAnyExplicitNamedTuple] # cmd: mypy m.py @@ -822,7 +822,7 @@ Point = NamedTuple('Point', [('x', List[Any]), ('y', Any)]) [out] -m.py:3: error: Explicit "Any" is not allowed in a NamedTuple declaration +m.py:3: error: Explicit "Any" is not allowed [case testDisallowAnyExplicitTypeVarConstraint] # cmd: mypy m.py @@ -837,7 +837,7 @@ from typing import Any, List, TypeVar T = TypeVar('T', Any, List[Any]) [out] -m.py:3: error: Explicit "Any" is not allowed in a TypeVar declaration +m.py:3: error: Explicit "Any" is not allowed [case testDisallowAnyExplicitNewType] # cmd: mypy m.py @@ -855,7 +855,7 @@ from typing import Any, List, NewType Bar = NewType('Bar', List[Any]) [out] -m.py:5: error: Explicit "Any" is not allowed in a NewType declaration +m.py:5: error: Explicit "Any" is not allowed [case testDisallowAnyExplicitTypedDictSimple] # cmd: mypy m.py @@ -873,7 +873,7 @@ M = TypedDict('M', {'x': str, 'y': Any}) # error M(x='x', y=2) # no error def f(m: M) -> None: pass # no error [out] -m.py:4: error: Explicit "Any" is not allowed in a in TypedDict declaration +m.py:4: error: Explicit "Any" is not allowed [case testDisallowAnyExplicitTypedDictGeneric] # cmd: mypy m.py @@ -890,4 +890,4 @@ from typing import Any, List M = TypedDict('M', {'x': str, 'y': List[Any]}) # error N = TypedDict('N', {'x': str, 'y': List}) # no error [out] -m.py:4: error: Explicit "Any" is not allowed in a in TypedDict declaration +m.py:4: error: Explicit "Any" is not allowed From 398b2d850dca74efeb67461d2c87b6729d5410d2 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ilinskiy Date: Thu, 22 Jun 2017 10:53:01 -0700 Subject: [PATCH 04/13] Simplify test by splitting it into two --- test-data/unit/cmdline.test | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/test-data/unit/cmdline.test b/test-data/unit/cmdline.test index 3b41e5433434..c743df5ec131 100644 --- a/test-data/unit/cmdline.test +++ b/test-data/unit/cmdline.test @@ -717,7 +717,7 @@ disallow_any = explicit [file m.py] from typing import Any, List v: Any = '' -w = [''] # type: List[Any] +w = '' # type: Any class X: y = '' # type: Any @@ -726,6 +726,20 @@ m.py:2: error: Explicit "Any" is not allowed m.py:3: error: Explicit "Any" is not allowed m.py:5: error: Explicit "Any" is not allowed +[case testDisallowAnyExplicitGenericVarDeclaration] +# cmd: mypy m.py + +[file mypy.ini] +[[mypy] +[[mypy-m*] +disallow_any = explicit + +[file m.py] +from typing import Any, List +v: List[Any] = [] +[out] +m.py:2: error: Explicit "Any" is not allowed + [case testDisallowAnyExplicitInheritance] # cmd: mypy m.py From 4b9643ff344053f125e44da266be9f601ec440e8 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ilinskiy Date: Thu, 22 Jun 2017 11:01:10 -0700 Subject: [PATCH 05/13] Add test to check for generic alias with generic type "Any" --- test-data/unit/cmdline.test | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test-data/unit/cmdline.test b/test-data/unit/cmdline.test index c743df5ec131..0fbbc8325cbd 100644 --- a/test-data/unit/cmdline.test +++ b/test-data/unit/cmdline.test @@ -800,8 +800,12 @@ TupleAny = Tuple[Any, T] # error def foo(x: TupleAny[str]) -> None: # no error pass +def goo(x: TupleAny[Any]) -> None: # error + pass + [out] m.py:5: error: Explicit "Any" is not allowed +m.py:10: error: Explicit "Any" is not allowed [case testDisallowAnyExplicitCast] # cmd: mypy m.py From 8bcaf994f7a44b0b3700da46756cd1f27b229ea8 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ilinskiy Date: Thu, 22 Jun 2017 11:07:38 -0700 Subject: [PATCH 06/13] Uncomment test for NewType with explicit Any --- test-data/unit/cmdline.test | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test-data/unit/cmdline.test b/test-data/unit/cmdline.test index 0fbbc8325cbd..60625623b735 100644 --- a/test-data/unit/cmdline.test +++ b/test-data/unit/cmdline.test @@ -868,12 +868,12 @@ disallow_any = explicit [file m.py] from typing import Any, List, NewType -# The commented out line is covered without the flag enabled - it is tested elsewhere -# Baz = NewType('Baz', Any) +Baz = NewType('Baz', Any) Bar = NewType('Bar', List[Any]) [out] -m.py:5: error: Explicit "Any" is not allowed +m.py:3: error: Argument 2 to NewType(...) must be subclassable (got Any) +m.py:4: error: Explicit "Any" is not allowed [case testDisallowAnyExplicitTypedDictSimple] # cmd: mypy m.py From 3d8780876a49b5bae34f25d82ab91072d4148fcb Mon Sep 17 00:00:00 2001 From: Svyatoslav Ilinskiy Date: Thu, 22 Jun 2017 12:57:49 -0700 Subject: [PATCH 07/13] Improve wording in a comment --- mypy/typeanal.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/typeanal.py b/mypy/typeanal.py index d9ed93ee681d..947ec1ba4d81 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -771,7 +771,7 @@ def visit_any(self, t: AnyType) -> bool: return t.explicit def visit_typeddict_type(self, t: TypedDictType) -> bool: - # typedict is checked during TypedDict declaration + # typeddict is checked during TypedDict declaration, so don't typecheck it here. return False From 4394530d9b17782a5c84a6d5e2b90d11b81766ef Mon Sep 17 00:00:00 2001 From: Svyatoslav Ilinskiy Date: Mon, 26 Jun 2017 17:28:04 -0700 Subject: [PATCH 08/13] Rename function to mark_any_non_explicit --- mypy/semanal.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index cb2ebf7a8e64..977ac49d2fa3 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -1582,7 +1582,7 @@ def visit_assignment_stmt(self, s: AssignmentStmt) -> None: self.msg.explicit_any(s) # when this type alias gets "inlined", the "Any" is not explicit anymore, # so we need to mark it as such - unmark_any_as_explicit(res) + mark_any_non_explicit(res) name = s.lvalues[0] node = self.lookup(name.name, name) @@ -4412,14 +4412,14 @@ def find_fixed_callable_return(expr: Expression) -> Optional[CallableType]: return None -def unmark_any_as_explicit(t: Type) -> None: +def mark_any_non_explicit(t: Type) -> None: """For all types within t, if that type is Any, set explicit to False""" - t.accept(UnmarkAnyAsExplicit()) + t.accept(MarkAnyNonExplicit()) -class UnmarkAnyAsExplicit(TypeQuery[None]): +class MarkAnyNonExplicit(TypeQuery[None]): def __init__(self) -> None: - super().__init__(UnmarkAnyAsExplicit.do_nothing_strategy) + super().__init__(MarkAnyNonExplicit.do_nothing_strategy) def visit_any(self, t: AnyType) -> None: t.explicit = False From 022684cd0b6d51d5f7a9812ba62e09c1c8e7136e Mon Sep 17 00:00:00 2001 From: Svyatoslav Ilinskiy Date: Mon, 26 Jun 2017 17:32:08 -0700 Subject: [PATCH 09/13] Update comment about `explicit` and `implicit` attributes on AnyType --- mypy/types.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mypy/types.py b/mypy/types.py index f7ea3a8e9cc6..53e409b5bc87 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -259,6 +259,9 @@ def __init__(self, column: int = -1) -> None: super().__init__(line, column) # Was this Any type was inferred without a type annotation? + # Note that this is not always the opposite of explicit. + # For instance, if "Any" comes from an unimported type, + # both explicit and implicit will be False self.implicit = implicit # Does this come from an unfollowed import? See --disallow-any=unimported option self.from_unimported_type = from_unimported_type From f4c87fe75121701d4c6ca25d0d10847292acb30b Mon Sep 17 00:00:00 2001 From: Svyatoslav Ilinskiy Date: Tue, 27 Jun 2017 17:07:41 -0700 Subject: [PATCH 10/13] Add comment to the test about explicit Any in NewType --- test-data/unit/cmdline.test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-data/unit/cmdline.test b/test-data/unit/cmdline.test index 60625623b735..4e4ebd303aa6 100644 --- a/test-data/unit/cmdline.test +++ b/test-data/unit/cmdline.test @@ -868,7 +868,7 @@ disallow_any = explicit [file m.py] from typing import Any, List, NewType -Baz = NewType('Baz', Any) +Baz = NewType('Baz', Any) # this error does not come from `--disallow-any=explicit` flag Bar = NewType('Bar', List[Any]) [out] From 5ffbd1e0d5992b3704b1635cee57e151325ec98e Mon Sep 17 00:00:00 2001 From: Svyatoslav Ilinskiy Date: Thu, 29 Jun 2017 11:52:53 -0700 Subject: [PATCH 11/13] Use TypeTranslator to make Any non-explicit + extract method to check for explicit Any --- mypy/checker.py | 15 +++-------- mypy/checkexpr.py | 14 ++++------- mypy/semanal.py | 64 ++++++++++++++++++----------------------------- mypy/typeanal.py | 14 +++++++++++ mypy/types.py | 20 ++++++++++++--- 5 files changed, 65 insertions(+), 62 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 4ffe1b2fa8a6..c799cad07e0e 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -29,7 +29,7 @@ ARG_POS, MDEF, CONTRAVARIANT, COVARIANT) from mypy import nodes -from mypy.typeanal import has_any_from_unimported_type, has_explicit_any +from mypy.typeanal import has_any_from_unimported_type, check_for_explicit_any from mypy.types import ( Type, AnyType, CallableType, FunctionLike, Overloaded, TupleType, TypedDictType, Instance, NoneTyp, strip_type, TypeType, @@ -627,11 +627,8 @@ def is_implicit_any(t: Type) -> bool: if has_any_from_unimported_type(arg_type): prefix = "Argument {} to \"{}\"".format(idx + 1, fdef.name()) self.msg.unimported_type_becomes_any(prefix, arg_type, fdef) - if ('explicit' in self.options.disallow_any and - not self.is_typeshed_stub and - fdef.type and - has_explicit_any(fdef.type)): - self.msg.explicit_any(fdef) + check_for_explicit_any(fdef.type, self.options, self.is_typeshed_stub, + self.msg, context=fdef) if name in nodes.reverse_op_method_set: self.check_reverse_op_method(item, typ, name) elif name in ('__getattr__', '__getattribute__'): @@ -1218,11 +1215,7 @@ def visit_assignment_stmt(self, s: AssignmentStmt) -> None: self.msg.unimported_type_becomes_any("A type on this line", AnyType(), s) else: self.msg.unimported_type_becomes_any("Type of variable", s.type, s) - if ('explicit' in self.options.disallow_any and - not self.is_typeshed_stub and - s.type and - has_explicit_any(s.type)): - self.msg.explicit_any(s) + check_for_explicit_any(s.type, self.options, self.is_typeshed_stub, self.msg, context=s) if len(s.lvalues) > 1: # Chained assignment (e.g. x = y = ...). diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index c09a3f430eb1..bf6756364de5 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -4,7 +4,7 @@ from typing import cast, Dict, Set, List, Tuple, Callable, Union, Optional from mypy.errors import report_internal_error -from mypy.typeanal import has_any_from_unimported_type, has_explicit_any +from mypy.typeanal import has_any_from_unimported_type, check_for_explicit_any from mypy.types import ( Type, AnyType, CallableType, Overloaded, NoneTyp, TypeVarDef, TupleType, TypedDictType, Instance, TypeVarType, ErasedType, UnionType, @@ -1691,10 +1691,8 @@ def visit_cast_expr(self, expr: CastExpr) -> Type: self.msg.redundant_cast(target_type, expr) if 'unimported' in options.disallow_any and has_any_from_unimported_type(target_type): self.msg.unimported_type_becomes_any("Target type of cast", target_type, expr) - if ('explicit' in options.disallow_any and - not self.chk.is_typeshed_stub and - has_explicit_any(target_type)): - self.msg.explicit_any(expr) + check_for_explicit_any(target_type, self.chk.options, self.chk.is_typeshed_stub, self.msg, + context=expr) return target_type def visit_reveal_type_expr(self, expr: RevealTypeExpr) -> Type: @@ -2415,10 +2413,8 @@ def visit_namedtuple_expr(self, e: NamedTupleExpr) -> Type: if ('unimported' in self.chk.options.disallow_any and has_any_from_unimported_type(tuple_type)): self.msg.unimported_type_becomes_any("NamedTuple type", tuple_type, e) - if ('explicit' in self.chk.options.disallow_any and - not self.chk.is_typeshed_stub and - has_explicit_any(tuple_type)): - self.msg.explicit_any(e) + check_for_explicit_any(tuple_type, self.chk.options, self.chk.is_typeshed_stub, + self.msg, context=e) # TODO: Perhaps return a type object type? return AnyType() diff --git a/mypy/semanal.py b/mypy/semanal.py index 977ac49d2fa3..962cb7eb0106 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -80,12 +80,13 @@ NoneTyp, CallableType, Overloaded, Instance, Type, TypeVarType, AnyType, FunctionLike, UnboundType, TypeList, TypeVarDef, TypeType, TupleType, UnionType, StarType, EllipsisType, function_type, TypedDictType, - TypeQuery + TypeTranslator, ) 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, has_explicit_any + TypeVariableQuery, TypeVarList, remove_dups, has_any_from_unimported_type, + check_for_explicit_any ) from mypy.exprtotype import expr_to_unanalyzed_type, TypeTranslationError from mypy.sametypes import is_same_type @@ -1018,10 +1019,8 @@ def analyze_base_classes(self, defn: ClassDef) -> None: else: prefix = "Base type" self.msg.unimported_type_becomes_any(prefix, base, base_expr) - if ('explicit' in self.options.disallow_any and - not self.is_typeshed_stub_file and - has_explicit_any(base)): - self.msg.explicit_any(base_expr) + check_for_explicit_any(base, self.options, self.is_typeshed_stub_file, self.msg, + context=base_expr) # Add 'object' as implicit base if there is no other base class. if (not base_types and defn.fullname != 'builtins.object'): @@ -1576,13 +1575,12 @@ def visit_assignment_stmt(self, s: AssignmentStmt) -> None: allow_unnormalized=True) if res and (not isinstance(res, Instance) or res.args): # TODO: What if this gets reassigned? - if ('explicit' in self.options.disallow_any and - not self.is_typeshed_stub_file and - has_explicit_any(res)): - self.msg.explicit_any(s) - # when this type alias gets "inlined", the "Any" is not explicit anymore, - # so we need to mark it as such - mark_any_non_explicit(res) + check_for_explicit_any(res, self.options, self.is_typeshed_stub_file, self.msg, + context=s) + # when this type alias gets "inlined", the Any is not explicit anymore, + # so we need to replace it with non-explicit Anys + res = make_any_non_explicit(res) + assert not has_explicit_any(res) name = s.lvalues[0] node = self.lookup(name.name, name) @@ -1848,10 +1846,8 @@ def process_newtype_declaration(self, s: AssignmentStmt) -> None: self.fail(message.format(old_type), s) return - if ('explicit' in self.options.disallow_any and - not self.is_typeshed_stub_file and - has_explicit_any(old_type)): - self.msg.explicit_any(s) + check_for_explicit_any(old_type, self.options, self.is_typeshed_stub_file, self.msg, + context=s) if 'unimported' in self.options.disallow_any and has_any_from_unimported_type(old_type): self.msg.unimported_type_becomes_any("Argument 2 to NewType(...)", old_type, s) @@ -1977,10 +1973,9 @@ def process_typevar_declaration(self, s: AssignmentStmt) -> None: prefix = "Upper bound of type variable" self.msg.unimported_type_becomes_any(prefix, upper_bound, s) - if ('explicit' in self.options.disallow_any and - not self.is_typeshed_stub_file and - any(has_explicit_any(t) for t in values + [upper_bound])): - self.msg.explicit_any(s) + for t in values + [upper_bound]: + check_for_explicit_any(t, self.options, self.is_typeshed_stub_file, self.msg, + context=s) # Yes, it's a valid type variable definition! Add it to the symbol table. node = self.lookup(name, s) node.kind = TVAR @@ -2415,10 +2410,9 @@ def parse_typeddict_args(self, call: CallExpr, 'TypedDict() "total" argument must be True or False', call) dictexpr = args[1] items, types, ok = self.parse_typeddict_fields_with_types(dictexpr.items, call) - if ('explicit' in self.options.disallow_any and - not self.is_typeshed_stub_file and - any(has_explicit_any(t) for t in types)): - self.msg.explicit_any(call) + for t in types: + check_for_explicit_any(t, self.options, self.is_typeshed_stub_file, self.msg, + context=call) return items, types, total, ok @@ -4412,19 +4406,11 @@ def find_fixed_callable_return(expr: Expression) -> Optional[CallableType]: return None -def mark_any_non_explicit(t: Type) -> None: - """For all types within t, if that type is Any, set explicit to False""" - t.accept(MarkAnyNonExplicit()) +def make_any_non_explicit(t: Type) -> Type: + """Replace all Any types within in with Any that has attribute 'explicit' set to False""" + return t.accept(MakeAnyNonExplicit()) -class MarkAnyNonExplicit(TypeQuery[None]): - def __init__(self) -> None: - super().__init__(MarkAnyNonExplicit.do_nothing_strategy) - - def visit_any(self, t: AnyType) -> None: - t.explicit = False - - @classmethod - def do_nothing_strategy(cls, it: Iterable[None]) -> None: - # "it" can be a generator (which is lazy), force it to compute the value - list(it) +class MakeAnyNonExplicit(TypeTranslator): + def visit_any(self, t: AnyType) -> Type: + return t.copy_modified(explicit=False) diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 0e61710bc40c..bb455ff70244 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -6,6 +6,8 @@ from contextlib import contextmanager +from mypy.messages import MessageBuilder +from mypy.options import Options from mypy.types import ( Type, UnboundType, TypeVarType, TupleType, TypedDictType, UnionType, Instance, AnyType, CallableType, NoneTyp, DeletedType, TypeList, TypeVarDef, TypeVisitor, @@ -756,6 +758,18 @@ def visit_callable_type(self, t: CallableType) -> TypeVarList: return [] +def check_for_explicit_any(typ: Optional[Type], + options: Options, + is_typeshed_stub: bool, + msg: MessageBuilder, + context: Context) -> None: + if ('explicit' in options.disallow_any and + not is_typeshed_stub and + typ and + has_explicit_any(typ)): + msg.explicit_any(context) + + def has_explicit_any(t: Type) -> bool: """ Whether this type is or type it contains is an Any coming from explicit type annotation diff --git a/mypy/types.py b/mypy/types.py index 53e409b5bc87..21fed0a1c2c5 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -248,6 +248,9 @@ def serialize(self) -> JsonDict: assert False, "Sythetic types don't serialize" +_dummy = object() # type: Any + + class AnyType(Type): """The type 'Any'.""" @@ -271,6 +274,20 @@ def __init__(self, def accept(self, visitor: 'TypeVisitor[T]') -> T: return visitor.visit_any(self) + def copy_modified(self, + implicit: bool = _dummy, + from_unimported_type: bool = _dummy, + explicit: bool = _dummy, + ) -> 'AnyType': + if implicit is _dummy: + implicit = self.implicit + if from_unimported_type is _dummy: + from_unimported_type = self.from_unimported_type + if explicit is _dummy: + explicit = self.explicit + return AnyType(implicit=implicit, from_unimported_type=from_unimported_type, + explicit=explicit, line=self.line, column=self.column) + def serialize(self) -> JsonDict: return {'.class': 'AnyType'} @@ -514,9 +531,6 @@ def get_name(self) -> Optional[str]: pass fallback = None # type: Instance -_dummy = object() # type: Any - - FormalArgument = NamedTuple('FormalArgument', [ ('name', Optional[str]), ('pos', Optional[int]), From 24c88c1d6f3e9d7f7d1b13c1dc43bf2685fb6b7b Mon Sep 17 00:00:00 2001 From: Svyatoslav Ilinskiy Date: Thu, 29 Jun 2017 11:54:13 -0700 Subject: [PATCH 12/13] remove unneeded assert --- mypy/semanal.py | 1 - 1 file changed, 1 deletion(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 962cb7eb0106..19605aec1be5 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -1580,7 +1580,6 @@ def visit_assignment_stmt(self, s: AssignmentStmt) -> None: # when this type alias gets "inlined", the Any is not explicit anymore, # so we need to replace it with non-explicit Anys res = make_any_non_explicit(res) - assert not has_explicit_any(res) name = s.lvalues[0] node = self.lookup(name.name, name) From 8f90d49bb1567459d5ef90e83267cabefd97b52c Mon Sep 17 00:00:00 2001 From: Svyatoslav Ilinskiy Date: Thu, 29 Jun 2017 22:23:25 -0700 Subject: [PATCH 13/13] Add documentation --- docs/source/command_line.rst | 4 +++- docs/source/config_file.rst | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/source/command_line.rst b/docs/source/command_line.rst index 734386f788e6..78869fb10bbd 100644 --- a/docs/source/command_line.rst +++ b/docs/source/command_line.rst @@ -278,7 +278,7 @@ Here are some more useful flags: - ``--disallow-any`` disallows various types of ``Any`` in a module. The option takes a comma-separated list of the following values: - ``unimported``, ``unannotated``, ``expr``, ``decorated``. + ``unimported``, ``unannotated``, ``expr``, ``decorated``, ``explicit``. ``unimported`` disallows usage of types that come from unfollowed imports (such types become aliases for ``Any``). Unfollowed imports occur either @@ -301,6 +301,8 @@ Here are some more useful flags: ``decorated`` disallows functions that have ``Any`` in their signature after decorator transformation. + ``explicit`` disallows explicit ``Any`` in type positions such as type + annotations and generic type parameters. - ``--disallow-untyped-defs`` reports an error whenever it encounters a function definition without type annotations. diff --git a/docs/source/config_file.rst b/docs/source/config_file.rst index 6fbcfbb2db9a..20fc4241f409 100644 --- a/docs/source/config_file.rst +++ b/docs/source/config_file.rst @@ -150,8 +150,8 @@ overridden by the pattern sections matching the module name. - ``disallow_any`` (Comma-separated list, default empty) is an option to disallow various types of ``Any`` in a module. The flag takes a comma-separated list of the following arguments: ``unimported``, - ``unannotated``, ``expr``. For explanations see the discussion for the - :ref:`--disallow-any ` option. + ``unannotated``, ``expr``, ``decorated``, ``explicit``. For explanations + see the discussion for the :ref:`--disallow-any ` option. - ``disallow_untyped_calls`` (Boolean, default False) disallows calling functions without type annotations from functions with type