diff --git a/mypy/checker.py b/mypy/checker.py index 20a825e9cc5e..a3f187cdf431 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -444,6 +444,12 @@ def __init__( self.allow_constructor_cache = True self.local_type_map = LocalTypeMap(self) + self.can_skip_diagnostics: Final = ( + self.options.ignore_errors + or self.path in self.msg.errors.ignored_files + or (self.options.test_env and self.is_typeshed_stub) + ) + # If True, process function definitions. If False, don't. This is used # for processing module top levels in fine-grained incremental mode. self.recurse_into_functions = True @@ -801,7 +807,7 @@ def _visit_overloaded_func_def(self, defn: OverloadedFuncDef) -> None: ) defn.type = Overloaded([var_type]) # Check override validity after we analyzed current definition. - if defn.info: + if not self.can_skip_diagnostics and defn.info: found_method_base_classes = self.check_method_override(defn) if ( defn.is_explicit_override @@ -882,15 +888,7 @@ def check_overlapping_overloads(self, defn: OverloadedFuncDef) -> None: # At this point we should have set the impl already, and all remaining # items are decorators - if ( - self.options.ignore_errors - or self.msg.errors.file in self.msg.errors.ignored_files - or (self.is_typeshed_stub and self.options.test_env) - ): - # This is a little hacky, however, the quadratic check here is really expensive, this - # method has no side effects, so we should skip it if we aren't going to report - # anything. In some other places we swallow errors in stubs, but this error is very - # useful for stubs! + if self.can_skip_diagnostics: return # Compute some info about the implementation (if it exists) for use below @@ -1196,24 +1194,21 @@ def visit_func_def(self, defn: FuncDef) -> None: if not self.recurse_into_functions: return with self.tscope.function_scope(defn): - self._visit_func_def(defn) - - def _visit_func_def(self, defn: FuncDef) -> None: - """Type check a function definition.""" - self.check_func_item(defn, name=defn.name) - if defn.info: - if not defn.is_overload and not defn.is_decorated: - # If the definition is the implementation for an - # overload, the legality of the override has already - # been typechecked, and decorated methods will be - # checked when the decorator is. - found_method_base_classes = self.check_method_override(defn) - self.check_explicit_override_decorator(defn, found_method_base_classes) - self.check_inplace_operator_method(defn) - if defn.original_def: - # Override previous definition. - new_type = self.function_type(defn) - self.check_func_def_override(defn, new_type) + self.check_func_item(defn, name=defn.name) + if not self.can_skip_diagnostics: + if defn.info: + if not defn.is_overload and not defn.is_decorated: + # If the definition is the implementation for an + # overload, the legality of the override has already + # been typechecked, and decorated methods will be + # checked when the decorator is. + found_method_base_classes = self.check_method_override(defn) + self.check_explicit_override_decorator(defn, found_method_base_classes) + self.check_inplace_operator_method(defn) + if defn.original_def: + # Override previous definition. + new_type = self.function_type(defn) + self.check_func_def_override(defn, new_type) def check_func_item( self, @@ -1317,75 +1312,8 @@ def check_func_def( # We may be checking a function definition or an anonymous # function. In the first case, set up another reference with the # precise type. - if isinstance(item, FuncDef): - fdef = item - # Check if __init__ has an invalid return type. - if ( - fdef.info - and fdef.name in ("__init__", "__init_subclass__") - and not isinstance( - get_proper_type(typ.ret_type), (NoneType, UninhabitedType) - ) - and not self.dynamic_funcs[-1] - ): - self.fail( - message_registry.MUST_HAVE_NONE_RETURN_TYPE.format(fdef.name), item - ) - - # Check validity of __new__ signature - if fdef.info and fdef.name == "__new__": - self.check___new___signature(fdef, typ) - - self.check_for_missing_annotations(fdef) - if self.options.disallow_any_unimported: - if fdef.type and isinstance(fdef.type, CallableType): - ret_type = fdef.type.ret_type - if has_any_from_unimported_type(ret_type): - self.msg.unimported_type_becomes_any("Return type", ret_type, fdef) - for idx, arg_type in enumerate(fdef.type.arg_types): - if has_any_from_unimported_type(arg_type): - prefix = f'Argument {idx + 1} to "{fdef.name}"' - self.msg.unimported_type_becomes_any(prefix, arg_type, fdef) - check_for_explicit_any( - fdef.type, self.options, self.is_typeshed_stub, self.msg, context=fdef - ) - - if name: # Special method names - if ( - defn.info - and self.is_reverse_op_method(name) - and defn not in self.overload_impl_stack - ): - self.check_reverse_op_method(item, typ, name, defn) - elif name in ("__getattr__", "__getattribute__"): - self.check_getattr_method(typ, defn, name) - elif name == "__setattr__": - self.check_setattr_method(typ, defn) - - # Refuse contravariant return type variable - if isinstance(typ.ret_type, TypeVarType): - if typ.ret_type.variance == CONTRAVARIANT: - self.fail( - message_registry.RETURN_TYPE_CANNOT_BE_CONTRAVARIANT, typ.ret_type - ) - self.check_unbound_return_typevar(typ) - elif ( - isinstance(original_typ.ret_type, TypeVarType) and original_typ.ret_type.values - ): - # Since type vars with values are expanded, the return type is changed - # to a raw value. This is a hack to get it back. - self.check_unbound_return_typevar(original_typ) - - # Check that Generator functions have the appropriate return type. - if defn.is_generator: - if defn.is_async_generator: - if not self.is_async_generator_return_type(typ.ret_type): - self.fail( - message_registry.INVALID_RETURN_TYPE_FOR_ASYNC_GENERATOR, typ - ) - else: - if not self.is_generator_return_type(typ.ret_type, defn.is_coroutine): - self.fail(message_registry.INVALID_RETURN_TYPE_FOR_GENERATOR, typ) + if not self.can_skip_diagnostics: + self.check_funcdef_item(item, typ, name, defn=defn, original_typ=original_typ) # Fix the type if decorated with `@types.coroutine` or `@asyncio.coroutine`. if defn.is_awaitable_coroutine: @@ -1453,7 +1381,8 @@ def check_func_def( # Type check initialization expressions. body_is_trivial = is_trivial_body(defn.body) - self.check_default_args(item, body_is_trivial) + if not self.can_skip_diagnostics: + self.check_default_args(item, body_is_trivial) # Type check body in a new scope. with self.binder.top_frame_context(): @@ -1500,10 +1429,7 @@ def check_func_def( # if during semantic analysis we found that there are no attributes # defined via self here. if ( - not ( - self.options.ignore_errors - or self.msg.errors.file in self.msg.errors.ignored_files - ) + not self.can_skip_diagnostics or self.options.preserve_asts or not isinstance(defn, FuncDef) or defn.has_self_attr_def @@ -1541,69 +1467,139 @@ def check_func_def( ) ) - # Ignore plugin generated methods, these usually don't need any bodies. - if defn.info is not FUNC_NO_INFO and ( - defn.name not in defn.info.names or defn.info.names[defn.name].plugin_generated + if ( + self.can_skip_diagnostics + # Ignore plugin generated methods, these usually don't need any bodies. + or ( + defn.info is not FUNC_NO_INFO + and ( + defn.name not in defn.info.names + or defn.info.names[defn.name].plugin_generated + ) + ) + # Ignore also definitions that appear in `if TYPE_CHECKING: ...` blocks. + # These can't be called at runtime anyway (similar to plugin-generated). + or (isinstance(defn, FuncDef) and defn.is_mypy_only) + # We want to minimize the fallout from checking empty bodies + # that was absent in many mypy versions. + or (body_is_trivial and is_subtype(NoneType(), return_type)) ): show_error = False - # Ignore also definitions that appear in `if TYPE_CHECKING: ...` blocks. - # These can't be called at runtime anyway (similar to plugin-generated). - if isinstance(defn, FuncDef) and defn.is_mypy_only: - show_error = False - - # We want to minimize the fallout from checking empty bodies - # that was absent in many mypy versions. - if body_is_trivial and is_subtype(NoneType(), return_type): - show_error = False - - may_be_abstract = ( - body_is_trivial - and defn.info is not FUNC_NO_INFO - and defn.info.metaclass_type is not None - and defn.info.metaclass_type.type.has_base("abc.ABCMeta") - ) - - if self.options.warn_no_return: - if ( - not self.current_node_deferred - and not isinstance(return_type, (NoneType, AnyType)) - and show_error - ): - # Control flow fell off the end of a function that was - # declared to return a non-None type. - if isinstance(return_type, UninhabitedType): - # This is a NoReturn function - msg = message_registry.INVALID_IMPLICIT_RETURN - else: - msg = message_registry.MISSING_RETURN_STATEMENT + if show_error: + may_be_abstract = ( + body_is_trivial + and defn.info is not FUNC_NO_INFO + and defn.info.metaclass_type is not None + and defn.info.metaclass_type.type.has_base("abc.ABCMeta") + ) + if self.options.warn_no_return: + if not self.current_node_deferred and not isinstance( + return_type, (NoneType, AnyType) + ): + # Control flow fell off the end of a function that was + # declared to return a non-None type. + if isinstance(return_type, UninhabitedType): + # This is a NoReturn function + msg = message_registry.INVALID_IMPLICIT_RETURN + else: + msg = message_registry.MISSING_RETURN_STATEMENT + if body_is_trivial: + msg = msg._replace(code=codes.EMPTY_BODY) + self.fail(msg, defn) + if may_be_abstract: + self.note(message_registry.EMPTY_BODY_ABSTRACT, defn) + else: + msg = message_registry.INCOMPATIBLE_RETURN_VALUE_TYPE if body_is_trivial: msg = msg._replace(code=codes.EMPTY_BODY) - self.fail(msg, defn) - if may_be_abstract: + # similar to code in check_return_stmt + if ( + not self.check_subtype( + subtype_label="implicitly returns", + subtype=NoneType(), + supertype_label="expected", + supertype=return_type, + context=defn, + msg=msg, + ) + and may_be_abstract + ): self.note(message_registry.EMPTY_BODY_ABSTRACT, defn) - elif show_error: - msg = message_registry.INCOMPATIBLE_RETURN_VALUE_TYPE - if body_is_trivial: - msg = msg._replace(code=codes.EMPTY_BODY) - # similar to code in check_return_stmt - if ( - not self.check_subtype( - subtype_label="implicitly returns", - subtype=NoneType(), - supertype_label="expected", - supertype=return_type, - context=defn, - msg=msg, - ) - and may_be_abstract - ): - self.note(message_registry.EMPTY_BODY_ABSTRACT, defn) self.return_types.pop() self.binder = old_binder + def check_funcdef_item( + self, + item: FuncItem, + typ: CallableType, + name: str | None, + *, + defn: FuncItem, + original_typ: CallableType, + ) -> None: + if isinstance(item, FuncDef): + fdef = item + # Check if __init__ has an invalid return type. + if ( + fdef.info + and fdef.name in ("__init__", "__init_subclass__") + and not isinstance(get_proper_type(typ.ret_type), (NoneType, UninhabitedType)) + and not self.dynamic_funcs[-1] + ): + self.fail(message_registry.MUST_HAVE_NONE_RETURN_TYPE.format(fdef.name), item) + + # Check validity of __new__ signature + if fdef.info and fdef.name == "__new__": + self.check___new___signature(fdef, typ) + + self.check_for_missing_annotations(fdef) + if self.options.disallow_any_unimported: + if fdef.type and isinstance(fdef.type, CallableType): + ret_type = fdef.type.ret_type + if has_any_from_unimported_type(ret_type): + self.msg.unimported_type_becomes_any("Return type", ret_type, fdef) + for idx, arg_type in enumerate(fdef.type.arg_types): + if has_any_from_unimported_type(arg_type): + prefix = f'Argument {idx + 1} to "{fdef.name}"' + self.msg.unimported_type_becomes_any(prefix, arg_type, fdef) + check_for_explicit_any( + fdef.type, self.options, self.is_typeshed_stub, self.msg, context=fdef + ) + + if name: # Special method names + if ( + defn.info + and self.is_reverse_op_method(name) + and defn not in self.overload_impl_stack + ): + self.check_reverse_op_method(item, typ, name, defn) + elif name in ("__getattr__", "__getattribute__"): + self.check_getattr_method(typ, defn, name) + elif name == "__setattr__": + self.check_setattr_method(typ, defn) + + # Refuse contravariant return type variable + if isinstance(typ.ret_type, TypeVarType): + if typ.ret_type.variance == CONTRAVARIANT: + self.fail(message_registry.RETURN_TYPE_CANNOT_BE_CONTRAVARIANT, typ.ret_type) + self.check_unbound_return_typevar(typ) + elif isinstance(original_typ.ret_type, TypeVarType) and original_typ.ret_type.values: + # Since type vars with values are expanded, the return type is changed + # to a raw value. This is a hack to get it back. + self.check_unbound_return_typevar(original_typ) + + # Check that Generator functions have the appropriate return type. + if defn.is_generator: + if defn.is_async_generator: + if not self.is_async_generator_return_type(typ.ret_type): + self.fail(message_registry.INVALID_RETURN_TYPE_FOR_ASYNC_GENERATOR, typ) + else: + if not self.is_generator_return_type(typ.ret_type, defn.is_coroutine): + self.fail(message_registry.INVALID_RETURN_TYPE_FOR_GENERATOR, typ) + def require_correct_self_argument(self, func: Type, defn: FuncDef) -> bool: func = get_proper_type(func) if not isinstance(func, CallableType): @@ -1636,6 +1632,9 @@ def require_correct_self_argument(self, func: Type, defn: FuncDef) -> bool: ) return False + if self.can_skip_diagnostics: + return True + arg_type = func.arg_types[0] if defn.is_class or defn.name == "__new__": ref_type = mypy.types.TypeType.make_normalized(ref_type) @@ -1750,6 +1749,10 @@ def is_reverse_op_method(self, method_name: str) -> bool: return method_name in operators.reverse_op_method_set def check_for_missing_annotations(self, fdef: FuncItem) -> None: + show_untyped = not self.is_typeshed_stub or self.options.warn_incomplete_stub + if not show_untyped: + return + # Check for functions with unspecified/not fully specified types. def is_unannotated_any(t: Type) -> bool: if not isinstance(t, ProperType): @@ -1760,9 +1763,8 @@ def is_unannotated_any(t: Type) -> bool: not is_unannotated_any(t) for t in fdef.type.arg_types + [fdef.type.ret_type] ) - show_untyped = not self.is_typeshed_stub or self.options.warn_incomplete_stub check_incomplete_defs = self.options.disallow_incomplete_defs and has_explicit_annotation - if show_untyped and (self.options.disallow_untyped_defs or check_incomplete_defs): + if self.options.disallow_untyped_defs or check_incomplete_defs: if fdef.type is None and self.options.disallow_untyped_defs: if not fdef.arguments or ( len(fdef.arguments) == 1 @@ -2192,8 +2194,7 @@ def check_method_override( Return a list of base classes which contain an attribute with the method name. """ - if self.options.ignore_errors or self.msg.errors.file in self.msg.errors.ignored_files: - # Method override checks may be expensive, so skip them in third-party libraries. + if self.can_skip_diagnostics: return None # Check against definitions in base classes. check_override_compatibility = ( @@ -2687,14 +2688,15 @@ def visit_class_def(self, defn: ClassDef) -> None: with self.scope.push_class(defn.info): self.accept(defn.defs) self.binder = old_binder - if not (defn.info.typeddict_type or defn.info.tuple_type or defn.info.is_enum): - # If it is not a normal class (not a special form) check class keywords. - self.check_init_subclass(defn) - if not defn.has_incompatible_baseclass: - # Otherwise we've already found errors; more errors are not useful - self.check_multiple_inheritance(typ) - self.check_metaclass_compatibility(typ) - self.check_final_deletable(typ) + if not self.can_skip_diagnostics: + if not (defn.info.typeddict_type or defn.info.tuple_type or defn.info.is_enum): + # If it is not a normal class (not a special form) check class keywords. + self.check_init_subclass(defn) + if not defn.has_incompatible_baseclass: + # Otherwise we've already found errors; more errors are not useful + self.check_multiple_inheritance(typ) + self.check_metaclass_compatibility(typ) + self.check_final_deletable(typ) if defn.decorators: sig: Type = type_object_type(defn.info, self.named_type) @@ -2943,6 +2945,8 @@ def check_protocol_variance(self, defn: ClassDef) -> None: if they are actually covariant/contravariant, since this may break transitivity of subtyping, see PEP 544. """ + if self.can_skip_diagnostics: + return if defn.type_args is not None: # Using new-style syntax (PEP 695), so variance will be inferred return @@ -3443,8 +3447,10 @@ def check_assignment( rvalue_type = remove_instance_last_known_values(rvalue_type) self.infer_variable_type(inferred, lvalue, rvalue_type, rvalue) self.check_assignment_to_slots(lvalue) - if isinstance(lvalue, RefExpr) and not ( - isinstance(lvalue, NameExpr) and lvalue.name == "__match_args__" + if ( + isinstance(lvalue, RefExpr) + and not (isinstance(lvalue, NameExpr) and lvalue.name == "__match_args__") + and not self.can_skip_diagnostics ): # We check override here at the end after storing the inferred type, since # override check will try to access the current attribute via symbol tables diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 149575ab37aa..e1e09106c430 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -3679,7 +3679,7 @@ def visit_comparison_expr(self, e: ComparisonExpr) -> Type: self.msg.unsupported_operand_types("in", left_type, right_type, e) else: container_type = UnionType.make_union(container_types) - if self.dangerous_comparison( + if not self.chk.can_skip_diagnostics and self.dangerous_comparison( left_type, container_type, original_container=right_type, @@ -3702,7 +3702,9 @@ def visit_comparison_expr(self, e: ComparisonExpr) -> Type: # testCustomEqCheckStrictEquality for an example. if not w.has_new_errors() and operator in ("==", "!="): right_type = self.accept(right) - if self.dangerous_comparison(left_type, right_type): + if not self.chk.can_skip_diagnostics and self.dangerous_comparison( + left_type, right_type + ): # Show the most specific literal types possible left_type = try_getting_literal(left_type) right_type = try_getting_literal(right_type) @@ -3711,7 +3713,9 @@ def visit_comparison_expr(self, e: ComparisonExpr) -> Type: elif operator == "is" or operator == "is not": right_type = self.accept(right) # validate the right operand sub_result = self.bool_type() - if self.dangerous_comparison(left_type, right_type, identity_check=True): + if not self.chk.can_skip_diagnostics and self.dangerous_comparison( + left_type, right_type, identity_check=True + ): # Show the most specific literal types possible left_type = try_getting_literal(left_type) right_type = try_getting_literal(right_type) diff --git a/mypy/errors.py b/mypy/errors.py index edfb3bd1607a..7ed5875fcaf3 100644 --- a/mypy/errors.py +++ b/mypy/errors.py @@ -1000,6 +1000,8 @@ def prefer_simple_messages(self) -> bool: if self.file in self.ignored_files: # Errors ignored, so no point generating fancy messages return True + if self.options.ignore_errors: + return True if self._watchers: _watcher = self._watchers[-1] if _watcher._filter is True and _watcher._filtered is None: