blob: 287354292102c3c008055ab4bd4ce840cdb59b31 [file] [log] [blame]
//! Unsafety checker: every impl either implements a trait defined in this
//! crate or pertains to a type defined in this crate.
use rustc_errors::{codes::*, struct_span_code_err};
use rustc_hir::Unsafety;
use rustc_middle::ty::print::PrintTraitRefExt as _;
use rustc_middle::ty::{ImplPolarity::*, ImplTraitHeader, TraitDef, TyCtxt};
use rustc_span::def_id::LocalDefId;
use rustc_span::ErrorGuaranteed;
pub(super) fn check_item(
tcx: TyCtxt<'_>,
def_id: LocalDefId,
trait_header: ImplTraitHeader<'_>,
trait_def: &TraitDef,
) -> Result<(), ErrorGuaranteed> {
let unsafe_attr =
tcx.generics_of(def_id).own_params.iter().find(|p| p.pure_wrt_drop).map(|_| "may_dangle");
let trait_ref = trait_header.trait_ref.instantiate_identity();
match (trait_def.unsafety, unsafe_attr, trait_header.unsafety, trait_header.polarity) {
(Unsafety::Normal, None, Unsafety::Unsafe, Positive | Reservation) => {
let span = tcx.def_span(def_id);
return Err(struct_span_code_err!(
tcx.dcx(),
tcx.def_span(def_id),
E0199,
"implementing the trait `{}` is not unsafe",
trait_ref.print_trait_sugared()
)
.with_span_suggestion_verbose(
span.with_hi(span.lo() + rustc_span::BytePos(7)),
"remove `unsafe` from this trait implementation",
"",
rustc_errors::Applicability::MachineApplicable,
)
.emit());
}
(Unsafety::Unsafe, _, Unsafety::Normal, Positive | Reservation) => {
let span = tcx.def_span(def_id);
return Err(struct_span_code_err!(
tcx.dcx(),
span,
E0200,
"the trait `{}` requires an `unsafe impl` declaration",
trait_ref.print_trait_sugared()
)
.with_note(format!(
"the trait `{}` enforces invariants that the compiler can't check. \
Review the trait documentation and make sure this implementation \
upholds those invariants before adding the `unsafe` keyword",
trait_ref.print_trait_sugared()
))
.with_span_suggestion_verbose(
span.shrink_to_lo(),
"add `unsafe` to this trait implementation",
"unsafe ",
rustc_errors::Applicability::MaybeIncorrect,
)
.emit());
}
(Unsafety::Normal, Some(attr_name), Unsafety::Normal, Positive | Reservation) => {
let span = tcx.def_span(def_id);
return Err(struct_span_code_err!(
tcx.dcx(),
span,
E0569,
"requires an `unsafe impl` declaration due to `#[{}]` attribute",
attr_name
)
.with_note(format!(
"the trait `{}` enforces invariants that the compiler can't check. \
Review the trait documentation and make sure this implementation \
upholds those invariants before adding the `unsafe` keyword",
trait_ref.print_trait_sugared()
))
.with_span_suggestion_verbose(
span.shrink_to_lo(),
"add `unsafe` to this trait implementation",
"unsafe ",
rustc_errors::Applicability::MaybeIncorrect,
)
.emit());
}
(_, _, Unsafety::Unsafe, Negative) => {
// Reported in AST validation
assert!(tcx.dcx().has_errors().is_some(), "unsafe negative impl");
Ok(())
}
(_, _, Unsafety::Normal, Negative)
| (Unsafety::Unsafe, _, Unsafety::Unsafe, Positive | Reservation)
| (Unsafety::Normal, Some(_), Unsafety::Unsafe, Positive | Reservation)
| (Unsafety::Normal, None, Unsafety::Normal, _) => Ok(()),
}
}