Skip to content

Commit 6292edc

Browse files
committed
Add --warn-implicit-any
1 parent 665a810 commit 6292edc

File tree

5 files changed

+76
-2
lines changed

5 files changed

+76
-2
lines changed

mypy/main.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,10 @@ def add_invertible_flag(flag: str,
231231
add_invertible_flag('--warn-return-any', default=False, strict_flag=True,
232232
help="warn about returning values of type Any"
233233
" from non-Any typed functions")
234+
add_invertible_flag('--warn-implicit-any', default=False,
235+
help='warn about implicit creation of type "Any" '
236+
"(experimental -- only warns in some limited circusmstances.)"
237+
)
234238
add_invertible_flag('--warn-unused-ignores', default=False, strict_flag=True,
235239
help="warn about unneeded '# type: ignore' comments")
236240
add_invertible_flag('--show-error-context', default=False,

mypy/options.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ class Options:
2727
"show_none_errors",
2828
"warn_no_return",
2929
"warn_return_any",
30+
"warn_implicit_any",
3031
"ignore_errors",
3132
"strict_boolean",
3233
}
@@ -70,6 +71,9 @@ def __init__(self) -> None:
7071
# declared with a precise type
7172
self.warn_return_any = False
7273

74+
# Warn about implicit creation of Any type (work in progress)
75+
self.warn_implicit_any = False
76+
7377
# Warn about unused '# type: ignore' comments
7478
self.warn_unused_ignores = False
7579

mypy/semanal.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3251,12 +3251,15 @@ def name_already_defined(self, name: str, ctx: Context) -> None:
32513251
self.fail("Name '{}' already defined".format(name), ctx)
32523252

32533253
def fail(self, msg: str, ctx: Context, serious: bool = False, *,
3254-
blocker: bool = False) -> None:
3254+
blocker: bool = False, implicit_any: bool = False) -> None:
32553255
if (not serious and
32563256
not self.options.check_untyped_defs and
32573257
self.function_stack and
32583258
self.function_stack[-1].is_dynamic()):
32593259
return
3260+
if implicit_any:
3261+
if not self.options.warn_implicit_any or self.cur_mod_node.is_stub:
3262+
return
32603263
# In case it's a bug and we don't really have context
32613264
assert ctx is not None, msg
32623265
self.errors.report(ctx.get_line(), ctx.get_column(), msg, blocker=blocker)
@@ -3680,7 +3683,14 @@ def analyze(self, type: Type) -> None:
36803683
analyzer = TypeAnalyserPass3(self.fail)
36813684
type.accept(analyzer)
36823685

3683-
def fail(self, msg: str, ctx: Context, *, blocker: bool = False) -> None:
3686+
def fail(self, msg: str, ctx: Context, *, blocker: bool = False,
3687+
implicit_any: bool = False) -> None:
3688+
if implicit_any:
3689+
if not self.options.warn_implicit_any or self.errors.file.endswith('.pyi'):
3690+
return
3691+
# TempNode, so must have already reported in the first pass
3692+
if ctx.get_line() == -1:
3693+
return
36843694
self.errors.report(ctx.get_line(), ctx.get_column(), msg)
36853695

36863696
def fail_blocker(self, msg: str, ctx: Context) -> None:

mypy/typeanal.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ def visit_unbound_type(self, t: UnboundType) -> Type:
152152
elif fullname == 'typing.Tuple':
153153
if len(t.args) == 0 and not t.empty_tuple_index:
154154
# Bare 'Tuple' is same as 'tuple'
155+
self.implicit_any('Tuple without type args', t)
155156
return self.builtin_type('builtins.tuple')
156157
if len(t.args) == 2 and isinstance(t.args[1], EllipsisType):
157158
# Tuple[T, ...] (uniform, variable-length tuple)
@@ -174,6 +175,7 @@ def visit_unbound_type(self, t: UnboundType) -> Type:
174175
return self.analyze_callable_type(t)
175176
elif fullname == 'typing.Type':
176177
if len(t.args) == 0:
178+
self.implicit_any('Type without type args', t)
177179
return TypeType(AnyType(), line=t.line)
178180
if len(t.args) != 1:
179181
self.fail('Type[...] must have exactly one type argument', t)
@@ -183,6 +185,7 @@ def visit_unbound_type(self, t: UnboundType) -> Type:
183185
if self.nesting_level > 0:
184186
self.fail('Invalid type: ClassVar nested inside other type', t)
185187
if len(t.args) == 0:
188+
self.implicit_any('ClassVar without type args', t)
186189
return AnyType(line=t.line)
187190
if len(t.args) != 1:
188191
self.fail('ClassVar[...] must have at most one type argument', t)
@@ -202,6 +205,7 @@ def visit_unbound_type(self, t: UnboundType) -> Type:
202205
act_len = len(an_args)
203206
if exp_len > 0 and act_len == 0:
204207
# Interpret bare Alias same as normal generic, i.e., Alias[Any, Any, ...]
208+
self.implicit_any('Generic type without type args', t)
205209
return self.replace_alias_tvars(override, all_vars, [AnyType()] * exp_len,
206210
t.line, t.column)
207211
if exp_len == 0 and act_len == 0:
@@ -220,6 +224,7 @@ def visit_unbound_type(self, t: UnboundType) -> Type:
220224
# context. This is slightly problematic as it allows using the type 'Any'
221225
# as a base class -- however, this will fail soon at runtime so the problem
222226
# is pretty minor.
227+
self.implicit_any('Assigning value of type Any', t)
223228
return AnyType()
224229
# Allow unbound type variables when defining an alias
225230
if not (self.aliasing and sym.kind == TVAR and
@@ -259,6 +264,7 @@ def visit_unbound_type(self, t: UnboundType) -> Type:
259264
fallback=instance)
260265
return instance
261266
else:
267+
self.implicit_any('Fallback', t)
262268
return AnyType()
263269

