diff --git a/crates/ruff_linter/Cargo.toml b/crates/ruff_linter/Cargo.toml index 30773a734a..1232ac8fb1 100644 --- a/crates/ruff_linter/Cargo.toml +++ b/crates/ruff_linter/Cargo.toml @@ -85,8 +85,6 @@ tempfile = { workspace = true } [features] default = [] schemars = ["dep:schemars"] -# Enables the UnreachableCode rule -unreachable-code = [] [lints] workspace = true diff --git a/crates/ruff_linter/resources/test/fixtures/ruff/RUF014.py b/crates/ruff_linter/resources/test/fixtures/ruff/RUF014.py deleted file mode 100644 index d1ae40f3ca..0000000000 --- a/crates/ruff_linter/resources/test/fixtures/ruff/RUF014.py +++ /dev/null @@ -1,185 +0,0 @@ -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) -''' diff --git a/crates/ruff_linter/src/checkers/ast/analyze/statement.rs b/crates/ruff_linter/src/checkers/ast/analyze/statement.rs index 5d8c429416..ff764de5f8 100644 --- a/crates/ruff_linter/src/checkers/ast/analyze/statement.rs +++ b/crates/ruff_linter/src/checkers/ast/analyze/statement.rs @@ -355,12 +355,6 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { if checker.enabled(Rule::TrioAsyncFunctionWithTimeout) { flake8_trio::rules::async_function_with_timeout(checker, function_def); } - #[cfg(feature = "unreachable-code")] - 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()); } diff --git a/crates/ruff_linter/src/codes.rs b/crates/ruff_linter/src/codes.rs index 62a3896727..1f24c4f186 100644 --- a/crates/ruff_linter/src/codes.rs +++ b/crates/ruff_linter/src/codes.rs @@ -914,9 +914,6 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> { (Ruff, "011") => (RuleGroup::Stable, rules::ruff::rules::StaticKeyDictComprehension), (Ruff, "012") => (RuleGroup::Stable, rules::ruff::rules::MutableClassDefault), (Ruff, "013") => (RuleGroup::Stable, rules::ruff::rules::ImplicitOptional), - #[cfg(feature = "unreachable-code")] // When removing this feature gate, also update rules_selector.rs - #[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), #[allow(deprecated)] diff --git a/crates/ruff_linter/src/rule_selector.rs b/crates/ruff_linter/src/rule_selector.rs index ea83c058ed..a5ee86ea23 100644 --- a/crates/ruff_linter/src/rule_selector.rs +++ b/crates/ruff_linter/src/rule_selector.rs @@ -289,9 +289,6 @@ mod schema { (!prefix.is_empty()).then(|| prefix.to_string()) })), ) - // Filter out rule gated behind `#[cfg(feature = "unreachable-code")]`, which is - // off-by-default - .filter(|prefix| prefix != "RUF014") .sorted() .map(Value::String) .collect(), @@ -407,40 +404,28 @@ pub mod clap_completion { let prefix = l.common_prefix(); (!prefix.is_empty()).then(|| PossibleValue::new(prefix).help(l.name())) }) - .chain( - RuleCodePrefix::iter() - // Filter out rule gated behind `#[cfg(feature = "unreachable-code")]`, which is - // off-by-default - .filter(|prefix| { - format!( - "{}{}", - prefix.linter().common_prefix(), - prefix.short_code() - ) != "RUF014" - }) - .filter_map(|prefix| { - // Ex) `UP` - if prefix.short_code().is_empty() { - let code = prefix.linter().common_prefix(); - let name = prefix.linter().name(); - return Some(PossibleValue::new(code).help(name)); - } + .chain(RuleCodePrefix::iter().filter_map(|prefix| { + // Ex) `UP` + if prefix.short_code().is_empty() { + let code = prefix.linter().common_prefix(); + let name = prefix.linter().name(); + return Some(PossibleValue::new(code).help(name)); + } - // Ex) `UP004` - if is_single_rule_selector(&prefix) { - let rule = prefix.rules().next()?; - let code = format!( - "{}{}", - prefix.linter().common_prefix(), - prefix.short_code() - ); - let name: &'static str = rule.into(); - return Some(PossibleValue::new(code).help(name)); - } + // Ex) `UP004` + if is_single_rule_selector(&prefix) { + let rule = prefix.rules().next()?; + let code = format!( + "{}{}", + prefix.linter().common_prefix(), + prefix.short_code() + ); + let name: &'static str = rule.into(); + return Some(PossibleValue::new(code).help(name)); + } - None - }), - ), + None + })), ), )) } diff --git a/crates/ruff_linter/src/rules/ruff/mod.rs b/crates/ruff_linter/src/rules/ruff/mod.rs index bda96268fd..837abed45f 100644 --- a/crates/ruff_linter/src/rules/ruff/mod.rs +++ b/crates/ruff_linter/src/rules/ruff/mod.rs @@ -37,10 +37,6 @@ 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"))] diff --git a/crates/ruff_linter/src/rules/ruff/rules/mod.rs b/crates/ruff_linter/src/rules/ruff/rules/mod.rs index 37bc4e9c91..1a89cb6a71 100644 --- a/crates/ruff_linter/src/rules/ruff/rules/mod.rs +++ b/crates/ruff_linter/src/rules/ruff/rules/mod.rs @@ -16,8 +16,6 @@ pub(crate) use quadratic_list_summation::*; pub(crate) use static_key_dict_comprehension::*; pub(crate) use unnecessary_iterable_allocation_for_first_element::*; pub(crate) use unnecessary_key_check::*; -#[cfg(feature = "unreachable-code")] -pub(crate) use unreachable::*; pub(crate) use unused_noqa::*; mod ambiguous_unicode_character; @@ -39,8 +37,6 @@ mod parenthesize_logical_operators; mod static_key_dict_comprehension; mod unnecessary_iterable_allocation_for_first_element; mod unnecessary_key_check; -#[cfg(feature = "unreachable-code")] -pub(crate) mod unreachable; mod unused_noqa; #[derive(Clone, Copy)] diff --git a/crates/ruff_linter/src/rules/ruff/rules/snapshots/ruff_linter__rules__ruff__rules__unreachable__tests__assert.py.md.snap b/crates/ruff_linter/src/rules/ruff/rules/snapshots/ruff_linter__rules__ruff__rules__unreachable__tests__assert.py.md.snap deleted file mode 100644 index 50dcab6daa..0000000000 --- a/crates/ruff_linter/src/rules/ruff/rules/snapshots/ruff_linter__rules__ruff__rules__unreachable__tests__assert.py.md.snap +++ /dev/null @@ -1,97 +0,0 @@ ---- -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 -``` - - diff --git a/crates/ruff_linter/src/rules/ruff/rules/snapshots/ruff_linter__rules__ruff__rules__unreachable__tests__async-for.py.md.snap b/crates/ruff_linter/src/rules/ruff/rules/snapshots/ruff_linter__rules__ruff__rules__unreachable__tests__async-for.py.md.snap deleted file mode 100644 index 2847a7d873..0000000000 --- a/crates/ruff_linter/src/rules/ruff/rules/snapshots/ruff_linter__rules__ruff__rules__unreachable__tests__async-for.py.md.snap +++ /dev/null @@ -1,241 +0,0 @@ ---- -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(): - async for i in range(5): - print(i) -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0[["`*(empty)*`"]] - block1["print(i)\n"] - block2["async for i in range(5): - print(i)\n"] - - start --> block2 - block2 -- "range(5)" --> block1 - block2 -- "else" --> block0 - block1 --> block2 - block0 --> return -``` - -## Function 1 -### Source -```python -def func(): - async for i in range(20): - print(i) - else: - return 0 -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0["print(i)\n"] - block1["return 0\n"] - block2["async for i in range(20): - print(i) - else: - return 0\n"] - - start --> block2 - block2 -- "range(20)" --> block0 - block2 -- "else" --> block1 - block1 --> return - block0 --> return -``` - -## Function 2 -### Source -```python -def func(): - async for i in range(10): - if i == 5: - return 1 - return 0 -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0["return 0\n"] - block1["return 1\n"] - block2["if i == 5: - return 1\n"] - block3["async for i in range(10): - if i == 5: - return 1\n"] - - start --> block3 - block3 -- "range(10)" --> block2 - block3 -- "else" --> block0 - block2 -- "i == 5" --> block1 - block2 -- "else" --> block3 - block1 --> return - block0 --> return -``` - -## Function 3 -### Source -```python -def func(): - async for i in range(111): - if i == 5: - return 1 - else: - return 0 - return 2 -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0["return 2\n"] - block1["return 1\n"] - block2["if i == 5: - return 1\n"] - block3["return 0\n"] - block4["async for i in range(111): - if i == 5: - return 1 - else: - return 0\n"] - - start --> block4 - block4 -- "range(111)" --> block2 - block4 -- "else" --> block3 - block3 --> return - block2 -- "i == 5" --> block1 - block2 -- "else" --> block4 - block1 --> return - block0 --> return -``` - -## Function 4 -### Source -```python -def func(): - async for i in range(12): - continue -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0[["`*(empty)*`"]] - block1["continue\n"] - block2["async for i in range(12): - continue\n"] - - start --> block2 - block2 -- "range(12)" --> block1 - block2 -- "else" --> block0 - block1 --> block2 - block0 --> return -``` - -## Function 5 -### Source -```python -def func(): - async for i in range(1110): - if True: - continue -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0[["`*(empty)*`"]] - block1["continue\n"] - block2["if True: - continue\n"] - block3["async for i in range(1110): - if True: - continue\n"] - - start --> block3 - block3 -- "range(1110)" --> block2 - block3 -- "else" --> block0 - block2 -- "True" --> block1 - block2 -- "else" --> block3 - block1 --> block3 - block0 --> return -``` - -## Function 6 -### Source -```python -def func(): - async for i in range(13): - break -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0[["`*(empty)*`"]] - block1["break\n"] - block2["async for i in range(13): - break\n"] - - start --> block2 - block2 -- "range(13)" --> block1 - block2 -- "else" --> block0 - block1 --> block0 - block0 --> return -``` - -## Function 7 -### Source -```python -def func(): - async for i in range(1110): - if True: - break -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0[["`*(empty)*`"]] - block1["break\n"] - block2["if True: - break\n"] - block3["async for i in range(1110): - if True: - break\n"] - - start --> block3 - block3 -- "range(1110)" --> block2 - block3 -- "else" --> block0 - block2 -- "True" --> block1 - block2 -- "else" --> block3 - block1 --> block0 - block0 --> return -``` - - diff --git a/crates/ruff_linter/src/rules/ruff/rules/snapshots/ruff_linter__rules__ruff__rules__unreachable__tests__for.py.md.snap b/crates/ruff_linter/src/rules/ruff/rules/snapshots/ruff_linter__rules__ruff__rules__unreachable__tests__for.py.md.snap deleted file mode 100644 index 6850c5f69b..0000000000 --- a/crates/ruff_linter/src/rules/ruff/rules/snapshots/ruff_linter__rules__ruff__rules__unreachable__tests__for.py.md.snap +++ /dev/null @@ -1,302 +0,0 @@ ---- -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(): - for i in range(5): - print(i) -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0[["`*(empty)*`"]] - block1["print(i)\n"] - block2["for i in range(5): - print(i)\n"] - - start --> block2 - block2 -- "range(5)" --> block1 - block2 -- "else" --> block0 - block1 --> block2 - block0 --> return -``` - -## Function 1 -### Source -```python -def func(): - for i in range(20): - print(i) - else: - return 0 -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0["print(i)\n"] - block1["return 0\n"] - block2["for i in range(20): - print(i) - else: - return 0\n"] - - start --> block2 - block2 -- "range(20)" --> block0 - block2 -- "else" --> block1 - block1 --> return - block0 --> return -``` - -## Function 2 -### Source -```python -def func(): - for i in range(10): - if i == 5: - return 1 - return 0 -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0["return 0\n"] - block1["return 1\n"] - block2["if i == 5: - return 1\n"] - block3["for i in range(10): - if i == 5: - return 1\n"] - - start --> block3 - block3 -- "range(10)" --> block2 - block3 -- "else" --> block0 - block2 -- "i == 5" --> block1 - block2 -- "else" --> block3 - block1 --> return - block0 --> return -``` - -## Function 3 -### Source -```python -def func(): - for i in range(111): - if i == 5: - return 1 - else: - return 0 - return 2 -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0["return 2\n"] - block1["return 1\n"] - block2["if i == 5: - return 1\n"] - block3["return 0\n"] - block4["for i in range(111): - if i == 5: - return 1 - else: - return 0\n"] - - start --> block4 - block4 -- "range(111)" --> block2 - block4 -- "else" --> block3 - block3 --> return - block2 -- "i == 5" --> block1 - block2 -- "else" --> block4 - block1 --> return - block0 --> return -``` - -## Function 4 -### Source -```python -def func(): - for i in range(12): - continue -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0[["`*(empty)*`"]] - block1["continue\n"] - block2["for i in range(12): - continue\n"] - - start --> block2 - block2 -- "range(12)" --> block1 - block2 -- "else" --> block0 - block1 --> block2 - block0 --> return -``` - -## Function 5 -### Source -```python -def func(): - for i in range(1110): - if True: - continue -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0[["`*(empty)*`"]] - block1["continue\n"] - block2["if True: - continue\n"] - block3["for i in range(1110): - if True: - continue\n"] - - start --> block3 - block3 -- "range(1110)" --> block2 - block3 -- "else" --> block0 - block2 -- "True" --> block1 - block2 -- "else" --> block3 - block1 --> block3 - block0 --> return -``` - -## Function 6 -### Source -```python -def func(): - for i in range(13): - break -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0[["`*(empty)*`"]] - block1["break\n"] - block2["for i in range(13): - break\n"] - - start --> block2 - block2 -- "range(13)" --> block1 - block2 -- "else" --> block0 - block1 --> block0 - block0 --> return -``` - -## Function 7 -### Source -```python -def func(): - for i in range(1110): - if True: - break -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0[["`*(empty)*`"]] - block1["break\n"] - block2["if True: - break\n"] - block3["for i in range(1110): - if True: - break\n"] - - start --> block3 - block3 -- "range(1110)" --> block2 - block3 -- "else" --> block0 - block2 -- "True" --> block1 - block2 -- "else" --> block3 - block1 --> block0 - block0 --> return -``` - -## Function 8 -### Source -```python -def func(): - for i in range(5): - pass - else: - return 1 -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0["pass\n"] - block1["return 1\n"] - block2["for i in range(5): - pass - else: - return 1\n"] - - start --> block2 - block2 -- "range(5)" --> block0 - block2 -- "else" --> block1 - block1 --> return - block0 --> return -``` - -## Function 9 -### Source -```python -def func(): - for i in range(5): - pass - else: - return 1 - x = 1 -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0["x = 1\n"] - block1["pass\n"] - block2["return 1\n"] - block3["for i in range(5): - pass - else: - return 1\n"] - - start --> block3 - block3 -- "range(5)" --> block1 - block3 -- "else" --> block2 - block2 --> return - block1 --> block3 - block0 --> return -``` - - diff --git a/crates/ruff_linter/src/rules/ruff/rules/snapshots/ruff_linter__rules__ruff__rules__unreachable__tests__if.py.md.snap b/crates/ruff_linter/src/rules/ruff/rules/snapshots/ruff_linter__rules__ruff__rules__unreachable__tests__if.py.md.snap deleted file mode 100644 index 6899a85774..0000000000 --- a/crates/ruff_linter/src/rules/ruff/rules/snapshots/ruff_linter__rules__ruff__rules__unreachable__tests__if.py.md.snap +++ /dev/null @@ -1,553 +0,0 @@ ---- -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(): - if False: - return 0 - return 1 -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0["return 1\n"] - block1["return 0\n"] - block2["if False: - return 0\n"] - - start --> block2 - block2 -- "False" --> block1 - block2 -- "else" --> block0 - block1 --> return - block0 --> return -``` - -## Function 1 -### Source -```python -def func(): - if True: - return 1 - return 0 -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0["return 0\n"] - block1["return 1\n"] - block2["if True: - return 1\n"] - - start --> block2 - block2 -- "True" --> block1 - block2 -- "else" --> block0 - block1 --> return - block0 --> return -``` - -## Function 2 -### Source -```python -def func(): - if False: - return 0 - else: - return 1 -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0["return 0\n"] - block1["return 1\n"] - block2["if False: - return 0 - else: - return 1\n"] - - start --> block2 - block2 -- "False" --> block0 - block2 -- "else" --> block1 - block1 --> return - block0 --> return -``` - -## Function 3 -### Source -```python -def func(): - if True: - return 1 - else: - return 0 -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0["return 1\n"] - block1["return 0\n"] - block2["if True: - return 1 - else: - return 0\n"] - - start --> block2 - block2 -- "True" --> block0 - block2 -- "else" --> block1 - block1 --> return - block0 --> return -``` - -## Function 4 -### Source -```python -def func(): - if False: - return 0 - else: - return 1 - return "unreachable" -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0["return #quot;unreachable#quot;\n"] - block1["return 0\n"] - block2["return 1\n"] - block3["if False: - return 0 - else: - return 1\n"] - - start --> block3 - block3 -- "False" --> block1 - block3 -- "else" --> block2 - block2 --> return - block1 --> return - block0 --> return -``` - -## Function 5 -### Source -```python -def func(): - if True: - return 1 - else: - return 0 - return "unreachable" -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0["return #quot;unreachable#quot;\n"] - block1["return 1\n"] - block2["return 0\n"] - block3["if True: - return 1 - else: - return 0\n"] - - start --> block3 - block3 -- "True" --> block1 - block3 -- "else" --> block2 - block2 --> return - block1 --> return - block0 --> return -``` - -## Function 6 -### Source -```python -def func(): - if True: - if True: - return 1 - return 2 - else: - return 3 - return "unreachable2" -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0["return #quot;unreachable2#quot;\n"] - block1["return 2\n"] - block2["return 1\n"] - block3["if True: - return 1\n"] - block4["return 3\n"] - block5["if True: - if True: - return 1 - return 2 - else: - return 3\n"] - - start --> block5 - block5 -- "True" --> block3 - block5 -- "else" --> block4 - block4 --> return - block3 -- "True" --> block2 - block3 -- "else" --> block1 - block2 --> return - block1 --> return - block0 --> return -``` - -## Function 7 -### Source -```python -def func(): - if False: - return 0 -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0[["`*(empty)*`"]] - block1["return 0\n"] - block2["if False: - return 0\n"] - - start --> block2 - block2 -- "False" --> block1 - block2 -- "else" --> block0 - block1 --> return - block0 --> return -``` - -## Function 8 -### Source -```python -def func(): - if True: - return 1 -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0[["`*(empty)*`"]] - block1["return 1\n"] - block2["if True: - return 1\n"] - - start --> block2 - block2 -- "True" --> block1 - block2 -- "else" --> block0 - block1 --> return - block0 --> return -``` - -## Function 9 -### Source -```python -def func(): - if True: - return 1 - elif False: - return 2 - else: - return 0 -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0["return 1\n"] - block1["return 0\n"] - block2["return 2\n"] - block3["if True: - return 1 - elif False: - return 2 - else: - return 0\n"] - block4["if True: - return 1 - elif False: - return 2 - else: - return 0\n"] - - start --> block4 - block4 -- "True" --> block0 - block4 -- "else" --> block3 - block3 -- "False" --> block2 - block3 -- "else" --> block1 - block2 --> return - block1 --> return - block0 --> return -``` - -## Function 10 -### Source -```python -def func(): - if False: - return 1 - elif True: - return 2 - else: - return 0 -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0["return 1\n"] - block1["return 0\n"] - block2["return 2\n"] - block3["if False: - return 1 - elif True: - return 2 - else: - return 0\n"] - block4["if False: - return 1 - elif True: - return 2 - else: - return 0\n"] - - start --> block4 - block4 -- "False" --> block0 - block4 -- "else" --> block3 - block3 -- "True" --> block2 - block3 -- "else" --> block1 - block2 --> return - block1 --> return - block0 --> return -``` - -## Function 11 -### Source -```python -def func(): - if True: - if False: - return 0 - elif True: - return 1 - else: - return 2 - return 3 - elif True: - return 4 - else: - return 5 - return 6 -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0["return 6\n"] - block1["return 3\n"] - block2["return 0\n"] - block3["return 2\n"] - block4["return 1\n"] - block5["if False: - return 0 - elif True: - return 1 - else: - return 2\n"] - block6["if False: - return 0 - elif True: - return 1 - else: - return 2\n"] - block7["return 5\n"] - block8["return 4\n"] - block9["if True: - if False: - return 0 - elif True: - return 1 - else: - return 2 - return 3 - elif True: - return 4 - else: - return 5\n"] - block10["if True: - if False: - return 0 - elif True: - return 1 - else: - return 2 - return 3 - elif True: - return 4 - else: - return 5\n"] - - start --> block10 - block10 -- "True" --> block6 - block10 -- "else" --> block9 - block9 -- "True" --> block8 - block9 -- "else" --> block7 - block8 --> return - block7 --> return - block6 -- "False" --> block2 - block6 -- "else" --> block5 - block5 -- "True" --> block4 - block5 -- "else" --> block3 - block4 --> return - block3 --> return - block2 --> return - block1 --> return - block0 --> return -``` - -## Function 12 -### Source -```python -def func(): - if False: - return "unreached" - elif False: - return "also unreached" - return "reached" -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0["return #quot;reached#quot;\n"] - block1["return #quot;unreached#quot;\n"] - block2["return #quot;also unreached#quot;\n"] - block3["if False: - return #quot;unreached#quot; - elif False: - return #quot;also unreached#quot;\n"] - block4["if False: - return #quot;unreached#quot; - elif False: - return #quot;also unreached#quot;\n"] - - start --> block4 - block4 -- "False" --> block1 - block4 -- "else" --> block3 - block3 -- "False" --> block2 - block3 -- "else" --> block0 - block2 --> return - block1 --> return - block0 --> return -``` - -## Function 13 -### Source -```python -def func(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 -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0["return buffer.data\n"] - block1["return base64.b64decode(data)\n"] - block2["buffer = self._buffers[id]\n"] - block3["self.error(f#quot;can't resolve buffer '{id}'#quot;)\n"] - block4["id = data[#quot;id#quot;]\nif id in self._buffers: - buffer = self._buffers[id] - else: - self.error(f#quot;can't resolve buffer '{id}'#quot;)\n"] - block5["buffer = data\n"] - block6["if isinstance(data, str): - return base64.b64decode(data) - elif isinstance(data, Buffer): - buffer = data - else: - id = data[#quot;id#quot;] - - if id in self._buffers: - buffer = self._buffers[id] - else: - self.error(f#quot;can't resolve buffer '{id}'#quot;)\n"] - block7["data = obj[#quot;data#quot;]\nif isinstance(data, str): - return base64.b64decode(data) - elif isinstance(data, Buffer): - buffer = data - else: - id = data[#quot;id#quot;] - - if id in self._buffers: - buffer = self._buffers[id] - else: - self.error(f#quot;can't resolve buffer '{id}'#quot;)\n"] - - start --> block7 - block7 -- "isinstance(data, str)" --> block1 - block7 -- "else" --> block6 - block6 -- "isinstance(data, Buffer)" --> block5 - block6 -- "else" --> block4 - block5 --> block0 - block4 -- "id in self._buffers" --> block2 - block4 -- "else" --> block3 - block3 --> block0 - block2 --> block0 - block1 --> return - block0 --> return -``` - - diff --git a/crates/ruff_linter/src/rules/ruff/rules/snapshots/ruff_linter__rules__ruff__rules__unreachable__tests__match.py.md.snap b/crates/ruff_linter/src/rules/ruff/rules/snapshots/ruff_linter__rules__ruff__rules__unreachable__tests__match.py.md.snap deleted file mode 100644 index d74b08116f..0000000000 --- a/crates/ruff_linter/src/rules/ruff/rules/snapshots/ruff_linter__rules__ruff__rules__unreachable__tests__match.py.md.snap +++ /dev/null @@ -1,815 +0,0 @@ ---- -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(status): - match status: - case _: - return 0 - return "unreachable" -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0["return #quot;unreachable#quot;\n"] - block1["return 0\n"] - block2["match status: - case _: - return 0\n"] - - start --> block2 - block2 --> block1 - block1 --> return - block0 --> return -``` - -## Function 1 -### Source -```python -def func(status): - match status: - case 1: - return 1 - return 0 -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0["return 0\n"] - block1["return 1\n"] - block2["match status: - case 1: - return 1\n"] - - start --> block2 - block2 -- "case 1" --> block1 - block2 -- "else" --> block0 - block1 --> return - block0 --> return -``` - -## Function 2 -### Source -```python -def func(status): - match status: - case 1: - return 1 - case _: - return 0 -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0["return 0\n"] - block1["match status: - case 1: - return 1 - case _: - return 0\n"] - block2["return 1\n"] - block3["match status: - case 1: - return 1 - case _: - return 0\n"] - - start --> block3 - block3 -- "case 1" --> block2 - block3 -- "else" --> block1 - block2 --> return - block1 --> block0 - block0 --> return -``` - -## Function 3 -### Source -```python -def func(status): - match status: - case 1 | 2 | 3: - return 5 - return 6 -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0["return 6\n"] - block1["return 5\n"] - block2["match status: - case 1 | 2 | 3: - return 5\n"] - - start --> block2 - block2 -- "case 1 | 2 | 3" --> block1 - block2 -- "else" --> block0 - block1 --> return - block0 --> return -``` - -## Function 4 -### Source -```python -def func(status): - match status: - case 1 | 2 | 3: - return 5 - case _: - return 10 - return 0 -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0["return 0\n"] - block1["return 10\n"] - block2["match status: - case 1 | 2 | 3: - return 5 - case _: - return 10\n"] - block3["return 5\n"] - block4["match status: - case 1 | 2 | 3: - return 5 - case _: - return 10\n"] - - start --> block4 - block4 -- "case 1 | 2 | 3" --> block3 - block4 -- "else" --> block2 - block3 --> return - block2 --> block1 - block1 --> return - block0 --> return -``` - -## Function 5 -### Source -```python -def func(status): - match status: - case 0: - return 0 - case 1: - return 1 - case 1: - return "1 again" - case _: - return 3 -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0["return 3\n"] - block1["match status: - case 0: - return 0 - case 1: - return 1 - case 1: - return #quot;1 again#quot; - case _: - return 3\n"] - block2["return #quot;1 again#quot;\n"] - block3["match status: - case 0: - return 0 - case 1: - return 1 - case 1: - return #quot;1 again#quot; - case _: - return 3\n"] - block4["return 1\n"] - block5["match status: - case 0: - return 0 - case 1: - return 1 - case 1: - return #quot;1 again#quot; - case _: - return 3\n"] - block6["return 0\n"] - block7["match status: - case 0: - return 0 - case 1: - return 1 - case 1: - return #quot;1 again#quot; - case _: - return 3\n"] - - start --> block7 - block7 -- "case 0" --> block6 - block7 -- "else" --> block5 - block6 --> return - block5 -- "case 1" --> block4 - block5 -- "else" --> block3 - block4 --> return - block3 -- "case 1" --> block2 - block3 -- "else" --> block1 - block2 --> return - block1 --> block0 - block0 --> return -``` - -## Function 6 -### Source -```python -def func(status): - i = 0 - match status, i: - case _, _: - return 0 -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0[["`*(empty)*`"]] - block1["return 0\n"] - block2["match status, i: - case _, _: - return 0\n"] - block3["i = 0\n"] - - start --> block3 - block3 --> block2 - block2 -- "case _, _" --> block1 - block2 -- "else" --> block0 - block1 --> return - block0 --> return -``` - -## Function 7 -### Source -```python -def func(status): - i = 0 - match status, i: - case _, 0: - return 0 - case _, 2: - return 0 -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0[["`*(empty)*`"]] - block1["return 0\n"] - block2["match status, i: - case _, 0: - return 0 - case _, 2: - return 0\n"] - block3["return 0\n"] - block4["match status, i: - case _, 0: - return 0 - case _, 2: - return 0\n"] - block5["i = 0\n"] - - start --> block5 - block5 --> block4 - block4 -- "case _, 0" --> block3 - block4 -- "else" --> block2 - block3 --> return - block2 -- "case _, 2" --> block1 - block2 -- "else" --> block0 - block1 --> return - block0 --> return -``` - -## Function 8 -### Source -```python -def func(point): - match point: - case (0, 0): - print("Origin") - case _: - raise ValueError("oops") -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0[["`*(empty)*`"]] - block1["raise ValueError(#quot;oops#quot;)\n"] - block2["match point: - case (0, 0): - print(#quot;Origin#quot;) - case _: - raise ValueError(#quot;oops#quot;)\n"] - block3["print(#quot;Origin#quot;)\n"] - block4["match point: - case (0, 0): - print(#quot;Origin#quot;) - case _: - raise ValueError(#quot;oops#quot;)\n"] - - start --> block4 - block4 -- "case (0, 0)" --> block3 - block4 -- "else" --> block2 - block3 --> block0 - block2 --> block1 - block1 --> return - block0 --> return -``` - -## Function 9 -### Source -```python -def func(point): - match point: - case (0, 0): - print("Origin") - case (0, y): - print(f"Y={y}") - case (x, 0): - print(f"X={x}") - case (x, y): - print(f"X={x}, Y={y}") - case _: - raise ValueError("Not a point") -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0[["`*(empty)*`"]] - block1["raise ValueError(#quot;Not a point#quot;)\n"] - block2["match point: - case (0, 0): - print(#quot;Origin#quot;) - case (0, y): - print(f#quot;Y={y}#quot;) - case (x, 0): - print(f#quot;X={x}#quot;) - case (x, y): - print(f#quot;X={x}, Y={y}#quot;) - case _: - raise ValueError(#quot;Not a point#quot;)\n"] - block3["print(f#quot;X={x}, Y={y}#quot;)\n"] - block4["match point: - case (0, 0): - print(#quot;Origin#quot;) - case (0, y): - print(f#quot;Y={y}#quot;) - case (x, 0): - print(f#quot;X={x}#quot;) - case (x, y): - print(f#quot;X={x}, Y={y}#quot;) - case _: - raise ValueError(#quot;Not a point#quot;)\n"] - block5["print(f#quot;X={x}#quot;)\n"] - block6["match point: - case (0, 0): - print(#quot;Origin#quot;) - case (0, y): - print(f#quot;Y={y}#quot;) - case (x, 0): - print(f#quot;X={x}#quot;) - case (x, y): - print(f#quot;X={x}, Y={y}#quot;) - case _: - raise ValueError(#quot;Not a point#quot;)\n"] - block7["print(f#quot;Y={y}#quot;)\n"] - block8["match point: - case (0, 0): - print(#quot;Origin#quot;) - case (0, y): - print(f#quot;Y={y}#quot;) - case (x, 0): - print(f#quot;X={x}#quot;) - case (x, y): - print(f#quot;X={x}, Y={y}#quot;) - case _: - raise ValueError(#quot;Not a point#quot;)\n"] - block9["print(#quot;Origin#quot;)\n"] - block10["match point: - case (0, 0): - print(#quot;Origin#quot;) - case (0, y): - print(f#quot;Y={y}#quot;) - case (x, 0): - print(f#quot;X={x}#quot;) - case (x, y): - print(f#quot;X={x}, Y={y}#quot;) - case _: - raise ValueError(#quot;Not a point#quot;)\n"] - - start --> block10 - block10 -- "case (0, 0)" --> block9 - block10 -- "else" --> block8 - block9 --> block0 - block8 -- "case (0, y)" --> block7 - block8 -- "else" --> block6 - block7 --> block0 - block6 -- "case (x, 0)" --> block5 - block6 -- "else" --> block4 - block5 --> block0 - block4 -- "case (x, y)" --> block3 - block4 -- "else" --> block2 - block3 --> block0 - block2 --> block1 - block1 --> return - block0 --> return -``` - -## Function 10 -### Source -```python -def where_is(point): - class Point: - x: int - y: int - - match point: - case Point(x=0, y=0): - print("Origin") - case Point(x=0, y=y): - print(f"Y={y}") - case Point(x=x, y=0): - print(f"X={x}") - case Point(): - print("Somewhere else") - case _: - print("Not a point") -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0[["`*(empty)*`"]] - block1["print(#quot;Not a point#quot;)\n"] - block2["match point: - case Point(x=0, y=0): - print(#quot;Origin#quot;) - case Point(x=0, y=y): - print(f#quot;Y={y}#quot;) - case Point(x=x, y=0): - print(f#quot;X={x}#quot;) - case Point(): - print(#quot;Somewhere else#quot;) - case _: - print(#quot;Not a point#quot;)\n"] - block3["print(#quot;Somewhere else#quot;)\n"] - block4["match point: - case Point(x=0, y=0): - print(#quot;Origin#quot;) - case Point(x=0, y=y): - print(f#quot;Y={y}#quot;) - case Point(x=x, y=0): - print(f#quot;X={x}#quot;) - case Point(): - print(#quot;Somewhere else#quot;) - case _: - print(#quot;Not a point#quot;)\n"] - block5["print(f#quot;X={x}#quot;)\n"] - block6["match point: - case Point(x=0, y=0): - print(#quot;Origin#quot;) - case Point(x=0, y=y): - print(f#quot;Y={y}#quot;) - case Point(x=x, y=0): - print(f#quot;X={x}#quot;) - case Point(): - print(#quot;Somewhere else#quot;) - case _: - print(#quot;Not a point#quot;)\n"] - block7["print(f#quot;Y={y}#quot;)\n"] - block8["match point: - case Point(x=0, y=0): - print(#quot;Origin#quot;) - case Point(x=0, y=y): - print(f#quot;Y={y}#quot;) - case Point(x=x, y=0): - print(f#quot;X={x}#quot;) - case Point(): - print(#quot;Somewhere else#quot;) - case _: - print(#quot;Not a point#quot;)\n"] - block9["print(#quot;Origin#quot;)\n"] - block10["match point: - case Point(x=0, y=0): - print(#quot;Origin#quot;) - case Point(x=0, y=y): - print(f#quot;Y={y}#quot;) - case Point(x=x, y=0): - print(f#quot;X={x}#quot;) - case Point(): - print(#quot;Somewhere else#quot;) - case _: - print(#quot;Not a point#quot;)\n"] - block11["class Point: - x: int - y: int\n"] - - start --> block11 - block11 --> block10 - block10 -- "case Point(x=0, y=0)" --> block9 - block10 -- "else" --> block8 - block9 --> block0 - block8 -- "case Point(x=0, y=y)" --> block7 - block8 -- "else" --> block6 - block7 --> block0 - block6 -- "case Point(x=x, y=0)" --> block5 - block6 -- "else" --> block4 - block5 --> block0 - block4 -- "case Point()" --> block3 - block4 -- "else" --> block2 - block3 --> block0 - block2 --> block1 - block1 --> block0 - block0 --> return -``` - -## Function 11 -### Source -```python -def func(points): - match points: - case []: - print("No points") - case [Point(0, 0)]: - print("The origin") - case [Point(x, y)]: - print(f"Single point {x}, {y}") - case [Point(0, y1), Point(0, y2)]: - print(f"Two on the Y axis at {y1}, {y2}") - case _: - print("Something else") -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0[["`*(empty)*`"]] - block1["print(#quot;Something else#quot;)\n"] - block2["match points: - case []: - print(#quot;No points#quot;) - case [Point(0, 0)]: - print(#quot;The origin#quot;) - case [Point(x, y)]: - print(f#quot;Single point {x}, {y}#quot;) - case [Point(0, y1), Point(0, y2)]: - print(f#quot;Two on the Y axis at {y1}, {y2}#quot;) - case _: - print(#quot;Something else#quot;)\n"] - block3["print(f#quot;Two on the Y axis at {y1}, {y2}#quot;)\n"] - block4["match points: - case []: - print(#quot;No points#quot;) - case [Point(0, 0)]: - print(#quot;The origin#quot;) - case [Point(x, y)]: - print(f#quot;Single point {x}, {y}#quot;) - case [Point(0, y1), Point(0, y2)]: - print(f#quot;Two on the Y axis at {y1}, {y2}#quot;) - case _: - print(#quot;Something else#quot;)\n"] - block5["print(f#quot;Single point {x}, {y}#quot;)\n"] - block6["match points: - case []: - print(#quot;No points#quot;) - case [Point(0, 0)]: - print(#quot;The origin#quot;) - case [Point(x, y)]: - print(f#quot;Single point {x}, {y}#quot;) - case [Point(0, y1), Point(0, y2)]: - print(f#quot;Two on the Y axis at {y1}, {y2}#quot;) - case _: - print(#quot;Something else#quot;)\n"] - block7["print(#quot;The origin#quot;)\n"] - block8["match points: - case []: - print(#quot;No points#quot;) - case [Point(0, 0)]: - print(#quot;The origin#quot;) - case [Point(x, y)]: - print(f#quot;Single point {x}, {y}#quot;) - case [Point(0, y1), Point(0, y2)]: - print(f#quot;Two on the Y axis at {y1}, {y2}#quot;) - case _: - print(#quot;Something else#quot;)\n"] - block9["print(#quot;No points#quot;)\n"] - block10["match points: - case []: - print(#quot;No points#quot;) - case [Point(0, 0)]: - print(#quot;The origin#quot;) - case [Point(x, y)]: - print(f#quot;Single point {x}, {y}#quot;) - case [Point(0, y1), Point(0, y2)]: - print(f#quot;Two on the Y axis at {y1}, {y2}#quot;) - case _: - print(#quot;Something else#quot;)\n"] - - start --> block10 - block10 -- "case []" --> block9 - block10 -- "else" --> block8 - block9 --> block0 - block8 -- "case [Point(0, 0)]" --> block7 - block8 -- "else" --> block6 - block7 --> block0 - block6 -- "case [Point(x, y)]" --> block5 - block6 -- "else" --> block4 - block5 --> block0 - block4 -- "case [Point(0, y1), Point(0, y2)]" --> block3 - block4 -- "else" --> block2 - block3 --> block0 - block2 --> block1 - block1 --> block0 - block0 --> return -``` - -## Function 12 -### Source -```python -def func(point): - match point: - case Point(x, y) if x == y: - print(f"Y=X at {x}") - case Point(x, y): - print(f"Not on the diagonal") -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0[["`*(empty)*`"]] - block1["print(f#quot;Not on the diagonal#quot;)\n"] - block2["match point: - case Point(x, y) if x == y: - print(f#quot;Y=X at {x}#quot;) - case Point(x, y): - print(f#quot;Not on the diagonal#quot;)\n"] - block3["print(f#quot;Y=X at {x}#quot;)\n"] - block4["match point: - case Point(x, y) if x == y: - print(f#quot;Y=X at {x}#quot;) - case Point(x, y): - print(f#quot;Not on the diagonal#quot;)\n"] - - start --> block4 - block4 -- "case Point(x, y) if x == y" --> block3 - block4 -- "else" --> block2 - block3 --> block0 - block2 -- "case Point(x, y)" --> block1 - block2 -- "else" --> block0 - block1 --> block0 - block0 --> return -``` - -## Function 13 -### Source -```python -def func(): - from enum import Enum - class Color(Enum): - RED = 'red' - GREEN = 'green' - BLUE = 'blue' - - color = Color(input("Enter your choice of 'red', 'blue' or 'green': ")) - - match color: - case Color.RED: - print("I see red!") - case Color.GREEN: - print("Grass is green") - case Color.BLUE: - print("I'm feeling the blues :(") -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0[["`*(empty)*`"]] - block1["print(#quot;I'm feeling the blues :(#quot;)\n"] - block2["match color: - case Color.RED: - print(#quot;I see red!#quot;) - case Color.GREEN: - print(#quot;Grass is green#quot;) - case Color.BLUE: - print(#quot;I'm feeling the blues :(#quot;)\n"] - block3["print(#quot;Grass is green#quot;)\n"] - block4["match color: - case Color.RED: - print(#quot;I see red!#quot;) - case Color.GREEN: - print(#quot;Grass is green#quot;) - case Color.BLUE: - print(#quot;I'm feeling the blues :(#quot;)\n"] - block5["print(#quot;I see red!#quot;)\n"] - block6["match color: - case Color.RED: - print(#quot;I see red!#quot;) - case Color.GREEN: - print(#quot;Grass is green#quot;) - case Color.BLUE: - print(#quot;I'm feeling the blues :(#quot;)\n"] - block7["from enum import Enum\nclass Color(Enum): - RED = 'red' - GREEN = 'green' - BLUE = 'blue'\ncolor = Color(input(#quot;Enter your choice of 'red', 'blue' or 'green': #quot;))\n"] - - start --> block7 - block7 --> block6 - block6 -- "case Color.RED" --> block5 - block6 -- "else" --> block4 - block5 --> block0 - block4 -- "case Color.GREEN" --> block3 - block4 -- "else" --> block2 - block3 --> block0 - block2 -- "case Color.BLUE" --> block1 - block2 -- "else" --> block0 - block1 --> block0 - block0 --> return -``` - -## Function 14 -### Source -```python -def func(point): - match point: - case (0, 0): - print("Origin") - case foo: - raise ValueError("oops") -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0[["`*(empty)*`"]] - block1["raise ValueError(#quot;oops#quot;)\n"] - block2["match point: - case (0, 0): - print(#quot;Origin#quot;) - case foo: - raise ValueError(#quot;oops#quot;)\n"] - block3["print(#quot;Origin#quot;)\n"] - block4["match point: - case (0, 0): - print(#quot;Origin#quot;) - case foo: - raise ValueError(#quot;oops#quot;)\n"] - - start --> block4 - block4 -- "case (0, 0)" --> block3 - block4 -- "else" --> block2 - block3 --> block0 - block2 --> block1 - block1 --> return - block0 --> return -``` - - diff --git a/crates/ruff_linter/src/rules/ruff/rules/snapshots/ruff_linter__rules__ruff__rules__unreachable__tests__raise.py.md.snap b/crates/ruff_linter/src/rules/ruff/rules/snapshots/ruff_linter__rules__ruff__rules__unreachable__tests__raise.py.md.snap deleted file mode 100644 index d0265fa698..0000000000 --- a/crates/ruff_linter/src/rules/ruff/rules/snapshots/ruff_linter__rules__ruff__rules__unreachable__tests__raise.py.md.snap +++ /dev/null @@ -1,41 +0,0 @@ ---- -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(): - raise Exception -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0["raise Exception\n"] - - start --> block0 - block0 --> return -``` - -## Function 1 -### Source -```python -def func(): - raise "a glass!" -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0["raise #quot;a glass!#quot;\n"] - - start --> block0 - block0 --> return -``` - - diff --git a/crates/ruff_linter/src/rules/ruff/rules/snapshots/ruff_linter__rules__ruff__rules__unreachable__tests__simple.py.md.snap b/crates/ruff_linter/src/rules/ruff/rules/snapshots/ruff_linter__rules__ruff__rules__unreachable__tests__simple.py.md.snap deleted file mode 100644 index 015cf43dcc..0000000000 --- a/crates/ruff_linter/src/rules/ruff/rules/snapshots/ruff_linter__rules__ruff__rules__unreachable__tests__simple.py.md.snap +++ /dev/null @@ -1,136 +0,0 @@ ---- -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(): - pass -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0["pass\n"] - - start --> block0 - block0 --> return -``` - -## Function 1 -### Source -```python -def func(): - pass -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0["pass\n"] - - start --> block0 - block0 --> return -``` - -## Function 2 -### Source -```python -def func(): - return -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0["return\n"] - - start --> block0 - block0 --> return -``` - -## Function 3 -### Source -```python -def func(): - return 1 -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0["return 1\n"] - - start --> block0 - block0 --> return -``` - -## Function 4 -### Source -```python -def func(): - return 1 - return "unreachable" -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0["return #quot;unreachable#quot;\n"] - block1["return 1\n"] - - start --> block1 - block1 --> return - block0 --> return -``` - -## Function 5 -### Source -```python -def func(): - i = 0 -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0["i = 0\n"] - - start --> block0 - block0 --> return -``` - -## Function 6 -### Source -```python -def func(): - i = 0 - i += 2 - return i -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0["i = 0\ni += 2\nreturn i\n"] - - start --> block0 - block0 --> return -``` - - diff --git a/crates/ruff_linter/src/rules/ruff/rules/snapshots/ruff_linter__rules__ruff__rules__unreachable__tests__while.py.md.snap b/crates/ruff_linter/src/rules/ruff/rules/snapshots/ruff_linter__rules__ruff__rules__unreachable__tests__while.py.md.snap deleted file mode 100644 index 3491d2b7e2..0000000000 --- a/crates/ruff_linter/src/rules/ruff/rules/snapshots/ruff_linter__rules__ruff__rules__unreachable__tests__while.py.md.snap +++ /dev/null @@ -1,527 +0,0 @@ ---- -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(): - while False: - return "unreachable" - return 1 -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0["return 1\n"] - block1["return #quot;unreachable#quot;\n"] - block2["while False: - return #quot;unreachable#quot;\n"] - - start --> block2 - block2 -- "False" --> block1 - block2 -- "else" --> block0 - block1 --> return - block0 --> return -``` - -## Function 1 -### Source -```python -def func(): - while False: - return "unreachable" - else: - return 1 -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0["return #quot;unreachable#quot;\n"] - block1["return 1\n"] - block2["while False: - return #quot;unreachable#quot; - else: - return 1\n"] - - start --> block2 - block2 -- "False" --> block0 - block2 -- "else" --> block1 - block1 --> return - block0 --> return -``` - -## Function 2 -### Source -```python -def func(): - while False: - return "unreachable" - else: - return 1 - return "also unreachable" -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0["return #quot;also unreachable#quot;\n"] - block1["return #quot;unreachable#quot;\n"] - block2["return 1\n"] - block3["while False: - return #quot;unreachable#quot; - else: - return 1\n"] - - start --> block3 - block3 -- "False" --> block1 - block3 -- "else" --> block2 - block2 --> return - block1 --> return - block0 --> return -``` - -## Function 3 -### Source -```python -def func(): - while True: - return 1 - return "unreachable" -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0["return #quot;unreachable#quot;\n"] - block1["return 1\n"] - block2["while True: - return 1\n"] - - start --> block2 - block2 -- "True" --> block1 - block2 -- "else" --> block0 - block1 --> return - block0 --> return -``` - -## Function 4 -### Source -```python -def func(): - while True: - return 1 - else: - return "unreachable" -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0["return 1\n"] - block1["return #quot;unreachable#quot;\n"] - block2["while True: - return 1 - else: - return #quot;unreachable#quot;\n"] - - start --> block2 - block2 -- "True" --> block0 - block2 -- "else" --> block1 - block1 --> return - block0 --> return -``` - -## Function 5 -### Source -```python -def func(): - while True: - return 1 - else: - return "unreachable" - return "also unreachable" -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0["return #quot;also unreachable#quot;\n"] - block1["return 1\n"] - block2["return #quot;unreachable#quot;\n"] - block3["while True: - return 1 - else: - return #quot;unreachable#quot;\n"] - - start --> block3 - block3 -- "True" --> block1 - block3 -- "else" --> block2 - block2 --> return - block1 --> return - block0 --> return -``` - -## Function 6 -### Source -```python -def func(): - i = 0 - while False: - i += 1 - return i -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0["return i\n"] - block1["i += 1\n"] - block2["i = 0\nwhile False: - i += 1\n"] - - start --> block2 - block2 -- "False" --> block1 - block2 -- "else" --> block0 - block1 --> block2 - block0 --> return -``` - -## Function 7 -### Source -```python -def func(): - i = 0 - while True: - i += 1 - return i -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0["return i\n"] - block1["i += 1\n"] - block2["i = 0\nwhile True: - i += 1\n"] - - start --> block2 - block2 -- "True" --> block1 - block2 -- "else" --> block0 - block1 --> block2 - block0 --> return -``` - -## Function 8 -### Source -```python -def func(): - while True: - pass - return 1 -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0["return 1\n"] - block1["pass\n"] - block2["while True: - pass\n"] - - start --> block2 - block2 -- "True" --> block1 - block2 -- "else" --> block0 - block1 --> block2 - block0 --> return -``` - -## Function 9 -### Source -```python -def func(): - i = 0 - while True: - if True: - print("ok") - i += 1 - return i -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0["return i\n"] - block1["i += 1\n"] - block2["print(#quot;ok#quot;)\n"] - block3["if True: - print(#quot;ok#quot;)\n"] - block4["i = 0\nwhile True: - if True: - print(#quot;ok#quot;) - i += 1\n"] - - start --> block4 - block4 -- "True" --> block3 - block4 -- "else" --> block0 - block3 -- "True" --> block2 - block3 -- "else" --> block1 - block2 --> block1 - block1 --> block4 - block0 --> return -``` - -## Function 10 -### Source -```python -def func(): - i = 0 - while True: - if False: - print("ok") - i += 1 - return i -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0["return i\n"] - block1["i += 1\n"] - block2["print(#quot;ok#quot;)\n"] - block3["if False: - print(#quot;ok#quot;)\n"] - block4["i = 0\nwhile True: - if False: - print(#quot;ok#quot;) - i += 1\n"] - - start --> block4 - block4 -- "True" --> block3 - block4 -- "else" --> block0 - block3 -- "False" --> block2 - block3 -- "else" --> block1 - block2 --> block1 - block1 --> block4 - block0 --> return -``` - -## Function 11 -### Source -```python -def func(): - while True: - if True: - return 1 - return 0 -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0["return 0\n"] - block1["return 1\n"] - block2["if True: - return 1\n"] - block3["while True: - if True: - return 1\n"] - - start --> block3 - block3 -- "True" --> block2 - block3 -- "else" --> block0 - block2 -- "True" --> block1 - block2 -- "else" --> block3 - block1 --> return - block0 --> return -``` - -## Function 12 -### Source -```python -def func(): - while True: - continue -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0[["`*(empty)*`"]] - block1["continue\n"] - block2["while True: - continue\n"] - - start --> block2 - block2 -- "True" --> block1 - block2 -- "else" --> block0 - block1 --> block2 - block0 --> return -``` - -## Function 13 -### Source -```python -def func(): - while False: - continue -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0[["`*(empty)*`"]] - block1["continue\n"] - block2["while False: - continue\n"] - - start --> block2 - block2 -- "False" --> block1 - block2 -- "else" --> block0 - block1 --> block2 - block0 --> return -``` - -## Function 14 -### Source -```python -def func(): - while True: - break -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0[["`*(empty)*`"]] - block1["break\n"] - block2["while True: - break\n"] - - start --> block2 - block2 -- "True" --> block1 - block2 -- "else" --> block0 - block1 --> block0 - block0 --> return -``` - -## Function 15 -### Source -```python -def func(): - while False: - break -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0[["`*(empty)*`"]] - block1["break\n"] - block2["while False: - break\n"] - - start --> block2 - block2 -- "False" --> block1 - block2 -- "else" --> block0 - block1 --> block0 - block0 --> return -``` - -## Function 16 -### Source -```python -def func(): - while True: - if True: - continue -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0[["`*(empty)*`"]] - block1["continue\n"] - block2["if True: - continue\n"] - block3["while True: - if True: - continue\n"] - - start --> block3 - block3 -- "True" --> block2 - block3 -- "else" --> block0 - block2 -- "True" --> block1 - block2 -- "else" --> block3 - block1 --> block3 - block0 --> return -``` - -## Function 17 -### Source -```python -def func(): - while True: - if True: - break -``` - -### Control Flow Graph -```mermaid -flowchart TD - start(("Start")) - return(("End")) - block0[["`*(empty)*`"]] - block1["break\n"] - block2["if True: - break\n"] - block3["while True: - if True: - break\n"] - - start --> block3 - block3 -- "True" --> block2 - block3 -- "else" --> block0 - block2 -- "True" --> block1 - block2 -- "else" --> block3 - block1 --> block0 - block0 --> return -``` - - diff --git a/crates/ruff_linter/src/rules/ruff/rules/unreachable.rs b/crates/ruff_linter/src/rules/ruff/rules/unreachable.rs deleted file mode 100644 index 8504016848..0000000000 --- a/crates/ruff_linter/src/rules/ruff/rules/unreachable.rs +++ /dev/null @@ -1,1114 +0,0 @@ -use std::{fmt, iter, usize}; - -use log::error; -use ruff_python_ast::{ - Expr, ExprBooleanLiteral, Identifier, MatchCase, Pattern, PatternMatchAs, PatternMatchOr, Stmt, - StmtFor, StmtMatch, StmtReturn, StmtTry, StmtWhile, StmtWith, -}; -use ruff_text_size::{Ranged, TextRange, TextSize}; - -use ruff_diagnostics::{Diagnostic, Violation}; -use ruff_index::{IndexSlice, IndexVec}; -use ruff_macros::{derive_message_formats, newtype_index, violation}; - -/// ## What it does -/// Checks for unreachable code. -/// -/// ## Why is this bad? -/// Unreachable code can be a maintenance burden without ever being used. -/// -/// ## Example -/// ```python -/// def function(): -/// if False: -/// return "unreachable" -/// return "reachable" -/// ``` -/// -/// Use instead: -/// ```python -/// def function(): -/// return "reachable" -/// ``` -#[violation] -pub struct UnreachableCode { - name: String, -} - -impl Violation for UnreachableCode { - #[derive_message_formats] - fn message(&self) -> String { - let UnreachableCode { name } = self; - format!("Unreachable code in {name}") - } -} - -pub(crate) fn in_function(name: &Identifier, body: &[Stmt]) -> Vec { - // Create basic code blocks from the body. - let basic_blocks = BasicBlocks::from(body); - - // Basic on the code blocks we can (more) easily follow what statements are - // and aren't reached, we'll mark them as such in `reached_map`. - let mut reached_map = Bitmap::with_capacity(basic_blocks.len()); - - if let Some(start_index) = basic_blocks.start_index() { - mark_reached(&mut reached_map, &basic_blocks.blocks, start_index); - } - - // For each unreached code block create a diagnostic. - reached_map - .unset() - .filter_map(|idx| { - let block = &basic_blocks.blocks[idx]; - if block.is_sentinel() { - return None; - } - - // TODO: add more information to the diagnostic. Include the entire - // code block, not just the first line. Maybe something to indicate - // the code flow and where it prevents this block from being reached - // for example. - let Some(stmt) = block.stmts.first() else { - // This should never happen. - error!("Got an unexpected empty code block"); - return None; - }; - Some(Diagnostic::new( - UnreachableCode { - name: name.as_str().to_owned(), - }, - stmt.range(), - )) - }) - .collect() -} - -/// Simple bitmap. -#[derive(Debug)] -struct Bitmap { - bits: Box<[usize]>, - capacity: usize, -} - -impl Bitmap { - /// Create a new `Bitmap` with `capacity` capacity. - fn with_capacity(capacity: usize) -> Bitmap { - let mut size = capacity / usize::BITS as usize; - if (capacity % usize::BITS as usize) != 0 { - size += 1; - } - Bitmap { - bits: vec![0; size].into_boxed_slice(), - capacity, - } - } - - /// Set bit at index `idx` to true. - /// - /// Returns a boolean indicating if the bit was already set. - fn set(&mut self, idx: BlockIndex) -> bool { - let bits_index = (idx.as_u32() / usize::BITS) as usize; - let shift = idx.as_u32() % usize::BITS; - if (self.bits[bits_index] & (1 << shift)) == 0 { - self.bits[bits_index] |= 1 << shift; - false - } else { - true - } - } - - /// Returns an iterator of all unset indices. - fn unset(&self) -> impl Iterator + '_ { - let mut index = 0; - let mut shift = 0; - let last_max_shift = self.capacity % usize::BITS as usize; - iter::from_fn(move || loop { - if shift >= usize::BITS as usize { - shift = 0; - index += 1; - } - if self.bits.len() <= index || (index >= self.bits.len() - 1 && shift >= last_max_shift) - { - return None; - } - - let is_set = (self.bits[index] & (1 << shift)) != 0; - shift += 1; - if !is_set { - return Some(BlockIndex::from_usize( - (index * usize::BITS as usize) + shift - 1, - )); - } - }) - } -} - -/// Set bits in `reached_map` for all blocks that are reached in `blocks` -/// starting with block at index `idx`. -fn mark_reached( - reached_map: &mut Bitmap, - blocks: &IndexSlice>, - start_index: BlockIndex, -) { - let mut idx = start_index; - - loop { - let block = &blocks[idx]; - if reached_map.set(idx) { - return; // Block already visited, no needed to do it again. - } - - match &block.next { - NextBlock::Always(next) => idx = *next, - NextBlock::If { - condition, - next, - orelse, - } => { - match taken(condition) { - Some(true) => idx = *next, // Always taken. - Some(false) => idx = *orelse, // Never taken. - None => { - // Don't know, both branches might be taken. - idx = *next; - mark_reached(reached_map, blocks, *orelse); - } - } - } - NextBlock::Terminate => return, - } - } -} - -/// Determines if `condition` is taken. -/// Returns `Some(true)` if the condition is always true, e.g. `if True`, same -/// with `Some(false)` if it's never taken. If it can't be determined it returns -/// `None`, e.g. `If i == 100`. -fn taken(condition: &Condition) -> Option { - // TODO: add more cases to this where we can determine a condition - // statically. For now we only consider constant booleans. - match condition { - Condition::Test(expr) => match expr { - Expr::BooleanLiteral(ExprBooleanLiteral { value, .. }) => Some(*value), - _ => None, - }, - Condition::Iterator(_) => None, - Condition::Match { .. } => None, - } -} - -/// Index into [`BasicBlocks::blocks`]. -#[newtype_index] -#[derive(PartialOrd, Ord)] -struct BlockIndex; - -/// Collection of basic block. -#[derive(Debug, PartialEq)] -struct BasicBlocks<'stmt> { - /// # Notes - /// - /// The order of these block is unspecified. However it's guaranteed that - /// the last block is the first statement in the function and the first - /// block is the last statement. The block are more or less in reverse - /// order, but it gets fussy around control flow statements (e.g. `while` - /// statements). - /// - /// For loop blocks, and similar recurring control flows, the end of the - /// body will point to the loop block again (to create the loop). However an - /// oddity here is that this block might contain statements before the loop - /// itself which, of course, won't be executed again. - /// - /// For example: - /// ```python - /// i = 0 # block 0 - /// while True: # - /// continue # block 1 - /// ``` - /// Will create a connection between block 1 (loop body) and block 0, which - /// includes the `i = 0` statement. - /// - /// To keep `NextBlock` simple(r) `NextBlock::If`'s `next` and `orelse` - /// fields only use `BlockIndex`, which means that they can't terminate - /// themselves. To support this we insert *empty*/fake blocks before the end - /// of the function that we can link to. - /// - /// Finally `BasicBlock` can also be a sentinel node, see the associated - /// constants of [`BasicBlock`]. - blocks: IndexVec>, -} - -impl BasicBlocks<'_> { - fn len(&self) -> usize { - self.blocks.len() - } - - fn start_index(&self) -> Option { - self.blocks.indices().last() - } -} - -impl<'stmt> From<&'stmt [Stmt]> for BasicBlocks<'stmt> { - /// # Notes - /// - /// This assumes that `stmts` is a function body. - fn from(stmts: &'stmt [Stmt]) -> BasicBlocks<'stmt> { - let mut blocks = BasicBlocksBuilder::with_capacity(stmts.len()); - - blocks.create_blocks(stmts, None); - - blocks.finish() - } -} - -/// Basic code block, sequence of statements unconditionally executed -/// "together". -#[derive(Debug, PartialEq)] -struct BasicBlock<'stmt> { - stmts: &'stmt [Stmt], - next: NextBlock<'stmt>, -} - -/// Edge between basic blocks (in the control-flow graph). -#[derive(Debug, PartialEq)] -enum NextBlock<'stmt> { - /// Always continue with a block. - Always(BlockIndex), - /// Condition jump. - If { - /// Condition that needs to be evaluated to jump to the `next` or - /// `orelse` block. - condition: Condition<'stmt>, - /// Next block if `condition` is true. - next: BlockIndex, - /// Next block if `condition` is false. - orelse: BlockIndex, - }, - /// The end. - Terminate, -} - -/// Condition used to determine to take the `next` or `orelse` branch in -/// [`NextBlock::If`]. -#[derive(Clone, Debug, PartialEq)] -enum Condition<'stmt> { - /// Conditional statement, this should evaluate to a boolean, for e.g. `if` - /// or `while`. - Test(&'stmt Expr), - /// Iterator for `for` statements, e.g. for `i in range(10)` this will be - /// `range(10)`. - Iterator(&'stmt Expr), - Match { - /// `match $subject`. - subject: &'stmt Expr, - /// `case $case`, include pattern, guard, etc. - case: &'stmt MatchCase, - }, -} - -impl<'stmt> Ranged for Condition<'stmt> { - fn range(&self) -> TextRange { - match self { - Condition::Test(expr) | Condition::Iterator(expr) => expr.range(), - // The case of the match statement, without the body. - Condition::Match { subject: _, case } => TextRange::new( - case.start(), - case.guard - .as_ref() - .map_or(case.pattern.end(), |guard| guard.end()), - ), - } - } -} - -impl<'stmt> BasicBlock<'stmt> { - /// A sentinel block indicating an empty termination block. - const EMPTY: BasicBlock<'static> = BasicBlock { - stmts: &[], - next: NextBlock::Terminate, - }; - - /// A sentinel block indicating an exception was raised. - const EXCEPTION: BasicBlock<'static> = BasicBlock { - stmts: &[Stmt::Return(StmtReturn { - range: TextRange::new(TextSize::new(0), TextSize::new(0)), - value: None, - })], - next: NextBlock::Terminate, - }; - - /// Return true if the block is a sentinel or fake block. - fn is_sentinel(&self) -> bool { - self.is_empty() || self.is_exception() - } - - /// Returns an empty block that terminates. - fn is_empty(&self) -> bool { - matches!(self.next, NextBlock::Terminate) && self.stmts.is_empty() - } - - /// Returns true if `self` an [`BasicBlock::EXCEPTION`]. - fn is_exception(&self) -> bool { - matches!(self.next, NextBlock::Terminate) && BasicBlock::EXCEPTION.stmts == self.stmts - } -} - -/// Handle a loop block, such as a `while`, `for` or `async for` statement. -fn loop_block<'stmt>( - blocks: &mut BasicBlocksBuilder<'stmt>, - condition: Condition<'stmt>, - body: &'stmt [Stmt], - orelse: &'stmt [Stmt], - after: Option, -) -> NextBlock<'stmt> { - let after_block = blocks.maybe_next_block_index(after, || orelse.is_empty()); - // NOTE: a while loop's body must not be empty, so we can safely - // create at least one block from it. - let last_statement_index = blocks.append_blocks(body, after); - let last_orelse_statement = blocks.append_blocks_if_not_empty(orelse, after_block); - // `create_blocks` always continues to the next block by - // default. However in a while loop we want to continue with the - // while block (we're about to create) to create the loop. - // NOTE: `blocks.len()` is an invalid index at time of creation - // as it points to the block which we're about to create. - blocks.change_next_block( - last_statement_index, - after_block, - blocks.blocks.next_index(), - |block| { - // For `break` statements we don't want to continue with the - // loop, but instead with the statement after the loop (i.e. - // not change anything). - !block.stmts.last().is_some_and(Stmt::is_break_stmt) - }, - ); - NextBlock::If { - condition, - next: last_statement_index, - orelse: last_orelse_statement, - } -} - -/// Handle a single match case. -/// -/// `next_after_block` is the block *after* the entire match statement that is -/// taken after this case is taken. -/// `orelse_after_block` is the next match case (or the block after the match -/// statement if this is the last case). -fn match_case<'stmt>( - blocks: &mut BasicBlocksBuilder<'stmt>, - match_stmt: &'stmt Stmt, - subject: &'stmt Expr, - case: &'stmt MatchCase, - next_after_block: BlockIndex, - orelse_after_block: BlockIndex, -) -> BasicBlock<'stmt> { - // FIXME: this is not ideal, we want to only use the `case` statement here, - // but that is type `MatchCase`, not `Stmt`. For now we'll point to the - // entire match statement. - let stmts = std::slice::from_ref(match_stmt); - let next_block_index = if case.body.is_empty() { - next_after_block - } else { - let from = blocks.last_index(); - let last_statement_index = blocks.append_blocks(&case.body, Some(next_after_block)); - if let Some(from) = from { - blocks.change_next_block(last_statement_index, from, next_after_block, |_| true); - } - last_statement_index - }; - let next = if is_wildcard(case) { - // Wildcard case is always taken. - NextBlock::Always(next_block_index) - } else { - NextBlock::If { - condition: Condition::Match { subject, case }, - next: next_block_index, - orelse: orelse_after_block, - } - }; - BasicBlock { stmts, next } -} - -/// Returns true if the [`MatchCase`] is a wildcard pattern. -fn is_wildcard(pattern: &MatchCase) -> bool { - /// Returns true if the [`Pattern`] is a wildcard pattern. - fn is_wildcard_pattern(pattern: &Pattern) -> bool { - match pattern { - Pattern::MatchValue(_) - | Pattern::MatchSingleton(_) - | Pattern::MatchSequence(_) - | Pattern::MatchMapping(_) - | Pattern::MatchClass(_) - | Pattern::MatchStar(_) => false, - Pattern::MatchAs(PatternMatchAs { pattern, .. }) => pattern.is_none(), - Pattern::MatchOr(PatternMatchOr { patterns, .. }) => { - patterns.iter().all(is_wildcard_pattern) - } - } - } - - pattern.guard.is_none() && is_wildcard_pattern(&pattern.pattern) -} - -#[derive(Debug, Default)] -struct BasicBlocksBuilder<'stmt> { - blocks: IndexVec>, -} - -impl<'stmt> BasicBlocksBuilder<'stmt> { - fn with_capacity(capacity: usize) -> Self { - Self { - blocks: IndexVec::with_capacity(capacity), - } - } - - /// Creates basic blocks from `stmts` and appends them to `blocks`. - fn create_blocks( - &mut self, - stmts: &'stmt [Stmt], - mut after: Option, - ) -> Option { - // We process the statements in reverse so that we can always point to the - // next block (as that should always be processed). - let mut stmts_iter = stmts.iter().enumerate().rev().peekable(); - while let Some((i, stmt)) = stmts_iter.next() { - let next = match stmt { - // Statements that continue to the next statement after execution. - Stmt::FunctionDef(_) - | Stmt::Import(_) - | Stmt::ImportFrom(_) - | Stmt::ClassDef(_) - | Stmt::Global(_) - | Stmt::Nonlocal(_) - | Stmt::Delete(_) - | Stmt::Assign(_) - | Stmt::AugAssign(_) - | Stmt::AnnAssign(_) - | Stmt::Break(_) - | Stmt::TypeAlias(_) - | Stmt::IpyEscapeCommand(_) - | Stmt::Pass(_) => self.unconditional_next_block(after), - Stmt::Continue(_) => { - // NOTE: the next branch gets fixed up in `change_next_block`. - self.unconditional_next_block(after) - } - // Statements that (can) divert the control flow. - Stmt::If(stmt_if) => { - let after_consequent_block = - self.maybe_next_block_index(after, || needs_next_block(&stmt_if.body)); - let after_alternate_block = self.maybe_next_block_index(after, || { - stmt_if - .elif_else_clauses - .last() - .map_or(true, |clause| needs_next_block(&clause.body)) - }); - - let consequent = - self.append_blocks_if_not_empty(&stmt_if.body, after_consequent_block); - - // Block ID of the next elif or else clause. - let mut next_branch = after_alternate_block; - - for clause in stmt_if.elif_else_clauses.iter().rev() { - let consequent = - self.append_blocks_if_not_empty(&clause.body, after_consequent_block); - - next_branch = if let Some(test) = &clause.test { - let next = NextBlock::If { - condition: Condition::Test(test), - next: consequent, - orelse: next_branch, - }; - let stmts = std::slice::from_ref(stmt); - let block = BasicBlock { stmts, next }; - self.blocks.push(block) - } else { - consequent - }; - } - - NextBlock::If { - condition: Condition::Test(&stmt_if.test), - next: consequent, - orelse: next_branch, - } - } - Stmt::While(StmtWhile { - test: condition, - body, - orelse, - .. - }) => loop_block(self, Condition::Test(condition), body, orelse, after), - Stmt::For(StmtFor { - iter: condition, - body, - orelse, - .. - }) => loop_block(self, Condition::Iterator(condition), body, orelse, after), - Stmt::Try(StmtTry { - body, - handlers, - orelse, - finalbody, - .. - }) => { - // TODO: handle `try` statements. The `try` control flow is very - // complex, what blocks are and aren't taken and from which - // block the control flow is actually returns is **very** - // specific to the contents of the block. Read - // - // very carefully. - // For now we'll skip over it. - let _ = (body, handlers, orelse, finalbody); // Silence unused code warnings. - self.unconditional_next_block(after) - } - Stmt::With(StmtWith { items, body, .. }) => { - // TODO: handle `with` statements, see - // . - // I recommend to `try` statements first as `with` can desugar - // to a `try` statement. - // For now we'll skip over it. - let _ = (items, body); // Silence unused code warnings. - self.unconditional_next_block(after) - } - Stmt::Match(StmtMatch { subject, cases, .. }) => { - let next_after_block = self.maybe_next_block_index(after, || { - // We don't need need a next block if all cases don't need a - // next block, i.e. if no cases need a next block, and we - // have a wildcard case (to ensure one of the block is - // always taken). - // NOTE: match statement require at least one case, so we - // don't have to worry about empty `cases`. - // TODO: support exhaustive cases without a wildcard. - cases.iter().any(|case| needs_next_block(&case.body)) - || !cases.iter().any(is_wildcard) - }); - let mut orelse_after_block = next_after_block; - for case in cases.iter().rev() { - let block = match_case( - self, - stmt, - subject, - case, - next_after_block, - orelse_after_block, - ); - // For the case above this use the just added case as the - // `orelse` branch, this convert the match statement to - // (essentially) a bunch of if statements. - orelse_after_block = self.blocks.push(block); - } - // TODO: currently we don't include the lines before the match - // statement in the block, unlike what we do for other - // statements. - after = Some(orelse_after_block); - continue; - } - Stmt::Raise(_) => { - // TODO: this needs special handling within `try` and `with` - // statements. For now we just terminate the execution, it's - // possible it's continued in an `catch` or `finally` block, - // possibly outside of the function. - // Also see `Stmt::Assert` handling. - NextBlock::Terminate - } - Stmt::Assert(stmt) => { - // TODO: this needs special handling within `try` and `with` - // statements. For now we just terminate the execution if the - // assertion fails, it's possible it's continued in an `catch` - // or `finally` block, possibly outside of the function. - // Also see `Stmt::Raise` handling. - let next = self.force_next_block_index(); - let orelse = self.fake_exception_block_index(); - NextBlock::If { - condition: Condition::Test(&stmt.test), - next, - orelse, - } - } - Stmt::Expr(stmt) => { - match &*stmt.value { - Expr::BoolOp(_) - | Expr::BinOp(_) - | Expr::UnaryOp(_) - | Expr::Dict(_) - | Expr::Set(_) - | Expr::Compare(_) - | Expr::Call(_) - | Expr::FString(_) - | Expr::StringLiteral(_) - | Expr::BytesLiteral(_) - | Expr::NumberLiteral(_) - | Expr::BooleanLiteral(_) - | Expr::NoneLiteral(_) - | Expr::EllipsisLiteral(_) - | Expr::Attribute(_) - | Expr::Subscript(_) - | Expr::Starred(_) - | Expr::Name(_) - | Expr::List(_) - | Expr::IpyEscapeCommand(_) - | Expr::Tuple(_) - | Expr::Slice(_) => self.unconditional_next_block(after), - // TODO: handle these expressions. - Expr::NamedExpr(_) - | Expr::Lambda(_) - | Expr::IfExp(_) - | Expr::ListComp(_) - | Expr::SetComp(_) - | Expr::DictComp(_) - | Expr::GeneratorExp(_) - | Expr::Await(_) - | Expr::Yield(_) - | Expr::YieldFrom(_) => self.unconditional_next_block(after), - } - } - // The tough branches are done, here is an easy one. - Stmt::Return(_) => NextBlock::Terminate, - }; - - // Include any statements in the block that don't divert the control flow. - let mut start = i; - let end = i + 1; - while stmts_iter - .next_if(|(_, stmt)| !is_control_flow_stmt(stmt)) - .is_some() - { - start -= 1; - } - - let block = BasicBlock { - stmts: &stmts[start..end], - next, - }; - after = Some(self.blocks.push(block)); - } - - after - } - - /// Calls [`create_blocks`] and returns this first block reached (i.e. the last - /// block). - fn append_blocks(&mut self, stmts: &'stmt [Stmt], after: Option) -> BlockIndex { - assert!(!stmts.is_empty()); - self.create_blocks(stmts, after) - .expect("Expect `create_blocks` to create a block if `stmts` is not empty") - } - - /// If `stmts` is not empty this calls [`create_blocks`] and returns this first - /// block reached (i.e. the last block). If `stmts` is empty this returns - /// `after` and doesn't change `blocks`. - fn append_blocks_if_not_empty( - &mut self, - stmts: &'stmt [Stmt], - after: BlockIndex, - ) -> BlockIndex { - if stmts.is_empty() { - after // Empty body, continue with block `after` it. - } else { - self.append_blocks(stmts, Some(after)) - } - } - - /// Select the next block from `blocks` unconditionally. - fn unconditional_next_block(&self, after: Option) -> NextBlock<'static> { - if let Some(after) = after { - return NextBlock::Always(after); - } - - // Either we continue with the next block (that is the last block `blocks`). - // Or it's the last statement, thus we terminate. - self.blocks - .last_index() - .map_or(NextBlock::Terminate, NextBlock::Always) - } - - /// Select the next block index from `blocks`. If there is no next block it will - /// add a fake/empty block. - fn force_next_block_index(&mut self) -> BlockIndex { - self.maybe_next_block_index(None, || true) - } - - /// Select the next block index from `blocks`. If there is no next block it will - /// add a fake/empty block if `condition` returns true. If `condition` returns - /// false the returned index may not be used. - fn maybe_next_block_index( - &mut self, - after: Option, - condition: impl FnOnce() -> bool, - ) -> BlockIndex { - if let Some(after) = after { - // Next block is already determined. - after - } else if let Some(idx) = self.blocks.last_index() { - // Otherwise we either continue with the next block (that is the last - // block in `blocks`). - idx - } else if condition() { - // Or if there are no blocks, but need one based on `condition` than we - // add a fake end block. - self.blocks.push(BasicBlock::EMPTY) - } else { - // NOTE: invalid, but because `condition` returned false this shouldn't - // be used. This only used as an optimisation to avoid adding fake end - // blocks. - BlockIndex::MAX - } - } - - /// Returns a block index for a fake exception block in `blocks`. - fn fake_exception_block_index(&mut self) -> BlockIndex { - for (i, block) in self.blocks.iter_enumerated() { - if block.is_exception() { - return i; - } - } - self.blocks.push(BasicBlock::EXCEPTION) - } - - /// Change the next basic block for the block, or chain of blocks, in index - /// `fixup_index` from `from` to `to`. - /// - /// This doesn't change the target if it's `NextBlock::Terminate`. - fn change_next_block( - &mut self, - mut fixup_index: BlockIndex, - from: BlockIndex, - to: BlockIndex, - check_condition: impl Fn(&BasicBlock) -> bool + Copy, - ) { - /// Check if we found our target and if `check_condition` is met. - fn is_target( - block: &BasicBlock<'_>, - got: BlockIndex, - expected: BlockIndex, - check_condition: impl Fn(&BasicBlock) -> bool, - ) -> bool { - got == expected && check_condition(block) - } - - loop { - match self.blocks.get(fixup_index).map(|b| &b.next) { - Some(NextBlock::Always(next)) => { - let next = *next; - if is_target(&self.blocks[fixup_index], next, from, check_condition) { - // Found our target, change it. - self.blocks[fixup_index].next = NextBlock::Always(to); - } - return; - } - Some(NextBlock::If { - condition, - next, - orelse, - }) => { - let idx = fixup_index; - let condition = condition.clone(); - let next = *next; - let orelse = *orelse; - let new_next = if is_target(&self.blocks[idx], next, from, check_condition) { - // Found our target in the next branch, change it (below). - Some(to) - } else { - // Follow the chain. - fixup_index = next; - None - }; - - let new_orelse = if is_target(&self.blocks[idx], orelse, from, check_condition) - { - // Found our target in the else branch, change it (below). - Some(to) - } else if new_next.is_none() { - // If we done with the next branch we only continue with the - // else branch. - fixup_index = orelse; - None - } else { - // If we're not done with the next and else branches we need - // to deal with the else branch before deal with the next - // branch (in the next iteration). - self.change_next_block(orelse, from, to, check_condition); - None - }; - - let (next, orelse) = match (new_next, new_orelse) { - (Some(new_next), Some(new_orelse)) => (new_next, new_orelse), - (Some(new_next), None) => (new_next, orelse), - (None, Some(new_orelse)) => (next, new_orelse), - (None, None) => continue, // Not changing anything. - }; - - self.blocks[idx].next = NextBlock::If { - condition, - next, - orelse, - }; - } - Some(NextBlock::Terminate) | None => return, - } - } - } - - fn finish(mut self) -> BasicBlocks<'stmt> { - if self.blocks.is_empty() { - self.blocks.push(BasicBlock::EMPTY); - } - - BasicBlocks { - blocks: self.blocks, - } - } -} - -impl<'stmt> std::ops::Deref for BasicBlocksBuilder<'stmt> { - type Target = IndexSlice>; - - fn deref(&self) -> &Self::Target { - &self.blocks - } -} - -impl<'stmt> std::ops::DerefMut for BasicBlocksBuilder<'stmt> { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.blocks - } -} - -/// Returns true if `stmts` need a next block, false otherwise. -fn needs_next_block(stmts: &[Stmt]) -> bool { - // No statements, we automatically continue with the next block. - let Some(last) = stmts.last() else { - return true; - }; - - match last { - Stmt::Return(_) | Stmt::Raise(_) => false, - Stmt::If(stmt) => needs_next_block(&stmt.body) || stmt.elif_else_clauses.last().map_or(true, |clause| needs_next_block(&clause.body)), - Stmt::FunctionDef(_) - | Stmt::Import(_) - | Stmt::ImportFrom(_) - | Stmt::ClassDef(_) - | Stmt::Global(_) - | Stmt::Nonlocal(_) - | Stmt::Delete(_) - | Stmt::Assign(_) - | Stmt::AugAssign(_) - | Stmt::AnnAssign(_) - | Stmt::Expr(_) - | Stmt::Pass(_) - | Stmt::TypeAlias(_) - | Stmt::IpyEscapeCommand(_) - // TODO: check below. - | Stmt::Break(_) - | Stmt::Continue(_) - | Stmt::For(_) - | Stmt::While(_) - | Stmt::With(_) - | Stmt::Match(_) - | Stmt::Try(_) - | Stmt::Assert(_) => true, - } -} - -/// Returns true if `stmt` contains a control flow statement, e.g. an `if` or -/// `return` statement. -fn is_control_flow_stmt(stmt: &Stmt) -> bool { - match stmt { - Stmt::FunctionDef(_) - | Stmt::Import(_) - | Stmt::ImportFrom(_) - | Stmt::ClassDef(_) - | Stmt::Global(_) - | Stmt::Nonlocal(_) - | Stmt::Delete(_) - | Stmt::Assign(_) - | Stmt::AugAssign(_) - | Stmt::AnnAssign(_) - | Stmt::Expr(_) - | Stmt::TypeAlias(_) - | Stmt::IpyEscapeCommand(_) - | Stmt::Pass(_) => false, - Stmt::Return(_) - | Stmt::For(_) - | Stmt::While(_) - | Stmt::If(_) - | Stmt::With(_) - | Stmt::Match(_) - | Stmt::Raise(_) - | Stmt::Try(_) - | Stmt::Assert(_) - | Stmt::Break(_) - | Stmt::Continue(_) => true, - } -} - -/// Type to create a Mermaid graph. -/// -/// To learn amount Mermaid see , for the syntax -/// see . -struct MermaidGraph<'stmt, 'source> { - graph: &'stmt BasicBlocks<'stmt>, - source: &'source str, -} - -impl<'stmt, 'source> fmt::Display for MermaidGraph<'stmt, 'source> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // Flowchart type of graph, top down. - writeln!(f, "flowchart TD")?; - - // List all blocks. - writeln!(f, " start((\"Start\"))")?; - writeln!(f, " return((\"End\"))")?; - for (i, block) in self.graph.blocks.iter().enumerate() { - let (open, close) = if block.is_sentinel() { - ("[[", "]]") - } else { - ("[", "]") - }; - write!(f, " block{i}{open}\"")?; - if block.is_empty() { - write!(f, "`*(empty)*`")?; - } else if block.is_exception() { - write!(f, "Exception raised")?; - } else { - for stmt in block.stmts { - let code_line = &self.source[stmt.range()].trim(); - mermaid_write_quoted_str(f, code_line)?; - write!(f, "\\n")?; - } - } - writeln!(f, "\"{close}")?; - } - writeln!(f)?; - - // Then link all the blocks. - writeln!(f, " start --> block{}", self.graph.blocks.len() - 1)?; - for (i, block) in self.graph.blocks.iter_enumerated().rev() { - let i = i.as_u32(); - match &block.next { - NextBlock::Always(target) => { - writeln!(f, " block{i} --> block{target}", target = target.as_u32())?; - } - NextBlock::If { - condition, - next, - orelse, - } => { - let condition_code = &self.source[condition.range()].trim(); - writeln!( - f, - " block{i} -- \"{condition_code}\" --> block{next}", - next = next.as_u32() - )?; - writeln!( - f, - " block{i} -- \"else\" --> block{orelse}", - orelse = orelse.as_u32() - )?; - } - NextBlock::Terminate => writeln!(f, " block{i} --> return")?, - } - } - - Ok(()) - } -} - -/// Escape double quotes (`"`) in `value` using `#quot;`. -fn mermaid_write_quoted_str(f: &mut fmt::Formatter<'_>, value: &str) -> fmt::Result { - let mut parts = value.split('"'); - if let Some(v) = parts.next() { - write!(f, "{v}")?; - } - for v in parts { - write!(f, "#quot;{v}")?; - } - Ok(()) -} - -#[cfg(test)] -mod tests { - use std::fs; - use std::path::PathBuf; - - use ruff_python_parser::{parse, Mode}; - use ruff_text_size::Ranged; - use std::fmt::Write; - use test_case::test_case; - - use crate::rules::ruff::rules::unreachable::{ - BasicBlocks, BlockIndex, MermaidGraph, NextBlock, - }; - - #[test_case("simple.py")] - #[test_case("if.py")] - #[test_case("while.py")] - #[test_case("for.py")] - #[test_case("async-for.py")] - //#[test_case("try.py")] // TODO. - #[test_case("raise.py")] - #[test_case("assert.py")] - #[test_case("match.py")] - fn control_flow_graph(filename: &str) { - let path = PathBuf::from_iter(["resources/test/fixtures/control-flow-graph", filename]); - let source = fs::read_to_string(path).expect("failed to read file"); - let stmts = parse(&source, Mode::Module) - .unwrap_or_else(|err| panic!("failed to parse source: '{source}': {err}")) - .expect_module() - .body; - - let mut output = String::new(); - - for (i, stmts) in stmts.into_iter().enumerate() { - let Some(func) = stmts.function_def_stmt() else { - use std::io::Write; - let _ = std::io::stderr().write_all(b"unexpected statement kind, ignoring"); - continue; - }; - - let got = BasicBlocks::from(&*func.body); - // Basic sanity checks. - assert!(!got.blocks.is_empty(), "basic blocks should never be empty"); - assert_eq!( - got.blocks.first().unwrap().next, - NextBlock::Terminate, - "first block should always terminate" - ); - - let got_mermaid = MermaidGraph { - graph: &got, - source: &source, - }; - - // All block index should be valid. - let valid = BlockIndex::from_usize(got.blocks.len()); - for block in &got.blocks { - match block.next { - NextBlock::Always(index) => assert!(index < valid, "invalid block index"), - NextBlock::If { next, orelse, .. } => { - assert!(next < valid, "invalid next block index"); - assert!(orelse < valid, "invalid orelse block index"); - } - NextBlock::Terminate => {} - } - } - - writeln!( - output, - "## Function {i}\n### Source\n```python\n{}\n```\n\n### Control Flow Graph\n```mermaid\n{}```\n", - &source[func.range()], - got_mermaid - ) - .unwrap(); - } - - insta::with_settings!({ - omit_expression => true, - input_file => filename, - description => "This is a Mermaid graph. You can use https://mermaid.live to visualize it as a diagram." - }, { - insta::assert_snapshot!(format!("{filename}.md"), output); - }); - } -} diff --git a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF014_RUF014.py.snap b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF014_RUF014.py.snap deleted file mode 100644 index a30be8cd6e..0000000000 --- a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF014_RUF014.py.snap +++ /dev/null @@ -1,249 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/ruff/mod.rs ---- -RUF014.py:3:5: RUF014 Unreachable code in after_return - | -1 | def after_return(): -2 | return "reachable" -3 | return "unreachable" - | ^^^^^^^^^^^^^^^^^^^^ RUF014 -4 | -5 | async def also_works_on_async_functions(): - | - -RUF014.py:7:5: RUF014 Unreachable code in also_works_on_async_functions - | -5 | async def also_works_on_async_functions(): -6 | return "reachable" -7 | return "unreachable" - | ^^^^^^^^^^^^^^^^^^^^ RUF014 -8 | -9 | def if_always_true(): - | - -RUF014.py:12:5: RUF014 Unreachable code in if_always_true - | -10 | if True: -11 | return "reachable" -12 | return "unreachable" - | ^^^^^^^^^^^^^^^^^^^^ RUF014 -13 | -14 | def if_always_false(): - | - -RUF014.py:16:9: RUF014 Unreachable code in if_always_false - | -14 | def if_always_false(): -15 | if False: -16 | return "unreachable" - | ^^^^^^^^^^^^^^^^^^^^ RUF014 -17 | return "reachable" - | - -RUF014.py:21:9: RUF014 Unreachable code in if_elif_always_false - | -19 | def if_elif_always_false(): -20 | if False: -21 | return "unreachable" - | ^^^^^^^^^^^^^^^^^^^^ RUF014 -22 | elif False: -23 | return "also unreachable" - | - -RUF014.py:23:9: RUF014 Unreachable code in if_elif_always_false - | -21 | return "unreachable" -22 | elif False: -23 | return "also unreachable" - | ^^^^^^^^^^^^^^^^^^^^^^^^^ RUF014 -24 | return "reachable" - | - -RUF014.py:28:9: RUF014 Unreachable code in if_elif_always_true - | -26 | def if_elif_always_true(): -27 | if False: -28 | return "unreachable" - | ^^^^^^^^^^^^^^^^^^^^ RUF014 -29 | elif True: -30 | return "reachable" - | - -RUF014.py:31:5: RUF014 Unreachable code in if_elif_always_true - | -29 | elif True: -30 | return "reachable" -31 | return "also unreachable" - | ^^^^^^^^^^^^^^^^^^^^^^^^^ RUF014 -32 | -33 | def ends_with_if(): - | - -RUF014.py:35:9: RUF014 Unreachable code in ends_with_if - | -33 | def ends_with_if(): -34 | if False: -35 | return "unreachable" - | ^^^^^^^^^^^^^^^^^^^^ RUF014 -36 | else: -37 | return "reachable" - | - -RUF014.py:42:5: RUF014 Unreachable code in infinite_loop - | -40 | while True: -41 | continue -42 | return "unreachable" - | ^^^^^^^^^^^^^^^^^^^^ RUF014 -43 | -44 | ''' TODO: we could determine these, but we don't yet. - | - -RUF014.py:75:5: RUF014 Unreachable code in match_wildcard - | -73 | case _: -74 | return "reachable" -75 | return "unreachable" - | ^^^^^^^^^^^^^^^^^^^^ RUF014 -76 | -77 | def match_case_and_wildcard(status): - | - -RUF014.py:83:5: RUF014 Unreachable code in match_case_and_wildcard - | -81 | case _: -82 | return "reachable" -83 | return "unreachable" - | ^^^^^^^^^^^^^^^^^^^^ RUF014 -84 | -85 | def raise_exception(): - | - -RUF014.py:87:5: RUF014 Unreachable code in raise_exception - | -85 | def raise_exception(): -86 | raise Exception -87 | return "unreachable" - | ^^^^^^^^^^^^^^^^^^^^ RUF014 -88 | -89 | def while_false(): - | - -RUF014.py:91:9: RUF014 Unreachable code in while_false - | -89 | def while_false(): -90 | while False: -91 | return "unreachable" - | ^^^^^^^^^^^^^^^^^^^^ RUF014 -92 | return "reachable" - | - -RUF014.py:96:9: RUF014 Unreachable code in while_false_else - | -94 | def while_false_else(): -95 | while False: -96 | return "unreachable" - | ^^^^^^^^^^^^^^^^^^^^ RUF014 -97 | else: -98 | return "reachable" - | - -RUF014.py:102:9: RUF014 Unreachable code in while_false_else_return - | -100 | def while_false_else_return(): -101 | while False: -102 | return "unreachable" - | ^^^^^^^^^^^^^^^^^^^^ RUF014 -103 | else: -104 | return "reachable" - | - -RUF014.py:105:5: RUF014 Unreachable code in while_false_else_return - | -103 | else: -104 | return "reachable" -105 | return "also unreachable" - | ^^^^^^^^^^^^^^^^^^^^^^^^^ RUF014 -106 | -107 | def while_true(): - | - -RUF014.py:110:5: RUF014 Unreachable code in while_true - | -108 | while True: -109 | return "reachable" -110 | return "unreachable" - | ^^^^^^^^^^^^^^^^^^^^ RUF014 -111 | -112 | def while_true_else(): - | - -RUF014.py:116:9: RUF014 Unreachable code in while_true_else - | -114 | return "reachable" -115 | else: -116 | return "unreachable" - | ^^^^^^^^^^^^^^^^^^^^ RUF014 -117 | -118 | def while_true_else_return(): - | - -RUF014.py:122:9: RUF014 Unreachable code in while_true_else_return - | -120 | return "reachable" -121 | else: -122 | return "unreachable" - | ^^^^^^^^^^^^^^^^^^^^ RUF014 -123 | return "also unreachable" - | - -RUF014.py:123:5: RUF014 Unreachable code in while_true_else_return - | -121 | else: -122 | return "unreachable" -123 | return "also unreachable" - | ^^^^^^^^^^^^^^^^^^^^^^^^^ RUF014 -124 | -125 | def while_false_var_i(): - | - -RUF014.py:128:9: RUF014 Unreachable code in while_false_var_i - | -126 | i = 0 -127 | while False: -128 | i += 1 - | ^^^^^^ RUF014 -129 | return i - | - -RUF014.py:135:5: RUF014 Unreachable code in while_true_var_i - | -133 | while True: -134 | i += 1 -135 | return i - | ^^^^^^^^ RUF014 -136 | -137 | def while_infinite(): - | - -RUF014.py:140:5: RUF014 Unreachable code in while_infinite - | -138 | while True: -139 | pass -140 | return "unreachable" - | ^^^^^^^^^^^^^^^^^^^^ RUF014 -141 | -142 | def while_if_true(): - | - -RUF014.py:146:5: RUF014 Unreachable code in while_if_true - | -144 | if True: -145 | return "reachable" -146 | return "unreachable" - | ^^^^^^^^^^^^^^^^^^^^ RUF014 -147 | -148 | # Test case found in the Bokeh repository that trigger a false positive. - | - - diff --git a/crates/ruff_workspace/src/configuration.rs b/crates/ruff_workspace/src/configuration.rs index b0f5f8bb7c..5325e9ba72 100644 --- a/crates/ruff_workspace/src/configuration.rs +++ b/crates/ruff_workspace/src/configuration.rs @@ -1213,8 +1213,6 @@ mod tests { } .as_rule_table(preview.map(|preview| preview.mode).unwrap_or_default()) .iter_enabled() - // Filter out rule gated behind `#[cfg(feature = "unreachable-code")]`, which is off-by-default - .filter(|rule| rule.noqa_code() != "RUF014") .collect() }