Report func-returns-value for decorated methods#21534
Open
jbbqqf wants to merge 1 commit into
Open
Conversation
Decorated functions — most commonly @staticmethod and @classmethod, but also any user decorator that preserves a '-> None' return type — were silently skipped by the func-returns-value check because Decorator nodes were not handled in defn_returns_none. The synthetic Var carries the decorated callable type, so use its return type directly (the Var is flagged as inferred so the existing Var branch declined to use it). Fixes python#14179.
Contributor
|
Diff from mypy_primer, showing the effect of this PR on open source code: spark (https://github.com/apache/spark)
+ python/pyspark/sql/profiler.py:80: error: "zero" of "PStatsParam" does not return a value (it only ever returns None) [func-returns-value]
+ python/pyspark/sql/profiler.py:80: error: "zero" of "MemUsageParam" does not return a value (it only ever returns None) [func-returns-value]
colour (https://github.com/colour-science/colour)
+ colour/colorimetry/tests/test_tristimulus_values.py:1716: error: "assert_allclose" does not return a value (it only ever returns None) [func-returns-value]
+ colour/colorimetry/tests/test_tristimulus_values.py:1730: error: "assert_allclose" does not return a value (it only ever returns None) [func-returns-value]
+ colour/colorimetry/tests/test_tristimulus_values.py:1765: error: "assert_allclose" does not return a value (it only ever returns None) [func-returns-value]
+ colour/colorimetry/tests/test_tristimulus_values.py:1779: error: "assert_allclose" does not return a value (it only ever returns None) [func-returns-value]
zulip (https://github.com/zulip/zulip)
+ zerver/tests/test_message_fetch.py:2416: error: "sort" of "list" does not return a value (it only ever returns None) [func-returns-value]
+ zerver/tests/test_message_fetch.py:2436: error: "sort" of "list" does not return a value (it only ever returns None) [func-returns-value]
egglog-python (https://github.com/egraphs-good/egglog-python)
+ python/egglog/exp/any_expr.py:665: error: "setitem" does not return a value (it only ever returns None) [func-returns-value]
+ python/egglog/exp/any_expr.py:668: error: "delitem" does not return a value (it only ever returns None) [func-returns-value]
pandera (https://github.com/pandera-dev/pandera)
+ tests/pandas/test_decorators.py:756: error: "optional_in" does not return a value (it only ever returns None) [func-returns-value]
pandas (https://github.com/pandas-dev/pandas)
+ pandas/core/series.py:3978: error: "_update_inplace" of "NDFrame" does not return a value (it only ever returns None) [func-returns-value]
+ pandas/core/series.py:6806: error: "_update_inplace" of "NDFrame" does not return a value (it only ever returns None) [func-returns-value]
+ pandas/core/frame.py:8338: error: "_update_inplace" of "NDFrame" does not return a value (it only ever returns None) [func-returns-value]
+ pandas/core/frame.py:8348: error: "_update_inplace" of "NDFrame" does not return a value (it only ever returns None) [func-returns-value]
+ pandas/core/frame.py:8363: error: "_update_inplace" of "NDFrame" does not return a value (it only ever returns None) [func-returns-value]
jax (https://github.com/google/jax)
+ jax/_src/environment_info.py:63: error: "print" does not return a value (it only ever returns None) [func-returns-value]
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes #14179. Decorated functions —
@staticmethod,@classmethod, and user-defined decorators that preserve a-> Nonereturn — were silently skipped by the[func-returns-value]check while their undecorated counterparts were not. The check is now consistent across all of them.Why
ExpressionChecker.defn_returns_nonehandledFuncDef,OverloadedFuncDef, andVar, but notDecorator. A@staticmethodbecomes aDecoratornode whose syntheticvar.is_inferredisTrue, so the existingVarbranch deliberately skipped it. The function's-> Noneannotation is still propagated intodecorator.var.typeas aCallableType, so we can reuse it directly without losing precision for decorators that change the return type (e.g. one that turns() -> Noneinto() -> intis correctly not flagged — verified in the test suite and ad-hoc).What changed
mypy/checkexpr.py: add aDecoratorbranch todefn_returns_nonethat inspectsdecorator.var.type(the type the call site actually sees).test-data/unit/check-errorcodes.test: newtestErrorCodeFuncReturnsValueStaticAndClassMethodcovering instance, static, class methods on both an instance and the class itself.37 lines added, 0 removed across 2 files.
Reproduce BEFORE/AFTER yourself (copy-paste)
What I ran locally
The new regression test fails verbatim on
origin/master:Edge cases considered
@staticmethod def f() -> None@classmethod def f(cls) -> None-> None() -> NoneviaTypeVarbound toCallableCallable[..., int]wrapping a-> Nonefunction@staticmethod def f() -> int@property,@overload,@abstractmethodtestsNotes
Decoratorcase through the same return-type check the other branches use, rather than e.g. peering atdecorator.func.type(which would over-trigger for decorators that genuinely change the return type).defn_returns_nonecold doesn't have to rediscover whyDecoratorneeds special handling.🤖 Disclosure: I'm a data engineer; this PR was prepared with help from an AI assistant (Claude) — I reviewed every change, wrote the regression test first, and validated locally on the full
testcheck.pysuite before pushing. Happy to iterate on review feedback.