264270
def get_type_var_names(self, tp: Type) -> List[str]:
@@ -374,6 +380,7 @@ def analyze_callable_type(self, t: UnboundType) -> Type:
374380
fallback = self.builtin_type('builtins.function')
375381
if len(t.args) == 0:
376382
# Callable (bare). Treat as Callable[..., Any].
383+
self.implicit_any('Callable without type args', t)
377384
ret = CallableType([AnyType(), AnyType()],
378385
[nodes.ARG_STAR, nodes.ARG_STAR2],
379386
[None, None],
@@ -492,6 +499,10 @@ def builtin_type(self, fully_qualified_name: str, args: List[Type] = None) -> In
492499
def tuple_type(self, items: List[Type]) -> TupleType:
493500
return TupleType(items, fallback=self.builtin_type('builtins.tuple', [AnyType()]))
494501

502+
def implicit_any(self, details: str, t: Type) -> None:
503+
msg = 'Type Any created implicitly: ' + details
504+
self.fail(msg, t, implicit_any=True) # type: ignore
505+
495506

496507
class TypeAnalyserPass3(TypeVisitor[None]):
497508
"""Analyze type argument counts and values of generic types.
@@ -522,6 +533,8 @@ def visit_instance(self, t: Instance) -> None:
522533
if len(t.args) != len(info.type_vars):
523534
if len(t.args) == 0:
524535
# Insert implicit 'Any' type arguments.
536+
if t.type.fullname() not in ('typing.Generator'):
537+
self.implicit_any('{} without type args'.format(t), t)
525538
t.args = [AnyType()] * len(info.type_vars)
526539
return
527540
# Invalid number of type parameters.
@@ -624,6 +637,10 @@ def visit_partial_type(self, t: PartialType) -> None:
624637
def visit_type_type(self, t: TypeType) -> None:
625638
pass
626639

640+
def implicit_any(self, details: str, t: Type) -> None:
641+
msg = 'Type Any created implicitly: ' + details
642+
self.fail(msg, t, implicit_any=True) # type: ignore
643+
627644

628645
TypeVarList = List[Tuple[str, TypeVarExpr]]
629646

test-data/unit/check-warnings.test

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,3 +165,42 @@ from typing import Any
165165
def g() -> Any: pass
166166
def f() -> Any: return g()
167167
[out]
168+
169+
[case testWarnImplicitAny]
170+
# flags: --warn-implicit-any
171+
from typing import TypeVar, List, Tuple, Generic, Callable, Union
172+
173+
T = TypeVar('T')
174+
U = TypeVar('U')
175+
A = TypeVar('A', str, int)
176+
177+
class X(Generic[T]):
178+
pass
179+
180+
class Y(Generic[T, U]):
181+
pass
182+
183+
a1: Tuple # E: Type Any created implicitly: Tuple without type args
184+
a2: Callable # E: Type Any created implicitly: Callable without type args
185+
a4: list # E: Type Any created implicitly: builtins.list without type args
186+
a5: List # E: Type Any created implicitly: builtins.list without type args
187+
a6: X # E: Type Any created implicitly: __main__.X without type args
188+
a7: Y # E: Type Any created implicitly: __main__.Y without type args
189+
190+
def f(x: X) -> None: # E: Type Any created implicitly: __main__.X without type args
191+
pass
192+
193+
def g() -> X: # E: Type Any created implicitly: __main__.X without type args
194+
pass
195+
196+
b1: str
197+
b2: X[int]
198+
b3: Y[int, str]
199+
b4 = (1, 2)
200+
b5 = [1, 2]
201+
b6 = 'abc'
202+
203+
Z = Union[A, X[A]]
204+
def q(z: Z) -> None: ... # E: Type Any created implicitly: Generic type without type args
205+
206+
[builtins fixtures/list.pyi]

0 commit comments

Comments
 (0)