Skip to content

Commit 005fd6b

Browse files
ilinumddfisher
authored andcommitted
Do not report higher-kinded types as Anys with --disallow-any=expr (#3667)
Do not report higher-kinded types as Anys with --disallow-any=expr as well as in `--any-exprs-report` and `--html-report` We can't fully represent higher-kinded types (e.g. calls to NewType) in mypy's type system, so we treat them as 'Any' and give specific errors for each higher-kinded type (e.g. NewType).
1 parent 7670ac9 commit 005fd6b

File tree

5 files changed

+107
-10
lines changed

5 files changed

+107
-10
lines changed

mypy/checkexpr.py

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2385,11 +2385,10 @@ def visit_temp_node(self, e: TempNode) -> Type:
23852385
return e.type
23862386

23872387
def visit_type_var_expr(self, e: TypeVarExpr) -> Type:
2388-
# TODO: Perhaps return a special type used for type variables only?
2389-
return AnyType()
2388+
return AnyType(special_form=True)
23902389

23912390
def visit_newtype_expr(self, e: NewTypeExpr) -> Type:
2392-
return AnyType()
2391+
return AnyType(special_form=True)
23932392

23942393
def visit_namedtuple_expr(self, e: NamedTupleExpr) -> Type:
23952394
tuple_type = e.info.tuple_type
@@ -2399,8 +2398,7 @@ def visit_namedtuple_expr(self, e: NamedTupleExpr) -> Type:
23992398
self.msg.unimported_type_becomes_any("NamedTuple type", tuple_type, e)
24002399
check_for_explicit_any(tuple_type, self.chk.options, self.chk.is_typeshed_stub,
24012400
self.msg, context=e)
2402-
# TODO: Perhaps return a type object type?
2403-
return AnyType()
2401+
return AnyType(special_form=True)
24042402

24052403
def visit_enum_call_expr(self, e: EnumCallExpr) -> Type:
24062404
for name, value in zip(e.items, e.values):
@@ -2415,12 +2413,10 @@ def visit_enum_call_expr(self, e: EnumCallExpr) -> Type:
24152413
# to have type Any in the typeshed stub.)
24162414
var.type = typ
24172415
var.is_inferred = True
2418-
# TODO: Perhaps return a type object type?
2419-
return AnyType()
2416+
return AnyType(special_form=True)
24202417

24212418
def visit_typeddict_expr(self, e: TypedDictExpr) -> Type:
2422-
# TODO: Perhaps return a type object type?
2423-
return AnyType()
2419+
return AnyType(special_form=True)
24242420

24252421
def visit__promote_expr(self, e: PromoteExpr) -> Type:
24262422
return e.type
@@ -2455,7 +2451,7 @@ def __init__(self) -> None:
24552451
super().__init__(any)
24562452

24572453
def visit_any(self, t: AnyType) -> bool:
2458-
return True
2454+
return not t.special_form # special forms are not real Any types
24592455

24602456

24612457
def has_coroutine_decorator(t: Type) -> bool:

mypy/stats.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,10 @@ def type(self, t: Optional[Type]) -> None:
177177
self.record_line(self.line, TYPE_UNANALYZED)
178178
return
179179

180+
if isinstance(t, AnyType) and t.special_form:
181+
# This is not a real Any type, so don't collect stats for it.
182+
return
183+
180184
if isinstance(t, AnyType):
181185
self.log(' !! Any type around line %d' % self.line)
182186
self.num_any += 1

mypy/types.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,7 @@ def __init__(self,
258258
from_unimported_type: bool = False,
259259
explicit: bool = False,
260260
from_omitted_generics: bool = False,
261+
special_form: bool = False,
261262
line: int = -1,
262263
column: int = -1) -> None:
263264
super().__init__(line, column)
@@ -272,6 +273,9 @@ def __init__(self,
272273
self.explicit = explicit
273274
# Does this type come from omitted generics?
274275
self.from_omitted_generics = from_omitted_generics
276+
# Is this a type that can't be represented in mypy's type system? For instance, type of
277+
# call to NewType(...)). Even though these types aren't real Anys, we treat them as such.
278+
self.special_form = special_form
275279

276280
def accept(self, visitor: 'TypeVisitor[T]') -> T:
277281
return visitor.visit_any(self)
@@ -281,6 +285,7 @@ def copy_modified(self,
281285
from_unimported_type: bool = _dummy,
282286
explicit: bool = _dummy,
283287
from_omitted_generics: bool = _dummy,
288+
special_form: bool = _dummy,
284289
) -> 'AnyType':
285290
if implicit is _dummy:
286291
implicit = self.implicit
@@ -290,8 +295,11 @@ def copy_modified(self,
290295
explicit = self.explicit
291296
if from_omitted_generics is _dummy:
292297
from_omitted_generics = self.from_omitted_generics
298+
if special_form is _dummy:
299+
special_form = self.special_form
293300
return AnyType(implicit=implicit, from_unimported_type=from_unimported_type,
294301
explicit=explicit, from_omitted_generics=from_omitted_generics,
302+
special_form=special_form,
295303
line=self.line, column=self.column)
296304

297305
def serialize(self) -> JsonDict:

test-data/unit/check-flags.test

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -832,3 +832,51 @@ l: List = []
832832
l.append(1) # E: Expression type contains "Any" (has type List[Any])
833833
k = l[0] # E: Expression type contains "Any" (has type List[Any]) # E: Expression has type "Any"
834834
[builtins fixtures/list.pyi]
835+
836+
[case testDisallowAnyExprTypeVar]
837+
# flags: --disallow-any=expr
838+
from typing import TypeVar
839+
840+
T = TypeVar('T') # no error
841+
842+
def f(t: T) -> T:
843+
return t
844+
[builtins fixtures/list.pyi]
845+
846+
[case testDisallowAnyExprNamedTuple]
847+
# flags: --disallow-any=expr
848+
from typing import NamedTuple
849+
850+
Point = NamedTuple('Point', [('x', int), ('y', int)]) # no error
851+
852+
def origin() -> Point:
853+
return Point(x=0, y=0)
854+
[builtins fixtures/list.pyi]
855+
856+
[case testDisallowAnyExprNewType]
857+
# flags: --disallow-any=expr
858+
from typing import NewType
859+
860+
NT = NewType('NT', int) # no error
861+
862+
def nt() -> NT:
863+
return NT(1)
864+
[builtins fixtures/list.pyi]
865+
866+
[case testDisallowAnyExprEnum]
867+
# flags: --disallow-any=expr
868+
from enum import Enum
869+
E = Enum('E', '1, 2, 3') # no error
870+
871+
def k(s: E) -> None: pass
872+
[builtins fixtures/list.pyi]
873+
874+
[case testDisallowAnyExprTypedDict]
875+
# flags: --disallow-any=expr
876+
from mypy_extensions import TypedDict
877+
878+
Movie = TypedDict('Movie', {'name': str, 'year': int})
879+
880+
def g(m: Movie) -> Movie:
881+
return m
882+
[builtins fixtures/dict.pyi]

test-data/unit/reports.test

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,3 +233,44 @@ i 1 7 85.71%
233233
j 0 6 100.00%
234234
------------------------------
235235
Total 1 13 92.31%
236+
237+
[case testAnyExprReportHigherKindedTypesAreNotAny]
238+
# cmd: mypy --any-exprs-report report i.py
239+
240+
[file i.py]
241+
from enum import Enum
242+
from mypy_extensions import TypedDict
243+
from typing import NewType, NamedTuple, TypeVar
244+
245+
from typing import TypeVar
246+
247+
T = TypeVar('T') # no error
248+
249+
def f(t: T) -> T:
250+
return t
251+
252+
Point = NamedTuple('Point', [('x', int), ('y', int)]) # no error
253+
254+
def origin() -> Point:
255+
return Point(x=0, y=0)
256+
257+
NT = NewType('NT', int) # no error
258+
259+
def nt() -> NT:
260+
return NT(1)
261+
262+
E = Enum('E', '1, 2, 3') # no error
263+
264+
def k(s: E) -> None: pass
265+
266+
Movie = TypedDict('Movie', {'name': str, 'year': int})
267+
268+
def g(m: Movie) -> Movie:
269+
return m
270+
271+
[outfile report/any-exprs.txt]
272+
Name Anys Exprs Coverage
273+
------------------------------
274+
i 0 16 100.00%
275+
------------------------------
276+
Total 0 16 100.00%

0 commit comments

Comments
 (0)