Skip to content

Suppress redundant-expr for validation guards that always terminate#21648

Open
piyush-003 wants to merge 2 commits into
python:masterfrom
piyush-003:fix-21533-proper
Open

Suppress redundant-expr for validation guards that always terminate#21648
piyush-003 wants to merge 2 commits into
python:masterfrom
piyush-003:fix-21533-proper

Conversation

@piyush-003

Copy link
Copy Markdown

Fixes #21533.

Summary

This PR suppresses redundant-expr warnings for and expressions used in validation guards whose bodies always terminate execution (for example, raise, assert False, or calls returning NoReturn).

The implementation reuses is_noop_for_reachability() to detect these cases, following a similar approach to unreachable-code diagnostics. This makes the behavior of constructs such as:

if not x and not y:

    raise TypeError(...)

consistent with equivalent nested if statements when the body only performs validation and terminates execution.

A regression test has been added for the original reproducer from #21533.

Verification

The following tests were run successfully:

  • python runtests.py check-expressions.test

  • python runtests.py check-errorcodes.test

Notes

AI assistance was used to help understand and navigate the mypy codebase during investigation and implementation. The final code changes, testing, debugging, and validation were performed manually before submitting this PR.

@github-actions

Copy link
Copy Markdown
Contributor

Diff from mypy_primer, showing the effect of this PR on open source code:

