Skip to content

Commit

Permalink
Revert "Remove unreachable-code feature (astral-sh#9463)"
Browse files Browse the repository at this point in the history
This reverts commit f9dd7bb.
  • Loading branch information
augustelalande authored and dylwil3 committed Jan 2, 2025
1 parent 2327082 commit 0a20ab6
Show file tree
Hide file tree
Showing 17 changed files with 4,277 additions and 0 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/ruff_linter/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ license = { workspace = true }
[dependencies]
ruff_cache = { workspace = true }
ruff_diagnostics = { workspace = true, features = ["serde"] }
ruff_index = { workspace = true }
ruff_notebook = { workspace = true }
ruff_macros = { workspace = true }
ruff_python_ast = { workspace = true, features = ["serde", "cache"] }
Expand Down
185 changes: 185 additions & 0 deletions crates/ruff_linter/resources/test/fixtures/ruff/RUF014.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
def after_return():
return "reachable"
return "unreachable"

async def also_works_on_async_functions():
return "reachable"
return "unreachable"

def if_always_true():
if True:
return "reachable"
return "unreachable"

def if_always_false():
if False:
return "unreachable"
return "reachable"

def if_elif_always_false():
if False:
return "unreachable"
elif False:
return "also unreachable"
return "reachable"

def if_elif_always_true():
if False:
return "unreachable"
elif True:
return "reachable"
return "also unreachable"

def ends_with_if():
if False:
return "unreachable"
else:
return "reachable"

def infinite_loop():
while True:
continue
return "unreachable"

''' TODO: we could determine these, but we don't yet.
def for_range_return():
for i in range(10):
if i == 5:
return "reachable"
return "unreachable"
def for_range_else():
for i in range(111):
if i == 5:
return "reachable"
else:
return "unreachable"
return "also unreachable"
def for_range_break():
for i in range(13):
return "reachable"
return "unreachable"
def for_range_if_break():
for i in range(1110):
if True:
return "reachable"
return "unreachable"
'''

def match_wildcard(status):
match status:
case _:
return "reachable"
return "unreachable"

def match_case_and_wildcard(status):
match status:
case 1:
return "reachable"
case _:
return "reachable"
return "unreachable"

def raise_exception():
raise Exception
return "unreachable"

def while_false():
while False:
return "unreachable"
return "reachable"

def while_false_else():
while False:
return "unreachable"
else:
return "reachable"

def while_false_else_return():
while False:
return "unreachable"
else:
return "reachable"
return "also unreachable"

def while_true():
while True:
return "reachable"
return "unreachable"

def while_true_else():
while True:
return "reachable"
else:
return "unreachable"

def while_true_else_return():
while True:
return "reachable"
else:
return "unreachable"
return "also unreachable"

def while_false_var_i():
i = 0
while False:
i += 1
return i

def while_true_var_i():
i = 0
while True:
i += 1
return i

def while_infinite():
while True:
pass
return "unreachable"

def while_if_true():
while True:
if True:
return "reachable"
return "unreachable"

# Test case found in the Bokeh repository that trigger a false positive.
def bokeh1(self, obj: BytesRep) -> bytes:
data = obj["data"]

if isinstance(data, str):
return base64.b64decode(data)
elif isinstance(data, Buffer):
buffer = data
else:
id = data["id"]

if id in self._buffers:
buffer = self._buffers[id]
else:
self.error(f"can't resolve buffer '{id}'")

return buffer.data

