Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions clippy_lints/src/multiple_unsafe_ops_per_block.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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, _) => {
Expand Down
119 changes: 82 additions & 37 deletions tests/ui/multiple_unsafe_ops_per_block.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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
Expand All @@ -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() {}
143 changes: 103 additions & 40 deletions tests/ui/multiple_unsafe_ops_per_block.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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