1//! ## Recommended reading
2//! - [Errors and lints](https://rustc-dev-guide.rust-lang.org/diagnostics.html)
34use rustc_errors::{Diag, MultiSpan};
5use rustc_hir::def_id::DefId;
6use rustc_hir::{HirId, LangItem};
7use rustc_span::{STDLIB_STABLE_CRATES, Span};
8use tracing::debug;
910use crate::ferrocene::post_mono::InstantiationSite;
11use crate::ferrocene::{LintState, UNVALIDATED, UnvalidatedImplCause, Use, UseKind};
1213/// Diagnostics.
14impl<'tcx> LintState<'tcx> {
15fn func_span(&self, def_id: DefId) -> Span {
16match self.tcx.opt_item_ident(def_id) {
17Some(name) => name.span,
18None => self.tcx.def_span(def_id),
19 }
20 }
2122pub(super) fn lint_use(&mut self, lint_node: HirId, use_: Use<'tcx>) {
23let Self { tcx, item: owner, .. } = *self;
24let (callee, receiver_span) = (use_.def_id(), use_.span);
2526{
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:26",
"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(26u32),
::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:?}");
2728tcx.emit_node_span_lint(UNVALIDATED, lint_node, receiver_span, rustc_errors::DiagDecorator(|diag| {
29let callee_descr = tcx.def_descr(callee);
30let owner_descr = tcx.def_descr(owner.into());
31diag.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!(
32"validated {owner_descr} {} an unvalidated {callee_descr}",
33 use_.present_tense()
34 ));
3536// Need to do this lazily or `with_no_trimmed_paths` will panic :/
37let name = match use_.opt_instance() {
38None => tcx.def_path_str(callee),
39Some(instance) => tcx.def_path_str_with_args(callee, instance.args),
40 };
41diag.span_label(self.func_span(callee), ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("`{0}` is unvalidated", name))
})format!("`{name}` is unvalidated"));
4243if let UseKind::ContainsFnPtr(_, ty) = use_.kind {
44diag.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"));
45diag.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"));
46 }
4748if STDLIB_STABLE_CRATES.contains(&tcx.crate_name(callee.krate)) {
49diag.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!(
50"contact Ferrocene support to see if this {callee_descr} is possible to certify"
51));
52 }
5354// Don't show this "takes place in a validated function" label more than once per function.
55 // We really do need this as a separate bit of state from shown_lints because the lint might not be
56 // emitted. ideally we would just `cancel` the diagnostic if we don't want to emit it,
57 // but we don't get an owned `Diag` from `node_span_lint` :(
58if !self.shown_item {
59self.shown_item = true;
60let mut validated_span = MultiSpan::from_span(self.func_span(owner.into()));
61if let Some(annotation) = self.annotation {
62validated_span.push_span_label(annotation, "marked as validated here");
63 }
6465self.decorate_cast(use_, diag);
66self.decorate_instantiation(use_, diag, Some(&mut validated_span));
6768diag.span_note(
69validated_span,
70::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)),
71 );
72if self.annotation.is_none() {
73diag.note("main functions are assumed to be validated");
74 }
75 } else {
76self.decorate_cast(use_, diag);
77self.decorate_instantiation(use_, diag, None);
78 }
79 }));
80 }
8182fn decorate_cast(&self, use_: Use<'tcx>, diag: &mut Diag<'_, ()>) {
83let tcx = self.tcx;
84if #[allow(non_exhaustive_omitted_patterns)] match use_.kind {
UseKind::FnPtrCast(..) => true,
_ => false,
}matches!(use_.kind, UseKind::FnPtrCast(..)) {
85diag.note("once a function is cast to a function pointer, Ferrocene can no longer tell whether it is validated");
86diag.note("as a precaution, it must assume you will eventually call the function");
87 } else if let UseKind::TraitObjectCast(cause, ty) = use_.kind {
88diag.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"));
89match cause {
90 UnvalidatedImplCause::AssocFn(assoc_fn) => {
91diag.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!(
92"as a precaution, it must assume you will eventually call `{}`",
93 tcx.def_path_str(assoc_fn)
94 ));
95 }
96 UnvalidatedImplCause::UnresolvedGenericImpl(..) => {
97{
::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")98 }
99 }
100 }
101 }
102103fn decorate_instantiation(
104&self,
105 use_: Use<'tcx>,
106 diag: &mut Diag<'_, ()>,
107 validated_span: Option<&mut MultiSpan>,
108 ) {
109let tcx = self.tcx;
110if let Some(InstantiationSite {
111 caller_span,
112 caller_instance,
113 pre_mono_callee,
114 drop_fn,
115 lint_node: _,
116 }) = use_.from_instantiation
117 {
118let caller_descr =
119tcx.def_path_str_with_args(caller_instance.def_id(), caller_instance.args);
120121let drop = tcx.require_lang_item(LangItem::Drop, caller_span);
122let get_drop_impl = |def_id| {
123tcx.trait_impl_of_assoc(def_id).filter(|impl_| tcx.impl_trait_id(*impl_) == drop)
124 };
125126let msg = if let Some(impl_) =
127get_drop_impl(use_.def_id()).or(drop_fn.and_then(|drop| get_drop_impl(drop)))
128 {
129let dropped_ty = tcx.type_of(impl_).skip_binder();
130// Call to drop(), injected by the compiler.
131::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}`")132 } else {
133let 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!(
134"generic {} `{}`",
135 tcx.def_descr(pre_mono_callee),
136rustc_middle::ty::print::with_no_trimmed_paths!(
137 tcx.def_path_str(pre_mono_callee)
138 )
139 );
140141::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0} instantiated by `{1}`",
callee_descr, caller_descr))
})format!("{callee_descr} instantiated by `{caller_descr}`")142 };
143144if let Some(multi) = validated_span {
145multi.push_span_label(caller_span, msg);
146 } else {
147diag.span_note_once(
148caller_span,
149::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"),
150 );
151 }
152 }
153 }
154}
155156impl<'tcx> Use<'tcx> {
157fn present_tense(self) -> &'static str {
158match self.kind {
159 UseKind::Called(..) => "calls",
160// originally this said "type-erases" but that's unfamiliar jargon, and it's not clear
161 // that it actually helps understanding.
162UseKind::TraitObjectCast(..) | UseKind::FnPtrCast(..) => "possibly calls",
163 UseKind::ContainsFnPtr(..) => "uses",
164 }
165 }
166}