diff --git a/compiler/rustc_mir_transform/src/check_unsafety.rs b/compiler/rustc_mir_transform/src/check_unsafety.rs index d564f48016626..2bee6cebdcd73 100644 --- a/compiler/rustc_mir_transform/src/check_unsafety.rs +++ b/compiler/rustc_mir_transform/src/check_unsafety.rs @@ -1,5 +1,5 @@ use rustc_data_structures::fx::FxHashMap; -use rustc_errors::struct_span_err; +use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder}; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::hir_id::HirId; @@ -569,6 +569,8 @@ pub fn check_unsafety(tcx: TyCtxt<'_>, def_id: LocalDefId) { } let UnsafetyCheckResult { violations, unused_unsafes, .. } = tcx.unsafety_check_result(def_id); + // Only suggest wrapping the entire function body in an unsafe block once + let mut suggest_unsafe_block = true; for &UnsafetyViolation { source_info, lint_root, kind, details } in violations.iter() { let (description, note) = details.description_and_note(); @@ -597,13 +599,16 @@ pub fn check_unsafety(tcx: TyCtxt<'_>, def_id: LocalDefId) { lint_root, source_info.span, |lint| { - lint.build(&format!( + let mut err = lint.build(&format!( "{} is unsafe and requires unsafe block (error E0133)", description, - )) - .span_label(source_info.span, description) - .note(note) - .emit(); + )); + err.span_label(source_info.span, description).note(note); + if suggest_unsafe_block { + suggest_wrapping_unsafe_block(tcx, def_id, &mut err); + suggest_unsafe_block = false; + } + err.emit(); }, ), } @@ -614,6 +619,25 @@ pub fn check_unsafety(tcx: TyCtxt<'_>, def_id: LocalDefId) { } } +fn suggest_wrapping_unsafe_block( + tcx: TyCtxt<'_>, + def_id: LocalDefId, + err: &mut DiagnosticBuilder<'_, ()>, +) { + let body_span = tcx.hir().body(tcx.hir().body_owned_by(def_id)).value.span; + + let suggestion = vec![ + (tcx.sess.source_map().start_point(body_span).shrink_to_hi(), " unsafe {".into()), + (tcx.sess.source_map().end_point(body_span).shrink_to_lo(), "}".into()), + ]; + + err.multipart_suggestion_verbose( + "consider wrapping the function in an unsafe block", + suggestion, + Applicability::MaybeIncorrect, + ); +} + fn unsafe_op_in_unsafe_fn_allowed(tcx: TyCtxt<'_>, id: HirId) -> bool { tcx.lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN, id).0 == Level::Allow } diff --git a/src/test/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.mir.stderr b/src/test/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.mir.stderr index fd58e1b1ebe37..7ee5b7bff0210 100644 --- a/src/test/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.mir.stderr +++ b/src/test/ui/unsafe/rfc-2585-unsafe_op_in_unsafe_fn.mir.stderr @@ -10,6 +10,14 @@ note: the lint level is defined here LL | #![deny(unsafe_op_in_unsafe_fn)] | ^^^^^^^^^^^^^^^^^^^^^^ = note: consult the function's documentation for information on how to avoid undefined behavior +help: consider wrapping the function in an unsafe block + | +LL ~ unsafe fn deny_level() { unsafe { +LL | unsf(); + ... +LL | +LL ~ }} + | error: dereference of raw pointer is unsafe and requires unsafe block (error E0133) --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:15:5 @@ -52,6 +60,14 @@ LL | #[deny(warnings)] | ^^^^^^^^ = note: `#[deny(unsafe_op_in_unsafe_fn)]` implied by `#[deny(warnings)]` = note: consult the function's documentation for information on how to avoid undefined behavior +help: consider wrapping the function in an unsafe block + | +LL ~ unsafe fn warning_level() { unsafe { +LL | unsf(); + ... +LL | +LL ~ }} + | error: dereference of raw pointer is unsafe and requires unsafe block (error E0133) --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:31:5 diff --git a/src/test/ui/unsafe/wrapping-unsafe-block-sugg.fixed b/src/test/ui/unsafe/wrapping-unsafe-block-sugg.fixed new file mode 100644 index 0000000000000..81b7d68bc4aca --- /dev/null +++ b/src/test/ui/unsafe/wrapping-unsafe-block-sugg.fixed @@ -0,0 +1,12 @@ +// run-rustfix + +#![deny(unsafe_op_in_unsafe_fn)] + +unsafe fn unsf() {} + +pub unsafe fn foo() { unsafe { + unsf(); //~ ERROR call to unsafe function is unsafe + unsf(); //~ ERROR call to unsafe function is unsafe +}} + +fn main() {} diff --git a/src/test/ui/unsafe/wrapping-unsafe-block-sugg.rs b/src/test/ui/unsafe/wrapping-unsafe-block-sugg.rs new file mode 100644 index 0000000000000..ef6936d91d187 --- /dev/null +++ b/src/test/ui/unsafe/wrapping-unsafe-block-sugg.rs @@ -0,0 +1,12 @@ +// run-rustfix + +#![deny(unsafe_op_in_unsafe_fn)] + +unsafe fn unsf() {} + +pub unsafe fn foo() { + unsf(); //~ ERROR call to unsafe function is unsafe + unsf(); //~ ERROR call to unsafe function is unsafe +} + +fn main() {} diff --git a/src/test/ui/unsafe/wrapping-unsafe-block-sugg.stderr b/src/test/ui/unsafe/wrapping-unsafe-block-sugg.stderr new file mode 100644 index 0000000000000..9eb5e983b1e22 --- /dev/null +++ b/src/test/ui/unsafe/wrapping-unsafe-block-sugg.stderr @@ -0,0 +1,30 @@ +error: call to unsafe function is unsafe and requires unsafe block (error E0133) + --> $DIR/wrapping-unsafe-block-sugg.rs:8:5 + | +LL | unsf(); + | ^^^^^^ call to unsafe function + | +note: the lint level is defined here + --> $DIR/wrapping-unsafe-block-sugg.rs:3:9 + | +LL | #![deny(unsafe_op_in_unsafe_fn)] + | ^^^^^^^^^^^^^^^^^^^^^^ + = note: consult the function's documentation for information on how to avoid undefined behavior +help: consider wrapping the function in an unsafe block + | +LL ~ pub unsafe fn foo() { unsafe { +LL | unsf(); +LL | unsf(); +LL ~ }} + | + +error: call to unsafe function is unsafe and requires unsafe block (error E0133) + --> $DIR/wrapping-unsafe-block-sugg.rs:9:5 + | +LL | unsf(); + | ^^^^^^ call to unsafe function + | + = note: consult the function's documentation for information on how to avoid undefined behavior + +error: aborting due to 2 previous errors +