about summary refs log tree commit diff
diff options
context:
space:
mode:
authorSamuel Tardieu <sam@rfc1149.net>2025-09-14 07:17:04 +0000
committerGitHub <noreply@github.com>2025-09-14 07:17:04 +0000
commitcb286b44792cb7d6a969d057593a7408f470d5f3 (patch)
tree7ed3379a458411f78250f6fdbb31dc189e9ecf29
parentadcd2defc79cf4e573254c6a3f6803c37b085ec1 (diff)
parentda6d23b116471cdaccd60e8096c97cae259cc79e (diff)
downloadrust-cb286b44792cb7d6a969d057593a7408f470d5f3.tar.gz
rust-cb286b44792cb7d6a969d057593a7408f470d5f3.zip
fix(multiple_unsafe_ops_per_block): ignore unsafe ops from `.await` desugaring (#15654)
Fixes https://github.com/rust-lang/rust-clippy/issues/13879

changelog: [`multiple_unsafe_ops_per_block`]: 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.rs119
-rw-r--r--tests/ui/multiple_unsafe_ops_per_block.stderr143
3 files changed, 193 insertions, 77 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 016fd89a7b7..132673d5164 100644
--- a/tests/ui/multiple_unsafe_ops_per_block.rs
+++ b/tests/ui/multiple_unsafe_ops_per_block.rs
@@ -1,10 +1,10 @@
 //@needs-asm-support
 //@aux-build:proc_macros.rs
-#![allow(unused)]
-#![allow(deref_nullptr)]
-#![allow(clippy::unnecessary_operation)]
-#![allow(dropping_copy_types)]
-#![allow(clippy::assign_op_pattern)]
+#![expect(
+    dropping_copy_types,
+    clippy::unnecessary_operation,
+    clippy::unnecessary_literal_unwrap
+)]
 #![warn(clippy::multiple_unsafe_ops_per_block)]
 
 extern crate proc_macros;
@@ -105,17 +105,17 @@ fn correct3() {
     }
 }
 
-// tests from the issue (https://github.com/rust-lang/rust-clippy/issues/10064)
-
-unsafe fn read_char_bad(ptr: *const u8) -> char {
-    unsafe { char::from_u32_unchecked(*ptr.cast::<u32>()) }
-    //~^ multiple_unsafe_ops_per_block
-}
+fn issue10064() {
+    unsafe fn read_char_bad(ptr: *const u8) -> char {
+        unsafe { char::from_u32_unchecked(*ptr.cast::<u32>()) }
+        //~^ multiple_unsafe_ops_per_block
+    }
 
-// no lint
-unsafe fn read_char_good(ptr: *const u8) -> char {
-    let int_value = unsafe { *ptr.cast::<u32>() };
-    unsafe { core::char::from_u32_unchecked(int_value) }
+    // no lint
+    unsafe fn read_char_good(ptr: *const u8) -> char {
+        let int_value = unsafe { *ptr.cast::<u32>() };
+        unsafe { core::char::from_u32_unchecked(int_value) }
+    }
 }
 
 // no lint
@@ -126,42 +126,87 @@ fn issue10259() {
     });
 }
 
