10000 Add --warn-implicit-any · python/mypy@468bd49 · GitHub
[go: up one dir, main page]

Skip to content

Commit 468bd49

Browse files
committed
Add --warn-implicit-any
1 parent 72565f0 commit 468bd49

File tree

5 files changed

+72
-2
lines changed

5 files changed

+72
-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
@@ -3297,12 +3297,15 @@ def name_already_defined(self, name: str, ctx: Context) -> None:
32973297
self.fail("Name '{}' already defined".format(name), ctx)
32983298

32993299
def fail(self, msg: str, ctx: Context, serious: bool = False, *,
3300-
blocker: bool = False) -> None:
3300+
blocker: bool = False, implicit_any: bool = False) -> None:
33013301
if (not serious and
33023302
not self.options.check_untyped_defs and
33033303
self.function_stack and
33043304
self.function_stack[-1].is_dynamic()):
33053305
return
3306+
if implicit_any:
3307+
if not self.options.warn_implicit_any or self.cur_mod_node.is_stub:
3308+
return
33063309
# In case it's a bug and we don't really have context
33073310
assert ctx is not None, msg
33083311
self.errors.report(ctx.get_line(), ctx.get_column(), msg, blocker=blocker)
@@ -3711,7 +3714,14 @@ def analyze(self, type: Type) -> None:
37113714
analyzer = TypeAnalyserPass3(self.fail)
37123715
type.accept(analyzer)
37133716

3714-
def fail(self, msg: str, ctx: Context, *, blocker: bool = False) -> None:
3717+
def fail(self, msg: str, ctx: Context, *, blocker: bool = False,
3718+
implicit_any: bool = False) -> None:
3719+
if implicit_any:
3720+
if not self.options.warn_implicit_any or self.errors.file.endswith('.pyi'):
3721+
return
3722+
# TempNode, so must have already reported in the first pass
3723+
if ctx.get_line() == -1:
3724+
return
37153725
self.errors.report(ctx.get_line(), ctx.get_column(), msg)
37163726

37173727
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
@@ -137,6 +137,7 @@ def visit_unbound_type(self, t: UnboundType) -> Type:
137137
elif fullname == 'typing.Tuple':
138138
if len(t.args) == 0 and not t.empty_tuple_index:
139139
# Bare 'Tuple' is same as 'tuple'
140+
self.implicit_any('Tuple without type args', t)
140141
return self.builtin_type('builtins.tuple')
141142
if len(t.args) == 2 and isinstance(t.args[1], EllipsisType):
142143
# Tuple[T, ...] (uniform, variable-length tuple)
@@ -159,6 +160,7 @@ def visit_unbound_type(self, t: UnboundType) -> Type:
159160
return self.analyze_callable_type(t)
160161
elif fullname == 'typing.Type':
161162
if len(t.args) == 0:
163+
self.implicit_any('Type without type args', t)
162164
return TypeType(AnyType(), line=t.line)
163165
if len(t.args) != 1:
164166
self.fail('Type[...] must have exactly one type argument', t)
@@ -168,6 +170,7 @@ def visit_unbound_type(self, t: UnboundType) -> Type:
168170
if self.nesting_level > 0:
169171
self.fail('Invalid type: ClassVar nested inside other type', t)
170172
if len(t.args) == 0:
173+
self.implicit_any('ClassVar without type args', t)
171174
return AnyType(line=t.line)
172175
if len(t.args) != 1:
173176
self.fail('ClassVar[...] must have at most one type argument', t)
@@ -187,6 +190,7 @@ def visit_unbound_type(self, t: UnboundType) -> Type:
187190
act_len = len(an_args)
188191
if exp_len > 0 and act_len == 0:
189192
# Interpret bare Alias same as normal generic, i.e., Alias[Any, Any, ...]
193+
self.implicit_any('Generic type without type args', t)
190194
return self.replace_alias_tvars(override, all_vars, [AnyType()] * exp_len,
191 F438 195
t.line, t.column)
192196
if exp_len == 0 and act_len == 0:
@@ -205,6 +209,7 @@ def visit_unbound_type(self, t: UnboundType) -> Type:
205209
# context. This is slightly problematic as it allows using the type 'Any'
206210
# as a base class -- however, this will fail soon at runtime so the problem
207211
# is pretty minor.
212+
self.implicit_any('Assigning value of type Any', t)
208213
return AnyType()
209214
# Allow unbound type variables when defining an alias
210215
if not (self.aliasing and sym.kind == UNBOUND_TVAR):
@@ -243,6 +248,7 @@ def visit_unbound_type(self, t: UnboundType) -> Type:
243248
fallback=instance)
244249
return instance
245250
else:
251+
self.implicit_any('Fallback', t)
246252
return AnyType()
247253