spack (https://github.com/spack/spack)
+ lib/spack/spack/spec.py:2863: error: Item "None" of "InstallRecord | None" has no attribute "spec"  [union-attr]
+ lib/spack/spack/spec.py:2863: error: Item "None" of "InstallRecord | None" has no attribute "deprecated_for"  [union-attr]
+ lib/spack/spack/cmd/mark.py:66: error: Need type annotation for "specs_from_cli" (hint: "specs_from_cli: list[<type>] = ...")  [var-annotated]

optuna (https://github.com/optuna/optuna)
+ optuna/visualization/matplotlib/_contour.py:157: error: Incompatible return value type (got "tuple[list[str | float | None], list[str | float]]", expected "tuple[list[str | float], list[str | float]]")  [return-value]
+ optuna/visualization/matplotlib/_contour.py:212: error: Argument 2 to "_calculate_axis_data" has incompatible type "list[str | float | None]"; expected "Sequence[str | float]"  [arg-type]

prefect (https://github.com/PrefectHQ/prefect)
- src/prefect/cli/shell.py:231: error: Incompatible types in "await" (actual type "UUID | Coroutine[Any, Any, UUID]", expected type "Awaitable[Any]")  [misc]
- src/prefect/cli/api.py:227: error: Argument 1 to "request" of "BaseAsyncClient" has incompatible type "str"; expected "Literal['GET', 'POST', 'PUT', 'DELETE', 'PATCH']"  [arg-type]
- src/prefect/cli/api.py:228: error: Argument 2 to "request" of "BaseAsyncClient" has incompatible type "str"; expected "Literal['/admin/storage', '/admin/settings', '/admin/version', '/artifacts/', '/artifacts/{id}', '/artifacts/{key}/latest', '/artifacts/count', '/artifacts/filter', '/artifacts/latest/count', '/artifacts/latest/filter', '/automations/', '/automations/{id}', '/automations/count', '/automations/filter', '/automations/owned-by/{resource_id}', '/automations/related-to/{resource_id}', '/block_capabilities/', '/block_documents/', '/block_documents/{id}', '/block_documents/count', '/block_documents/filter', '/block_schemas/', '/block_schemas/{id}', '/block_schemas/checksum/{checksum}', '/block_schemas/filter', '/block_types/', '/block_types/{id}', '/block_types/filter', '/block_types/install_system_block_types', '/block_types/slug/{slug}', '/block_types/slug/{slug}/block_documents', '/block_types/slug/{slug}/block_documents/name/{block_document_name}', '/collections/views/{view}', '/concurrency_limits/', '/concurrency_limits/{id}', '/concurrency_limits/decrement', '/concurrency_limits/filter', '/concurrency_limits/increment', '/concurrency_limits/tag/{tag}', '/concurrency_limits/tag/{tag}/reset', '/csrf-token', '/deployments/', '/deployments/{id}', '/deployments/{id}/branch', '/deployments/{id}/create_flow_run', '/deployments/{id}/pause_deployment', '/deployments/{id}/resume_deployment', '/deployments/{id}/schedule', '/deployments/{id}/schedules', '/deployments/{id}/schedules/{schedule_id}', '/deployments/{id}/work_queue_check', '/deployments/count', '/deployments/filter', '/deployments/get_scheduled_flow_runs', '/deployments/name/{flow_name}/{deployment_name}', '/deployments/paginate', '/events', '/events/count-by/{countable}', '/events/filter', '/events/filter/next', '/flow_run_states/', '/flow_run_states/{id}', '/flow_runs/', '/flow_runs/{id}', '/flow_runs/{id}/graph', '/flow_runs/{id}/graph-v2', '/flow_runs/{id}/input', '/flow_runs/{id}/input/{key}', '/flow_runs/{id}/input/filter', '/flow_runs/{id}/labels', '/flow_runs/{id}/logs/download', '/flow_runs/{id}/resume', '/flow_runs/{id}/set_state', '/flow_runs/count', '/flow_runs/filter', '/flow_runs/history', '/flow_runs/lateness', '/flow_runs/paginate', '/flows/', '/flows/{id}', '/flows/count', '/flows/filter', '/flows/name/{name}', '/flows/paginate', '/health', '/hello', '/logs/', '/logs/filter', '/ready', '/saved_searches/', '/saved_searches/{id}', '/saved_searches/filter', '/task_run_states/', '/task_run_states/{id}', '/task_runs/', '/task_runs/{id}', '/task_runs/{id}/set_state', '/task_runs/count', '/task_runs/filter', '/task_runs/history', '/task_workers/filter', '/templates/validate', '/ui/flow_runs/count-task-runs', '/ui/flow_runs/history', '/ui/flows/count-deployments', '/ui/flows/next-runs', '/ui/schemas/validate', '/ui/task_runs/count', '/ui/task_runs/dashboard/counts', '/v2/concurrency_limits/', '/v2/concurrency_limits/{id_or_name}', '/v2/concurrency_limits/decrement', '/v2/concurrency_limits/decrement-with-lease', '/v2/concurrency_limits/filter', '/v2/concurrency_limits/increment', '/v2/concurrency_limits/increment-with-lease', '/v2/concurrency_limits/leases/{lease_id}/renew', '/variables/', '/variables/{id}', '/variables/count', '/variables/filter', '/variables/name/{name}', '/version', '/work_pools/', '/work_pools/{name}', '/work_pools/{name}/concurrency_status', '/work_pools/{name}/get_scheduled_flow_runs', '/work_pools/{work_pool_name}/queues', '/work_pools/{work_pool_name}/queues/{name}', '/work_pools/{work_pool_name}/queues/filter', '/work_pools/{work_pool_name}/workers/{name}', '/work_pools/{work_pool_name}/workers/filter', '/work_pools/{work_pool_name}/workers/heartbeat', '/work_pools/count', '/work_pools/filter', '/work_queues/', '/work_queues/{id}', '/work_queues/{id}/get_runs', '/work_queues/{id}/concurrency_status', '/work_queues/{id}/status', '/work_queues/filter', '/work_queues/name/{name}']"  [arg-type]
- src/prefect/cli/flow_run.py:265: error: Argument "key" to "sorted" has incompatible type "Callable[[FlowRun], Any | None]"; expected "Callable[[FlowRun], SupportsDunderLT[Any] | SupportsDunderGT[Any]]"  [arg-type]
- src/prefect/cli/flow_run.py:265: error: Incompatible return value type (got "Any | None", expected "SupportsDunderLT[Any] | SupportsDunderGT[Any]")  [return-value]
- src/prefect/cli/flow_run.py:268: error: Item "None" of "State[Any] | None" has no attribute "state_details"  [union-attr]
- src/prefect/cli/flow_run.py:269: error: Item "None" of "State[Any] | None" has no attribute "is_scheduled"  [union-attr]
- src/prefect/cli/flow_run.py:270: error: Item "None" of "State[Any] | None" has no attribute "timestamp"  [union-attr]
- src/prefect/cli/flow_run.py:276: error: Item "None" of "State[Any] | None" has no attribute "type"  [union-attr]
- src/prefect/cli/deployment.py:750: error: Value of "anchor_date" has incompatible type "datetime"; expected "str"  [typeddict-item]
- src/prefect/cli/automation.py:216: error: Incompatible types in assignment (expression has type "Automation | None", variable has type "list[Automation]")  [assignment]
- src/prefect/cli/automation.py:219: error: "list[Automation]" has no attribute "id"  [attr-defined]
- src/prefect/cli/automation.py:220: error: "list[Automation]" has no attribute "id"  [attr-defined]
- src/prefect/cli/automation.py:267: error: Incompatible types in assignment (expression has type "Automation | None", variable has type "list[Automation]")  [assignment]
- src/prefect/cli/automation.py:270: error: "list[Automation]" has no attribute "id"  [attr-defined]
- src/prefect/cli/automation.py:271: error: "list[Automation]" has no attribute "id"  [attr-defined]
- src/prefect/cli/automation.py:341: error: Argument 1 to "delete_automation" of "AutomationAsyncClient" has incompatible type "str"; expected "UUID"  [arg-type]
- src/prefect/cli/automation.py:345: error: Incompatible types in assignment (expression has type "list[Automation]", variable has type "Automation | None")  [assignment]
- src/prefect/cli/artifact.py:93: error: Argument "key" to "sorted" has incompatible type "Callable[[Artifact], str | None]"; expected "Callable[[Artifact], SupportsDunderLT[Any] | SupportsDunderGT[Any]]"  [arg-type]
- src/prefect/cli/artifact.py:93: error: Incompatible return value type (got "str | None", expected "SupportsDunderLT[Any] | SupportsDunderGT[Any]")  [return-value]
- src/prefect/cli/work_pool.py:1072: error: Argument "work_pool_name" to "get_scheduled_flow_runs_for_work_pool" of "WorkPoolAsyncClient" has incompatible type "str | None"; expected "str"  [arg-type]
- src/prefect/cli/work_queue.py:584: error: Invalid index type "UUID | None" for "dict[UUID, str]"; expected type "UUID"  [index]
- src/prefect/cli/work_queue.py:637: error: Argument 1 to "append" of "list" has incompatible type "str | None"; expected "str"  [arg-type]
- src/prefect/cli/work_queue.py:740: error: Unsupported operand types for > ("datetime" and "None")  [operator]
- src/prefect/cli/work_queue.py:740: note: Left operand is of type "Any | None"

isort (https://github.com/pycqa/isort)
+ isort/main.py:1153: error: Unused "type: ignore" comment  [unused-ignore]

aiohttp (https://github.com/aio-libs/aiohttp)
+ aiohttp/web.py:564:5: error: Statement is unreachable  [unreachable]
+ aiohttp/web.py:564:5: note: See https://mypy.rtfd.io/en/stable/_refs.html#code-unreachable for more info

CPython (Argument Clinic) (https://github.com/python/cpython)
+ Tools/clinic/libclinic/block_parser.py:185: error: Statement is unreachable  [unreachable]
+ Tools/clinic/libclinic/dsl_parser.py:567: error: Statement is unreachable  [unreachable]
+ Tools/clinic/libclinic/dsl_parser.py:591: error: Statement is unreachable  [unreachable]
+ Tools/clinic/libclinic/dsl_parser.py:957: error: Statement is unreachable  [unreachable]
+ Tools/clinic/libclinic/dsl_parser.py:1122: error: Statement is unreachable  [unreachable]
+ Tools/clinic/libclinic/clanguage.py:432: error: Statement is unreachable  [unreachable]
+ Tools/clinic/libclinic/cli.py:215: error: Statement is unreachable  [unreachable]

mypy (https://github.com/python/mypy)
+ mypy/plugins/dataclasses.py:360: error: Value of type "dict[str, Any] | None" is not indexable  [index]
+ mypy/plugins/dataclasses.py:360: note: See https://mypy.rtfd.io/en/stable/_refs.html#code-index for more info
+ mypy/plugins/dataclasses.py:365: error: Value of type "dict[str, Any] | None" is not indexable  [index]
+ mypy/checker.py:3640: error: Argument 1 to "is_proper_subtype" has incompatible type "Type | None"; expected "Type"  [arg-type]
+ mypy/checker.py:3640: error: Argument 2 to "is_proper_subtype" has incompatible type "Type | None"; expected "Type"  [arg-type]
+ mypy/checker.py:3642: error: Argument 1 to "is_subtype" has incompatible type "Type | None"; expected "Type"  [arg-type]
+ mypy/checker.py:3642: error: Argument 2 to "is_subtype" has incompatible type "Type | None"; expected "Type"  [arg-type]
+ mypy/stubgen.py:2023: error: Statement is unreachable  [unreachable]
+ mypy/stubgen.py:2023: note: See https://mypy.rtfd.io/en/stable/_refs.html#code-unreachable for more info
+ mypy/main.py:124: error: Statement is unreachable  [unreachable]
+ mypy/main.py:1443: error: Statement is unreachable  [unreachable]
+ mypy/main.py:1645: error: Need type annotation for "package_root" (hint: "package_root: list[<type>] = ...")  [var-annotated]
+ mypy/main.py:1645: note: See https://mypy.rtfd.io/en/stable/_refs.html#code-var-annotated for more info
+ mypy/main.py:1653: error: Statement is unreachable  [unreachable]
+ mypy/main.py:1680: error: Statement is unreachable  [unreachable]
+ mypy/test/testcmdline.py:48: error: Statement is unreachable  [unreachable]
+ mypy/test/testcheck.py:68: error: Statement is unreachable  [unreachable]

dulwich (https://github.com/dulwich/dulwich)
+ dulwich/index.py:1447: error: Item "ConflictedIndexEntry" of "IndexEntry | ConflictedIndexEntry" has no attribute "sha"  [union-attr]
+ dulwich/index.py:1452: error: Argument 4 to "_expand_tree" of "Index" has incompatible type "IndexEntry | ConflictedIndexEntry"; expected "IndexEntry"  [arg-type]

pydantic (https://github.com/pydantic/pydantic)
+ pydantic/_internal/_generate_schema.py:2407: error: Cannot determine type of "unused_metadata_value"  [has-type]

beartype (https://github.com/beartype/beartype)
+ beartype/_check/error/_pep/errpep484604.py:178: error: Argument "types" to "join_delimited_disjunction_types" has incompatible type "set[type | None]"; expected "Iterable[type]"  [arg-type]

trio (https://github.com/python-trio/trio)
+ src/trio/_tests/test_exports.py:309: error: Unused "type: ignore[misc, explicit-any]" comment  [unused-ignore]
+ src/trio/_tests/test_exports.py:327: error: Unused "type: ignore" comment  [unused-ignore]

pytest (https://github.com/pytest-dev/pytest)
+ src/_pytest/pytester.py:1545: error: Statement is unreachable  [unreachable]
+ src/_pytest/skipping.py:267: error: Statement is unreachable  [unreachable]

bokeh (https://github.com/bokeh/bokeh)
+ src/bokeh/core/serialization.py: note: In member "encode" of class "Serializer":
+ src/bokeh/core/serialization.py:253:9: error: Statement is unreachable  [unreachable]
+ src/bokeh/core/serialization.py: note: At top level:
+ src/bokeh/command/subcommands/serve.py: note: In member "invoke" of class "Serve":
+ src/bokeh/command/subcommands/serve.py:902:9: error: Statement is unreachable  [unreachable]

mkdocs (https://github.com/mkdocs/mkdocs)
+ mkdocs/commands/build.py:315: error: Argument 1 to "join" of "str" has incompatible type "list[str | None]"; expected "Iterable[str]"  [arg-type]

build (https://github.com/pypa/build)
+ src/build/__main__.py:717: error: Statement is unreachable  [unreachable]
+ src/build/__main__.py:819: error: Statement is unreachable  [unreachable]
+ tests/test_integration.py:124: error: Statement is unreachable  [unreachable]

docstring-adder (https://github.com/astral-sh/docstring-adder)
+ add_docstrings.py:1493: error: Statement is unreachable  [unreachable]

xarray (https://github.com/pydata/xarray)
+ xarray/coding/strings.py: note: In function "validate_char_dim_name":
+ xarray/coding/strings.py:144: error: Cannot determine type of "original_shape"  [has-type]

typeshed-stats (https://github.com/AlexWaygood/typeshed-stats)
+ src/typeshed_stats/_cli.py:271: error: Statement is unreachable  [unreachable]

mkosi (https://github.com/systemd/mkosi)
+ mkosi/config.py:770:5: error: Statement is unreachable  [unreachable]
+ mkosi/config.py:820:5: error: Statement is unreachable  [unreachable]
+ mkosi/config.py:5060:17: error: Statement is unreachable  [unreachable]
+ mkosi/config.py:5095:9: error: Statement is unreachable  [unreachable]
+ mkosi/config.py:5486:5: error: Statement is unreachable  [unreachable]
+ mkosi/config.py:6254:9: error: Statement is unreachable  [unreachable]
+ mkosi/config.py:6264:9: error: Statement is unreachable  [unreachable]
+ mkosi/config.py:6269:9: error: Statement is unreachable  [unreachable]
+ mkosi/config.py:6275:9: error: Statement is unreachable  [unreachable]
+ mkosi/config.py:6288:9: error: Statement is unreachable  [unreachable]
+ mkosi/bootloader.py:144:5: error: Statement is unreachable  [unreachable]
+ mkosi/bootloader.py:545:5: error: Statement is unreachable  [unreachable]
+ mkosi/qemu.py:914:5: error: Statement is unreachable  [unreachable]
+ mkosi/vmspawn.py:50:5: error: Statement is unreachable  [unreachable]
+ mkosi/__init__.py:2267:5: error: Statement is unreachable  [unreachable]
+ mkosi/__init__.py:2684:5: error: Statement is unreachable  [unreachable]
+ mkosi/__init__.py:3347:28: error: Cannot determine type of "uid"  [has-type]
+ mkosi/__init__.py:3426:9: error: Statement is unreachable  [unreachable]
+ mkosi/__init__.py:3580:5: error: Statement is unreachable  [unreachable]
+ mkosi/__init__.py:4338:5: error: Statement is unreachable  [unreachable]
+ mkosi/__init__.py:5137:9: error: Statement is unreachable  [unreachable]
+ mkosi/__init__.py:5143:9: error: Statement is unreachable  [unreachable]
+ mkosi/__init__.py:5212:5: error: Statement is unreachable  [unreachable]
+ mkosi/distribution/arch.py:99:17: error: Statement is unreachable  [unreachable]
+ mkosi/distribution/kali.py:35:9: error: Statement is unreachable  [unreachable]
+ mkosi/distribution/opensuse.py:209:13: error: Statement is unreachable  [unreachable]
+ mkosi/distribution/fedora.py:168:9: error: Statement is unreachable  [unreachable]
+ mkosi/distribution/centos.py:149:9: error: Statement is unreachable  [unreachable]

jax (https://github.com/google/jax)
- jax/_src/interpreters/mlir.py:1790: error: List item 0 has incompatible type "str | None"; expected "str"  [list-item]

meson (https://github.com/mesonbuild/meson)
+ mesonbuild/mtest.py:614:20: error: Cannot determine type of "duration_max_len"  [has-type]
+ mesonbuild/mtest.py:618:24: error: Cannot determine type of "duration_max_len"  [has-type]
+ mesonbuild/mtest.py:1883:24: error: Cannot determine type of "duration_max_len"  [has-type]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

False positive redundant-expr, but only for if x and y:, not for if x: if y:

1 participant