-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[internal
] Return statements in finally block point to end block for unreachable
(PLW0101
)
#15276
Conversation
|
Is this overlapping with #15278 or do they fix different issues? |
Yes they are attempting to fix the same issue. Technically, this PR is more of a band-aid because the CFG should not fall through to the finally block in the |
I agree we should apply both corrections to the CFG construction, but I think we should keep the rule as "test" for now. I'm not so worried about discovering more edge cases where the CFG needs improvements, that's fine. However, infinite loops or panics are more disruptive since they cause the whole |
Thanks for the explanation. What's the CFG if we wrap the |
Like this, which looks correct to me: def l():
while T:
try:
try:
while ():
if 3:
break
finally:
return
finally:
return flowchart TD
start(("Start"))
return(("End"))
block0[["`*(empty)*`"]]
block1[["Loop continue"]]
block2["return\n"]
block3["return\n"]
block4[["Loop continue"]]
block5["break\n"]
block6["if 3:
break\n"]
block7["while ():
if 3:
break\n"]
block8[["Exception raised"]]
block9["try:
while ():
if 3:
break
finally:
return\n"]
block10[["Exception raised"]]
block11["try:
try:
while ():
if 3:
break
finally:
return
finally:
return\n"]
block12["while T:
try:
try:
while ():
if 3:
break
finally:
return
finally:
return\n"]
start --> block12
block12 -- "T" --> block11
block12 -- "else" --> block0
block11 -- "Exception raised" --> block10
block11 -- "else" --> block9
block10 --> block2
block9 -- "Exception raised" --> block8
block9 -- "else" --> block7
block8 --> block3
block7 -- "()" --> block6
block7 -- "else" --> block3
block6 -- "3" --> block5
block6 -- "else" --> block4
block5 --> block3
block4 --> block7
block3 --> block2
block2 --> return
block1 --> block12
block0 --> return
|
// if we are already in a `finally` block, terminate | ||
if Some(idx) == finally_index { | ||
return; | ||
} | ||
// re-route to finally if present and not already re-routed | ||
if let Some(finally_index) = finally_index { | ||
blocks.blocks[idx].next = NextBlock::Always(finally_index); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would probably be a small refactor and is something better left for its own PR but we could have better runtime assertions around preventing loops by introducing a blocks.set_next(idx, next)
method that contains an assertion that idx
and the next
's index aren't equal.
* main: [`pylint`] Fix `unreachable` infinite loop (`PLW0101`) (#15278) fix invalid syntax in workflow file (#15357) [`pycodestyle`] Avoid false positives related to type aliases (`E252`) (#15356) [`flake8-builtins`] Disapply `A005` to stub files (#15350) Improve logging system using `logLevel`, avoid trace value (#15232) [`flake8-builtins`] Rename `A005` and improve its error message (#15348) Spruce up docs for pydoclint rules (#15325) [`flake8-type-checking`] Apply `TC008` more eagerly in `TYPE_CHECKING` blocks and disapply it in stubs (#15180) [red-knot] `knot_extensions` Python API (#15103) Display Union of Literals as a Literal (#14993) [red-knot] all types are assignable to object (#15332) [`ruff`] Parenthesize arguments to `int` when removing `int` would change semantics in `unnecessary-cast-to-int` (`RUF046`) (#15277) [`eradicate`] Correctly handle metadata blocks directly followed by normal blocks (`ERA001`) (#15330) Narrowing for class patterns in match statements (#15223) [red-knot] add call checking (#15200) Spruce up docs for `slice-to-remove-prefix-or-suffix` (`FURB188`) (#15328) [`internal`] Return statements in finally block point to end block for `unreachable` (`PLW0101`) (#15276) [`ruff`] Treat `)` as a regex metacharacter (`RUF043`, `RUF055`) (#15318) Use uv consistently throughout the documentation (#15302)
Note:
PLW0101
remains in testing rather than preview, so this PR does not modify any public behavior (hence the title beginning withinternal
rather thanpylint
, for the sake of the changelog.)Fixes an error in the processing of
try
statements in the control flow graph builder.When processing a try statement, the block following a
return
was forced to point to thefinally
block. However, if the return was in thefinally
block, this caused the block to point to itself. In the case where the wholetry-finally
statement was also included inside of a loop, this caused an infinite loop in the builder for the control flow graph as it attempted to resolve edges.Closes #15248
Test function
Source
Control Flow Graph