'''
TODO: because `try` statements aren't handled this triggers a false positive as
the last statement is reached, but the rules thinks it isn't (it doesn't
see/process the break statement).
# Test case found in the Bokeh repository that trigger a false positive.
def bokeh2(self, host: str = DEFAULT_HOST, port: int = DEFAULT_PORT) -> None:
self.stop_serving = False
while True:
try:
self.server = HTTPServer((host, port), HtmlOnlyHandler)
self.host = host
self.port = port
break
except OSError:
log.debug(f"port {port} is in use, trying to next one")
port += 1
self.thread = threading.Thread(target=self._run_web_server)
'''
5 changes: 5 additions & 0 deletions crates/ruff_linter/src/checkers/ast/analyze/statement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,11 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
if checker.enabled(Rule::AsyncFunctionWithTimeout) {
flake8_async::rules::async_function_with_timeout(checker, function_def);
}
if checker.enabled(Rule::UnreachableCode) {
checker
.diagnostics
.extend(ruff::rules::unreachable::in_function(name, body));
}
if checker.enabled(Rule::ReimplementedOperator) {
refurb::rules::reimplemented_operator(checker, &function_def.into());
}
Expand Down
2 changes: 2 additions & 0 deletions crates/ruff_linter/src/codes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -960,6 +960,8 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
(Ruff, "011") => (RuleGroup::Removed, rules::ruff::rules::RuffStaticKeyDictComprehension),
(Ruff, "012") => (RuleGroup::Stable, rules::ruff::rules::MutableClassDefault),
(Ruff, "013") => (RuleGroup::Stable, rules::ruff::rules::ImplicitOptional),
#[allow(deprecated)]
(Ruff, "014") => (RuleGroup::Nursery, rules::ruff::rules::UnreachableCode),
(Ruff, "015") => (RuleGroup::Stable, rules::ruff::rules::UnnecessaryIterableAllocationForFirstElement),
(Ruff, "016") => (RuleGroup::Stable, rules::ruff::rules::InvalidIndexType),
(Ruff, "017") => (RuleGroup::Stable, rules::ruff::rules::QuadraticListSummation),
Expand Down
4 changes: 4 additions & 0 deletions crates/ruff_linter/src/rules/ruff/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ mod tests {
Path::new("RUF015.py")
)]
#[test_case(Rule::InvalidIndexType, Path::new("RUF016.py"))]
#[cfg_attr(
feature = "unreachable-code",
test_case(Rule::UnreachableCode, Path::new("RUF014.py"))
)]
#[test_case(Rule::QuadraticListSummation, Path::new("RUF017_1.py"))]
#[test_case(Rule::QuadraticListSummation, Path::new("RUF017_0.py"))]
#[test_case(Rule::AssignmentInAssert, Path::new("RUF018.py"))]
Expand Down
4 changes: 4 additions & 0 deletions crates/ruff_linter/src/rules/ruff/rules/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ pub(crate) use unnecessary_nested_literal::*;
pub(crate) use unnecessary_regular_expression::*;
pub(crate) use unnecessary_round::*;
pub(crate) use unraw_re_pattern::*;
#[cfg(feature = "unreachable-code")]
pub(crate) use unreachable::*;
pub(crate) use unsafe_markup_use::*;
pub(crate) use unused_async::*;
pub(crate) use unused_noqa::*;
Expand Down Expand Up @@ -93,6 +95,8 @@ mod unnecessary_nested_literal;
mod unnecessary_regular_expression;
mod unnecessary_round;
mod unraw_re_pattern;
#[cfg(feature = "unreachable-code")]
pub(crate) mod unreachable;
mod unsafe_markup_use;
mod unused_async;
mod unused_noqa;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
---
source: crates/ruff_linter/src/rules/ruff/rules/unreachable.rs
description: "This is a Mermaid graph. You can use https://mermaid.live to visualize it as a diagram."
---
## Function 0
### Source
```python
def func():
assert True
```

### Control Flow Graph
```mermaid
flowchart TD
start(("Start"))
return(("End"))
block0[["`*(empty)*`"]]
block1[["Exception raised"]]
block2["assert True\n"]
start --> block2
block2 -- "True" --> block0
block2 -- "else" --> block1
block1 --> return
block0 --> return
```

## Function 1
### Source
```python
def func():
assert False
```

### Control Flow Graph
```mermaid
flowchart TD
start(("Start"))
return(("End"))
block0[["`*(empty)*`"]]
block1[["Exception raised"]]
block2["assert False\n"]
start --> block2
block2 -- "False" --> block0
block2 -- "else" --> block1
block1 --> return
block0 --> return
```

## Function 2
### Source
```python
def func():
assert True, "oops"
```

### Control Flow Graph
```mermaid
flowchart TD
start(("Start"))
return(("End"))
block0[["`*(empty)*`"]]
block1[["Exception raised"]]
block2["assert True, #quot;oops#quot;\n"]
start --> block2
block2 -- "True" --> block0
block2 -- "else" --> block1
block1 --> return
block0 --> return
```

## Function 3
### Source
```python
def func():
assert False, "oops"
```

### Control Flow Graph
```mermaid
flowchart TD
start(("Start"))
return(("End"))
block0[["`*(empty)*`"]]
block1[["Exception raised"]]
block2["assert False, #quot;oops#quot;\n"]
start --> block2
block2 -- "False" --> block0
block2 -- "else" --> block1
block1 --> return
block0 --> return
```


Loading

0 comments on commit 0a20ab6

Please sign in to comment.