1//! ## Recommended reading
2//! - [Errors and lints](https://rustc-dev-guide.rust-lang.org/diagnostics.html)
34use rustc_errors::{Diag, MultiSpan};
5use rustc_hir::HirId;
6use rustc_hir::attrs::LangItem;
7use rustc_hir::def_id::DefId;
8use rustc_span::{STDLIB_STABLE_CRATES, Span};
9use tracing::debug;
1011use crate::ferrocene::post_mono::InstantiationSite;
12use crate::ferrocene::{LintState, UNVALIDATED, UnvalidatedImplCause, Use, UseKind};
1314/// Diagnostics.
15impl<'tcx> LintState<'tcx> {
16fn func_span(&self, def_id: DefId) -> Span {
17match self.tcx.opt_item_ident(def_id) {
18Some(name) => name.span,
19None => self.tcx.def_span(def_id),
20 }
21 }
2223pub(super) fn lint_use(&mut self, lint_node: HirId, use_: Use<'tcx>) {
24let Self { tcx, item: owner, .. } = *self;
25let (callee, receiver_span) = (use_.def_id(), use_.span);
2627{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_lint/src/ferrocene/diagnostics.rs:27",
"rustc_lint::ferrocene::diagnostics",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_lint/src/ferrocene/diagnostics.rs"),
::tracing_core::__macro_support::Option::Some(27u32),
::tracing_core::__macro_support::Option::Some("rustc_lint::ferrocene::diagnostics"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("linting node {0:?}",
lint_node) as &dyn ::tracing::field::Value))])
});
} else { ; }
};debug!("linting node {lint_node:?}");
2829tcx.emit_node_span_lint(UNVALIDATED, lint_node, receiver_span, rustc_errors::DiagDecorator(|diag| {
30let callee_descr = tcx.def_descr(callee);
31let owner_descr = tcx.def_descr(owner.into());
32diag.primary_message(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("validated {1} {0} an unvalidated {2}",
use_.present_tense(), owner_descr, callee_descr))
})format!(
33"validated {owner_descr} {} an unvalidated {callee_descr}",
34 use_.present_tense()
35 ));
3637// Need to do this lazily or `with_no_trimmed_paths` will panic :/
38let name = match use_.opt_instance() {
39None => tcx.def_path_str(callee),
40Some(instance) => tcx.def_path_str_with_args(callee, instance.args),
41 };
42diag.span_label(self.func_span(callee), ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("`{0}` is unvalidated", name))
})format!("`{name}` is unvalidated"));
4344if let UseKind::ContainsFnPtr(_, ty) = use_.kind {
45diag.note(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("`{0}` contains a function pointer that might be called at runtime",
name))
})format!("`{name}` contains a function pointer that might be called at runtime"));
46diag.note(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("the Ferrocene compiler does not know if the `{0}` was verified, so it must assume it is unverified",
ty))
})format!("the Ferrocene compiler does not know if the `{ty}` was verified, so it must assume it is unverified"));
47 }
4849if STDLIB_STABLE_CRATES.contains(&tcx.crate_name(callee.krate)) {
50diag.help_once(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("contact Ferrocene support to see if this {0} is possible to certify",
callee_descr))
})format!(
51"contact Ferrocene support to see if this {callee_descr} is possible to certify"
52));
53 }
5455// Don't show this "takes place in a validated function" label more than once per function.
56 // We really do need this as a separate bit of state from shown_lints because the lint might not be
57 // emitted. ideally we would just `cancel` the diagnostic if we don't want to emit it,
58 // but we don't get an owned `Diag` from `node_span_lint` :(
59if !self.shown_item {
60self.shown_item = true;
61let mut validated_span = MultiSpan::from_span(self.func_span(owner.into()));
62if let Some(annotation) = self.annotation {
63validated_span.push_span_label(annotation, "marked as validated here");
64 }
6566self.decorate_cast(use_, diag);
67self.decorate_instantiation(use_, diag, Some(&mut validated_span));
6869diag.span_note(
70validated_span,
71::alloc::__export::must_use({
::alloc::fmt::format(format_args!("`{0}` is validated",
tcx.def_path_str(owner)))
})format!("`{}` is validated", tcx.def_path_str(owner)),
72 );
73if self.annotation.is_none() {
74diag.note("main functions are assumed to be validated");
75 }
76 } else {
77self.decorate_cast(use_, diag);
78self.decorate_instantiation(use_, diag, None);
79 }
80 }));
81 }
8283fn decorate_cast(&self, use_: Use<'tcx>, diag: &mut Diag<'_, ()>) {
84let tcx = self.tcx;
85if #[allow(non_exhaustive_omitted_patterns)] match use_.kind {
UseKind::FnPtrCast(..) => true,
_ => false,
}matches!(use_.kind, UseKind::FnPtrCast(..)) {
86diag.note("once a function is cast to a function pointer, Ferrocene can no longer tell whether it is validated");
87diag.note("as a precaution, it must assume you will eventually call the function");
88 } else if let UseKind::TraitObjectCast(cause, ty) = use_.kind {
89diag.note(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("once `{0}` is cast to a dynamic trait object, Ferrocene can no longer tell whether it is validated",
ty))
})format!("once `{ty}` is cast to a dynamic trait object, Ferrocene can no longer tell whether it is validated"));
90match cause {
91 UnvalidatedImplCause::AssocFn(assoc_fn) => {
92diag.note(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("as a precaution, it must assume you will eventually call `{0}`",
tcx.def_path_str(assoc_fn)))
})format!(
93"as a precaution, it must assume you will eventually call `{}`",
94 tcx.def_path_str(assoc_fn)
95 ));
96 }
97 UnvalidatedImplCause::UnresolvedGenericImpl(..) => {
98{
::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
format_args!("all generics should be resolved by post-mono")));
}unreachable!("all generics should be resolved by post-mono")99 }
100 }
101 }
102 }
103104fn decorate_instantiation(
105&self,
106 use_: Use<'tcx>,
107 diag: &mut Diag<'_, ()>,
108 validated_span: Option<&mut MultiSpan>,
109 ) {
110let tcx = self.tcx;
111if let Some(InstantiationSite {
112 caller_span,
113 caller_instance,
114 pre_mono_callee,
115 drop_fn,
116 lint_node: _,
117 }) = use_.from_instantiation
118 {
119let caller_descr =
120tcx.def_path_str_with_args(caller_instance.def_id(), caller_instance.args);
121122let drop = tcx.require_lang_item(LangItem::Drop, caller_span);
123let get_drop_impl = |def_id| {
124tcx.trait_impl_of_assoc(def_id).filter(|impl_| tcx.impl_trait_id(*impl_) == drop)
125 };
126127let msg = if let Some(impl_) =
128get_drop_impl(use_.def_id()).or(drop_fn.and_then(|drop| get_drop_impl(drop)))
129 {
130let dropped_ty = tcx.type_of(impl_).skip_binder();
131// Call to drop(), injected by the compiler.
132::alloc::__export::must_use({
::alloc::fmt::format(format_args!("`{0}` dropped here, in `{1}`",
dropped_ty, caller_descr))
})format!("`{dropped_ty}` dropped here, in `{caller_descr}`")133 } else {
134let callee_descr = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("generic {0} `{1}`",
tcx.def_descr(pre_mono_callee),
{
let _guard = NoTrimmedGuard::new();
tcx.def_path_str(pre_mono_callee)
}))
})format!(
135"generic {} `{}`",
136 tcx.def_descr(pre_mono_callee),
137rustc_middle::ty::print::with_no_trimmed_paths!(
138 tcx.def_path_str(pre_mono_callee)
139 )
140 );
141142::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0} instantiated by `{1}`",
callee_descr, caller_descr))
})format!("{callee_descr} instantiated by `{caller_descr}`")143 };
144145if let Some(multi) = validated_span {
146multi.push_span_label(caller_span, msg);
147 } else {
148diag.span_note_once(
149caller_span,
150::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}, which is called from a validated entrypoint",
msg))
})format!("{msg}, which is called from a validated entrypoint"),
151 );
152 }
153 }
154 }
155}
156157impl<'tcx> Use<'tcx> {
158fn present_tense(self) -> &'static str {
159match self.kind {
160 UseKind::Called(..) => "calls",
161// originally this said "type-erases" but that's unfamiliar jargon, and it's not clear
162 // that it actually helps understanding.
163UseKind::TraitObjectCast(..) | UseKind::FnPtrCast(..) => "possibly calls",
164 UseKind::ContainsFnPtr(..) => "uses",
165 }
166 }
167}