Skip to content

Commit 840a310

Browse files
authored
selfcheck: Enable the redundant-expr error code (#13547)
* selfcheck: Enable the `redundant-expr` error code * Remove unnecessary `type: ignore` * Revert changes to `mypy/build.py`, add a TODO * Use a `cast` instead * Use explicit annotation instead of `assert_type` * Bump `types-typed-ast`, remove unneeded `type: ignore`
1 parent d9bdd6d commit 840a310

14 files changed

+31
-36
lines changed

build-requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
-r mypy-requirements.txt
22
types-psutil
33
types-setuptools
4-
types-typed-ast>=1.5.0,<1.6.0
4+
types-typed-ast>=1.5.8,<1.6.0

mypy/build.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1292,11 +1292,15 @@ def find_cache_meta(id: str, path: str, manager: BuildManager) -> CacheMeta | No
12921292
)
12931293

12941294
# Don't check for path match, that is dealt with in validate_meta().
1295+
#
1296+
# TODO: these `type: ignore`s wouldn't be necessary
1297+
# if the type annotations for CacheMeta were more accurate
1298+
# (all of these attributes can be `None`)
12951299
if (
12961300
m.id != id
1297-
or m.mtime is None
1298-
or m.size is None
1299-
or m.dependencies is None
1301+
or m.mtime is None # type: ignore[redundant-expr]
1302+
or m.size is None # type: ignore[redundant-expr]
1303+
or m.dependencies is None # type: ignore[redundant-expr]
13001304
or m.data_mtime is None
13011305
):
13021306
manager.log(f"Metadata abandoned for {id}: attributes are missing")

