From 06c248a126b403caa088f3fa6546f85b0ba2b1d5 Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Fri, 19 Apr 2024 00:03:52 -0400 Subject: [PATCH] [`ruff]` Ignore stub functions in `unused-async` (`RUF029`) (#11026) ## Summary We should ignore methods that appear to be stubs, e.g.: ```python async def foo() -> int: ... ``` Closes https://github.com/astral-sh/ruff/issues/11018. --- .../resources/test/fixtures/ruff/RUF029.py | 8 ++- .../src/rules/ruff/rules/unused_async.rs | 6 +++ ..._rules__ruff__tests__RUF029_RUF029.py.snap | 52 ++++++++----------- 3 files changed, 34 insertions(+), 32 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/ruff/RUF029.py b/crates/ruff_linter/resources/test/fixtures/ruff/RUF029.py index e2a6cf0af7..f2c42b3d9c 100644 --- a/crates/ruff_linter/resources/test/fixtures/ruff/RUF029.py +++ b/crates/ruff_linter/resources/test/fixtures/ruff/RUF029.py @@ -22,7 +22,7 @@ async def pass_3(): # OK: uses an async loop class Foo: - async def pass_4(): # OK: method of a class + async def pass_4(self): # OK: method of a class pass @@ -31,6 +31,10 @@ def foo(): await bla +async def pass_6(): # OK: just a stub + ... + + async def fail_1a(): # RUF029 time.sleep(1) @@ -58,7 +62,7 @@ async def fail_4a(): # RUF029: the /outer/ function does not await async def fail_4b(): # RUF029: the /outer/ function does not await class Foo: - async def foo(): + async def foo(self): await bla diff --git a/crates/ruff_linter/src/rules/ruff/rules/unused_async.rs b/crates/ruff_linter/src/rules/ruff/rules/unused_async.rs index 470b16cfa8..e6180c945a 100644 --- a/crates/ruff_linter/src/rules/ruff/rules/unused_async.rs +++ b/crates/ruff_linter/src/rules/ruff/rules/unused_async.rs @@ -3,6 +3,7 @@ use ruff_macros::{derive_message_formats, violation}; use ruff_python_ast::identifier::Identifier; use ruff_python_ast::visitor::preorder; use ruff_python_ast::{self as ast, AnyNodeRef, Expr, Stmt}; +use ruff_python_semantic::analyze::function_type::is_stub; use crate::checkers::ast::Checker; @@ -160,6 +161,11 @@ pub(crate) fn unused_async( return; } + // Ignore stubs (e.g., `...`). + if is_stub(function_def, checker.semantic()) { + return; + } + let found_await_or_async = { let mut visitor = AsyncExprVisitor::default(); preorder::walk_body(&mut visitor, body); diff --git a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF029_RUF029.py.snap b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF029_RUF029.py.snap index fba1b92b00..34cbd80ea6 100644 --- a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF029_RUF029.py.snap +++ b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF029_RUF029.py.snap @@ -1,56 +1,48 @@ --- source: crates/ruff_linter/src/rules/ruff/mod.rs --- -RUF029.py:34:11: RUF029 Function `fail_1a` is declared `async`, but doesn't `await` or use `async` features. +RUF029.py:38:11: RUF029 Function `fail_1a` is declared `async`, but doesn't `await` or use `async` features. | -34 | async def fail_1a(): # RUF029 +38 | async def fail_1a(): # RUF029 | ^^^^^^^ RUF029 -35 | time.sleep(1) +39 | time.sleep(1) | -RUF029.py:38:11: RUF029 Function `fail_1b` is declared `async`, but doesn't `await` or use `async` features. +RUF029.py:42:11: RUF029 Function `fail_1b` is declared `async`, but doesn't `await` or use `async` features. | -38 | async def fail_1b(): # RUF029: yield does not require async +42 | async def fail_1b(): # RUF029: yield does not require async | ^^^^^^^ RUF029 -39 | yield "hello" +43 | yield "hello" | -RUF029.py:42:11: RUF029 Function `fail_2` is declared `async`, but doesn't `await` or use `async` features. +RUF029.py:46:11: RUF029 Function `fail_2` is declared `async`, but doesn't `await` or use `async` features. | -42 | async def fail_2(): # RUF029 +46 | async def fail_2(): # RUF029 | ^^^^^^ RUF029 -43 | with None as i: -44 | pass +47 | with None as i: +48 | pass | -RUF029.py:47:11: RUF029 Function `fail_3` is declared `async`, but doesn't `await` or use `async` features. +RUF029.py:51:11: RUF029 Function `fail_3` is declared `async`, but doesn't `await` or use `async` features. | -47 | async def fail_3(): # RUF029 +51 | async def fail_3(): # RUF029 | ^^^^^^ RUF029 -48 | for i in []: -49 | pass +52 | for i in []: +53 | pass | -RUF029.py:54:11: RUF029 Function `fail_4a` is declared `async`, but doesn't `await` or use `async` features. +RUF029.py:58:11: RUF029 Function `fail_4a` is declared `async`, but doesn't `await` or use `async` features. | -54 | async def fail_4a(): # RUF029: the /outer/ function does not await +58 | async def fail_4a(): # RUF029: the /outer/ function does not await | ^^^^^^^ RUF029 -55 | async def foo(): -56 | await bla +59 | async def foo(): +60 | await bla | -RUF029.py:59:11: RUF029 Function `fail_4b` is declared `async`, but doesn't `await` or use `async` features. +RUF029.py:63:11: RUF029 Function `fail_4b` is declared `async`, but doesn't `await` or use `async` features. | -59 | async def fail_4b(): # RUF029: the /outer/ function does not await +63 | async def fail_4b(): # RUF029: the /outer/ function does not await | ^^^^^^^ RUF029 -60 | class Foo: -61 | async def foo(): - | - -RUF029.py:66:15: RUF029 Function `fail_4c` is declared `async`, but doesn't `await` or use `async` features. - | -65 | def foo(): -66 | async def fail_4c(): # RUF029: the /inner/ function does not await - | ^^^^^^^ RUF029 -67 | pass +64 | class Foo: +65 | async def foo(self): |