-fn _fn_ptr(x: unsafe fn()) {
-    unsafe {
-        //~^ multiple_unsafe_ops_per_block
-        x();
-        x();
+fn issue10367() {
+    fn fn_ptr(x: unsafe fn()) {
+        unsafe {
+            //~^ multiple_unsafe_ops_per_block
+            x();
+            x();
+        }
     }
-}
 
-fn _assoc_const() {
-    trait X {
-        const X: unsafe fn();
+    fn assoc_const() {
+        trait X {
+            const X: unsafe fn();
+        }
+        fn _f<T: X>() {
+            unsafe {
+                //~^ multiple_unsafe_ops_per_block
+                T::X();
+                T::X();
+            }
+        }
     }
-    fn _f<T: X>() {
+
+    fn field_fn_ptr(x: unsafe fn()) {
+        struct X(unsafe fn());
+        let x = X(x);
         unsafe {
             //~^ multiple_unsafe_ops_per_block
-            T::X();
-            T::X();
+            x.0();
+            x.0();
         }
     }
 }
 
-fn _field_fn_ptr(x: unsafe fn()) {
-    struct X(unsafe fn());
-    let x = X(x);
+// await expands to an unsafe block with several operations, but this is fine.
+async fn issue11312() {
+    async fn helper() {}
+
+    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
-        x.0();
-        x.0();
+        not_very_safe();
+        STATIC += 1;
+        foo().await;
     }
-}
 
-// await expands to an unsafe block with several operations, but this is fine.: #11312
-async fn await_desugaring_silent() {
-    async fn helper() {}
+    async unsafe fn foo_unchecked() {}
 
-    helper().await;
+    // 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 3130cecc252..922a464c6b6 100644
--- a/tests/ui/multiple_unsafe_ops_per_block.stderr
+++ b/tests/ui/multiple_unsafe_ops_per_block.stderr
@@ -113,84 +113,147 @@ LL |         asm!("nop");
    |         ^^^^^^^^^^^
 
 error: this `unsafe` block contains 2 unsafe operations, expected only one
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:111:5
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:110:9
    |
-LL |     unsafe { char::from_u32_unchecked(*ptr.cast::<u32>()) }
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |         unsafe { char::from_u32_unchecked(*ptr.cast::<u32>()) }
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
 note: unsafe function call occurs here
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:111:14
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:110:18
    |
-LL |     unsafe { char::from_u32_unchecked(*ptr.cast::<u32>()) }
-   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |         unsafe { char::from_u32_unchecked(*ptr.cast::<u32>()) }
+   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 note: raw pointer dereference occurs here
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:111:39
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:110:43
    |
-LL |     unsafe { char::from_u32_unchecked(*ptr.cast::<u32>()) }
-   |                                       ^^^^^^^^^^^^^^^^^^
+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:130:5
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:131:9
    |
-LL | /     unsafe {
+LL | /         unsafe {
 LL | |
-LL | |         x();
-LL | |         x();
-LL | |     }
-   | |_____^
+LL | |             x();
+LL | |             x();
+LL | |         }
+   | |_________^
    |
 note: unsafe function call occurs here
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:132:9
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:133:13
    |
-LL |         x();
-   |         ^^^
+LL |             x();
+   |             ^^^
 note: unsafe function call occurs here
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:133:9
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:134:13
    |
-LL |         x();
-   |         ^^^
+LL |             x();
+   |             ^^^
 
 error: this `unsafe` block contains 2 unsafe operations, expected only one
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:142:9
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:143:13
+   |
+LL | /             unsafe {
+LL | |
+LL | |                 T::X();
+LL | |                 T::X();
+LL | |             }
+   | |_____________^
+   |
+note: unsafe function call occurs here
+  --> 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:146:17
+   |
+LL |                 T::X();
+   |                 ^^^^^^
+
+error: this `unsafe` block contains 2 unsafe operations, expected only one
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:154:9
    |
 LL | /         unsafe {
 LL | |
-LL | |             T::X();
-LL | |             T::X();
+LL | |             x.0();
+LL | |             x.0();
 LL | |         }
    | |_________^
    |
 note: unsafe function call occurs here
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:144:13
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:156:13
    |
-LL |             T::X();
-   |             ^^^^^^
+LL |             x.0();
+   |             ^^^^^
 note: unsafe function call occurs here
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:145:13
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:157:13
    |
-LL |             T::X();
-   |             ^^^^^^
+LL |             x.0();
+   |             ^^^^^
 
 error: this `unsafe` block contains 2 unsafe operations, expected only one
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:153:5
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:184:5
    |
 LL | /     unsafe {
 LL | |
-LL | |         x.0();
-LL | |         x.0();
+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:155:9
+  --> 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 |         x.0();
-   |         ^^^^^
+LL |         Some(foo_unchecked()).unwrap_unchecked().await;
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 note: unsafe function call occurs here
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:156:9
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:208:14
    |
-LL |         x.0();
-   |         ^^^^^
+LL |         Some(foo_unchecked()).unwrap_unchecked().await;
+   |              ^^^^^^^^^^^^^^^
 
-error: aborting due to 8 previous errors
+error: aborting due to 11 previous errors