about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAda Alakbarova <ada.alakbarova@proton.me>2025-09-10 13:28:44 +0200
committerAda Alakbarova <ada.alakbarova@proton.me>2025-09-12 18:23:29 +0200
commitda6d23b116471cdaccd60e8096c97cae259cc79e (patch)
tree2ffc431543f28163a123ee60478e113fb72f9c46
parent8938bb268d72deae0bb8a7a1eebe7563949ebc60 (diff)
fix: ignore unsafe ops from `.await` desugaring
-rw-r--r--clippy_lints/src/multiple_unsafe_ops_per_block.rs8
-rw-r--r--tests/ui/multiple_unsafe_ops_per_block.rs49
-rw-r--r--tests/ui/multiple_unsafe_ops_per_block.stderr123
3 files changed, 149 insertions, 31 deletions
diff --git a/clippy_lints/src/multiple_unsafe_ops_per_block.rs b/clippy_lints/src/multiple_unsafe_ops_per_block.rs
index c6c27e22b90..bc5e72270f4 100644
--- a/clippy_lints/src/multiple_unsafe_ops_per_block.rs
+++ b/clippy_lints/src/multiple_unsafe_ops_per_block.rs
@@ -1,3 +1,4 @@
+use clippy_utils::desugar_await;
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::visitors::{Descend, Visitable, for_each_expr};
 use core::ops::ControlFlow::Continue;
