Skip to content

Commit 2b59992

Browse files
committed
Add suggestion to THIR unsafe_op_in_unsafe_fn lint
1 parent 2b2c0f9 commit 2b59992

13 files changed

+367
-40
lines changed

compiler/rustc_hir/src/hir.rs

+9
Original file line numberDiff line numberDiff line change
@@ -3566,6 +3566,15 @@ impl<'hir> OwnerNode<'hir> {
35663566
}
35673567
}
35683568

3569+
pub fn fn_sig(self) -> Option<&'hir FnSig<'hir>> {
3570+
match self {
3571+
OwnerNode::TraitItem(TraitItem { kind: TraitItemKind::Fn(fn_sig, _), .. })
3572+
| OwnerNode::ImplItem(ImplItem { kind: ImplItemKind::Fn(fn_sig, _), .. })
3573+
| OwnerNode::Item(Item { kind: ItemKind::Fn(fn_sig, _, _), .. }) => Some(fn_sig),
3574+
_ => None,
3575+
}
3576+
}
3577+
35693578
pub fn fn_decl(self) -> Option<&'hir FnDecl<'hir>> {
35703579
match self {
35713580
OwnerNode::TraitItem(TraitItem { kind: TraitItemKind::Fn(fn_sig, _), .. })

compiler/rustc_mir_build/messages.ftl

+3
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,7 @@ mir_build_unreachable_pattern = unreachable pattern
320320
.label = unreachable pattern
321321
.catchall_label = matches any value
322322
323+
mir_build_unsafe_fn_safe_body = an unsafe function restricts its caller, but its body is safe by default
323324
mir_build_unsafe_not_inherited = items do not inherit unsafety from separate enclosing items
324325
325326
mir_build_unsafe_op_in_unsafe_fn_borrow_of_layout_constrained_field_requires_unsafe =
@@ -386,3 +387,5 @@ mir_build_unused_unsafe = unnecessary `unsafe` block
386387
mir_build_unused_unsafe_enclosing_block_label = because it's nested under this `unsafe` block
387388
388389
mir_build_variant_defined_here = not covered
390+
391+
mir_build_wrap_suggestion = consider wrapping the function body in an unsafe block

compiler/rustc_mir_build/src/check_unsafety.rs

+66-10
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ struct UnsafetyVisitor<'a, 'tcx> {
3535
param_env: ParamEnv<'tcx>,
3636
inside_adt: bool,
3737
warnings: &'a mut Vec<UnusedUnsafeWarning>,
38+
39+
/// Flag to ensure that we only suggest wrapping the entire function body in
40+
/// an unsafe block once.
41+
suggest_unsafe_block: bool,
3842
}
3943

4044
impl<'tcx> UnsafetyVisitor<'_, 'tcx> {
@@ -95,7 +99,13 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> {
9599
SafetyContext::UnsafeFn if unsafe_op_in_unsafe_fn_allowed => {}
96100
SafetyContext::UnsafeFn => {
97101
// unsafe_op_in_unsafe_fn is disallowed
98-
kind.emit_unsafe_op_in_unsafe_fn_lint(self.tcx, self.hir_context, span);
102+
kind.emit_unsafe_op_in_unsafe_fn_lint(
103+
self.tcx,
104+
self.hir_context,
105+
span,
106+
self.suggest_unsafe_block,
107+
);
108+
self.suggest_unsafe_block = false;
99109
}
100110
SafetyContext::Safe => {
101111
kind.emit_requires_unsafe_err(
@@ -297,6 +307,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
297307
}
298308
PatKind::InlineConstant { def, .. } => {
299309
self.visit_inner_body(*def);
310+
visit::walk_pat(self, pat);
300311
}
301312
_ => {
302313
visit::walk_pat(self, pat);
@@ -545,7 +556,22 @@ impl UnsafeOpKind {
545556
tcx: TyCtxt<'_>,
546557
hir_id: hir::HirId,
547558
span: Span,
559+
suggest_unsafe_block: bool,
548560
) {
561+
let parent_id = tcx.hir().get_parent_item(hir_id);
562+
let parent_owner = tcx.hir().owner(parent_id);
563+
let should_suggest = parent_owner.fn_sig().map_or(false, |sig| sig.header.is_unsafe());
564+
let unsafe_not_inherited_note = if should_suggest {
565+
suggest_unsafe_block.then(|| {
566+
let body_span = tcx.hir().body(parent_owner.body_id().unwrap()).value.span;
567+
UnsafeNotInheritedLintNote {
568+
signature_span: tcx.def_span(parent_id.def_id),
569+
body_span,
570+
}
571+
})
572+
} else {
573+
None
574+
};
549575
// FIXME: ideally we would want to trim the def paths, but this is not
550576
// feasible with the current lint emission API (see issue #106126).
551577
match self {
@@ -556,61 +582,89 @@ impl UnsafeOpKind {
556582
UnsafeOpInUnsafeFnCallToUnsafeFunctionRequiresUnsafe {
557583
span,
558584
function: &with_no_trimmed_paths!(tcx.def_path_str(*did)),
585+
unsafe_not_inherited_note,
559586
},
560587
),
561588
CallToUnsafeFunction(None) => tcx.emit_spanned_lint(
562589
UNSAFE_OP_IN_UNSAFE_FN,
563590
hir_id,
564591
span,
565-
UnsafeOpInUnsafeFnCallToUnsafeFunctionRequiresUnsafeNameless { span },
592+
UnsafeOpInUnsafeFnCallToUnsafeFunctionRequiresUnsafeNameless {
593+
span,
594+
unsafe_not_inherited_note,
595+
},
566596
),
567597
UseOfInlineAssembly => tcx.emit_spanned_lint(
568598
UNSAFE_OP_IN_UNSAFE_FN,
569599
hir_id,
570600
span,
571-
UnsafeOpInUnsafeFnUseOfInlineAssemblyRequiresUnsafe { span },
601+
UnsafeOpInUnsafeFnUseOfInlineAssemblyRequiresUnsafe {
602+
span,
603+
unsafe_not_inherited_note,
604+
},
572605
),
573606
InitializingTypeWith => tcx.emit_spanned_lint(
574607
UNSAFE_OP_IN_UNSAFE_FN,
575608
hir_id,
576609
span,
577-
UnsafeOpInUnsafeFnInitializingTypeWithRequiresUnsafe { span },
610+
UnsafeOpInUnsafeFnInitializingTypeWithRequiresUnsafe {
611+
span,
612+
unsafe_not_inherited_note,
613+
},
578614
),
579615
UseOfMutableStatic => tcx.emit_spanned_lint(
580616
UNSAFE_OP_IN_UNSAFE_FN,
581617
hir_id,
582618
span,
583-
UnsafeOpInUnsafeFnUseOfMutableStaticRequiresUnsafe { span },
619+
UnsafeOpInUnsafeFnUseOfMutableStaticRequiresUnsafe {
620+
span,
621+
unsafe_not_inherited_note,
622+
},
584623
),
585624
UseOfExternStatic => tcx.emit_spanned_lint(
586625
UNSAFE_OP_IN_UNSAFE_FN,
587626
hir_id,
588627
span,
589-
UnsafeOpInUnsafeFnUseOfExternStaticRequiresUnsafe { span },
628+
UnsafeOpInUnsafeFnUseOfExternStaticRequiresUnsafe {
629+
span,
630+
unsafe_not_inherited_note,
631+
},
590632
),
591633
DerefOfRawPointer => tcx.emit_spanned_lint(
592634
UNSAFE_OP_IN_UNSAFE_FN,
593635
hir_id,
594636
span,
595-
UnsafeOpInUnsafeFnDerefOfRawPointerRequiresUnsafe { span },
637+
UnsafeOpInUnsafeFnDerefOfRawPointerRequiresUnsafe {
638+
span,
639+
unsafe_not_inherited_note,
640+
},
596641
),
597642
AccessToUnionField => tcx.emit_spanned_lint(
598643
UNSAFE_OP_IN_UNSAFE_FN,
599644
hir_id,
600645
span,
601-
UnsafeOpInUnsafeFnAccessToUnionFieldRequiresUnsafe { span },
646+
UnsafeOpInUnsafeFnAccessToUnionFieldRequiresUnsafe {
647+
span,
648+
unsafe_not_inherited_note,
649+
},
602650
),
603651
MutationOfLayoutConstrainedField => tcx.emit_spanned_lint(
604652
UNSAFE_OP_IN_UNSAFE_FN,
605653
hir_id,
606654
span,
607-
UnsafeOpInUnsafeFnMutationOfLayoutConstrainedFieldRequiresUnsafe { span },
655+
UnsafeOpInUnsafeFnMutationOfLayoutConstrainedFieldRequiresUnsafe {
656+
span,
657+
unsafe_not_inherited_note,
658+
},
608659
),
609660
BorrowOfLayoutConstrainedField => tcx.emit_spanned_lint(
610661
UNSAFE_OP_IN_UNSAFE_FN,
611662
hir_id,
612663
span,
613-
UnsafeOpInUnsafeFnBorrowOfLayoutConstrainedFieldRequiresUnsafe { span },
664+
UnsafeOpInUnsafeFnBorrowOfLayoutConstrainedFieldRequiresUnsafe {
665+
span,
666+
unsafe_not_inherited_note,
667+
},
614668
),
615669
CallToFunctionWith(did) => tcx.emit_spanned_lint(
616670
UNSAFE_OP_IN_UNSAFE_FN,
@@ -619,6 +673,7 @@ impl UnsafeOpKind {
619673
UnsafeOpInUnsafeFnCallToFunctionWithRequiresUnsafe {
620674
span,
621675
function: &with_no_trimmed_paths!(tcx.def_path_str(*did)),
676+
unsafe_not_inherited_note,
622677
},
623678
),
624679
}
@@ -833,6 +888,7 @@ pub fn thir_check_unsafety(tcx: TyCtxt<'_>, def: LocalDefId) {
833888
param_env: tcx.param_env(def),
834889
inside_adt: false,
835890
warnings: &mut warnings,
891+
suggest_unsafe_block: true,
836892
};
837893
visitor.visit_expr(&thir[expr]);
838894

compiler/rustc_mir_build/src/errors.rs

+43
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ pub struct UnsafeOpInUnsafeFnCallToUnsafeFunctionRequiresUnsafe<'a> {
2929
#[label]
3030
pub span: Span,
3131
pub function: &'a str,
32+
#[subdiagnostic]
33+
pub unsafe_not_inherited_note: Option<UnsafeNotInheritedLintNote>,
3234
}
3335

3436
#[derive(LintDiagnostic)]
@@ -37,6 +39,8 @@ pub struct UnsafeOpInUnsafeFnCallToUnsafeFunctionRequiresUnsafe<'a> {
3739
pub struct UnsafeOpInUnsafeFnCallToUnsafeFunctionRequiresUnsafeNameless {
3840
#[label]
3941
pub span: Span,
42+
#[subdiagnostic]
43+
pub unsafe_not_inherited_note: Option<UnsafeNotInheritedLintNote>,
4044
}
4145

4246
#[derive(LintDiagnostic)]
@@ -45,6 +49,8 @@ pub struct UnsafeOpInUnsafeFnCallToUnsafeFunctionRequiresUnsafeNameless {
4549
pub struct UnsafeOpInUnsafeFnUseOfInlineAssemblyRequiresUnsafe {
4650
#[label]
4751
pub span: Span,
52+
#[subdiagnostic]
53+
pub unsafe_not_inherited_note: Option<UnsafeNotInheritedLintNote>,
4854
}
4955

5056
#[derive(LintDiagnostic)]
@@ -53,6 +59,8 @@ pub struct UnsafeOpInUnsafeFnUseOfInlineAssemblyRequiresUnsafe {
5359
pub struct UnsafeOpInUnsafeFnInitializingTypeWithRequiresUnsafe {
5460
#[label]
5561
pub span: Span,
62+
#[subdiagnostic]
63+
pub unsafe_not_inherited_note: Option<UnsafeNotInheritedLintNote>,
5664
}
5765

5866
#[derive(LintDiagnostic)]
@@ -61,6 +69,8 @@ pub struct UnsafeOpInUnsafeFnInitializingTypeWithRequiresUnsafe {
6169
pub struct UnsafeOpInUnsafeFnUseOfMutableStaticRequiresUnsafe {
6270
#[label]
6371
pub span: Span,
72+
#[subdiagnostic]
73+
pub unsafe_not_inherited_note: Option<UnsafeNotInheritedLintNote>,
6474
}
6575

6676
#[derive(LintDiagnostic)]
@@ -69,6 +79,8 @@ pub struct UnsafeOpInUnsafeFnUseOfMutableStaticRequiresUnsafe {
6979
pub struct UnsafeOpInUnsafeFnUseOfExternStaticRequiresUnsafe {
7080
#[label]
7181
pub span: Span,
82+
#[subdiagnostic]
83+
pub unsafe_not_inherited_note: Option<UnsafeNotInheritedLintNote>,
7284
}
7385

7486
#[derive(LintDiagnostic)]
@@ -77,6 +89,8 @@ pub struct UnsafeOpInUnsafeFnUseOfExternStaticRequiresUnsafe {
7789
pub struct UnsafeOpInUnsafeFnDerefOfRawPointerRequiresUnsafe {
7890
#[label]
7991
pub span: Span,
92+
#[subdiagnostic]
93+
pub unsafe_not_inherited_note: Option<UnsafeNotInheritedLintNote>,
8094
}
8195

8296
#[derive(LintDiagnostic)]
@@ -85,6 +99,8 @@ pub struct UnsafeOpInUnsafeFnDerefOfRawPointerRequiresUnsafe {
8599
pub struct UnsafeOpInUnsafeFnAccessToUnionFieldRequiresUnsafe {
86100
#[label]
87101
pub span: Span,
102+
#[subdiagnostic]
103+
pub unsafe_not_inherited_note: Option<UnsafeNotInheritedLintNote>,
88104
}
89105

90106
#[derive(LintDiagnostic)]
@@ -93,13 +109,17 @@ pub struct UnsafeOpInUnsafeFnAccessToUnionFieldRequiresUnsafe {
93109
pub struct UnsafeOpInUnsafeFnMutationOfLayoutConstrainedFieldRequiresUnsafe {
94110
#[label]
95111
pub span: Span,
112+
#[subdiagnostic]
113+
pub unsafe_not_inherited_note: Option<UnsafeNotInheritedLintNote>,
96114
}
97115

98116
#[derive(LintDiagnostic)]
99117
#[diag(mir_build_unsafe_op_in_unsafe_fn_borrow_of_layout_constrained_field_requires_unsafe)]
100118
pub struct UnsafeOpInUnsafeFnBorrowOfLayoutConstrainedFieldRequiresUnsafe {
101119
#[label]
102120
pub span: Span,
121+
#[subdiagnostic]
122+
pub unsafe_not_inherited_note: Option<UnsafeNotInheritedLintNote>,
103123
}
104124

105125
#[derive(LintDiagnostic)]
@@ -109,6 +129,8 @@ pub struct UnsafeOpInUnsafeFnCallToFunctionWithRequiresUnsafe<'a> {
109129
#[label]
110130
pub span: Span,
111131
pub function: &'a str,
132+
#[subdiagnostic]
133+
pub unsafe_not_inherited_note: Option<UnsafeNotInheritedLintNote>,
112134
}
113135

114136
#[derive(Diagnostic)]
@@ -376,6 +398,27 @@ pub struct UnsafeNotInheritedNote {
376398
pub span: Span,
377399
}
378400

401+
pub struct UnsafeNotInheritedLintNote {
402+
pub signature_span: Span,
403+
pub body_span: Span,
404+
}
405+
406+
impl AddToDiagnostic for UnsafeNotInheritedLintNote {
407+
fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F)
408+
where
409+
F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
410+
{
411+
diag.span_note(self.signature_span, fluent::mir_build_unsafe_fn_safe_body);
412+
let body_start = self.body_span.shrink_to_lo();
413+
let body_end = self.body_span.shrink_to_hi();
414+
diag.tool_only_multipart_suggestion(
415+
fluent::mir_build_wrap_suggestion,
416+
vec![(body_start, "{ unsafe ".into()), (body_end, "}".into())],
417+
Applicability::MaybeIncorrect,
418+
);
419+
}
420+
}
421+
379422
#[derive(LintDiagnostic)]
380423
#[diag(mir_build_unused_unsafe)]
381424
pub struct UnusedUnsafe {

tests/ui/unsafe/edition-2024-unsafe_op_in_unsafe_fn.stderr tests/ui/unsafe/edition-2024-unsafe_op_in_unsafe_fn.mir.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
warning: call to unsafe function is unsafe and requires unsafe block (error E0133)
2-
--> $DIR/edition-2024-unsafe_op_in_unsafe_fn.rs:12:5
2+
--> $DIR/edition-2024-unsafe_op_in_unsafe_fn.rs:13:5
33
|
44
LL | unsf();
55
| ^^^^^^ call to unsafe function
66
|
77
= note: consult the function's documentation for information on how to avoid undefined behavior
88
note: an unsafe function restricts its caller, but its body is safe by default
9-
--> $DIR/edition-2024-unsafe_op_in_unsafe_fn.rs:11:1
9+
--> $DIR/edition-2024-unsafe_op_in_unsafe_fn.rs:12:1
1010
|
1111
LL | unsafe fn foo() {
1212
| ^^^^^^^^^^^^^^^
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,21 @@
11
// edition: 2024
22
// compile-flags: -Zunstable-options
33
// check-pass
4+
// revisions: mir thir
5+
// [thir]compile-flags: -Zthir-unsafeck
46

57
#![crate_type = "lib"]
6-
78
#![deny(unused_unsafe)]
89

910
unsafe fn unsf() {}
1011

1112
unsafe fn foo() {
1213
unsf();
13-
//~^ WARN call to unsafe function is unsafe and requires unsafe block
14+
//[mir]~^ WARN call to unsafe function is unsafe and requires unsafe block
15+
//[thir]~^^ WARN call to unsafe function `unsf` is unsafe and requires unsafe block
1416

1517
// no unused_unsafe
16-
unsafe { unsf(); }
18+
unsafe {
19+
unsf();
20+
}
1721
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
warning: call to unsafe function `unsf` is unsafe and requires unsafe block (error E0133)
2+
--> $DIR/edition-2024-unsafe_op_in_unsafe_fn.rs:13:5
3+
|
4+
LL | unsf();
5+
| ^^^^^^ call to unsafe function
6+
|
7+
= note: consult the function's documentation for information on how to avoid undefined behavior
8+
note: an unsafe function restricts its caller, but its body is safe by default
9+
--> $DIR/edition-2024-unsafe_op_in_unsafe_fn.rs:12:1
10+
|
11+
LL | unsafe fn foo() {
12+
| ^^^^^^^^^^^^^^^
13+
= note: `#[warn(unsafe_op_in_unsafe_fn)]` on by default
14+
15+
warning: 1 warning emitted
16+

0 commit comments

Comments
 (0)