248254
def get_type_var_names(self, tp: Type) -> List[str]:
@@ -366,6 +372,7 @@ def analyze_callable_type(self, t: UnboundType) -> Type:
366372
fallback = self.builtin_type('builtins.function')
367373
if len(t.args) == 0:
368374
# Callable (bare). Treat as Callable[..., Any].
375+
self.implicit_any('Callable without type args', t)
369376
return CallableType([AnyType(), AnyType()],
370377
[nodes.ARG_STAR, nodes.ARG_STAR2],
371378
[None, None],
@@ -429,6 +436,10 @@ def builtin_type(self, fully_qualified_name: str, args: List[Type] = None) -> In
429436
def tuple_type(self, items: List[Type]) -> TupleType:
430437
return TupleType(items, fallback=self.builtin_type('builtins.tuple', [AnyType()]))
431438

439+
def implicit_any(self, details: str, t: Type) -> None:
440+
msg = 'Type Any created implicitly: ' + details
441+
self.fail(msg, t, implicit_any=True) # type: ignore
442+
432443

433444
class TypeAnalyserPass3(TypeVisitor[None]):
434445
"""Analyze type argument counts and values of generic types.
@@ -459,6 +470,8 @@ def visit_instance(self, t: Instance) -> None:
459470
if len(t.args) != len(info.type_vars):
460471
if len(t.args) == 0:
461472
# Insert implicit 'Any' type arguments.
473+
if t.type.fullname() not in ('typing.Generator'):
474+
self.implicit_any('{} without type args'.format(t), t)
462475
t.args = [AnyType()] * len(info.type_vars)
463476
return
464477
# Invalid number of type parameters.
@@ -561,6 +574,10 @@ def visit_partial_type(self, t: PartialType) -> None:
561574
def visit_type_type(self, t: TypeType) -> None:
562575
pass
563576

577+
def implicit_any(self, details: str, t: Type) -> None:
578+
msg = 'Type Any created implicitly: ' + details
579+
self.fail(msg, t, implicit_any=True) # type: ignore
580+
564581

565582
def make_optional_type(t: Type) -> Type:
566583
"""Return the type corresponding to Optional[t].

test-data/unit/check-warnings.test

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,3 +165,38 @@ 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
172+
173+
T = TypeVar('T')
174+
U = TypeVar('U')
175+
176+
class X(Generic[T]):
177+
pass
178+
179+
class Y(Generic[T, U]):
180+
pass
181+
182+
a1: Tuple # E: Type Any created implicitly: Tuple without type args
183+
a2: Callable # E: Type Any created implicitly: Callable without type args
184+
a4: list # E: Type Any created implicitly: builtins.list without type args
185+
a5: List # E: Type Any created implicitly: builtins.list without type args
186+
a6: X # E: Type Any created implicitly: __main__.X without type args
187+
a7: Y # E: Type A 8736 ny created implicitly: __main__.Y without type args
188+
189+
def f(x: X) -> None: # E: Type Any created implicitly: __main__.X without type args
190+
pass
191+
192+
def g() -> X: # E: Type Any created implicitly: __main__.X without type args
193+
pass
194+
195+
b1: str
196+
b2: X[int]
197+
b3: Y[int, str]
198+
b4 = (1, 2)
199+
b5 = [1, 2]
200+
b6 = 'abc'
201+
202+
[builtins fixtures/list.pyi]

0 commit comments

Comments
 (0)
0