@@ -97,6 +98,13 @@ fn collect_unsafe_exprs<'tcx>(
 ) {
     for_each_expr(cx, node, |expr| {
         match expr.kind {
+            // The `await` itself will desugar to two unsafe calls, but we should ignore those.
+            // Instead, check the expression that is `await`ed
+            _ if let Some(e) = desugar_await(expr) => {
+                collect_unsafe_exprs(cx, e, unsafe_ops);
+                return Continue(Descend::No);
+            },
+
             ExprKind::InlineAsm(_) => unsafe_ops.push(("inline assembly used here", expr.span)),
 
             ExprKind::Field(e, _) => {
diff --git a/tests/ui/multiple_unsafe_ops_per_block.rs b/tests/ui/multiple_unsafe_ops_per_block.rs
index 8f5408010dc..132673d5164 100644
--- a/tests/ui/multiple_unsafe_ops_per_block.rs
+++ b/tests/ui/multiple_unsafe_ops_per_block.rs
@@ -1,6 +1,10 @@
 //@needs-asm-support
 //@aux-build:proc_macros.rs
-#![expect(clippy::unnecessary_operation, dropping_copy_types)]
+#![expect(
+    dropping_copy_types,
+    clippy::unnecessary_operation,
+    clippy::unnecessary_literal_unwrap
+)]
 #![warn(clippy::multiple_unsafe_ops_per_block)]
 
 extern crate proc_macros;
@@ -162,4 +166,47 @@ async fn issue11312() {
     helper().await;
 }
 
+async fn issue13879() {
+    async fn foo() {}
+
+    // no lint: nothing unsafe beyond the `await` which we ignore
+    unsafe {
+        foo().await;
+    }
+
+    // no lint: only one unsafe call beyond the `await`
+    unsafe {
+        not_very_safe();
+        foo().await;
+    }
+
+    // lint: two unsafe calls beyond the `await`
+    unsafe {
+        //~^ multiple_unsafe_ops_per_block
+        not_very_safe();
+        STATIC += 1;
+        foo().await;
+    }
+
+    async unsafe fn foo_unchecked() {}
+
+    // no lint: only one unsafe call in the `await`ed expr
+    unsafe {
+        foo_unchecked().await;
+    }
+
+    // lint: one unsafe call in the `await`ed expr, and one outside
+    unsafe {
+        //~^ multiple_unsafe_ops_per_block
+        not_very_safe();
+        foo_unchecked().await;
+    }
+
+    // lint: two unsafe calls in the `await`ed expr
+    unsafe {
+        //~^ multiple_unsafe_ops_per_block
+        Some(foo_unchecked()).unwrap_unchecked().await;
+    }
+}
+
 fn main() {}
diff --git a/tests/ui/multiple_unsafe_ops_per_block.stderr b/tests/ui/multiple_unsafe_ops_per_block.stderr
index 89553218717..922a464c6b6 100644
--- a/tests/ui/multiple_unsafe_ops_per_block.stderr
+++ b/tests/ui/multiple_unsafe_ops_per_block.stderr
@@ -1,5 +1,5 @@
 error: this `unsafe` block contains 2 unsafe operations, expected only one
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:34:5
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:38:5
    |
 LL | /     unsafe {
 LL | |
@@ -9,12 +9,12 @@ LL | |     }
    | |_____^
    |
 note: modification of a mutable static occurs here
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:36:9
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:40:9
    |
 LL |         STATIC += 1;
    |         ^^^^^^^^^^^
 note: unsafe function call occurs here
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:37:9
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:41:9
    |
 LL |         not_very_safe();
    |         ^^^^^^^^^^^^^^^
@@ -22,7 +22,7 @@ LL |         not_very_safe();
    = help: to override `-D warnings` add `#[allow(clippy::multiple_unsafe_ops_per_block)]`
 
 error: this `unsafe` block contains 2 unsafe operations, expected only one
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:44:5
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:48:5
    |
 LL | /     unsafe {
 LL | |
@@ -32,18 +32,18 @@ LL | |     }
    | |_____^
    |
 note: union field access occurs here
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:46:14
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:50:14
    |
 LL |         drop(u.u);
    |              ^^^
 note: raw pointer dereference occurs here
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:47:9
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:51:9
    |
 LL |         *raw_ptr();
    |         ^^^^^^^^^^
 
 error: this `unsafe` block contains 3 unsafe operations, expected only one
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:52:5
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:56:5
    |
 LL | /     unsafe {
 LL | |
@@ -54,23 +54,23 @@ LL | |     }
    | |_____^
    |
 note: inline assembly used here
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:54:9
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:58:9
    |
 LL |         asm!("nop");
    |         ^^^^^^^^^^^
 note: unsafe method call occurs here
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:55:9
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:59:9
    |
 LL |         sample.not_very_safe();
    |         ^^^^^^^^^^^^^^^^^^^^^^
 note: modification of a mutable static occurs here
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:56:9
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:60:9
    |
 LL |         STATIC = 0;
    |         ^^^^^^^^^^
 
 error: this `unsafe` block contains 6 unsafe operations, expected only one
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:62:5
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:66:5
    |
 LL | /     unsafe {
 LL | |
@@ -82,55 +82,55 @@ LL | |     }
    | |_____^
    |
 note: union field access occurs here
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:64:14
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:68:14
    |
 LL |         drop(u.u);
    |              ^^^
 note: access of a mutable static occurs here
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:65:14
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:69:14
    |
 LL |         drop(STATIC);
    |              ^^^^^^
 note: unsafe method call occurs here
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:66:9
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:70:9
    |
 LL |         sample.not_very_safe();
    |         ^^^^^^^^^^^^^^^^^^^^^^
 note: unsafe function call occurs here
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:67:9
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:71:9
    |
 LL |         not_very_safe();
    |         ^^^^^^^^^^^^^^^
 note: raw pointer dereference occurs here
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:68:9
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:72:9
    |
 LL |         *raw_ptr();
    |         ^^^^^^^^^^
 note: inline assembly used here
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:69:9
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:73:9
    |
 LL |         asm!("nop");
    |         ^^^^^^^^^^^
 
 error: this `unsafe` block contains 2 unsafe operations, expected only one
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:106:9
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:110:9
    |
 LL |         unsafe { char::from_u32_unchecked(*ptr.cast::<u32>()) }
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
 note: unsafe function call occurs here
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:106:18
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:110:18
    |
 LL |         unsafe { char::from_u32_unchecked(*ptr.cast::<u32>()) }
    |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 note: raw pointer dereference occurs here
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:106:43
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:110:43
    |
 LL |         unsafe { char::from_u32_unchecked(*ptr.cast::<u32>()) }
    |                                           ^^^^^^^^^^^^^^^^^^
 
 error: this `unsafe` block contains 2 unsafe operations, expected only one
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:127:9
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:131:9
    |
 LL | /         unsafe {
 LL | |
@@ -140,18 +140,18 @@ LL | |         }
    | |_________^
    |
 note: unsafe function call occurs here
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:129:13
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:133:13
    |
 LL |             x();
    |             ^^^
 note: unsafe function call occurs here
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:130:13
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:134:13
    |
 LL |             x();
    |             ^^^
 
 error: this `unsafe` block contains 2 unsafe operations, expected only one
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:139:13
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:143:13
    |
 LL | /             unsafe {
 LL | |
@@ -161,18 +161,18 @@ LL | |             }
    | |_____________^
    |
 note: unsafe function call occurs here
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:141:17
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:145:17
    |
 LL |                 T::X();
    |                 ^^^^^^
 note: unsafe function call occurs here
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:142:17
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:146:17
    |
 LL |                 T::X();
    |                 ^^^^^^
 
 error: this `unsafe` block contains 2 unsafe operations, expected only one
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:150:9
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:154:9
    |
 LL | /         unsafe {
 LL | |
@@ -182,15 +182,78 @@ LL | |         }
    | |_________^
    |
 note: unsafe function call occurs here
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:152:13
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:156:13
    |
 LL |             x.0();
    |             ^^^^^
 note: unsafe function call occurs here
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:153:13
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:157:13
    |
 LL |             x.0();
    |             ^^^^^
 
-error: aborting due to 8 previous errors
+error: this `unsafe` block contains 2 unsafe operations, expected only one
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:184:5
+   |
+LL | /     unsafe {
+LL | |
+LL | |         not_very_safe();
+LL | |         STATIC += 1;
+LL | |         foo().await;
+LL | |     }
+   | |_____^
+   |
+note: unsafe function call occurs here
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:186:9
+   |
+LL |         not_very_safe();
+   |         ^^^^^^^^^^^^^^^
+note: modification of a mutable static occurs here
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:187:9
+   |
+LL |         STATIC += 1;
+   |         ^^^^^^^^^^^
+
+error: this `unsafe` block contains 2 unsafe operations, expected only one
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:199:5
+   |
+LL | /     unsafe {
+LL | |
+LL | |         not_very_safe();
+LL | |         foo_unchecked().await;
+LL | |     }
+   | |_____^
+   |
+note: unsafe function call occurs here
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:201:9
+   |
+LL |         not_very_safe();
+   |         ^^^^^^^^^^^^^^^
+note: unsafe function call occurs here
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:202:9
+   |
+LL |         foo_unchecked().await;
+   |         ^^^^^^^^^^^^^^^
+
+error: this `unsafe` block contains 2 unsafe operations, expected only one
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:206:5
+   |
+LL | /     unsafe {
+LL | |
+LL | |         Some(foo_unchecked()).unwrap_unchecked().await;
+LL | |     }
+   | |_____^
+   |
+note: unsafe method call occurs here
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:208:9
+   |
+LL |         Some(foo_unchecked()).unwrap_unchecked().await;
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: unsafe function call occurs here
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:208:14
+   |
+LL |         Some(foo_unchecked()).unwrap_unchecked().await;
+   |              ^^^^^^^^^^^^^^^
+
+error: aborting due to 11 previous errors