From 7d646a51c588068eda5a64e3b8cba92eecb268b5 Mon Sep 17 00:00:00 2001 From: qdegraaf Date: Mon, 6 Nov 2023 23:40:56 +0100 Subject: [PATCH 01/16] WIP: Extract `get_assigned_value` to method --- .../test/fixtures/flake8_trio/TRIO115.py | 4 + .../flake8_trio/rules/zero_sleep_call.rs | 35 ++--- ...8_trio__tests__TRIO115_TRIO115.py.snap.new | 123 ++++++++++++++++++ .../perflint/rules/unnecessary_list_cast.rs | 33 ++--- .../src/analyze/typing.rs | 51 ++++++++ 5 files changed, 196 insertions(+), 50 deletions(-) create mode 100644 crates/ruff_linter/src/rules/flake8_trio/snapshots/ruff_linter__rules__flake8_trio__tests__TRIO115_TRIO115.py.snap.new diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_trio/TRIO115.py b/crates/ruff_linter/resources/test/fixtures/flake8_trio/TRIO115.py index edfa942ac929b..4392fc66f5800 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_trio/TRIO115.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_trio/TRIO115.py @@ -26,3 +26,7 @@ async def func(): def func(): trio.run(trio.sleep(0)) # TRIO115 + + x, y = 0, 2000 + trio.sleep(x) + trio.sleep(y) \ No newline at end of file diff --git a/crates/ruff_linter/src/rules/flake8_trio/rules/zero_sleep_call.rs b/crates/ruff_linter/src/rules/flake8_trio/rules/zero_sleep_call.rs index 896dc009d76e9..050012c98d506 100644 --- a/crates/ruff_linter/src/rules/flake8_trio/rules/zero_sleep_call.rs +++ b/crates/ruff_linter/src/rules/flake8_trio/rules/zero_sleep_call.rs @@ -1,7 +1,7 @@ use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix}; use ruff_macros::{derive_message_formats, violation}; -use ruff_python_ast::Stmt; use ruff_python_ast::{self as ast, Expr, ExprCall, Int}; +use ruff_python_semantic::analyze::typing::get_assigned_value; use ruff_text_size::Ranged; use crate::checkers::ast::Checker; @@ -65,30 +65,15 @@ pub(crate) fn zero_sleep_call(checker: &mut Checker, call: &ExprCall) { } } Expr::Name(ast::ExprName { id, .. }) => { - let scope = checker.semantic().current_scope(); - if let Some(binding_id) = scope.get(id) { - let binding = checker.semantic().binding(binding_id); - if binding.kind.is_assignment() || binding.kind.is_named_expr_assignment() { - if let Some(parent_id) = binding.source { - let parent = checker.semantic().statement(parent_id); - if let Stmt::Assign(ast::StmtAssign { value, .. }) - | Stmt::AnnAssign(ast::StmtAnnAssign { - value: Some(value), .. - }) - | Stmt::AugAssign(ast::StmtAugAssign { value, .. }) = parent - { - let Expr::NumberLiteral(ast::ExprNumberLiteral { value: num, .. }) = - value.as_ref() - else { - return; - }; - let Some(int) = num.as_int() else { return }; - if *int != Int::ZERO { - return; - } - } - } - } + let Some(value) = get_assigned_value(id, checker.semantic()) else { + return; + }; + let Expr::NumberLiteral(ast::ExprNumberLiteral { value: num, .. }) = value else { + return; + }; + let Some(int) = num.as_int() else { return }; + if *int != Int::ZERO { + return; } } _ => return, diff --git a/crates/ruff_linter/src/rules/flake8_trio/snapshots/ruff_linter__rules__flake8_trio__tests__TRIO115_TRIO115.py.snap.new b/crates/ruff_linter/src/rules/flake8_trio/snapshots/ruff_linter__rules__flake8_trio__tests__TRIO115_TRIO115.py.snap.new new file mode 100644 index 0000000000000..89175d41a01bb --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_trio/snapshots/ruff_linter__rules__flake8_trio__tests__TRIO115_TRIO115.py.snap.new @@ -0,0 +1,123 @@ +--- +source: crates/ruff_linter/src/rules/flake8_trio/mod.rs +assertion_line: 26 +--- +TRIO115.py:6:11: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)` + | +5 | async def func(): +6 | await trio.sleep(0) # TRIO115 + | ^^^^^^^^^^^^^ TRIO115 +7 | await trio.sleep(1) # OK +8 | await trio.sleep(0, 1) # OK + | + = help: Replace with `trio.lowlevel.checkpoint()` + +ℹ Fix +3 3 | +4 4 | +5 5 | async def func(): +6 |- await trio.sleep(0) # TRIO115 + 6 |+ await trio.lowlevel.checkpoint # TRIO115 +7 7 | await trio.sleep(1) # OK +8 8 | await trio.sleep(0, 1) # OK +9 9 | await trio.sleep(...) # OK + +TRIO115.py:12:5: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)` + | +10 | await trio.sleep() # OK +11 | +12 | trio.sleep(0) # TRIO115 + | ^^^^^^^^^^^^^ TRIO115 +13 | foo = 0 +14 | trio.sleep(foo) # TRIO115 + | + = help: Replace with `trio.lowlevel.checkpoint()` + +ℹ Fix +9 9 | await trio.sleep(...) # OK +10 10 | await trio.sleep() # OK +11 11 | +12 |- trio.sleep(0) # TRIO115 + 12 |+ trio.lowlevel.checkpoint # TRIO115 +13 13 | foo = 0 +14 14 | trio.sleep(foo) # TRIO115 +15 15 | trio.sleep(1) # OK + +TRIO115.py:14:5: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)` + | +12 | trio.sleep(0) # TRIO115 +13 | foo = 0 +14 | trio.sleep(foo) # TRIO115 + | ^^^^^^^^^^^^^^^ TRIO115 +15 | trio.sleep(1) # OK +16 | time.sleep(0) # OK + | + = help: Replace with `trio.lowlevel.checkpoint()` + +ℹ Fix +11 11 | +12 12 | trio.sleep(0) # TRIO115 +13 13 | foo = 0 +14 |- trio.sleep(foo) # TRIO115 + 14 |+ trio.lowlevel.checkpoint # TRIO115 +15 15 | trio.sleep(1) # OK +16 16 | time.sleep(0) # OK +17 17 | + +TRIO115.py:18:5: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)` + | +16 | time.sleep(0) # OK +17 | +18 | sleep(0) # TRIO115 + | ^^^^^^^^ TRIO115 + | + = help: Replace with `trio.lowlevel.checkpoint()` + +ℹ Fix +15 15 | trio.sleep(1) # OK +16 16 | time.sleep(0) # OK +17 17 | +18 |- sleep(0) # TRIO115 + 18 |+ trio.lowlevel.checkpoint # TRIO115 +19 19 | +20 20 | +21 21 | trio.sleep(0) # TRIO115 + +TRIO115.py:21:1: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)` + | +21 | trio.sleep(0) # TRIO115 + | ^^^^^^^^^^^^^ TRIO115 + | + = help: Replace with `trio.lowlevel.checkpoint()` + +ℹ Fix +18 18 | sleep(0) # TRIO115 +19 19 | +20 20 | +21 |-trio.sleep(0) # TRIO115 + 21 |+trio.lowlevel.checkpoint # TRIO115 +22 22 | +23 23 | +24 24 | def func(): + +TRIO115.py:25:14: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)` + | +24 | def func(): +25 | trio.run(trio.sleep(0)) # TRIO115 + | ^^^^^^^^^^^^^ TRIO115 +26 | +27 | x, y = 0, 2000 + | + = help: Replace with `trio.lowlevel.checkpoint()` + +ℹ Fix +22 22 | +23 23 | +24 24 | def func(): +25 |- trio.run(trio.sleep(0)) # TRIO115 + 25 |+ trio.run(trio.lowlevel.checkpoint) # TRIO115 +26 26 | +27 27 | x, y = 0, 2000 +28 28 | trio.sleep(x) + + diff --git a/crates/ruff_linter/src/rules/perflint/rules/unnecessary_list_cast.rs b/crates/ruff_linter/src/rules/perflint/rules/unnecessary_list_cast.rs index 9769eed8d71e3..dcce2f2a09c56 100644 --- a/crates/ruff_linter/src/rules/perflint/rules/unnecessary_list_cast.rs +++ b/crates/ruff_linter/src/rules/perflint/rules/unnecessary_list_cast.rs @@ -1,7 +1,7 @@ use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix}; use ruff_macros::{derive_message_formats, violation}; -use ruff_python_ast::Stmt; use ruff_python_ast::{self as ast, Arguments, Expr}; +use ruff_python_semantic::analyze::typing::get_assigned_value; use ruff_text_size::TextRange; use crate::checkers::ast::Checker; @@ -98,30 +98,13 @@ pub(crate) fn unnecessary_list_cast(checker: &mut Checker, iter: &Expr) { range: iterable_range, .. }) => { - let scope = checker.semantic().current_scope(); - if let Some(binding_id) = scope.get(id) { - let binding = checker.semantic().binding(binding_id); - if binding.kind.is_assignment() || binding.kind.is_named_expr_assignment() { - if let Some(parent_id) = binding.source { - let parent = checker.semantic().statement(parent_id); - if let Stmt::Assign(ast::StmtAssign { value, .. }) - | Stmt::AnnAssign(ast::StmtAnnAssign { - value: Some(value), .. - }) - | Stmt::AugAssign(ast::StmtAugAssign { value, .. }) = parent - { - if matches!( - value.as_ref(), - Expr::Tuple(_) | Expr::List(_) | Expr::Set(_) - ) { - let mut diagnostic = - Diagnostic::new(UnnecessaryListCast, *list_range); - diagnostic.set_fix(remove_cast(*list_range, *iterable_range)); - checker.diagnostics.push(diagnostic); - } - } - } - } + let Some(value) = get_assigned_value(id, checker.semantic()) else { + return; + }; + if matches!(value, Expr::Tuple(_) | Expr::List(_) | Expr::Set(_)) { + let mut diagnostic = Diagnostic::new(UnnecessaryListCast, *list_range); + diagnostic.set_fix(remove_cast(*list_range, *iterable_range)); + checker.diagnostics.push(diagnostic); } } _ => {} diff --git a/crates/ruff_python_semantic/src/analyze/typing.rs b/crates/ruff_python_semantic/src/analyze/typing.rs index bd18f70ba8aae..9bbeb9e7711f8 100644 --- a/crates/ruff_python_semantic/src/analyze/typing.rs +++ b/crates/ruff_python_semantic/src/analyze/typing.rs @@ -568,3 +568,54 @@ pub fn resolve_assignment<'a>( _ => None, } } + +/// Get +pub fn get_assigned_value<'a>(id: &str, semantic: &'a SemanticModel<'a>) -> Option<&'a Expr> { + let scope = semantic.current_scope(); + let binding_id = scope.get(id)?; + let binding = semantic.binding(binding_id); + if binding.kind.is_assignment() { + println!("{id}: is_assignment"); + let parent_id = binding.source?; + let parent = semantic.statement(parent_id); + if let Stmt::Assign(ast::StmtAssign { value, .. }) + | Stmt::AnnAssign(ast::StmtAnnAssign { + value: Some(value), .. + }) + | Stmt::AugAssign(ast::StmtAugAssign { value, .. }) = parent + { + println!("{:?}", value); + return Some(value.as_ref()); + } + return None; + } + if binding.kind.is_unpacked_assignment() { + println!("{id}: is_unpacked_assignment"); + let parent_id = binding.source?; + let parent = semantic.statement(parent_id); + if let Stmt::Assign(ast::StmtAssign { value, .. }) + | Stmt::AnnAssign(ast::StmtAnnAssign { + value: Some(value), .. + }) + | Stmt::AugAssign(ast::StmtAugAssign { value, .. }) = parent + { + return Some(value.as_ref()); + } + return None; + } + if binding.kind.is_named_expr_assignment() { + println!("{id}: is_named_expr_assignment"); + let parent_id = binding.source?; + let parent = semantic.statement(parent_id); + if let Stmt::Assign(ast::StmtAssign { value, .. }) + | Stmt::AnnAssign(ast::StmtAnnAssign { + value: Some(value), .. + }) + | Stmt::AugAssign(ast::StmtAugAssign { value, .. }) = parent + { + return Some(value.as_ref()); + } + return None; + } + None +} From c186bbffdb92138716efff0cdc49a45d124d3224 Mon Sep 17 00:00:00 2001 From: qdegraaf Date: Thu, 9 Nov 2023 14:39:45 +0100 Subject: [PATCH 02/16] Expand checks for multi target Assigns, add docstring --- .../test/fixtures/flake8_trio/TRIO115.py | 4 - ...8_trio__tests__TRIO115_TRIO115.py.snap.new | 123 ------------------ .../src/analyze/typing.rs | 96 ++++++++------ 3 files changed, 56 insertions(+), 167 deletions(-) delete mode 100644 crates/ruff_linter/src/rules/flake8_trio/snapshots/ruff_linter__rules__flake8_trio__tests__TRIO115_TRIO115.py.snap.new diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_trio/TRIO115.py b/crates/ruff_linter/resources/test/fixtures/flake8_trio/TRIO115.py index 4392fc66f5800..edfa942ac929b 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_trio/TRIO115.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_trio/TRIO115.py @@ -26,7 +26,3 @@ async def func(): def func(): trio.run(trio.sleep(0)) # TRIO115 - - x, y = 0, 2000 - trio.sleep(x) - trio.sleep(y) \ No newline at end of file diff --git a/crates/ruff_linter/src/rules/flake8_trio/snapshots/ruff_linter__rules__flake8_trio__tests__TRIO115_TRIO115.py.snap.new b/crates/ruff_linter/src/rules/flake8_trio/snapshots/ruff_linter__rules__flake8_trio__tests__TRIO115_TRIO115.py.snap.new deleted file mode 100644 index 89175d41a01bb..0000000000000 --- a/crates/ruff_linter/src/rules/flake8_trio/snapshots/ruff_linter__rules__flake8_trio__tests__TRIO115_TRIO115.py.snap.new +++ /dev/null @@ -1,123 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/flake8_trio/mod.rs -assertion_line: 26 ---- -TRIO115.py:6:11: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)` - | -5 | async def func(): -6 | await trio.sleep(0) # TRIO115 - | ^^^^^^^^^^^^^ TRIO115 -7 | await trio.sleep(1) # OK -8 | await trio.sleep(0, 1) # OK - | - = help: Replace with `trio.lowlevel.checkpoint()` - -ℹ Fix -3 3 | -4 4 | -5 5 | async def func(): -6 |- await trio.sleep(0) # TRIO115 - 6 |+ await trio.lowlevel.checkpoint # TRIO115 -7 7 | await trio.sleep(1) # OK -8 8 | await trio.sleep(0, 1) # OK -9 9 | await trio.sleep(...) # OK - -TRIO115.py:12:5: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)` - | -10 | await trio.sleep() # OK -11 | -12 | trio.sleep(0) # TRIO115 - | ^^^^^^^^^^^^^ TRIO115 -13 | foo = 0 -14 | trio.sleep(foo) # TRIO115 - | - = help: Replace with `trio.lowlevel.checkpoint()` - -ℹ Fix -9 9 | await trio.sleep(...) # OK -10 10 | await trio.sleep() # OK -11 11 | -12 |- trio.sleep(0) # TRIO115 - 12 |+ trio.lowlevel.checkpoint # TRIO115 -13 13 | foo = 0 -14 14 | trio.sleep(foo) # TRIO115 -15 15 | trio.sleep(1) # OK - -TRIO115.py:14:5: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)` - | -12 | trio.sleep(0) # TRIO115 -13 | foo = 0 -14 | trio.sleep(foo) # TRIO115 - | ^^^^^^^^^^^^^^^ TRIO115 -15 | trio.sleep(1) # OK -16 | time.sleep(0) # OK - | - = help: Replace with `trio.lowlevel.checkpoint()` - -ℹ Fix -11 11 | -12 12 | trio.sleep(0) # TRIO115 -13 13 | foo = 0 -14 |- trio.sleep(foo) # TRIO115 - 14 |+ trio.lowlevel.checkpoint # TRIO115 -15 15 | trio.sleep(1) # OK -16 16 | time.sleep(0) # OK -17 17 | - -TRIO115.py:18:5: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)` - | -16 | time.sleep(0) # OK -17 | -18 | sleep(0) # TRIO115 - | ^^^^^^^^ TRIO115 - | - = help: Replace with `trio.lowlevel.checkpoint()` - -ℹ Fix -15 15 | trio.sleep(1) # OK -16 16 | time.sleep(0) # OK -17 17 | -18 |- sleep(0) # TRIO115 - 18 |+ trio.lowlevel.checkpoint # TRIO115 -19 19 | -20 20 | -21 21 | trio.sleep(0) # TRIO115 - -TRIO115.py:21:1: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)` - | -21 | trio.sleep(0) # TRIO115 - | ^^^^^^^^^^^^^ TRIO115 - | - = help: Replace with `trio.lowlevel.checkpoint()` - -ℹ Fix -18 18 | sleep(0) # TRIO115 -19 19 | -20 20 | -21 |-trio.sleep(0) # TRIO115 - 21 |+trio.lowlevel.checkpoint # TRIO115 -22 22 | -23 23 | -24 24 | def func(): - -TRIO115.py:25:14: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)` - | -24 | def func(): -25 | trio.run(trio.sleep(0)) # TRIO115 - | ^^^^^^^^^^^^^ TRIO115 -26 | -27 | x, y = 0, 2000 - | - = help: Replace with `trio.lowlevel.checkpoint()` - -ℹ Fix -22 22 | -23 23 | -24 24 | def func(): -25 |- trio.run(trio.sleep(0)) # TRIO115 - 25 |+ trio.run(trio.lowlevel.checkpoint) # TRIO115 -26 26 | -27 27 | x, y = 0, 2000 -28 28 | trio.sleep(x) - - diff --git a/crates/ruff_python_semantic/src/analyze/typing.rs b/crates/ruff_python_semantic/src/analyze/typing.rs index 9bbeb9e7711f8..ecfa8aa7ab34b 100644 --- a/crates/ruff_python_semantic/src/analyze/typing.rs +++ b/crates/ruff_python_semantic/src/analyze/typing.rs @@ -569,53 +569,69 @@ pub fn resolve_assignment<'a>( } } -/// Get +/// Get the assigned [`Expr`] value of a given id, if any. +/// +/// For example given +/// ```python +/// foo = 42 +/// (bar, bla) = 1, "str" +/// ``` +/// +/// This function will return a `NumberLiteral` with value `Int(42)` when called with `foo` and a +/// `StringLiteral` with value `"str"` when called with `bla`. +/// +/// TODOs: +/// - Handle unpacked assignment +/// - Handle complex assignment e.g. [x, y], (z,) = (1, 2), [3] pub fn get_assigned_value<'a>(id: &str, semantic: &'a SemanticModel<'a>) -> Option<&'a Expr> { let scope = semantic.current_scope(); let binding_id = scope.get(id)?; let binding = semantic.binding(binding_id); - if binding.kind.is_assignment() { - println!("{id}: is_assignment"); - let parent_id = binding.source?; - let parent = semantic.statement(parent_id); - if let Stmt::Assign(ast::StmtAssign { value, .. }) - | Stmt::AnnAssign(ast::StmtAnnAssign { - value: Some(value), .. - }) - | Stmt::AugAssign(ast::StmtAugAssign { value, .. }) = parent - { - println!("{:?}", value); - return Some(value.as_ref()); - } - return None; - } - if binding.kind.is_unpacked_assignment() { - println!("{id}: is_unpacked_assignment"); - let parent_id = binding.source?; - let parent = semantic.statement(parent_id); - if let Stmt::Assign(ast::StmtAssign { value, .. }) - | Stmt::AnnAssign(ast::StmtAnnAssign { - value: Some(value), .. - }) - | Stmt::AugAssign(ast::StmtAugAssign { value, .. }) = parent - { - return Some(value.as_ref()); - } - return None; - } - if binding.kind.is_named_expr_assignment() { - println!("{id}: is_named_expr_assignment"); + if binding.kind.is_assignment() || binding.kind.is_named_expr_assignment() { let parent_id = binding.source?; let parent = semantic.statement(parent_id); - if let Stmt::Assign(ast::StmtAssign { value, .. }) - | Stmt::AnnAssign(ast::StmtAnnAssign { - value: Some(value), .. - }) - | Stmt::AugAssign(ast::StmtAugAssign { value, .. }) = parent - { - return Some(value.as_ref()); + match parent { + Stmt::Assign(ast::StmtAssign { value, targets, .. }) => { + match value.as_ref() { + Expr::Tuple(ast::ExprTuple { elts, .. }) + | Expr::List(ast::ExprList { elts, .. }) + | Expr::Set(ast::ExprSet { elts, .. }) => { + let Some(first_target) = targets.first() else { + return None; + }; + match first_target { + Expr::Tuple(ast::ExprTuple { + elts: target_elts, .. + }) + | Expr::List(ast::ExprList { + elts: target_elts, .. + }) + | Expr::Set(ast::ExprSet { + elts: target_elts, .. + }) => { + if let Some(index) = target_elts.iter().position(|x| { + if let Expr::Name(ast::ExprName { id: target_id, .. }) = x { + return target_id == id; + } + false + }) { + return elts.get(index); + } + } + _ => return Some(value.as_ref()), + } + } + _ => return Some(value.as_ref()), + } + } + Stmt::AnnAssign(ast::StmtAnnAssign { + value: Some(value), .. + }) + | Stmt::AugAssign(ast::StmtAugAssign { value, .. }) => { + return Some(value.as_ref()); + } + _ => return None, } - return None; } None } From d231533d68686a6708596a3bdf2386017bb0719b Mon Sep 17 00:00:00 2001 From: qdegraaf Date: Thu, 9 Nov 2023 14:41:10 +0100 Subject: [PATCH 03/16] Add test for multi target --- .../test/fixtures/flake8_trio/TRIO115.py | 4 ++ ...lake8_trio__tests__TRIO115_TRIO115.py.snap | 44 +++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_trio/TRIO115.py b/crates/ruff_linter/resources/test/fixtures/flake8_trio/TRIO115.py index edfa942ac929b..7676dfae6edea 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_trio/TRIO115.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_trio/TRIO115.py @@ -19,6 +19,10 @@ async def func(): bar = "bar" trio.sleep(bar) + + x, y = 0, 2000 + trio.sleep(x) # TRIO115 + trio.sleep(y) # OK trio.sleep(0) # TRIO115 diff --git a/crates/ruff_linter/src/rules/flake8_trio/snapshots/ruff_linter__rules__flake8_trio__tests__TRIO115_TRIO115.py.snap b/crates/ruff_linter/src/rules/flake8_trio/snapshots/ruff_linter__rules__flake8_trio__tests__TRIO115_TRIO115.py.snap index 6a8c00d806204..650d2a4503481 100644 --- a/crates/ruff_linter/src/rules/flake8_trio/snapshots/ruff_linter__rules__flake8_trio__tests__TRIO115_TRIO115.py.snap +++ b/crates/ruff_linter/src/rules/flake8_trio/snapshots/ruff_linter__rules__flake8_trio__tests__TRIO115_TRIO115.py.snap @@ -71,6 +71,8 @@ TRIO115.py:18:5: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.s | ^^^^^^^^ TRIO115 19 | 20 | bar = "bar" +19 | +20 | x, y = 0, 2000 | = help: Replace with `trio.lowlevel.checkpoint()` @@ -83,29 +85,47 @@ TRIO115.py:18:5: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.s 19 19 | 20 20 | bar = "bar" 21 21 | trio.sleep(bar) +20 20 | x, y = 0, 2000 +21 21 | trio.sleep(x) # TRIO115 TRIO115.py:24:1: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)` +TRIO115.py:21:5: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)` | 24 | trio.sleep(0) # TRIO115 | ^^^^^^^^^^^^^ TRIO115 +20 | x, y = 0, 2000 +21 | trio.sleep(x) # TRIO115 + | ^^^^^^^^^^^^^ TRIO115 +22 | trio.sleep(y) # OK | = help: Replace with `trio.lowlevel.checkpoint()` ℹ Safe fix 21 21 | trio.sleep(bar) 22 22 | +ℹ Fix +18 18 | sleep(0) # TRIO115 +19 19 | +20 20 | x, y = 0, 2000 +21 |- trio.sleep(x) # TRIO115 + 21 |+ trio.lowlevel.checkpoint # TRIO115 +22 22 | trio.sleep(y) # OK 23 23 | 24 |-trio.sleep(0) # TRIO115 24 |+trio.lowlevel.checkpoint # TRIO115 25 25 | 26 26 | 27 27 | def func(): +24 24 | TRIO115.py:28:14: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)` +TRIO115.py:25:1: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)` | 27 | def func(): 28 | trio.run(trio.sleep(0)) # TRIO115 | ^^^^^^^^^^^^^ TRIO115 +25 | trio.sleep(0) # TRIO115 + | ^^^^^^^^^^^^^ TRIO115 | = help: Replace with `trio.lowlevel.checkpoint()` @@ -115,5 +135,29 @@ TRIO115.py:28:14: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio. 27 27 | def func(): 28 |- trio.run(trio.sleep(0)) # TRIO115 28 |+ trio.run(trio.lowlevel.checkpoint) # TRIO115 +ℹ Fix +22 22 | trio.sleep(y) # OK +23 23 | +24 24 | +25 |-trio.sleep(0) # TRIO115 + 25 |+trio.lowlevel.checkpoint # TRIO115 +26 26 | +27 27 | +28 28 | def func(): + +TRIO115.py:29:14: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)` + | +28 | def func(): +29 | trio.run(trio.sleep(0)) # TRIO115 + | ^^^^^^^^^^^^^ TRIO115 + | + = help: Replace with `trio.lowlevel.checkpoint()` + +ℹ Fix +26 26 | +27 27 | +28 28 | def func(): +29 |- trio.run(trio.sleep(0)) # TRIO115 + 29 |+ trio.run(trio.lowlevel.checkpoint) # TRIO115 From ec7d9bcbca0a4276c9ce6e4631f06fbd70a5eda4 Mon Sep 17 00:00:00 2001 From: qdegraaf Date: Thu, 9 Nov 2023 14:51:37 +0100 Subject: [PATCH 04/16] cargo test --- ...lake8_trio__tests__TRIO115_TRIO115.py.snap | 79 +++++++------------ 1 file changed, 27 insertions(+), 52 deletions(-) diff --git a/crates/ruff_linter/src/rules/flake8_trio/snapshots/ruff_linter__rules__flake8_trio__tests__TRIO115_TRIO115.py.snap b/crates/ruff_linter/src/rules/flake8_trio/snapshots/ruff_linter__rules__flake8_trio__tests__TRIO115_TRIO115.py.snap index 650d2a4503481..a72f98d1f569c 100644 --- a/crates/ruff_linter/src/rules/flake8_trio/snapshots/ruff_linter__rules__flake8_trio__tests__TRIO115_TRIO115.py.snap +++ b/crates/ruff_linter/src/rules/flake8_trio/snapshots/ruff_linter__rules__flake8_trio__tests__TRIO115_TRIO115.py.snap @@ -71,8 +71,6 @@ TRIO115.py:18:5: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.s | ^^^^^^^^ TRIO115 19 | 20 | bar = "bar" -19 | -20 | x, y = 0, 2000 | = help: Replace with `trio.lowlevel.checkpoint()` @@ -85,79 +83,56 @@ TRIO115.py:18:5: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.s 19 19 | 20 20 | bar = "bar" 21 21 | trio.sleep(bar) -20 20 | x, y = 0, 2000 -21 21 | trio.sleep(x) # TRIO115 -TRIO115.py:24:1: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)` -TRIO115.py:21:5: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)` +TRIO115.py:24:5: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)` | -24 | trio.sleep(0) # TRIO115 - | ^^^^^^^^^^^^^ TRIO115 -20 | x, y = 0, 2000 -21 | trio.sleep(x) # TRIO115 +23 | x, y = 0, 2000 +24 | trio.sleep(x) # TRIO115 | ^^^^^^^^^^^^^ TRIO115 -22 | trio.sleep(y) # OK +25 | trio.sleep(y) # OK | = help: Replace with `trio.lowlevel.checkpoint()` ℹ Safe fix 21 21 | trio.sleep(bar) -22 22 | -ℹ Fix -18 18 | sleep(0) # TRIO115 -19 19 | -20 20 | x, y = 0, 2000 -21 |- trio.sleep(x) # TRIO115 - 21 |+ trio.lowlevel.checkpoint # TRIO115 -22 22 | trio.sleep(y) # OK -23 23 | -24 |-trio.sleep(0) # TRIO115 - 24 |+trio.lowlevel.checkpoint # TRIO115 -25 25 | +22 22 | +23 23 | x, y = 0, 2000 +24 |- trio.sleep(x) # TRIO115 + 24 |+ trio.lowlevel.checkpoint # TRIO115 +25 25 | trio.sleep(y) # OK 26 26 | -27 27 | def func(): -24 24 | +27 27 | -TRIO115.py:28:14: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)` -TRIO115.py:25:1: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)` +TRIO115.py:28:1: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)` | -27 | def func(): -28 | trio.run(trio.sleep(0)) # TRIO115 - | ^^^^^^^^^^^^^ TRIO115 -25 | trio.sleep(0) # TRIO115 +28 | trio.sleep(0) # TRIO115 | ^^^^^^^^^^^^^ TRIO115 | = help: Replace with `trio.lowlevel.checkpoint()` ℹ Safe fix -25 25 | -26 26 | -27 27 | def func(): -28 |- trio.run(trio.sleep(0)) # TRIO115 - 28 |+ trio.run(trio.lowlevel.checkpoint) # TRIO115 -ℹ Fix -22 22 | trio.sleep(y) # OK -23 23 | -24 24 | -25 |-trio.sleep(0) # TRIO115 - 25 |+trio.lowlevel.checkpoint # TRIO115 +25 25 | trio.sleep(y) # OK 26 26 | 27 27 | -28 28 | def func(): +28 |-trio.sleep(0) # TRIO115 + 28 |+trio.lowlevel.checkpoint # TRIO115 +29 29 | +30 30 | +31 31 | def func(): -TRIO115.py:29:14: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)` +TRIO115.py:32:14: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)` | -28 | def func(): -29 | trio.run(trio.sleep(0)) # TRIO115 +31 | def func(): +32 | trio.run(trio.sleep(0)) # TRIO115 | ^^^^^^^^^^^^^ TRIO115 | = help: Replace with `trio.lowlevel.checkpoint()` -ℹ Fix -26 26 | -27 27 | -28 28 | def func(): -29 |- trio.run(trio.sleep(0)) # TRIO115 - 29 |+ trio.run(trio.lowlevel.checkpoint) # TRIO115 +ℹ Safe fix +29 29 | +30 30 | +31 31 | def func(): +32 |- trio.run(trio.sleep(0)) # TRIO115 + 32 |+ trio.run(trio.lowlevel.checkpoint) # TRIO115 From b96e21c1c37c906c005ba94d5e5ffdd92f85d2e5 Mon Sep 17 00:00:00 2001 From: qdegraaf Date: Thu, 9 Nov 2023 23:51:31 +0100 Subject: [PATCH 05/16] fmt --- .../src/analyze/typing.rs | 54 +++++++++---------- 1 file changed, 26 insertions(+), 28 deletions(-) diff --git a/crates/ruff_python_semantic/src/analyze/typing.rs b/crates/ruff_python_semantic/src/analyze/typing.rs index ecfa8aa7ab34b..8fc98a902805d 100644 --- a/crates/ruff_python_semantic/src/analyze/typing.rs +++ b/crates/ruff_python_semantic/src/analyze/typing.rs @@ -591,39 +591,37 @@ pub fn get_assigned_value<'a>(id: &str, semantic: &'a SemanticModel<'a>) -> Opti let parent_id = binding.source?; let parent = semantic.statement(parent_id); match parent { - Stmt::Assign(ast::StmtAssign { value, targets, .. }) => { - match value.as_ref() { - Expr::Tuple(ast::ExprTuple { elts, .. }) - | Expr::List(ast::ExprList { elts, .. }) - | Expr::Set(ast::ExprSet { elts, .. }) => { - let Some(first_target) = targets.first() else { - return None; - }; - match first_target { - Expr::Tuple(ast::ExprTuple { - elts: target_elts, .. - }) - | Expr::List(ast::ExprList { - elts: target_elts, .. - }) - | Expr::Set(ast::ExprSet { - elts: target_elts, .. - }) => { - if let Some(index) = target_elts.iter().position(|x| { - if let Expr::Name(ast::ExprName { id: target_id, .. }) = x { - return target_id == id; - } - false - }) { - return elts.get(index); + Stmt::Assign(ast::StmtAssign { value, targets, .. }) => match value.as_ref() { + Expr::Tuple(ast::ExprTuple { elts, .. }) + | Expr::List(ast::ExprList { elts, .. }) + | Expr::Set(ast::ExprSet { elts, .. }) => { + let Some(first_target) = targets.first() else { + return None; + }; + match first_target { + Expr::Tuple(ast::ExprTuple { + elts: target_elts, .. + }) + | Expr::List(ast::ExprList { + elts: target_elts, .. + }) + | Expr::Set(ast::ExprSet { + elts: target_elts, .. + }) => { + if let Some(index) = target_elts.iter().position(|x| { + if let Expr::Name(ast::ExprName { id: target_id, .. }) = x { + return target_id == id; } + false + }) { + return elts.get(index); } - _ => return Some(value.as_ref()), } + _ => return Some(value.as_ref()), } - _ => return Some(value.as_ref()), } - } + _ => return Some(value.as_ref()), + }, Stmt::AnnAssign(ast::StmtAnnAssign { value: Some(value), .. }) From a40909f022e045ab40a147b40b658c41cc87f6b4 Mon Sep 17 00:00:00 2001 From: qdegraaf Date: Thu, 9 Nov 2023 23:58:24 +0100 Subject: [PATCH 06/16] Fix docs --- crates/ruff_python_semantic/src/analyze/typing.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ruff_python_semantic/src/analyze/typing.rs b/crates/ruff_python_semantic/src/analyze/typing.rs index 8fc98a902805d..abb92ce22efe9 100644 --- a/crates/ruff_python_semantic/src/analyze/typing.rs +++ b/crates/ruff_python_semantic/src/analyze/typing.rs @@ -582,7 +582,7 @@ pub fn resolve_assignment<'a>( /// /// TODOs: /// - Handle unpacked assignment -/// - Handle complex assignment e.g. [x, y], (z,) = (1, 2), [3] +/// - Handle complex assignment e.g. `[x, y], (z,) = (1, 2), [3]` pub fn get_assigned_value<'a>(id: &str, semantic: &'a SemanticModel<'a>) -> Option<&'a Expr> { let scope = semantic.current_scope(); let binding_id = scope.get(id)?; From 1084c116f3ba3be2310724a2982063d987edcfbf Mon Sep 17 00:00:00 2001 From: qdegraaf Date: Mon, 13 Nov 2023 13:36:04 +0100 Subject: [PATCH 07/16] Make the get value func recursive --- .../test/fixtures/flake8_trio/TRIO115.py | 7 +- .../test/fixtures/perflint/PERF101.py | 6 ++ ...lake8_trio__tests__TRIO115_TRIO115.py.snap | 77 ++++++++++++----- ...__perflint__tests__PERF101_PERF101.py.snap | 18 ++++ .../src/analyze/typing.rs | 85 +++++++++++++------ 5 files changed, 146 insertions(+), 47 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_trio/TRIO115.py b/crates/ruff_linter/resources/test/fixtures/flake8_trio/TRIO115.py index 7676dfae6edea..757c228e32c05 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_trio/TRIO115.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_trio/TRIO115.py @@ -19,11 +19,16 @@ async def func(): bar = "bar" trio.sleep(bar) - + x, y = 0, 2000 trio.sleep(x) # TRIO115 trio.sleep(y) # OK + (a, b, [c, (d, e)]) = (1, 2, (0, [4, 0])) + trio.sleep(c) # TRIO115 + trio.sleep(d) # OK + trio.sleep(e) # TRIO115 + trio.sleep(0) # TRIO115 diff --git a/crates/ruff_linter/resources/test/fixtures/perflint/PERF101.py b/crates/ruff_linter/resources/test/fixtures/perflint/PERF101.py index 6123e6d6526bf..6003366367411 100644 --- a/crates/ruff_linter/resources/test/fixtures/perflint/PERF101.py +++ b/crates/ruff_linter/resources/test/fixtures/perflint/PERF101.py @@ -50,3 +50,9 @@ for i in itertools.product(foo_int): # Ok pass + + +x, y, nested_tuple = (1, 2, (3, 4, 5)) + +for i in list(nested_tuple): # PERF101 + pass \ No newline at end of file diff --git a/crates/ruff_linter/src/rules/flake8_trio/snapshots/ruff_linter__rules__flake8_trio__tests__TRIO115_TRIO115.py.snap b/crates/ruff_linter/src/rules/flake8_trio/snapshots/ruff_linter__rules__flake8_trio__tests__TRIO115_TRIO115.py.snap index a72f98d1f569c..8f2dd7be224f7 100644 --- a/crates/ruff_linter/src/rules/flake8_trio/snapshots/ruff_linter__rules__flake8_trio__tests__TRIO115_TRIO115.py.snap +++ b/crates/ruff_linter/src/rules/flake8_trio/snapshots/ruff_linter__rules__flake8_trio__tests__TRIO115_TRIO115.py.snap @@ -95,44 +95,83 @@ TRIO115.py:24:5: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.s ℹ Safe fix 21 21 | trio.sleep(bar) -22 22 | +22 22 | 23 23 | x, y = 0, 2000 24 |- trio.sleep(x) # TRIO115 24 |+ trio.lowlevel.checkpoint # TRIO115 25 25 | trio.sleep(y) # OK 26 26 | -27 27 | +27 27 | (a, b, [c, (d, e)]) = (1, 2, (0, [4, 0])) -TRIO115.py:28:1: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)` +TRIO115.py:28:5: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)` | -28 | trio.sleep(0) # TRIO115 - | ^^^^^^^^^^^^^ TRIO115 +27 | (a, b, [c, (d, e)]) = (1, 2, (0, [4, 0])) +28 | trio.sleep(c) # TRIO115 + | ^^^^^^^^^^^^^ TRIO115 +29 | trio.sleep(d) # OK +30 | trio.sleep(e) # TRIO115 | = help: Replace with `trio.lowlevel.checkpoint()` ℹ Safe fix 25 25 | trio.sleep(y) # OK 26 26 | -27 27 | -28 |-trio.sleep(0) # TRIO115 - 28 |+trio.lowlevel.checkpoint # TRIO115 -29 29 | -30 30 | -31 31 | def func(): +27 27 | (a, b, [c, (d, e)]) = (1, 2, (0, [4, 0])) +28 |- trio.sleep(c) # TRIO115 + 28 |+ trio.lowlevel.checkpoint # TRIO115 +29 29 | trio.sleep(d) # OK +30 30 | trio.sleep(e) # TRIO115 +31 31 | + +TRIO115.py:30:5: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)` + | +28 | trio.sleep(c) # TRIO115 +29 | trio.sleep(d) # OK +30 | trio.sleep(e) # TRIO115 + | ^^^^^^^^^^^^^ TRIO115 + | + = help: Replace with `trio.lowlevel.checkpoint()` + +ℹ Safe fix +27 27 | (a, b, [c, (d, e)]) = (1, 2, (0, [4, 0])) +28 28 | trio.sleep(c) # TRIO115 +29 29 | trio.sleep(d) # OK +30 |- trio.sleep(e) # TRIO115 + 30 |+ trio.lowlevel.checkpoint # TRIO115 +31 31 | +32 32 | +33 33 | trio.sleep(0) # TRIO115 + +TRIO115.py:33:1: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)` + | +33 | trio.sleep(0) # TRIO115 + | ^^^^^^^^^^^^^ TRIO115 + | + = help: Replace with `trio.lowlevel.checkpoint()` + +ℹ Safe fix +30 30 | trio.sleep(e) # TRIO115 +31 31 | +32 32 | +33 |-trio.sleep(0) # TRIO115 + 33 |+trio.lowlevel.checkpoint # TRIO115 +34 34 | +35 35 | +36 36 | def func(): -TRIO115.py:32:14: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)` +TRIO115.py:37:14: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)` | -31 | def func(): -32 | trio.run(trio.sleep(0)) # TRIO115 +36 | def func(): +37 | trio.run(trio.sleep(0)) # TRIO115 | ^^^^^^^^^^^^^ TRIO115 | = help: Replace with `trio.lowlevel.checkpoint()` ℹ Safe fix -29 29 | -30 30 | -31 31 | def func(): -32 |- trio.run(trio.sleep(0)) # TRIO115 - 32 |+ trio.run(trio.lowlevel.checkpoint) # TRIO115 +34 34 | +35 35 | +36 36 | def func(): +37 |- trio.run(trio.sleep(0)) # TRIO115 + 37 |+ trio.run(trio.lowlevel.checkpoint) # TRIO115 diff --git a/crates/ruff_linter/src/rules/perflint/snapshots/ruff_linter__rules__perflint__tests__PERF101_PERF101.py.snap b/crates/ruff_linter/src/rules/perflint/snapshots/ruff_linter__rules__perflint__tests__PERF101_PERF101.py.snap index 645b5e5177784..fff94ee3fb3e8 100644 --- a/crates/ruff_linter/src/rules/perflint/snapshots/ruff_linter__rules__perflint__tests__PERF101_PERF101.py.snap +++ b/crates/ruff_linter/src/rules/perflint/snapshots/ruff_linter__rules__perflint__tests__PERF101_PERF101.py.snap @@ -180,4 +180,22 @@ PERF101.py:34:10: PERF101 [*] Do not cast an iterable to `list` before iterating 38 36 | 39 37 | for i in list(foo_dict): # Ok +PERF101.py:57:10: PERF101 [*] Do not cast an iterable to `list` before iterating over it + | +55 | x, y, nested_tuple = (1, 2, (3, 4, 5)) +56 | +57 | for i in list(nested_tuple): # PERF101 + | ^^^^^^^^^^^^^^^^^^ PERF101 +58 | pass + | + = help: Remove `list()` cast + +ℹ Safe fix +54 54 | +55 55 | x, y, nested_tuple = (1, 2, (3, 4, 5)) +56 56 | +57 |-for i in list(nested_tuple): # PERF101 + 57 |+for i in nested_tuple: # PERF101 +58 58 | pass + diff --git a/crates/ruff_python_semantic/src/analyze/typing.rs b/crates/ruff_python_semantic/src/analyze/typing.rs index abb92ce22efe9..835430f360b8f 100644 --- a/crates/ruff_python_semantic/src/analyze/typing.rs +++ b/crates/ruff_python_semantic/src/analyze/typing.rs @@ -579,10 +579,6 @@ pub fn resolve_assignment<'a>( /// /// This function will return a `NumberLiteral` with value `Int(42)` when called with `foo` and a /// `StringLiteral` with value `"str"` when called with `bla`. -/// -/// TODOs: -/// - Handle unpacked assignment -/// - Handle complex assignment e.g. `[x, y], (z,) = (1, 2), [3]` pub fn get_assigned_value<'a>(id: &str, semantic: &'a SemanticModel<'a>) -> Option<&'a Expr> { let scope = semantic.current_scope(); let binding_id = scope.get(id)?; @@ -595,29 +591,19 @@ pub fn get_assigned_value<'a>(id: &str, semantic: &'a SemanticModel<'a>) -> Opti Expr::Tuple(ast::ExprTuple { elts, .. }) | Expr::List(ast::ExprList { elts, .. }) | Expr::Set(ast::ExprSet { elts, .. }) => { - let Some(first_target) = targets.first() else { - return None; - }; - match first_target { - Expr::Tuple(ast::ExprTuple { - elts: target_elts, .. - }) - | Expr::List(ast::ExprList { - elts: target_elts, .. - }) - | Expr::Set(ast::ExprSet { - elts: target_elts, .. - }) => { - if let Some(index) = target_elts.iter().position(|x| { - if let Expr::Name(ast::ExprName { id: target_id, .. }) = x { - return target_id == id; - } - false - }) { - return elts.get(index); - } - } - _ => return Some(value.as_ref()), + if let Some(target) = targets.iter().next() { + return match target { + Expr::Tuple(ast::ExprTuple { + elts: target_elts, .. + }) + | Expr::List(ast::ExprList { + elts: target_elts, .. + }) + | Expr::Set(ast::ExprSet { + elts: target_elts, .. + }) => get_value_by_id(id, target_elts, elts), + _ => Some(value.as_ref()), + }; } } _ => return Some(value.as_ref()), @@ -633,3 +619,48 @@ pub fn get_assigned_value<'a>(id: &str, semantic: &'a SemanticModel<'a>) -> Opti } None } + +fn get_value_by_id<'a>( + target_id: &str, + targets: &'a [Expr], + values: &'a [Expr], +) -> Option<&'a Expr> { + for (target, value) in targets.iter().zip(values.iter()) { + match target { + Expr::Tuple(ast::ExprTuple { + elts: target_elts, .. + }) + | Expr::List(ast::ExprList { + elts: target_elts, .. + }) + | Expr::Set(ast::ExprSet { + elts: target_elts, .. + }) => { + // Collection types can be mismatched like in: (a, b, [c, d]) = [1, 2, {3, 4}] + match value { + Expr::Tuple(ast::ExprTuple { + elts: value_elts, .. + }) + | Expr::List(ast::ExprList { + elts: value_elts, .. + }) + | Expr::Set(ast::ExprSet { + elts: value_elts, .. + }) => { + if let Some(result) = get_value_by_id(target_id, target_elts, value_elts) { + return Some(result); + } + } + _ => (), + }; + } + Expr::Name(ast::ExprName { id, .. }) => { + if *id == target_id { + return Some(value); + } + } + _ => (), + } + } + None +} From bfdf6e6d1c15a81dc7cc255ca3f5c96c885d519f Mon Sep 17 00:00:00 2001 From: qdegraaf Date: Mon, 13 Nov 2023 13:39:22 +0100 Subject: [PATCH 08/16] Add \n --- crates/ruff_linter/resources/test/fixtures/perflint/PERF101.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ruff_linter/resources/test/fixtures/perflint/PERF101.py b/crates/ruff_linter/resources/test/fixtures/perflint/PERF101.py index 6003366367411..025845250821b 100644 --- a/crates/ruff_linter/resources/test/fixtures/perflint/PERF101.py +++ b/crates/ruff_linter/resources/test/fixtures/perflint/PERF101.py @@ -55,4 +55,4 @@ x, y, nested_tuple = (1, 2, (3, 4, 5)) for i in list(nested_tuple): # PERF101 - pass \ No newline at end of file + pass From 214237100242c70a53ecf8ab00996a61c2c47e19 Mon Sep 17 00:00:00 2001 From: qdegraaf Date: Tue, 14 Nov 2023 11:01:50 +0100 Subject: [PATCH 09/16] Split off StmtAugAssign branch --- crates/ruff_python_semantic/src/analyze/typing.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/crates/ruff_python_semantic/src/analyze/typing.rs b/crates/ruff_python_semantic/src/analyze/typing.rs index 835430f360b8f..bb8e1b065838a 100644 --- a/crates/ruff_python_semantic/src/analyze/typing.rs +++ b/crates/ruff_python_semantic/src/analyze/typing.rs @@ -610,10 +610,12 @@ pub fn get_assigned_value<'a>(id: &str, semantic: &'a SemanticModel<'a>) -> Opti }, Stmt::AnnAssign(ast::StmtAnnAssign { value: Some(value), .. - }) - | Stmt::AugAssign(ast::StmtAugAssign { value, .. }) => { + }) => { return Some(value.as_ref()); } + Stmt::AugAssign(_) => { + return None + } _ => return None, } } From 6baa7be96fc9aabf6c567619026d9cd5894aa801 Mon Sep 17 00:00:00 2001 From: qdegraaf Date: Tue, 14 Nov 2023 16:05:59 +0100 Subject: [PATCH 10/16] cargo fmt --- crates/ruff_python_semantic/src/analyze/typing.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/crates/ruff_python_semantic/src/analyze/typing.rs b/crates/ruff_python_semantic/src/analyze/typing.rs index bb8e1b065838a..cd792635826d9 100644 --- a/crates/ruff_python_semantic/src/analyze/typing.rs +++ b/crates/ruff_python_semantic/src/analyze/typing.rs @@ -613,9 +613,7 @@ pub fn get_assigned_value<'a>(id: &str, semantic: &'a SemanticModel<'a>) -> Opti }) => { return Some(value.as_ref()); } - Stmt::AugAssign(_) => { - return None - } + Stmt::AugAssign(_) => return None, _ => return None, } } From 731b90be6de864d113758f3eeb221a4cd0364fd0 Mon Sep 17 00:00:00 2001 From: qdegraaf Date: Wed, 22 Nov 2023 11:15:47 +0100 Subject: [PATCH 11/16] Add multi target assign example to TRIO115 --- .../test/fixtures/flake8_trio/TRIO115.py | 6 ++ ...lake8_trio__tests__TRIO115_TRIO115.py.snap | 66 +++++++++++++++---- 2 files changed, 59 insertions(+), 13 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_trio/TRIO115.py b/crates/ruff_linter/resources/test/fixtures/flake8_trio/TRIO115.py index dcf3bb8212f31..d1784a252677a 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_trio/TRIO115.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_trio/TRIO115.py @@ -28,6 +28,12 @@ async def func(): trio.sleep(d) # OK trio.sleep(e) # TRIO115 + m_x, m_y = 0 + trio.sleep(m_y) + trio.sleep(m_x) + + + def func(): trio.run(trio.sleep(0)) # TRIO115 diff --git a/crates/ruff_linter/src/rules/flake8_trio/snapshots/ruff_linter__rules__flake8_trio__tests__TRIO115_TRIO115.py.snap b/crates/ruff_linter/src/rules/flake8_trio/snapshots/ruff_linter__rules__flake8_trio__tests__TRIO115_TRIO115.py.snap index abc6d41cbaac7..5c24baeb5c6b1 100644 --- a/crates/ruff_linter/src/rules/flake8_trio/snapshots/ruff_linter__rules__flake8_trio__tests__TRIO115_TRIO115.py.snap +++ b/crates/ruff_linter/src/rules/flake8_trio/snapshots/ruff_linter__rules__flake8_trio__tests__TRIO115_TRIO115.py.snap @@ -130,6 +130,8 @@ TRIO115.py:29:5: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.s 28 | trio.sleep(d) # OK 29 | trio.sleep(e) # TRIO115 | ^^^^^^^^^^^^^ TRIO115 +30 | +31 | m_x, m_y = 0 | = help: Replace with `trio.lowlevel.checkpoint()` @@ -140,26 +142,64 @@ TRIO115.py:29:5: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.s 29 |- trio.sleep(e) # TRIO115 29 |+ trio.lowlevel.checkpoint # TRIO115 30 30 | -31 31 | -32 32 | def func(): +31 31 | m_x, m_y = 0 +32 32 | trio.sleep(m_y) -TRIO115.py:39:5: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)` +TRIO115.py:32:5: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)` | -38 | def func(): -39 | sleep(0) # TRIO115 - | ^^^^^^^^ TRIO115 +31 | m_x, m_y = 0 +32 | trio.sleep(m_y) + | ^^^^^^^^^^^^^^^ TRIO115 +33 | trio.sleep(m_x) | = help: Replace with `trio.lowlevel.checkpoint()` ℹ Safe fix -33 33 | trio.run(trio.sleep(0)) # TRIO115 +29 29 | trio.sleep(e) # TRIO115 +30 30 | +31 31 | m_x, m_y = 0 +32 |- trio.sleep(m_y) + 32 |+ trio.lowlevel.checkpoint +33 33 | trio.sleep(m_x) 34 34 | 35 35 | -36 |-from trio import Event, sleep - 36 |+from trio import Event, sleep, lowlevel -37 37 | -38 38 | def func(): -39 |- sleep(0) # TRIO115 - 39 |+ lowlevel.checkpoint # TRIO115 + +TRIO115.py:33:5: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)` + | +31 | m_x, m_y = 0 +32 | trio.sleep(m_y) +33 | trio.sleep(m_x) + | ^^^^^^^^^^^^^^^ TRIO115 + | + = help: Replace with `trio.lowlevel.checkpoint()` + +ℹ Safe fix +30 30 | +31 31 | m_x, m_y = 0 +32 32 | trio.sleep(m_y) +33 |- trio.sleep(m_x) + 33 |+ trio.lowlevel.checkpoint +34 34 | +35 35 | +36 36 | + +TRIO115.py:45:5: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)` + | +44 | def func(): +45 | sleep(0) # TRIO115 + | ^^^^^^^^ TRIO115 + | + = help: Replace with `trio.lowlevel.checkpoint()` + +ℹ Safe fix +39 39 | trio.run(trio.sleep(0)) # TRIO115 +40 40 | +41 41 | +42 |-from trio import Event, sleep + 42 |+from trio import Event, sleep, lowlevel +43 43 | +44 44 | def func(): +45 |- sleep(0) # TRIO115 + 45 |+ lowlevel.checkpoint # TRIO115 From 71fdb5080b535bfbc2cff7d2409a80b8232bec3f Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Wed, 22 Nov 2023 18:40:23 +0000 Subject: [PATCH 12/16] Add repeated assignment test --- .../test/fixtures/flake8_trio/TRIO115.py | 9 +- ...lake8_trio__tests__TRIO115_TRIO115.py.snap | 91 ++++++++++++++----- 2 files changed, 72 insertions(+), 28 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_trio/TRIO115.py b/crates/ruff_linter/resources/test/fixtures/flake8_trio/TRIO115.py index d1784a252677a..5c5f7acf58832 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_trio/TRIO115.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_trio/TRIO115.py @@ -29,10 +29,12 @@ async def func(): trio.sleep(e) # TRIO115 m_x, m_y = 0 - trio.sleep(m_y) - trio.sleep(m_x) - + trio.sleep(m_y) # TRIO115 + trio.sleep(m_x) # TRIO115 + m_a = m_b = 0 + trio.sleep(m_a) # TRIO115 + trio.sleep(m_b) # TRIO115 def func(): @@ -41,5 +43,6 @@ def func(): from trio import Event, sleep + def func(): sleep(0) # TRIO115 diff --git a/crates/ruff_linter/src/rules/flake8_trio/snapshots/ruff_linter__rules__flake8_trio__tests__TRIO115_TRIO115.py.snap b/crates/ruff_linter/src/rules/flake8_trio/snapshots/ruff_linter__rules__flake8_trio__tests__TRIO115_TRIO115.py.snap index 5c24baeb5c6b1..46f54b7f8bd1b 100644 --- a/crates/ruff_linter/src/rules/flake8_trio/snapshots/ruff_linter__rules__flake8_trio__tests__TRIO115_TRIO115.py.snap +++ b/crates/ruff_linter/src/rules/flake8_trio/snapshots/ruff_linter__rules__flake8_trio__tests__TRIO115_TRIO115.py.snap @@ -143,14 +143,14 @@ TRIO115.py:29:5: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.s 29 |+ trio.lowlevel.checkpoint # TRIO115 30 30 | 31 31 | m_x, m_y = 0 -32 32 | trio.sleep(m_y) +32 32 | trio.sleep(m_y) # TRIO115 TRIO115.py:32:5: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)` | 31 | m_x, m_y = 0 -32 | trio.sleep(m_y) +32 | trio.sleep(m_y) # TRIO115 | ^^^^^^^^^^^^^^^ TRIO115 -33 | trio.sleep(m_x) +33 | trio.sleep(m_x) # TRIO115 | = help: Replace with `trio.lowlevel.checkpoint()` @@ -158,48 +158,89 @@ TRIO115.py:32:5: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.s 29 29 | trio.sleep(e) # TRIO115 30 30 | 31 31 | m_x, m_y = 0 -32 |- trio.sleep(m_y) - 32 |+ trio.lowlevel.checkpoint -33 33 | trio.sleep(m_x) +32 |- trio.sleep(m_y) # TRIO115 + 32 |+ trio.lowlevel.checkpoint # TRIO115 +33 33 | trio.sleep(m_x) # TRIO115 34 34 | -35 35 | +35 35 | m_a = m_b = 0 TRIO115.py:33:5: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)` | 31 | m_x, m_y = 0 -32 | trio.sleep(m_y) -33 | trio.sleep(m_x) +32 | trio.sleep(m_y) # TRIO115 +33 | trio.sleep(m_x) # TRIO115 | ^^^^^^^^^^^^^^^ TRIO115 +34 | +35 | m_a = m_b = 0 | = help: Replace with `trio.lowlevel.checkpoint()` ℹ Safe fix 30 30 | 31 31 | m_x, m_y = 0 -32 32 | trio.sleep(m_y) -33 |- trio.sleep(m_x) - 33 |+ trio.lowlevel.checkpoint +32 32 | trio.sleep(m_y) # TRIO115 +33 |- trio.sleep(m_x) # TRIO115 + 33 |+ trio.lowlevel.checkpoint # TRIO115 34 34 | -35 35 | -36 36 | +35 35 | m_a = m_b = 0 +36 36 | trio.sleep(m_a) # TRIO115 -TRIO115.py:45:5: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)` +TRIO115.py:36:5: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)` | -44 | def func(): -45 | sleep(0) # TRIO115 +35 | m_a = m_b = 0 +36 | trio.sleep(m_a) # TRIO115 + | ^^^^^^^^^^^^^^^ TRIO115 +37 | trio.sleep(m_b) # TRIO115 + | + = help: Replace with `trio.lowlevel.checkpoint()` + +ℹ Safe fix +33 33 | trio.sleep(m_x) # TRIO115 +34 34 | +35 35 | m_a = m_b = 0 +36 |- trio.sleep(m_a) # TRIO115 + 36 |+ trio.lowlevel.checkpoint # TRIO115 +37 37 | trio.sleep(m_b) # TRIO115 +38 38 | +39 39 | + +TRIO115.py:37:5: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)` + | +35 | m_a = m_b = 0 +36 | trio.sleep(m_a) # TRIO115 +37 | trio.sleep(m_b) # TRIO115 + | ^^^^^^^^^^^^^^^ TRIO115 + | + = help: Replace with `trio.lowlevel.checkpoint()` + +ℹ Safe fix +34 34 | +35 35 | m_a = m_b = 0 +36 36 | trio.sleep(m_a) # TRIO115 +37 |- trio.sleep(m_b) # TRIO115 + 37 |+ trio.lowlevel.checkpoint # TRIO115 +38 38 | +39 39 | +40 40 | def func(): + +TRIO115.py:48:5: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)` + | +47 | def func(): +48 | sleep(0) # TRIO115 | ^^^^^^^^ TRIO115 | = help: Replace with `trio.lowlevel.checkpoint()` ℹ Safe fix -39 39 | trio.run(trio.sleep(0)) # TRIO115 -40 40 | -41 41 | -42 |-from trio import Event, sleep - 42 |+from trio import Event, sleep, lowlevel +41 41 | trio.run(trio.sleep(0)) # TRIO115 +42 42 | 43 43 | -44 44 | def func(): -45 |- sleep(0) # TRIO115 - 45 |+ lowlevel.checkpoint # TRIO115 +44 |-from trio import Event, sleep + 44 |+from trio import Event, sleep, lowlevel +45 45 | +46 46 | +47 47 | def func(): +48 |- sleep(0) # TRIO115 + 48 |+ lowlevel.checkpoint # TRIO115 From 05aa3870094db1af4d6c073a70bfd60ed98ab089 Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Wed, 22 Nov 2023 18:42:06 +0000 Subject: [PATCH 13/16] Rename method --- .../src/rules/flake8_trio/rules/zero_sleep_call.rs | 4 ++-- .../src/rules/perflint/rules/unnecessary_list_cast.rs | 4 ++-- crates/ruff_python_semantic/src/analyze/typing.rs | 11 +++++------ 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/crates/ruff_linter/src/rules/flake8_trio/rules/zero_sleep_call.rs b/crates/ruff_linter/src/rules/flake8_trio/rules/zero_sleep_call.rs index b6bc7f5af052e..d1d41cdef988a 100644 --- a/crates/ruff_linter/src/rules/flake8_trio/rules/zero_sleep_call.rs +++ b/crates/ruff_linter/src/rules/flake8_trio/rules/zero_sleep_call.rs @@ -1,7 +1,7 @@ use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix}; use ruff_macros::{derive_message_formats, violation}; use ruff_python_ast::{self as ast, Expr, ExprCall, Int}; -use ruff_python_semantic::analyze::typing::get_assigned_value; +use ruff_python_semantic::analyze::typing::find_assigned_value; use ruff_text_size::Ranged; use crate::checkers::ast::Checker; @@ -65,7 +65,7 @@ pub(crate) fn zero_sleep_call(checker: &mut Checker, call: &ExprCall) { } } Expr::Name(ast::ExprName { id, .. }) => { - let Some(value) = get_assigned_value(id, checker.semantic()) else { + let Some(value) = find_assigned_value(id, checker.semantic()) else { return; }; let Expr::NumberLiteral(ast::ExprNumberLiteral { value: num, .. }) = value else { diff --git a/crates/ruff_linter/src/rules/perflint/rules/unnecessary_list_cast.rs b/crates/ruff_linter/src/rules/perflint/rules/unnecessary_list_cast.rs index c40623c730c48..4c73fd4800ecb 100644 --- a/crates/ruff_linter/src/rules/perflint/rules/unnecessary_list_cast.rs +++ b/crates/ruff_linter/src/rules/perflint/rules/unnecessary_list_cast.rs @@ -1,7 +1,7 @@ use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix}; use ruff_macros::{derive_message_formats, violation}; use ruff_python_ast::{self as ast, Arguments, Expr, Stmt}; -use ruff_python_semantic::analyze::typing::get_assigned_value; +use ruff_python_semantic::analyze::typing::find_assigned_value; use ruff_text_size::TextRange; use crate::checkers::ast::Checker; @@ -110,7 +110,7 @@ pub(crate) fn unnecessary_list_cast(checker: &mut Checker, iter: &Expr, body: &[ if body.iter().any(|stmt| match_append(stmt, id)) { return; } - let Some(value) = get_assigned_value(id, checker.semantic()) else { + let Some(value) = find_assigned_value(id, checker.semantic()) else { return; }; if matches!(value, Expr::Tuple(_) | Expr::List(_) | Expr::Set(_)) { diff --git a/crates/ruff_python_semantic/src/analyze/typing.rs b/crates/ruff_python_semantic/src/analyze/typing.rs index cd792635826d9..996392fa47a3a 100644 --- a/crates/ruff_python_semantic/src/analyze/typing.rs +++ b/crates/ruff_python_semantic/src/analyze/typing.rs @@ -569,9 +569,9 @@ pub fn resolve_assignment<'a>( } } -/// Get the assigned [`Expr`] value of a given id, if any. +/// Find the assigned [`Expr`] for a given symbol, if any. /// -/// For example given +/// For example given: /// ```python /// foo = 42 /// (bar, bla) = 1, "str" @@ -579,9 +579,8 @@ pub fn resolve_assignment<'a>( /// /// This function will return a `NumberLiteral` with value `Int(42)` when called with `foo` and a /// `StringLiteral` with value `"str"` when called with `bla`. -pub fn get_assigned_value<'a>(id: &str, semantic: &'a SemanticModel<'a>) -> Option<&'a Expr> { - let scope = semantic.current_scope(); - let binding_id = scope.get(id)?; +pub fn find_assigned_value<'a>(symbol: &str, semantic: &'a SemanticModel<'a>) -> Option<&'a Expr> { + let binding_id = semantic.lookup_symbol(symbol)?; let binding = semantic.binding(binding_id); if binding.kind.is_assignment() || binding.kind.is_named_expr_assignment() { let parent_id = binding.source?; @@ -601,7 +600,7 @@ pub fn get_assigned_value<'a>(id: &str, semantic: &'a SemanticModel<'a>) -> Opti }) | Expr::Set(ast::ExprSet { elts: target_elts, .. - }) => get_value_by_id(id, target_elts, elts), + }) => get_value_by_id(symbol, target_elts, elts), _ => Some(value.as_ref()), }; } From e33daf0a78d1728ed3ccf88eab513e074335e6ac Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Wed, 22 Nov 2023 18:43:21 +0000 Subject: [PATCH 14/16] Remove set match --- crates/ruff_python_semantic/src/analyze/typing.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/ruff_python_semantic/src/analyze/typing.rs b/crates/ruff_python_semantic/src/analyze/typing.rs index 996392fa47a3a..d8071a64bc7a2 100644 --- a/crates/ruff_python_semantic/src/analyze/typing.rs +++ b/crates/ruff_python_semantic/src/analyze/typing.rs @@ -588,8 +588,7 @@ pub fn find_assigned_value<'a>(symbol: &str, semantic: &'a SemanticModel<'a>) -> match parent { Stmt::Assign(ast::StmtAssign { value, targets, .. }) => match value.as_ref() { Expr::Tuple(ast::ExprTuple { elts, .. }) - | Expr::List(ast::ExprList { elts, .. }) - | Expr::Set(ast::ExprSet { elts, .. }) => { + | Expr::List(ast::ExprList { elts, .. }) => { if let Some(target) = targets.iter().next() { return match target { Expr::Tuple(ast::ExprTuple { From b21dea2a4f74da282b9bceb413501499bf9a4580 Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Wed, 22 Nov 2023 18:45:15 +0000 Subject: [PATCH 15/16] Add test --- .../test/fixtures/flake8_trio/TRIO115.py | 5 +++ ...lake8_trio__tests__TRIO115_TRIO115.py.snap | 34 ++++++++++--------- 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_trio/TRIO115.py b/crates/ruff_linter/resources/test/fixtures/flake8_trio/TRIO115.py index 5c5f7acf58832..df4fd84af386f 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_trio/TRIO115.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_trio/TRIO115.py @@ -36,6 +36,11 @@ async def func(): trio.sleep(m_a) # TRIO115 trio.sleep(m_b) # TRIO115 + m_c = (m_d, m_e) = (0, 0) + trio.sleep(m_c) # OK + trio.sleep(m_d) # TRIO115 + trio.sleep(m_e) # TRIO115 + def func(): trio.run(trio.sleep(0)) # TRIO115 diff --git a/crates/ruff_linter/src/rules/flake8_trio/snapshots/ruff_linter__rules__flake8_trio__tests__TRIO115_TRIO115.py.snap b/crates/ruff_linter/src/rules/flake8_trio/snapshots/ruff_linter__rules__flake8_trio__tests__TRIO115_TRIO115.py.snap index 46f54b7f8bd1b..4c2ad4d060e51 100644 --- a/crates/ruff_linter/src/rules/flake8_trio/snapshots/ruff_linter__rules__flake8_trio__tests__TRIO115_TRIO115.py.snap +++ b/crates/ruff_linter/src/rules/flake8_trio/snapshots/ruff_linter__rules__flake8_trio__tests__TRIO115_TRIO115.py.snap @@ -202,7 +202,7 @@ TRIO115.py:36:5: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.s 36 |+ trio.lowlevel.checkpoint # TRIO115 37 37 | trio.sleep(m_b) # TRIO115 38 38 | -39 39 | +39 39 | m_c = (m_d, m_e) = (0, 0) TRIO115.py:37:5: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)` | @@ -210,6 +210,8 @@ TRIO115.py:37:5: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.s 36 | trio.sleep(m_a) # TRIO115 37 | trio.sleep(m_b) # TRIO115 | ^^^^^^^^^^^^^^^ TRIO115 +38 | +39 | m_c = (m_d, m_e) = (0, 0) | = help: Replace with `trio.lowlevel.checkpoint()` @@ -220,27 +222,27 @@ TRIO115.py:37:5: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.s 37 |- trio.sleep(m_b) # TRIO115 37 |+ trio.lowlevel.checkpoint # TRIO115 38 38 | -39 39 | -40 40 | def func(): +39 39 | m_c = (m_d, m_e) = (0, 0) +40 40 | trio.sleep(m_c) # OK -TRIO115.py:48:5: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)` +TRIO115.py:53:5: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)` | -47 | def func(): -48 | sleep(0) # TRIO115 +52 | def func(): +53 | sleep(0) # TRIO115 | ^^^^^^^^ TRIO115 | = help: Replace with `trio.lowlevel.checkpoint()` ℹ Safe fix -41 41 | trio.run(trio.sleep(0)) # TRIO115 -42 42 | -43 43 | -44 |-from trio import Event, sleep - 44 |+from trio import Event, sleep, lowlevel -45 45 | -46 46 | -47 47 | def func(): -48 |- sleep(0) # TRIO115 - 48 |+ lowlevel.checkpoint # TRIO115 +46 46 | trio.run(trio.sleep(0)) # TRIO115 +47 47 | +48 48 | +49 |-from trio import Event, sleep + 49 |+from trio import Event, sleep, lowlevel +50 50 | +51 51 | +52 52 | def func(): +53 |- sleep(0) # TRIO115 + 53 |+ lowlevel.checkpoint # TRIO115 From 218f9eaa241c15e3c8991dd0d2094ae3a41fdf76 Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Tue, 12 Dec 2023 19:19:22 -0500 Subject: [PATCH 16/16] Fix tuple --- ...lake8_trio__tests__TRIO115_TRIO115.py.snap | 39 +++++++++++++++++++ .../src/analyze/typing.rs | 13 ++++++- 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/crates/ruff_linter/src/rules/flake8_trio/snapshots/ruff_linter__rules__flake8_trio__tests__TRIO115_TRIO115.py.snap b/crates/ruff_linter/src/rules/flake8_trio/snapshots/ruff_linter__rules__flake8_trio__tests__TRIO115_TRIO115.py.snap index c82083dc02f95..1ade9f757bbaa 100644 --- a/crates/ruff_linter/src/rules/flake8_trio/snapshots/ruff_linter__rules__flake8_trio__tests__TRIO115_TRIO115.py.snap +++ b/crates/ruff_linter/src/rules/flake8_trio/snapshots/ruff_linter__rules__flake8_trio__tests__TRIO115_TRIO115.py.snap @@ -225,6 +225,45 @@ TRIO115.py:37:5: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.s 39 39 | m_c = (m_d, m_e) = (0, 0) 40 40 | trio.sleep(m_c) # OK +TRIO115.py:41:5: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)` + | +39 | m_c = (m_d, m_e) = (0, 0) +40 | trio.sleep(m_c) # OK +41 | trio.sleep(m_d) # TRIO115 + | ^^^^^^^^^^^^^^^ TRIO115 +42 | trio.sleep(m_e) # TRIO115 + | + = help: Replace with `trio.lowlevel.checkpoint()` + +ℹ Safe fix +38 38 | +39 39 | m_c = (m_d, m_e) = (0, 0) +40 40 | trio.sleep(m_c) # OK +41 |- trio.sleep(m_d) # TRIO115 + 41 |+ trio.lowlevel.checkpoint() # TRIO115 +42 42 | trio.sleep(m_e) # TRIO115 +43 43 | +44 44 | + +TRIO115.py:42:5: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)` + | +40 | trio.sleep(m_c) # OK +41 | trio.sleep(m_d) # TRIO115 +42 | trio.sleep(m_e) # TRIO115 + | ^^^^^^^^^^^^^^^ TRIO115 + | + = help: Replace with `trio.lowlevel.checkpoint()` + +ℹ Safe fix +39 39 | m_c = (m_d, m_e) = (0, 0) +40 40 | trio.sleep(m_c) # OK +41 41 | trio.sleep(m_d) # TRIO115 +42 |- trio.sleep(m_e) # TRIO115 + 42 |+ trio.lowlevel.checkpoint() # TRIO115 +43 43 | +44 44 | +45 45 | def func(): + TRIO115.py:53:5: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)` | 52 | def func(): diff --git a/crates/ruff_python_semantic/src/analyze/typing.rs b/crates/ruff_python_semantic/src/analyze/typing.rs index d8071a64bc7a2..4ff2e27e3221c 100644 --- a/crates/ruff_python_semantic/src/analyze/typing.rs +++ b/crates/ruff_python_semantic/src/analyze/typing.rs @@ -589,7 +589,7 @@ pub fn find_assigned_value<'a>(symbol: &str, semantic: &'a SemanticModel<'a>) -> Stmt::Assign(ast::StmtAssign { value, targets, .. }) => match value.as_ref() { Expr::Tuple(ast::ExprTuple { elts, .. }) | Expr::List(ast::ExprList { elts, .. }) => { - if let Some(target) = targets.iter().next() { + if let Some(target) = targets.iter().find(|target| defines(symbol, target)) { return match target { Expr::Tuple(ast::ExprTuple { elts: target_elts, .. @@ -618,6 +618,17 @@ pub fn find_assigned_value<'a>(symbol: &str, semantic: &'a SemanticModel<'a>) -> None } +/// Returns `true` if the [`Expr`] defines the symbol. +fn defines(symbol: &str, expr: &Expr) -> bool { + match expr { + Expr::Name(ast::ExprName { id, .. }) => id == symbol, + Expr::Tuple(ast::ExprTuple { elts, .. }) + | Expr::List(ast::ExprList { elts, .. }) + | Expr::Set(ast::ExprSet { elts, .. }) => elts.iter().any(|elt| defines(symbol, elt)), + _ => false, + } +} + fn get_value_by_id<'a>( target_id: &str, targets: &'a [Expr],