mypy/checker.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1313,7 +1313,7 @@ def check___new___signature(self, fdef: FuncDef, typ: CallableType) -> None:
13131313
bound_type = bind_self(typ, self_type, is_classmethod=True)
13141314
# Check that __new__ (after binding cls) returns an instance
13151315
# type (or any).
1316-
if isinstance(fdef.info, TypeInfo) and fdef.info.is_metaclass():
1316+
if fdef.info.is_metaclass():
13171317
# This is a metaclass, so it must return a new unrelated type.
13181318
self.check_subtype(
13191319
bound_type.ret_type,
@@ -1901,7 +1901,7 @@ def check_override(
19011901
):
19021902
fail = True
19031903
op_method_wider_note = True
1904-
if isinstance(original, FunctionLike) and isinstance(override, FunctionLike):
1904+
if isinstance(override, FunctionLike):
19051905
if original_class_or_static and not override_class_or_static:
19061906
fail = True
19071907
elif isinstance(original, CallableType) and isinstance(override, CallableType):
@@ -2804,12 +2804,8 @@ def check_compatibility_all_supers(
28042804
# The type of "__slots__" and some other attributes usually doesn't need to
28052805
# be compatible with a base class. We'll still check the type of "__slots__"
28062806
# against "object" as an exception.
2807-
if (
2808-
isinstance(lvalue_node, Var)
2809-
and lvalue_node.allow_incompatible_override
2810-
and not (
2811-
lvalue_node.name == "__slots__" and base.fullname == "builtins.object"
2812-
)
2807+
if lvalue_node.allow_incompatible_override and not (
2808+
lvalue_node.name == "__slots__" and base.fullname == "builtins.object"
28132809
):
28142810
continue
28152811

mypy/checkmember.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,7 @@ def analyze_instance_member_access(
310310
# the first argument.
311311
pass
312312
else:
313-
if isinstance(signature, FunctionLike) and name != "__call__":
313+
if name != "__call__":
314314
# TODO: use proper treatment of special methods on unions instead
315315
# of this hack here and below (i.e. mx.self_type).
316316
dispatched_type = meet.meet_types(mx.original_type, typ)

mypy/fastparse.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -744,7 +744,7 @@ def _check_ifstmt_for_overloads(
744744
if stmt.else_body is None:
745745
return overload_name
746746

747-
if isinstance(stmt.else_body, Block) and len(stmt.else_body.body) == 1:
747+
if len(stmt.else_body.body) == 1:
748748
# For elif: else_body contains an IfStmt itself -> do a recursive check.
749749
if (
750750
isinstance(stmt.else_body.body[0], (Decorator, FuncDef, OverloadedFuncDef))
@@ -901,13 +901,11 @@ def do_func_def(
901901
# PEP 484 disallows both type annotations and type comments
902902
if n.returns or any(a.type_annotation is not None for a in args):
903903
self.fail(message_registry.DUPLICATE_TYPE_SIGNATURES, lineno, n.col_offset)
904-
translated_args = TypeConverter(
904+
translated_args: list[Type] = TypeConverter(
905905
self.errors, line=lineno, override_column=n.col_offset
906906
).translate_expr_list(func_type_ast.argtypes)
907-
arg_types = [
908-
a if a is not None else AnyType(TypeOfAny.unannotated)
909-
for a in translated_args
910-
]
907+
# Use a cast to work around `list` invariance
908+
arg_types = cast(List[Optional[Type]], translated_args)
911909
return_type = TypeConverter(self.errors, line=lineno).visit(func_type_ast.returns)
912910

913911
# add implicit self type

mypy/messages.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2486,11 +2486,7 @@ def [T <: int] f(self, x: int, y: T) -> None
24862486
slash = True
24872487

24882488
# If we got a "special arg" (i.e: self, cls, etc...), prepend it to the arg list
2489-
if (
2490-
isinstance(tp.definition, FuncDef)
2491-
and tp.definition.name is not None
2492-
and hasattr(tp.definition, "arguments")
2493-
):
2489+
if isinstance(tp.definition, FuncDef) and hasattr(tp.definition, "arguments"):
24942490
definition_arg_names = [arg.variable.name for arg in tp.definition.arguments]
24952491
if (
24962492
len(definition_arg_names) > len(tp.arg_names)
@@ -2684,7 +2680,7 @@ def find_defining_module(modules: dict[str, MypyFile], typ: CallableType) -> Myp
26842680
if not typ.definition:
26852681
return None
26862682
fullname = typ.definition.fullname
2687-
if fullname is not None and "." in fullname:
2683+
if "." in fullname:
26882684
for i in range(fullname.count(".")):
26892685
module_name = fullname.rsplit(".", i + 1)[0]
26902686
try:

mypy/nodes.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3580,7 +3580,10 @@ def serialize(self, prefix: str, name: str) -> JsonDict:
35803580
if prefix is not None:
35813581
fullname = self.node.fullname
35823582
if (
3583-
fullname is not None
3583+
# See the comment above SymbolNode.fullname -- fullname can often be None,
3584+
# but for complex reasons it's annotated as being `Bogus[str]` instead of `str | None`,
3585+
# meaning mypy erroneously thinks the `fullname is not None` check here is redundant
3586+
fullname is not None # type: ignore[redundant-expr]
35843587
and "." in fullname
35853588
and fullname != prefix + "." + name
35863589
and not (isinstance(self.node, Var) and self.node.from_module_getattr)

mypy/report.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -372,7 +372,7 @@ def visit_func_def(self, defn: FuncDef) -> None:
372372
if cur_indent is None:
373373
# Consume the line, but don't mark it as belonging to the function yet.
374374
cur_line += 1
375-
elif start_indent is not None and cur_indent > start_indent:
375+
elif cur_indent > start_indent:
376376
# A non-blank line that belongs to the function.
377377
cur_line += 1
378378
end_line = cur_line

mypy/semanal.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5892,7 +5892,7 @@ def in_checked_function(self) -> bool:
58925892
current_index = len(self.function_stack) - 1
58935893
while current_index >= 0:
58945894
current_func = self.function_stack[current_index]
5895-
if isinstance(current_func, FuncItem) and not isinstance(current_func, LambdaExpr):
5895+
if not isinstance(current_func, LambdaExpr):
58965896
return not current_func.is_dynamic()
58975897

58985898
# Special case, `lambda` inherits the "checked" state from its parent.

mypy/stubgen.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -643,7 +643,7 @@ def visit_overloaded_func_def(self, o: OverloadedFuncDef) -> None:
643643
self.visit_func_def(item.func, is_abstract=is_abstract, is_overload=is_overload)
644644
if is_overload:
645645
overload_chain = True
646-
elif overload_chain and is_overload:
646+
elif is_overload:
647647
self.visit_func_def(item.func, is_abstract=is_abstract, is_overload=is_overload)
648648
else:
649649
# skip the overload implementation and clear the decorator we just processed
@@ -725,9 +725,7 @@ def visit_func_def(
725725
retname = None # implicit Any
726726
else:
727727
retname = self.print_annotation(o.unanalyzed_type.ret_type)
728-
elif isinstance(o, FuncDef) and (
729-
o.abstract_status == IS_ABSTRACT or o.name in METHODS_WITH_RETURN_VALUE
730-
):
728+
elif o.abstract_status == IS_ABSTRACT or o.name in METHODS_WITH_RETURN_VALUE:
731729
# Always assume abstract methods return Any unless explicitly annotated. Also
732730
# some dunder methods should not have a None return type.
733731
retname = None # implicit Any

mypy/test/testsemanal.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ class TypeInfoMap(Dict[str, TypeInfo]):
220220
def __str__(self) -> str:
221221
a: list[str] = ["TypeInfoMap("]
222222
for x, y in sorted(self.items()):
223-
if isinstance(x, str) and (
223+
if (
224224
not x.startswith("builtins.")
225225
and not x.startswith("typing.")
226226
and not x.startswith("abc.")

mypy/test/testtypegen.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ def run_case(self, testcase: DataDrivenTestCase) -> None:
5353
# Filter nodes that should be included in the output.
5454
keys = []
5555
for node in nodes:
56-
if node.line is not None and node.line != -1 and map[node]:
56+
if node.line != -1 and map[node]:
5757
if ignore_node(node) or node in ignored:
5858
continue
5959
if re.match(mask, short_type(node)) or (

mypy/types.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1939,7 +1939,7 @@ def with_unpacked_kwargs(self) -> NormalizedCallableType:
19391939
if not self.unpack_kwargs:
19401940
return NormalizedCallableType(self.copy_modified())
19411941
last_type = get_proper_type(self.arg_types[-1])
1942-
assert isinstance(last_type, ProperType) and isinstance(last_type, TypedDictType)
1942+
assert isinstance(last_type, TypedDictType)
19431943
extra_kinds = [
19441944
ArgKind.ARG_NAMED if name in last_type.required_keys else ArgKind.ARG_NAMED_OPT
19451945
for name in last_type.items

mypy_self_check.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,4 @@ always_false = MYPYC
1111
plugins = misc/proper_plugin.py
1212
python_version = 3.7
1313
exclude = mypy/typeshed/|mypyc/test-data/|mypyc/lib-rt/
14-
enable_error_code = ignore-without-code
14+
enable_error_code = ignore-without-code,redundant-expr

0 commit comments

Comments
 (0)