diff --git a/mypy/checker.py b/mypy/checker.py index 96b55f321a73..d93dab43b0b4 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -7734,7 +7734,7 @@ def fail( ) -> ErrorInfo: """Produce an error message.""" if isinstance(msg, ErrorMessage): - return self.msg.fail(msg.value, context, code=msg.code) + msg, code = msg.value, msg.code # type: ignore[attr-defined] #TODO: https://github.com/python/mypy/issues/19891 return self.msg.fail(msg, context, code=code) def note( @@ -7744,12 +7744,12 @@ def note( offset: int = 0, *, code: ErrorCode | None = None, + parent_error: ErrorInfo | None = None, ) -> None: """Produce a note.""" if isinstance(msg, ErrorMessage): - self.msg.note(msg.value, context, code=msg.code) - return - self.msg.note(msg, context, offset=offset, code=code) + msg, code = msg.value, msg.code # type: ignore[attr-defined] #TODO: https://github.com/python/mypy/issues/19891 + self.msg.note(msg, context, offset=offset, code=code, parent_error=parent_error) def iterable_item_type( self, it: Instance | CallableType | TypeType | Overloaded, context: Context diff --git a/mypy/errors.py b/mypy/errors.py index f1b2faf67401..11d1cbbec207 100644 --- a/mypy/errors.py +++ b/mypy/errors.py @@ -509,7 +509,7 @@ def report( line: line number of error column: column number of error message: message to report - code: error code (defaults to 'misc'; not shown for notes) + code: error code (defaults to misc; or None for notes), not shown for notes blocker: if True, don't continue analysis after this error severity: 'error' or 'note' file: if non-None, override current file as context @@ -548,7 +548,16 @@ def report( end_line = line code = code or (parent_error.code if parent_error else None) - code = code or (codes.MISC if not blocker else None) + if code is None: + if blocker: + code = None # do we even need to do this for blockers? + elif severity == "note": + if parent_error is not None: + code = parent_error.code # this might be None, btw + else: + code = None + else: + code = codes.MISC info = ErrorInfo( import_ctx=self.import_context(), diff --git a/mypy/messages.py b/mypy/messages.py index 6329cad687f6..ff9c9dcd48f9 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -1516,9 +1516,9 @@ def incompatible_conditional_function_def( if isinstance(old_type, (CallableType, Overloaded)) and isinstance( new_type, (CallableType, Overloaded) ): - self.note("Original:", defn) + self.note("Original:", defn, parent_error=error) self.pretty_callable_or_overload(old_type, defn, offset=4, parent_error=error) - self.note("Redefinition:", defn) + self.note("Redefinition:", defn, parent_error=error) self.pretty_callable_or_overload(new_type, defn, offset=4, parent_error=error) def cannot_instantiate_abstract_class( diff --git a/mypy/plugins/proper_plugin.py b/mypy/plugins/proper_plugin.py index 0189bfbd22fc..1c7d7c55756d 100644 --- a/mypy/plugins/proper_plugin.py +++ b/mypy/plugins/proper_plugin.py @@ -63,7 +63,7 @@ def isinstance_proper_hook(ctx: FunctionContext) -> Type: ) and is_dangerous_target(right): if is_special_target(right): return ctx.default_return_type - ctx.api.fail( + parent_error = ctx.api.fail( "Never apply isinstance() to unexpanded types;" " use mypy.types.get_proper_type() first", ctx.context, @@ -72,6 +72,7 @@ def isinstance_proper_hook(ctx: FunctionContext) -> Type: "If you pass on the original type" " after the check, always use its unexpanded version", ctx.context, + parent_error=parent_error, ) return ctx.default_return_type diff --git a/test-data/unit/check-errorcodes.test b/test-data/unit/check-errorcodes.test index bb5f658ebb50..75a4e5d80efa 100644 --- a/test-data/unit/check-errorcodes.test +++ b/test-data/unit/check-errorcodes.test @@ -1,6 +1,27 @@ -- Tests for error codes and ignoring errors using error codes -- --- These implicitly use --show-error-codes. +-- These implicitly use --show-error-codes. # TODO: isn't this statement redundant now that this flag defaults to true? + +[case testErrorCodeMiscDoesntSuppressRevealType] +# Prevent regression on https://github.com/python/mypy/issues/19840 +from typing import reveal_type +reveal_type(1) #type: ignore[misc] # N: Revealed type is "Literal[1]?" + +[case testErrorCodeMiscDoesntSuppressRevealLocals] +reveal_locals() #type:ignore[misc] # N: There are no locals to reveal + +[case testErrorCodeMiscSuppressesAccompanyingNotes] +# flags: --strict +if x: #type: ignore[name-defined] + def y() -> None: pass +else: + def y() -> bool: return True # type: ignore[misc] + def y() -> str: return "hmm" # All conditional function variants must have identical signatures [misc] # E: All conditional function variants must have identical signatures [misc] \ + # N: Original: \ + # N: def y() -> None \ + # N: Redefinition: \ + # N: def y() -> str + [case testErrorCodeNoAttribute] import m