fix: ignore unsafe ops from .await desugaring

This commit is contained in:
Ada Alakbarova 2025-09-10 13:28:44 +02:00
parent 8938bb268d
commit da6d23b116
No known key found for this signature in database
3 changed files with 149 additions and 31 deletions

View file

@ -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, _) => {

View file

@ -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() {}

View file

@ -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