Make DecorateDiagCompat::Dynamic take an Any reference allowing to downcast to Session

This commit is contained in:
Guillaume Gomez
2026-03-25 23:25:45 +01:00
parent 99f9ba0a33
commit 6f751ec182
7 changed files with 101 additions and 66 deletions
+26 -2
View File
@@ -1,3 +1,5 @@
use std::any::Any;
/// This module provides types and traits for buffering lints until later in compilation.
use rustc_ast::node_id::NodeId;
use rustc_data_structures::fx::FxIndexMap;
@@ -10,9 +12,11 @@
/// We can't implement `Diagnostic` for `BuiltinLintDiag`, because decorating some of its
/// variants requires types we don't have yet. So, handle that case separately.
pub enum DecorateDiagCompat {
/// The third argument of the closure is a `Session`. However, due to the dependency tree,
/// we don't have access to `rustc_session` here, so we downcast it when needed.
Dynamic(
Box<
dyn for<'a> FnOnce(DiagCtxtHandle<'a>, Level) -> Diag<'a, ()>
dyn for<'a> FnOnce(DiagCtxtHandle<'a>, Level, &dyn Any) -> Diag<'a, ()>
+ DynSync
+ DynSend
+ 'static,
@@ -30,7 +34,7 @@ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
impl<D: for<'a> Diagnostic<'a, ()> + DynSync + DynSend + 'static> From<D> for DecorateDiagCompat {
#[inline]
fn from(d: D) -> Self {
Self::Dynamic(Box::new(|dcx, level| d.into_diag(dcx, level)))
Self::Dynamic(Box::new(|dcx, level, _| d.into_diag(dcx, level)))
}
}
@@ -97,6 +101,26 @@ pub fn dyn_buffer_lint<
node_id: NodeId,
span: impl Into<MultiSpan>,
callback: F,
) {
self.add_early_lint(BufferedEarlyLint {
lint_id: LintId::of(lint),
node_id,
span: Some(span.into()),
diagnostic: DecorateDiagCompat::Dynamic(Box::new(|dcx, level, _| callback(dcx, level))),
});
}
pub fn dyn_buffer_lint_any<
F: for<'a> FnOnce(DiagCtxtHandle<'a>, Level, &dyn Any) -> Diag<'a, ()>
+ DynSend
+ DynSync
+ 'static,
>(
&mut self,
lint: &'static Lint,
node_id: NodeId,
span: impl Into<MultiSpan>,
callback: F,
) {
self.add_early_lint(BufferedEarlyLint {
lint_id: LintId::of(lint),
-11
View File
@@ -7,7 +7,6 @@
use std::path::PathBuf;
use std::thread::panicking;
use rustc_data_structures::sync::{DynSend, DynSync};
use rustc_error_messages::{DiagArgMap, DiagArgName, DiagArgValue, IntoDiagArg};
use rustc_lint_defs::{Applicability, LintExpectationId};
use rustc_macros::{Decodable, Encodable};
@@ -119,16 +118,6 @@ fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> {
}
}
impl<'a> Diagnostic<'a, ()>
for Box<
dyn for<'b> FnOnce(DiagCtxtHandle<'b>, Level) -> Diag<'b, ()> + DynSync + DynSend + 'static,
>
{
fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> {
self(dcx, level)
}
}
/// Type used to emit diagnostic through a closure instead of implementing the `Diagnostic` trait.
pub struct DiagDecorator<F: FnOnce(&mut Diag<'_, ()>)>(pub F);
+7 -3
View File
@@ -15,9 +15,9 @@
use rustc_span::{DUMMY_SP, Ident, Span};
use tracing::debug;
use crate::DecorateBuiltinLint;
use crate::context::{EarlyContext, LintContext, LintStore};
use crate::passes::{EarlyLintPass, EarlyLintPassObject};
use crate::{DecorateBuiltinLint, DiagAndSess};
pub(super) mod diagnostics;
@@ -49,8 +49,12 @@ fn check_id(&mut self, id: ast::NodeId) {
},
);
}
DecorateDiagCompat::Dynamic(d) => {
self.context.opt_span_lint(lint_id.lint, span, d);
DecorateDiagCompat::Dynamic(callback) => {
self.context.opt_span_lint(
lint_id.lint,
span,
DiagAndSess { callback, sess: self.context.sess() },
);
}
}
}
@@ -1,5 +1,7 @@
use std::any::Any;
use std::borrow::Cow;
use rustc_data_structures::sync::DynSend;
use rustc_errors::{
Applicability, Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, Level,
elided_lifetime_in_path_suggestion,
@@ -13,6 +15,19 @@
mod check_cfg;
pub struct DiagAndSess<'sess> {
pub callback: Box<
dyn for<'b> FnOnce(DiagCtxtHandle<'b>, Level, &dyn Any) -> Diag<'b, ()> + DynSend + 'static,
>,
pub sess: &'sess Session,
}
impl<'a> Diagnostic<'a, ()> for DiagAndSess<'_> {
fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> {
(self.callback)(dcx, level, self.sess)
}
}
/// This is a diagnostic struct that will decorate a `BuiltinLintDiag`
/// Directly creating the lint structs is expensive, using this will only decorate the lint structs when needed.
pub struct DecorateBuiltinLint<'sess, 'tcx> {
+17 -13
View File
@@ -509,22 +509,26 @@ pub(crate) fn lint_if_path_starts_with_module(
return;
}
let (replacement, applicability) =
match self.tcx.sess.source_map().span_to_snippet(root_span) {
Ok(ref s) => {
// FIXME(Manishearth) ideally the emitting code
// can tell us whether or not this is global
let opt_colon = if s.trim_start().starts_with("::") { "" } else { "::" };
(format!("crate{opt_colon}{s}"), Applicability::MachineApplicable)
}
Err(_) => ("crate::<path>".to_string(), Applicability::HasPlaceholders),
};
self.lint_buffer.dyn_buffer_lint(
self.lint_buffer.dyn_buffer_lint_any(
ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE,
node_id,
root_span,
move |dcx, level| {
move |dcx, level, sess| {
let (replacement, applicability) = match sess
.downcast_ref::<Session>()
.expect("expected a `Session`")
.source_map()
.span_to_snippet(root_span)
{
Ok(ref s) => {
// FIXME(Manishearth) ideally the emitting code
// can tell us whether or not this is global
let opt_colon = if s.trim_start().starts_with("::") { "" } else { "::" };
(format!("crate{opt_colon}{s}"), Applicability::MachineApplicable)
}
Err(_) => ("crate::<path>".to_string(), Applicability::HasPlaceholders),
};
errors::AbsPathWithModule {
sugg: errors::AbsPathWithModuleSugg {
span: root_span,
+35 -36
View File
@@ -3641,49 +3641,48 @@ pub(crate) fn maybe_report_lifetime_uses(
match use_set {
Some(LifetimeUseSet::Many) => {}
Some(LifetimeUseSet::One { use_span, use_ctxt }) => {
debug!(?param.ident, ?param.ident.span, ?use_span);
let elidable = matches!(use_ctxt, LifetimeCtxt::Ref);
let param_ident = param.ident;
let deletion_span =
if param.bounds.is_empty() { deletion_span() } else { None };
let tcx = self.r.tcx;
let param_ident = param.ident;
let sugg_data = deletion_span.map(|deletion_span| {
if elidable {
let use_span =
tcx.sess.source_map().span_extend_while_whitespace(use_span);
(deletion_span, use_span, String::new())
} else {
(deletion_span, use_span, "'_".to_owned())
}
});
self.r.lint_buffer.dyn_buffer_lint(
self.r.lint_buffer.dyn_buffer_lint_any(
lint::builtin::SINGLE_USE_LIFETIMES,
param.id,
param.ident.span,
move |dcx, level| {
let suggestion =
sugg_data.map(|(deletion_span, use_span, replace_lt)| {
// issue 107998 for the case such as a wrong function pointer type
// `deletion_span` is empty and there is no need to report lifetime uses here
let deletion_span = if deletion_span.is_empty() {
None
} else {
Some(deletion_span)
};
errors::SingleUseLifetimeSugg {
deletion_span,
use_span,
replace_lt,
}
});
let param_span = param_ident.span;
debug!(?param_span, ?use_span, ?deletion_span);
param_ident.span,
move |dcx, level, sess| {
debug!(?param_ident, ?param_ident.span, ?use_span);
let elidable = matches!(use_ctxt, LifetimeCtxt::Ref);
let suggestion = if let Some(deletion_span) = deletion_span {
let (use_span, replace_lt) = if elidable {
let use_span = sess
.downcast_ref::<Session>()
.expect("expected a `Session`")
.source_map()
.span_extend_while_whitespace(use_span);
(use_span, String::new())
} else {
(use_span, "'_".to_owned())
};
debug!(?deletion_span, ?use_span);
// issue 107998 for the case such as a wrong function pointer type
// `deletion_span` is empty and there is no need to report lifetime uses here
let deletion_span = if deletion_span.is_empty() {
None
} else {
Some(deletion_span)
};
Some(errors::SingleUseLifetimeSugg {
deletion_span,
use_span,
replace_lt,
})
} else {
None
};
errors::SingleUseLifetime {
suggestion,
param_span,
param_span: param_ident.span,
use_span,
ident: param_ident,
}
+1 -1
View File
@@ -344,7 +344,7 @@ pub fn dyn_buffer_lint<
lint,
Some(span.into()),
node_id,
DecorateDiagCompat::Dynamic(Box::new(callback)),
DecorateDiagCompat::Dynamic(Box::new(|dcx, level, _| callback(dcx, level))),
)
}