From 10b5a8335bc840cba3626f32acf5296992ac6ed8 Mon Sep 17 00:00:00 2001 From: Asger Hautop Drewsen Date: Tue, 13 Feb 2024 18:58:41 +0100 Subject: [PATCH 1/2] Improve RUF006 detection --- .../resources/test/fixtures/ruff/RUF006.py | 23 ++++++++++++++++++ .../rules/ruff/rules/asyncio_dangling_task.rs | 21 ++++++++++++---- ..._rules__ruff__tests__RUF006_RUF006.py.snap | 24 +++++++++++++++++-- 3 files changed, 61 insertions(+), 7 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/ruff/RUF006.py b/crates/ruff_linter/resources/test/fixtures/ruff/RUF006.py index 754503cd1d686..2db2e711d54a3 100644 --- a/crates/ruff_linter/resources/test/fixtures/ruff/RUF006.py +++ b/crates/ruff_linter/resources/test/fixtures/ruff/RUF006.py @@ -162,3 +162,26 @@ async def f(x: bool): T = asyncio.create_task(asyncio.sleep(1)) else: T = None + + +# Error +def f(): + loop = asyncio.new_event_loop() + loop.create_task(main()) # Error + +# Error +def f(): + loop = asyncio.get_event_loop() + loop.create_task(main()) # Error + +# OK +def f(): + global task + loop = asyncio.new_event_loop() + task = loop.create_task(main()) # Error + +# OK +def f(): + global task + loop = asyncio.get_event_loop() + task = loop.create_task(main()) # Error diff --git a/crates/ruff_linter/src/rules/ruff/rules/asyncio_dangling_task.rs b/crates/ruff_linter/src/rules/ruff/rules/asyncio_dangling_task.rs index 22e6c1fa10198..73fea6ec8fd89 100644 --- a/crates/ruff_linter/src/rules/ruff/rules/asyncio_dangling_task.rs +++ b/crates/ruff_linter/src/rules/ruff/rules/asyncio_dangling_task.rs @@ -52,14 +52,15 @@ use ruff_text_size::Ranged; /// - [The Python Standard Library](https://docs.python.org/3/library/asyncio-task.html#asyncio.create_task) #[violation] pub struct AsyncioDanglingTask { + expr: &'static str, method: Method, } impl Violation for AsyncioDanglingTask { #[derive_message_formats] fn message(&self) -> String { - let AsyncioDanglingTask { method } = self; - format!("Store a reference to the return value of `asyncio.{method}`") + let AsyncioDanglingTask { expr, method } = self; + format!("Store a reference to the return value of `{expr}.{method}`") } } @@ -80,19 +81,29 @@ pub(crate) fn asyncio_dangling_task(expr: &Expr, semantic: &SemanticModel) -> Op }) { return Some(Diagnostic::new( - AsyncioDanglingTask { method }, + AsyncioDanglingTask { + expr: "asyncio", + method, + }, expr.range(), )); } - // Ex) `loop = asyncio.get_running_loop(); loop.create_task(...)` + // Ex) `loop = ...; loop.create_task(...)` if let Expr::Attribute(ast::ExprAttribute { attr, value, .. }) = func.as_ref() { if attr == "create_task" { if typing::resolve_assignment(value, semantic).is_some_and(|call_path| { - matches!(call_path.as_slice(), ["asyncio", "get_running_loop"]) + matches!( + call_path.as_slice(), + [ + "asyncio", + "get_event_loop" | "get_running_loop" | "new_event_loop" + ] + ) }) { return Some(Diagnostic::new( AsyncioDanglingTask { + expr: "loop", // TODO: How can you turn `value` into a string? method: Method::CreateTask, }, expr.range(), diff --git a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF006_RUF006.py.snap b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF006_RUF006.py.snap index def73e5a2e11e..46fa193ae9e43 100644 --- a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF006_RUF006.py.snap +++ b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF006_RUF006.py.snap @@ -25,7 +25,7 @@ RUF006.py:68:12: RUF006 Store a reference to the return value of `asyncio.create | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RUF006 | -RUF006.py:74:26: RUF006 Store a reference to the return value of `asyncio.create_task` +RUF006.py:74:26: RUF006 Store a reference to the return value of `loop.create_task` | 72 | def f(): 73 | loop = asyncio.get_running_loop() @@ -33,7 +33,7 @@ RUF006.py:74:26: RUF006 Store a reference to the return value of `asyncio.create | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RUF006 | -RUF006.py:97:5: RUF006 Store a reference to the return value of `asyncio.create_task` +RUF006.py:97:5: RUF006 Store a reference to the return value of `loop.create_task` | 95 | def f(): 96 | loop = asyncio.get_running_loop() @@ -41,4 +41,24 @@ RUF006.py:97:5: RUF006 Store a reference to the return value of `asyncio.create_ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RUF006 | +RUF006.py:170:5: RUF006 Store a reference to the return value of `loop.create_task` + | +168 | def f(): +169 | loop = asyncio.new_event_loop() +170 | loop.create_task(main()) # Error + | ^^^^^^^^^^^^^^^^^^^^^^^^ RUF006 +171 | +172 | # Error + | + +RUF006.py:175:5: RUF006 Store a reference to the return value of `loop.create_task` + | +173 | def f(): +174 | loop = asyncio.get_event_loop() +175 | loop.create_task(main()) # Error + | ^^^^^^^^^^^^^^^^^^^^^^^^ RUF006 +176 | +177 | # OK + | + From 19ec2be93f752c7dda104ed2f2fae345ac7e51bb Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Tue, 13 Feb 2024 13:16:12 -0500 Subject: [PATCH 2/2] Resolve TODO --- .../src/rules/ruff/rules/asyncio_dangling_task.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/crates/ruff_linter/src/rules/ruff/rules/asyncio_dangling_task.rs b/crates/ruff_linter/src/rules/ruff/rules/asyncio_dangling_task.rs index 73fea6ec8fd89..8c58b2d5ad4d2 100644 --- a/crates/ruff_linter/src/rules/ruff/rules/asyncio_dangling_task.rs +++ b/crates/ruff_linter/src/rules/ruff/rules/asyncio_dangling_task.rs @@ -3,6 +3,7 @@ use std::fmt; use ast::Stmt; use ruff_diagnostics::{Diagnostic, Violation}; use ruff_macros::{derive_message_formats, violation}; +use ruff_python_ast::call_path::compose_call_path; use ruff_python_ast::{self as ast, Expr}; use ruff_python_semantic::{analyze::typing, Scope, SemanticModel}; use ruff_text_size::Ranged; @@ -52,7 +53,7 @@ use ruff_text_size::Ranged; /// - [The Python Standard Library](https://docs.python.org/3/library/asyncio-task.html#asyncio.create_task) #[violation] pub struct AsyncioDanglingTask { - expr: &'static str, + expr: String, method: Method, } @@ -82,7 +83,7 @@ pub(crate) fn asyncio_dangling_task(expr: &Expr, semantic: &SemanticModel) -> Op { return Some(Diagnostic::new( AsyncioDanglingTask { - expr: "asyncio", + expr: "asyncio".to_string(), method, }, expr.range(), @@ -103,7 +104,7 @@ pub(crate) fn asyncio_dangling_task(expr: &Expr, semantic: &SemanticModel) -> Op }) { return Some(Diagnostic::new( AsyncioDanglingTask { - expr: "loop", // TODO: How can you turn `value` into a string? + expr: compose_call_path(value).unwrap_or_else(|| "asyncio".to_string()), method: Method::CreateTask, }, expr.range(),