Skip to content

Commit cb286b4

Browse files
fix(multiple_unsafe_ops_per_block): ignore unsafe ops from .await desugaring (#15654)
Fixes #13879 changelog: [`multiple_unsafe_ops_per_block`]: ignore unsafe ops from `.await` desugaring
2 parents adcd2de + da6d23b commit cb286b4

File tree

3 files changed

+193
-77
lines changed

3 files changed

+193
-77
lines changed

clippy_lints/src/multiple_unsafe_ops_per_block.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use clippy_utils::desugar_await;
12
use clippy_utils::diagnostics::span_lint_and_then;
23
use clippy_utils::visitors::{Descend, Visitable, for_each_expr};
34
use core::ops::ControlFlow::Continue;
@@ -97,6 +98,13 @@ fn collect_unsafe_exprs<'tcx>(
9798
) {
9899
for_each_expr(cx, node, |expr| {
99100
match expr.kind {
101+
// The `await` itself will desugar to two unsafe calls, but we should ignore those.
102+
// Instead, check the expression that is `await`ed
103+
_ if let Some(e) = desugar_await(expr) => {
104+
collect_unsafe_exprs(cx, e, unsafe_ops);
105+
return Continue(Descend::No);
106+
},
107+
100108
ExprKind::InlineAsm(_) => unsafe_ops.push(("inline assembly used here", expr.span)),
101109

102110
ExprKind::Field(e, _) => {

tests/ui/multiple_unsafe_ops_per_block.rs

Lines changed: 82 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
//@needs-asm-support
22
//@aux-build:proc_macros.rs
3-
#![allow(unused)]
4-
#![allow(deref_nullptr)]
5-
#![allow(clippy::unnecessary_operation)]
6-
#![allow(dropping_copy_types)]
7-
#![allow(clippy::assign_op_pattern)]
3+
#![expect(
4+
dropping_copy_types,
5+
clippy::unnecessary_operation,
6+
clippy::unnecessary_literal_unwrap
7+
)]
88
#![warn(clippy::multiple_unsafe_ops_per_block)]
99

1010
extern crate proc_macros;
@@ -105,17 +105,17 @@ fn correct3() {
105105
}
106106
}
107107

108-
// tests from the issue (https://github.com/rust-lang/rust-clippy/issues/10064)
109-
110-
unsafe fn read_char_bad(ptr: *const u8) -> char {
111-
unsafe { char::from_u32_unchecked(*ptr.cast::<u32>()) }
112-
//~^ multiple_unsafe_ops_per_block
113-
}
108+
fn issue10064() {
109+
unsafe fn read_char_bad(ptr: *const u8) -> char {
110+
unsafe { char::from_u32_unchecked(*ptr.cast::<u32>()) }
111+
//~^ multiple_unsafe_ops_per_block
112+
}
114113

115-
// no lint
116-
unsafe fn read_char_good(ptr: *const u8) -> char {
117-
let int_value = unsafe { *ptr.cast::<u32>() };
118-
unsafe { core::char::from_u32_unchecked(int_value) }
114+
// no lint
115+
unsafe fn read_char_good(ptr: *const u8) -> char {
116+
let int_value = unsafe { *ptr.cast::<u32>() };
117+
unsafe { core::char::from_u32_unchecked(int_value) }
118+
}
119119
}
120120

121121
// no lint
@@ -126,42 +126,87 @@ fn issue10259() {
126126
});
127127
}
128128

129-
fn _fn_ptr(x: unsafe fn()) {
130-
unsafe {
131-
//~^ multiple_unsafe_ops_per_block
132-
x();
133-
x();
129+
fn issue10367() {
130+
fn fn_ptr(x: unsafe fn()) {
131+
unsafe {
132+
//~^ multiple_unsafe_ops_per_block
133+
x();
134+
x();
135+
}
134136
}
135-
}
136137

137-
fn _assoc_const() {
138-
trait X {
139-
const X: unsafe fn();
138+
fn assoc_const() {
139+
trait X {
140+
const X: unsafe fn();
141+
}
142+
fn _f<T: X>() {
143+
unsafe {
144+
//~^ multiple_unsafe_ops_per_block
145+
T::X();
146+
T::X();
147+
}
148+
}
140149
}
141-
fn _f<T: X>() {
150+
151+
fn field_fn_ptr(x: unsafe fn()) {
152+
struct X(unsafe fn());
153+
let x = X(x);
142154
unsafe {
143155
//~^ multiple_unsafe_ops_per_block
144-
T::X();
145-
T::X();
156+
x.0();
157+
x.0();
146158
}
147159
}
148160
}
149161

150-
fn _field_fn_ptr(x: unsafe fn()) {
151-
struct X(unsafe fn());
152-
let x = X(x);
162+
// await expands to an unsafe block with several operations, but this is fine.
163+
async fn issue11312() {
164+
async fn helper() {}
165+
166+
helper().await;
167+
}
168+
169+
async fn issue13879() {
170+
async fn foo() {}
171+
172+
// no lint: nothing unsafe beyond the `await` which we ignore
173+
unsafe {
174+
foo().await;
175+
}
176+
177+
// no lint: only one unsafe call beyond the `await`
178+
unsafe {
179+
not_very_safe();
180+
foo().await;
181+
}
182+
183+
// lint: two unsafe calls beyond the `await`
153184
unsafe {
154185
//~^ multiple_unsafe_ops_per_block
155-
x.0();
156-
x.0();
186+
not_very_safe();
187+
STATIC += 1;
188+
foo().await;
157189
}
158-
}
159190

160-
// await expands to an unsafe block with several operations, but this is fine.: #11312
161-
async fn await_desugaring_silent() {
162-
async fn helper() {}
191+
async unsafe fn foo_unchecked() {}
163192

164-
helper().await;
193+
// no lint: only one unsafe call in the `await`ed expr
194+
unsafe {
195+
foo_unchecked().await;
196+
}
197+
198+
// lint: one unsafe call in the `await`ed expr, and one outside
199+
unsafe {
200+
//~^ multiple_unsafe_ops_per_block
201+
not_very_safe();
202+
foo_unchecked().await;
203+
}
204+
205+
// lint: two unsafe calls in the `await`ed expr
206+
unsafe {
207+
//~^ multiple_unsafe_ops_per_block
208+
Some(foo_unchecked()).unwrap_unchecked().await;
209+
}
165210
}
166211

167212
fn main() {}

tests/ui/multiple_unsafe_ops_per_block.stderr

Lines changed: 103 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -113,84 +113,147 @@ LL | asm!("nop");
113113
| ^^^^^^^^^^^
114114

115115
error: this `unsafe` block contains 2 unsafe operations, expected only one
116-
--> tests/ui/multiple_unsafe_ops_per_block.rs:111:5
116+
--> tests/ui/multiple_unsafe_ops_per_block.rs:110:9
117117
|
118-
LL | unsafe { char::from_u32_unchecked(*ptr.cast::<u32>()) }
119-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
118+
LL | unsafe { char::from_u32_unchecked(*ptr.cast::<u32>()) }
119+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
120120
|
121121
note: unsafe function call occurs here
122-
--> tests/ui/multiple_unsafe_ops_per_block.rs:111:14
122+
--> tests/ui/multiple_unsafe_ops_per_block.rs:110:18
123123
|
124-
LL | unsafe { char::from_u32_unchecked(*ptr.cast::<u32>()) }
125-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
124+
LL | unsafe { char::from_u32_unchecked(*ptr.cast::<u32>()) }
125+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
126126
note: raw pointer dereference occurs here
127-
--> tests/ui/multiple_unsafe_ops_per_block.rs:111:39
127+
--> tests/ui/multiple_unsafe_ops_per_block.rs:110:43
128128
|
129-
LL | unsafe { char::from_u32_unchecked(*ptr.cast::<u32>()) }
130-
| ^^^^^^^^^^^^^^^^^^
129+
LL | unsafe { char::from_u32_unchecked(*ptr.cast::<u32>()) }
130+
| ^^^^^^^^^^^^^^^^^^
131131

132132
error: this `unsafe` block contains 2 unsafe operations, expected only one
133-
--> tests/ui/multiple_unsafe_ops_per_block.rs:130:5
133+
--> tests/ui/multiple_unsafe_ops_per_block.rs:131:9
134134
|
135-
LL | / unsafe {
135+
LL | / unsafe {
136136
LL | |
137-
LL | | x();
138-
LL | | x();
139-
LL | | }
140-
| |_____^
137+
LL | | x();
138+
LL | | x();
139+
LL | | }
140+
| |_________^
141141
|
142142
note: unsafe function call occurs here
143-
--> tests/ui/multiple_unsafe_ops_per_block.rs:132:9
143+
--> tests/ui/multiple_unsafe_ops_per_block.rs:133:13
144144
|
145-
LL | x();
146-
| ^^^
145+
LL | x();
146+
| ^^^
147147
note: unsafe function call occurs here
148-
--> tests/ui/multiple_unsafe_ops_per_block.rs:133:9
148+
--> tests/ui/multiple_unsafe_ops_per_block.rs:134:13
149149
|
150-
LL | x();
151-
| ^^^
150+
LL | x();
151+
| ^^^
152152

153153
error: this `unsafe` block contains 2 unsafe operations, expected only one
154-
--> tests/ui/multiple_unsafe_ops_per_block.rs:142:9
154+
--> tests/ui/multiple_unsafe_ops_per_block.rs:143:13
155+
|
156+
LL | / unsafe {
157+
LL | |
158+
LL | | T::X();
159+
LL | | T::X();
160+
LL | | }
161+
| |_____________^
162+
|
163+
note: unsafe function call occurs here
164+
--> tests/ui/multiple_unsafe_ops_per_block.rs:145:17
165+
|
166+
LL | T::X();
167+
| ^^^^^^
168+
note: unsafe function call occurs here
169+
--> tests/ui/multiple_unsafe_ops_per_block.rs:146:17
170+
|
171+
LL | T::X();
172+
| ^^^^^^
173+
174+
error: this `unsafe` block contains 2 unsafe operations, expected only one
175+
--> tests/ui/multiple_unsafe_ops_per_block.rs:154:9
155176
|
156177
LL | / unsafe {
157178
LL | |
158-
LL | | T::X();
159-
LL | | T::X();
179+
LL | | x.0();
180+
LL | | x.0();
160181
LL | | }
161182
| |_________^
162183
|
163184
note: unsafe function call occurs here
164-
--> tests/ui/multiple_unsafe_ops_per_block.rs:144:13
185+
--> tests/ui/multiple_unsafe_ops_per_block.rs:156:13
165186
|
166-
LL | T::X();
167-
| ^^^^^^
187+
LL | x.0();
188+
| ^^^^^
168189
note: unsafe function call occurs here
169-
--> tests/ui/multiple_unsafe_ops_per_block.rs:145:13
190+
--> tests/ui/multiple_unsafe_ops_per_block.rs:157:13
170191
|
171-
LL | T::X();
172-
| ^^^^^^
192+
LL | x.0();
193+
| ^^^^^
173194

174195
error: this `unsafe` block contains 2 unsafe operations, expected only one
175-
--> tests/ui/multiple_unsafe_ops_per_block.rs:153:5
196+
--> tests/ui/multiple_unsafe_ops_per_block.rs:184:5
176197
|
177198
LL | / unsafe {
178199
LL | |
179-
LL | | x.0();
180-
LL | | x.0();
200+
LL | | not_very_safe();
201+
LL | | STATIC += 1;
202+
LL | | foo().await;
181203
LL | | }
182204
| |_____^
183205
|
184206
note: unsafe function call occurs here
185-
--> tests/ui/multiple_unsafe_ops_per_block.rs:155:9
207+
--> tests/ui/multiple_unsafe_ops_per_block.rs:186:9
208+
|
209+
LL | not_very_safe();
210+
| ^^^^^^^^^^^^^^^
211+
note: modification of a mutable static occurs here
212+
--> tests/ui/multiple_unsafe_ops_per_block.rs:187:9
213+
|
214+
LL | STATIC += 1;
215+
| ^^^^^^^^^^^
216+
217+
error: this `unsafe` block contains 2 unsafe operations, expected only one
218+
--> tests/ui/multiple_unsafe_ops_per_block.rs:199:5
219+
|
220+
LL | / unsafe {
221+
LL | |
222+
LL | | not_very_safe();
223+
LL | | foo_unchecked().await;
224+
LL | | }
225+
| |_____^
226+
|
227+
note: unsafe function call occurs here
228+
--> tests/ui/multiple_unsafe_ops_per_block.rs:201:9
229+
|
230+
LL | not_very_safe();
231+
| ^^^^^^^^^^^^^^^
232+
note: unsafe function call occurs here
233+
--> tests/ui/multiple_unsafe_ops_per_block.rs:202:9
234+
|
235+
LL | foo_unchecked().await;
236+
| ^^^^^^^^^^^^^^^
237+
238+
error: this `unsafe` block contains 2 unsafe operations, expected only one
239+
--> tests/ui/multiple_unsafe_ops_per_block.rs:206:5
240+
|
241+
LL | / unsafe {
242+
LL | |
243+
LL | | Some(foo_unchecked()).unwrap_unchecked().await;
244+
LL | | }
245+
| |_____^
246+
|
247+
note: unsafe method call occurs here
248+
--> tests/ui/multiple_unsafe_ops_per_block.rs:208:9
186249
|
187-
LL | x.0();
188-
| ^^^^^
250+
LL | Some(foo_unchecked()).unwrap_unchecked().await;
251+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
189252
note: unsafe function call occurs here
190-
--> tests/ui/multiple_unsafe_ops_per_block.rs:156:9
253+
--> tests/ui/multiple_unsafe_ops_per_block.rs:208:14
191254
|
192-
LL | x.0();
193-
| ^^^^^
255+
LL | Some(foo_unchecked()).unwrap_unchecked().await;
256+
| ^^^^^^^^^^^^^^^
194257

195-
error: aborting due to 8 previous errors
258+
error: aborting due to 11 previous errors
196259

0 commit comments

Comments
 (0)