Rollup merge of #145747 - joshtriplett:builtin-diag-dyn, r=jdonszelmann

Refactor lint buffering to avoid requiring a giant enum

Lint buffering currently relies on a giant enum `BuiltinLintDiag` containing all the lints that might potentially get buffered. In addition to being an unwieldy enum in a central crate, this also makes `rustc_lint_defs` a build bottleneck: it depends on various types from various crates (with a steady pressure to add more), and many crates depend on it.

Having all of these variants in a separate crate also prevents detecting when a variant becomes unused, which we can do with a dedicated type defined and used in the same crate.

Refactor this to use a dyn trait, to allow using `LintDiagnostic` types directly.

Because the existing `BuiltinLintDiag` requires some additional types in order to decorate some variants, which are only available later in `rustc_lint`, use an enum `DecorateDiagCompat` to handle both the `dyn LintDiagnostic` case and the `BuiltinLintDiag` case.

---

With the infrastructure in place, use it to migrate three of the enum variants to use `LintDiagnostic` directly, as a proof of concept and to demonstrate that the net result is a reduction in code size and a removal of a boilerplate-heavy layer of indirection.

Also remove an unused `BuiltinLintDiag` variant.
This commit is contained in:
Jacob Pratt 2025-08-22 22:00:59 -04:00 committed by GitHub
commit d3c9908a8a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
29 changed files with 265 additions and 277 deletions

View file

@ -3797,6 +3797,7 @@ dependencies = [
"annotate-snippets 0.11.5",
"derive_setters",
"rustc_abi",
"rustc_ast",
"rustc_data_structures",
"rustc_error_codes",
"rustc_error_messages",
@ -4134,7 +4135,6 @@ dependencies = [
name = "rustc_lint_defs"
version = "0.0.0"
dependencies = [
"rustc_abi",
"rustc_ast",
"rustc_data_structures",
"rustc_error_messages",

View file

@ -113,6 +113,10 @@ ast_passes_extern_without_abi = `extern` declarations without an explicit ABI ar
.suggestion = specify an ABI
.help = prior to Rust 2024, a default ABI was inferred
ast_passes_extern_without_abi_sugg = `extern` declarations without an explicit ABI are deprecated
.label = ABI should be specified here
.suggestion = explicitly specify the {$default_abi} ABI
ast_passes_feature_on_non_nightly = `#![feature]` may not be used on the {$channel} release channel
.suggestion = remove the attribute
.stable_since = the feature `{$name}` has been stable since `{$since}` and no longer requires an attribute to enable

View file

@ -27,14 +27,14 @@ use rustc_ast::*;
use rustc_ast_pretty::pprust::{self, State};
use rustc_attr_parsing::validate_attr;
use rustc_data_structures::fx::FxIndexMap;
use rustc_errors::DiagCtxtHandle;
use rustc_errors::{DiagCtxtHandle, LintBuffer};
use rustc_feature::Features;
use rustc_session::Session;
use rustc_session::lint::BuiltinLintDiag;
use rustc_session::lint::builtin::{
DEPRECATED_WHERE_CLAUSE_LOCATION, MISSING_ABI, MISSING_UNSAFE_ON_EXTERN,
PATTERNS_IN_FNS_WITHOUT_BODY,
};
use rustc_session::lint::{BuiltinLintDiag, LintBuffer};
use rustc_span::{Ident, Span, kw, sym};
use rustc_target::spec::{AbiMap, AbiMapping};
use thin_vec::thin_vec;
@ -876,7 +876,7 @@ impl<'a> AstValidator<'a> {
MISSING_ABI,
id,
span,
BuiltinLintDiag::MissingAbi(span, ExternAbi::FALLBACK),
errors::MissingAbiSugg { span, default_abi: ExternAbi::FALLBACK },
)
}
}

View file

@ -4,7 +4,7 @@ use rustc_abi::ExternAbi;
use rustc_ast::ParamKindOrd;
use rustc_errors::codes::*;
use rustc_errors::{Applicability, Diag, EmissionGuarantee, Subdiagnostic};
use rustc_macros::{Diagnostic, Subdiagnostic};
use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
use rustc_span::{Ident, Span, Symbol};
use crate::fluent_generated as fluent;
@ -815,6 +815,14 @@ pub(crate) struct MissingAbi {
pub span: Span,
}
#[derive(LintDiagnostic)]
#[diag(ast_passes_extern_without_abi_sugg)]
pub(crate) struct MissingAbiSugg {
#[suggestion(code = "extern {default_abi}", applicability = "machine-applicable")]
pub span: Span,
pub default_abi: ExternAbi,
}
#[derive(Diagnostic)]
#[diag(ast_passes_abi_custom_safe_foreign_function)]
pub(crate) struct AbiCustomSafeForeignFunction {

View file

@ -10,11 +10,12 @@ use rustc_ast::{
};
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::{
Applicability, Diag, MultiSpan, PResult, SingleLabelManySpans, listify, pluralize,
Applicability, BufferedEarlyLint, Diag, MultiSpan, PResult, SingleLabelManySpans, listify,
pluralize,
};
use rustc_expand::base::*;
use rustc_lint_defs::builtin::NAMED_ARGUMENTS_USED_POSITIONALLY;
use rustc_lint_defs::{BufferedEarlyLint, BuiltinLintDiag, LintId};
use rustc_lint_defs::{BuiltinLintDiag, LintId};
use rustc_parse::exp;
use rustc_parse_format as parse;
use rustc_span::{BytePos, ErrorGuaranteed, Ident, InnerSpan, Span, Symbol};
@ -595,7 +596,8 @@ fn make_format_args(
named_arg_sp: arg_name.span,
named_arg_name: arg_name.name.to_string(),
is_formatting_arg: matches!(used_as, Width | Precision),
},
}
.into(),
});
}
}

View file

@ -8,6 +8,7 @@ edition = "2024"
annotate-snippets = "0.11"
derive_setters = "0.1.6"
rustc_abi = { path = "../rustc_abi" }
rustc_ast = { path = "../rustc_ast" }
rustc_data_structures = { path = "../rustc_data_structures" }
rustc_error_codes = { path = "../rustc_error_codes" }
rustc_error_messages = { path = "../rustc_error_messages" }

View file

@ -0,0 +1,85 @@
/// 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;
use rustc_error_messages::MultiSpan;
use rustc_lint_defs::{BuiltinLintDiag, Lint, LintId};
use crate::{DynSend, LintDiagnostic, LintDiagnosticBox};
/// We can't implement `LintDiagnostic` for `BuiltinLintDiag`, because decorating some of its
/// variants requires types we don't have yet. So, handle that case separately.
pub enum DecorateDiagCompat {
Dynamic(Box<dyn for<'a> LintDiagnosticBox<'a, ()> + DynSend + 'static>),
Builtin(BuiltinLintDiag),
}
impl std::fmt::Debug for DecorateDiagCompat {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("DecorateDiagCompat").finish()
}
}
impl !LintDiagnostic<'_, ()> for BuiltinLintDiag {}
impl<D: for<'a> LintDiagnostic<'a, ()> + DynSend + 'static> From<D> for DecorateDiagCompat {
#[inline]
fn from(d: D) -> Self {
Self::Dynamic(Box::new(d))
}
}
impl From<BuiltinLintDiag> for DecorateDiagCompat {
#[inline]
fn from(b: BuiltinLintDiag) -> Self {
Self::Builtin(b)
}
}
/// Lints that are buffered up early on in the `Session` before the
/// `LintLevels` is calculated.
#[derive(Debug)]
pub struct BufferedEarlyLint {
/// The span of code that we are linting on.
pub span: Option<MultiSpan>,
/// The `NodeId` of the AST node that generated the lint.
pub node_id: NodeId,
/// A lint Id that can be passed to
/// `rustc_lint::early::EarlyContextAndPass::check_id`.
pub lint_id: LintId,
/// Customization of the `Diag<'_>` for the lint.
pub diagnostic: DecorateDiagCompat,
}
#[derive(Default, Debug)]
pub struct LintBuffer {
pub map: FxIndexMap<NodeId, Vec<BufferedEarlyLint>>,
}
impl LintBuffer {
pub fn add_early_lint(&mut self, early_lint: BufferedEarlyLint) {
self.map.entry(early_lint.node_id).or_default().push(early_lint);
}
pub fn take(&mut self, id: NodeId) -> Vec<BufferedEarlyLint> {
// FIXME(#120456) - is `swap_remove` correct?
self.map.swap_remove(&id).unwrap_or_default()
}
pub fn buffer_lint(
&mut self,
lint: &'static Lint,
node_id: NodeId,
span: impl Into<MultiSpan>,
decorate: impl Into<DecorateDiagCompat>,
) {
self.add_early_lint(BufferedEarlyLint {
lint_id: LintId::of(lint),
node_id,
span: Some(span.into()),
diagnostic: decorate.into(),
});
}
}

View file

@ -138,10 +138,20 @@ where
/// `#[derive(LintDiagnostic)]` -- see [rustc_macros::LintDiagnostic].
#[rustc_diagnostic_item = "LintDiagnostic"]
pub trait LintDiagnostic<'a, G: EmissionGuarantee> {
/// Decorate and emit a lint.
/// Decorate a lint with the information from this type.
fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, G>);
}
pub trait LintDiagnosticBox<'a, G: EmissionGuarantee> {
fn decorate_lint_box<'b>(self: Box<Self>, diag: &'b mut Diag<'a, G>);
}
impl<'a, G: EmissionGuarantee, D: LintDiagnostic<'a, G>> LintDiagnosticBox<'a, G> for D {
fn decorate_lint_box<'b>(self: Box<Self>, diag: &'b mut Diag<'a, G>) {
self.decorate_lint(diag);
}
}
#[derive(Clone, Debug, Encodable, Decodable)]
pub(crate) struct DiagLocation {
file: Cow<'static, str>,

View file

@ -40,9 +40,10 @@ use std::{fmt, panic};
use Level::*;
pub use codes::*;
pub use decorate_diag::{BufferedEarlyLint, DecorateDiagCompat, LintBuffer};
pub use diagnostic::{
BugAbort, Diag, DiagArgMap, DiagInner, DiagStyledString, Diagnostic, EmissionGuarantee,
FatalAbort, LintDiagnostic, StringPart, Subdiag, Subdiagnostic,
FatalAbort, LintDiagnostic, LintDiagnosticBox, StringPart, Subdiag, Subdiagnostic,
};
pub use diagnostic_impls::{
DiagSymbolList, ElidedLifetimeInPathSubdiag, ExpectedLifetimeParameter,
@ -80,6 +81,7 @@ use crate::timings::TimingRecord;
pub mod annotate_snippet_emitter_writer;
pub mod codes;
mod decorate_diag;
mod diagnostic;
mod diagnostic_impls;
pub mod emitter;

View file

@ -13,13 +13,13 @@ use rustc_ast::visit::{AssocCtxt, Visitor};
use rustc_ast::{self as ast, AttrVec, Attribute, HasAttrs, Item, NodeId, PatKind};
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
use rustc_data_structures::sync;
use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed, PResult};
use rustc_errors::{BufferedEarlyLint, DiagCtxtHandle, ErrorGuaranteed, PResult};
use rustc_feature::Features;
use rustc_hir as hir;
use rustc_hir::attrs::{AttributeKind, CfgEntry, Deprecation};
use rustc_hir::def::MacroKinds;
use rustc_hir::{Stability, find_attr};
use rustc_lint_defs::{BufferedEarlyLint, RegisteredTools};
use rustc_lint_defs::RegisteredTools;
use rustc_parse::MACRO_ARGUMENTS;
use rustc_parse::parser::{ForceCollect, Parser};
use rustc_session::config::CollapseMacroDebuginfo;

View file

@ -9,10 +9,11 @@ use rustc_attr_parsing::validate_attr;
use rustc_codegen_ssa::traits::CodegenBackend;
use rustc_data_structures::jobserver::Proxy;
use rustc_data_structures::sync;
use rustc_errors::LintBuffer;
use rustc_metadata::{DylibError, load_symbol_from_dylib};
use rustc_middle::ty::CurrentGcx;
use rustc_session::config::{Cfg, OutFileName, OutputFilenames, OutputTypes, Sysroot, host_tuple};
use rustc_session::lint::{self, BuiltinLintDiag, LintBuffer};
use rustc_session::lint::{self, BuiltinLintDiag};
use rustc_session::output::{CRATE_TYPES, categorize_crate_type};
use rustc_session::{EarlyDiagCtxt, Session, filesearch};
use rustc_span::edit_distance::find_best_match_for_name;

View file

@ -205,8 +205,6 @@ lint_confusable_identifier_pair = found both `{$existing_sym}` and `{$sym}` as i
.current_use = this identifier can be confused with `{$existing_sym}`
.other_use = other identifier used here
lint_custom_inner_attribute_unstable = custom inner attributes are unstable
lint_dangling_pointers_from_locals = a dangling pointer will be produced because the local variable `{$local_var_name}` will be dropped
.ret_ty = return type of the {$fn_kind} is `{$ret_ty}`
.local_var = `{$local_var_name}` is part the {$fn_kind} and will be dropped at the end of the {$fn_kind}
@ -271,10 +269,6 @@ lint_expectation = this lint expectation is unfulfilled
lint_extern_crate_not_idiomatic = `extern crate` is not idiomatic in the new edition
.suggestion = convert it to a `use`
lint_extern_without_abi = `extern` declarations without an explicit ABI are deprecated
.label = ABI should be specified here
.suggestion = explicitly specify the {$default_abi} ABI
lint_for_loops_over_fallibles =
for loop over {$article} `{$ref_prefix}{$ty}`. This is more readably written as an `if let` statement
.suggestion = consider using `if let` to clear intent
@ -294,19 +288,6 @@ lint_hidden_glob_reexport = private item shadows public glob re-export
lint_hidden_lifetime_parameters = hidden lifetime parameters in types are deprecated
lint_hidden_unicode_codepoints = unicode codepoint changing visible direction of text present in {$label}
.label = this {$label} contains {$count ->
[one] an invisible
*[other] invisible
} unicode text flow control {$count ->
[one] codepoint
*[other] codepoints
}
.note = these kind of unicode codepoints change the way text flows on applications that support them, but can cause confusion because they change the order of characters on the screen
.suggestion_remove = if their presence wasn't intentional, you can remove them
.suggestion_escape = if you want to keep them but make them visible in your source code, you can escape them
.no_suggestion_note_escape = if you want to keep them but make them visible in your source code, you can escape them: {$escaped}
lint_identifier_non_ascii_char = identifier contains non-ASCII characters
lint_identifier_uncommon_codepoints = identifier contains {$codepoints_len ->
@ -431,8 +412,6 @@ lint_improper_ctypes_union_non_exhaustive = this union is non-exhaustive
lint_incomplete_include =
include macro expected single expression in source
lint_inner_macro_attribute_unstable = inner macro attributes are unstable
lint_invalid_asm_label_binary = avoid using labels containing only the digits `0` and `1` in inline assembly
.label = use a different label that doesn't start with `0` or `1`
.help = start numbering with `2` instead
@ -870,10 +849,6 @@ lint_undropped_manually_drops = calls to `std::mem::drop` with `std::mem::Manual
.label = argument has type `{$arg_ty}`
.suggestion = use `std::mem::ManuallyDrop::into_inner` to get the inner value
lint_unexpected_builtin_cfg = unexpected `--cfg {$cfg}` flag
.controlled_by = config `{$cfg_name}` is only supposed to be controlled by `{$controlled_by}`
.incoherent = manually setting a built-in cfg can and does create incoherent behaviors
lint_unexpected_cfg_add_build_rs_println = or consider adding `{$build_rs_println}` to the top of the `build.rs`
lint_unexpected_cfg_add_cargo_feature = consider using a Cargo feature instead
lint_unexpected_cfg_add_cargo_toml_lint_cfg = or consider adding in `Cargo.toml` the `check-cfg` lint config for the lint:{$cargo_toml_lint_cfg}

View file

@ -11,7 +11,7 @@ use rustc_ast::util::parser::ExprPrecedence;
use rustc_data_structures::fx::FxIndexMap;
use rustc_data_structures::sync;
use rustc_data_structures::unord::UnordMap;
use rustc_errors::{Diag, LintDiagnostic, MultiSpan};
use rustc_errors::{Diag, LintBuffer, LintDiagnostic, MultiSpan};
use rustc_feature::Features;
use rustc_hir::def::Res;
use rustc_hir::def_id::{CrateNum, DefId};
@ -23,7 +23,7 @@ use rustc_middle::middle::privacy::EffectiveVisibilities;
use rustc_middle::ty::layout::{LayoutError, LayoutOfHelpers, TyAndLayout};
use rustc_middle::ty::print::{PrintError, PrintTraitRefExt as _, Printer, with_no_trimmed_paths};
use rustc_middle::ty::{self, GenericArg, RegisteredTools, Ty, TyCtxt, TypingEnv, TypingMode};
use rustc_session::lint::{FutureIncompatibleInfo, Lint, LintBuffer, LintExpectationId, LintId};
use rustc_session::lint::{FutureIncompatibleInfo, Lint, LintExpectationId, LintId};
use rustc_session::{DynLintStore, Session};
use rustc_span::edit_distance::find_best_match_for_names;
use rustc_span::{Ident, Span, Symbol, sym};

View file

@ -7,10 +7,11 @@
use rustc_ast::visit::{self as ast_visit, Visitor, walk_list};
use rustc_ast::{self as ast, HasAttrs};
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_errors::{BufferedEarlyLint, DecorateDiagCompat, LintBuffer};
use rustc_feature::Features;
use rustc_middle::ty::{RegisteredTools, TyCtxt};
use rustc_session::Session;
use rustc_session::lint::{BufferedEarlyLint, LintBuffer, LintPass};
use rustc_session::lint::LintPass;
use rustc_span::{Ident, Span};
use tracing::debug;
@ -36,8 +37,11 @@ impl<'ecx, 'tcx, T: EarlyLintPass> EarlyContextAndPass<'ecx, 'tcx, T> {
fn check_id(&mut self, id: ast::NodeId) {
for early_lint in self.context.buffered.take(id) {
let BufferedEarlyLint { span, node_id: _, lint_id, diagnostic } = early_lint;
self.context.opt_span_lint(lint_id.lint, span, |diag| {
diagnostics::decorate_builtin_lint(self.context.sess(), self.tcx, diagnostic, diag);
self.context.opt_span_lint(lint_id.lint, span, |diag| match diagnostic {
DecorateDiagCompat::Builtin(b) => {
diagnostics::decorate_builtin_lint(self.context.sess(), self.tcx, b, diag);
}
DecorateDiagCompat::Dynamic(d) => d.decorate_lint_box(diag),
});
}
}

View file

@ -158,9 +158,6 @@ pub fn decorate_builtin_lint(
}
.decorate_lint(diag);
}
BuiltinLintDiag::MissingAbi(label_span, default_abi) => {
lints::MissingAbi { span: label_span, default_abi }.decorate_lint(diag);
}
BuiltinLintDiag::LegacyDeriveHelpers(label_span) => {
lints::LegacyDeriveHelpers { span: label_span }.decorate_lint(diag);
}
@ -186,27 +183,6 @@ pub fn decorate_builtin_lint(
lints::ReservedMultihash { suggestion }.decorate_lint(diag);
}
}
BuiltinLintDiag::HiddenUnicodeCodepoints {
label,
count,
span_label,
labels,
escape,
spans,
} => {
lints::HiddenUnicodeCodepointsDiag {
label: &label,
count,
span_label,
labels: labels.map(|spans| lints::HiddenUnicodeCodepointsDiagLabels { spans }),
sub: if escape {
lints::HiddenUnicodeCodepointsDiagSub::Escape { spans }
} else {
lints::HiddenUnicodeCodepointsDiagSub::NoEscape { spans }
},
}
.decorate_lint(diag);
}
BuiltinLintDiag::UnusedBuiltinAttribute {
attr_name,
macro_name,
@ -466,17 +442,8 @@ pub fn decorate_builtin_lint(
}
.decorate_lint(diag)
}
BuiltinLintDiag::InnerAttributeUnstable { is_macro } => if is_macro {
lints::InnerAttributeUnstable::InnerMacroAttribute
} else {
lints::InnerAttributeUnstable::CustomInnerAttribute
}
.decorate_lint(diag),
BuiltinLintDiag::OutOfScopeMacroCalls { span, path, location } => {
lints::OutOfScopeMacroCalls { span, path, location }.decorate_lint(diag)
}
BuiltinLintDiag::UnexpectedBuiltinCfg { cfg, cfg_name, controlled_by } => {
lints::UnexpectedBuiltinCfg { cfg, cfg_name, controlled_by }.decorate_lint(diag)
}
}
}

View file

@ -133,10 +133,9 @@ pub use early::{EarlyCheckNode, check_ast_node};
pub use late::{check_crate, late_lint_mod, unerased_lint_store};
pub use levels::LintLevelsBuilder;
pub use passes::{EarlyLintPass, LateLintPass};
pub use rustc_errors::BufferedEarlyLint;
pub use rustc_session::lint::Level::{self, *};
pub use rustc_session::lint::{
BufferedEarlyLint, FutureIncompatibleInfo, Lint, LintId, LintPass, LintVec,
};
pub use rustc_session::lint::{FutureIncompatibleInfo, Lint, LintId, LintPass, LintVec};
rustc_fluent_macro::fluent_messages! { "../messages.ftl" }

View file

@ -1,7 +1,6 @@
#![allow(rustc::untranslatable_diagnostic)]
use std::num::NonZero;
use rustc_abi::ExternAbi;
use rustc_errors::codes::*;
use rustc_errors::{
Applicability, Diag, DiagArgValue, DiagMessage, DiagStyledString, ElidedLifetimeInPathSubdiag,
@ -816,80 +815,6 @@ pub(crate) enum InvalidReferenceCastingDiag<'tcx> {
},
}
// hidden_unicode_codepoints.rs
#[derive(LintDiagnostic)]
#[diag(lint_hidden_unicode_codepoints)]
#[note]
pub(crate) struct HiddenUnicodeCodepointsDiag<'a> {
pub label: &'a str,
pub count: usize,
#[label]
pub span_label: Span,
#[subdiagnostic]
pub labels: Option<HiddenUnicodeCodepointsDiagLabels>,
#[subdiagnostic]
pub sub: HiddenUnicodeCodepointsDiagSub,
}
pub(crate) struct HiddenUnicodeCodepointsDiagLabels {
pub spans: Vec<(char, Span)>,
}
impl Subdiagnostic for HiddenUnicodeCodepointsDiagLabels {
fn add_to_diag<G: EmissionGuarantee>(self, diag: &mut Diag<'_, G>) {
for (c, span) in self.spans {
diag.span_label(span, format!("{c:?}"));
}
}
}
pub(crate) enum HiddenUnicodeCodepointsDiagSub {
Escape { spans: Vec<(char, Span)> },
NoEscape { spans: Vec<(char, Span)> },
}
// Used because of multiple multipart_suggestion and note
impl Subdiagnostic for HiddenUnicodeCodepointsDiagSub {
fn add_to_diag<G: EmissionGuarantee>(self, diag: &mut Diag<'_, G>) {
match self {
HiddenUnicodeCodepointsDiagSub::Escape { spans } => {
diag.multipart_suggestion_with_style(
fluent::lint_suggestion_remove,
spans.iter().map(|(_, span)| (*span, "".to_string())).collect(),
Applicability::MachineApplicable,
SuggestionStyle::HideCodeAlways,
);
diag.multipart_suggestion(
fluent::lint_suggestion_escape,
spans
.into_iter()
.map(|(c, span)| {
let c = format!("{c:?}");
(span, c[1..c.len() - 1].to_string())
})
.collect(),
Applicability::MachineApplicable,
);
}
HiddenUnicodeCodepointsDiagSub::NoEscape { spans } => {
// FIXME: in other suggestions we've reversed the inner spans of doc comments. We
// should do the same here to provide the same good suggestions as we do for
// literals above.
diag.arg(
"escaped",
spans
.into_iter()
.map(|(c, _)| format!("{c:?}"))
.collect::<Vec<String>>()
.join(", "),
);
diag.note(fluent::lint_suggestion_remove);
diag.note(fluent::lint_no_suggestion_note_escape);
}
}
}
}
// map_unit_fn.rs
#[derive(LintDiagnostic)]
#[diag(lint_map_unit_fn)]
@ -2566,16 +2491,6 @@ pub(crate) mod unexpected_cfg_value {
}
}
#[derive(LintDiagnostic)]
#[diag(lint_unexpected_builtin_cfg)]
#[note(lint_controlled_by)]
#[note(lint_incoherent)]
pub(crate) struct UnexpectedBuiltinCfg {
pub(crate) cfg: String,
pub(crate) cfg_name: Symbol,
pub(crate) controlled_by: &'static str,
}
#[derive(LintDiagnostic)]
#[diag(lint_macro_use_deprecated)]
#[help]
@ -2689,14 +2604,6 @@ pub(crate) struct IllFormedAttributeInput {
pub docs: &'static str,
}
#[derive(LintDiagnostic)]
pub(crate) enum InnerAttributeUnstable {
#[diag(lint_inner_macro_attribute_unstable)]
InnerMacroAttribute,
#[diag(lint_custom_inner_attribute_unstable)]
CustomInnerAttribute,
}
#[derive(LintDiagnostic)]
#[diag(lint_unknown_diagnostic_attribute)]
pub(crate) struct UnknownDiagnosticAttribute {
@ -2889,14 +2796,6 @@ pub(crate) struct PatternsInFnsWithoutBodySub {
pub ident: Ident,
}
#[derive(LintDiagnostic)]
#[diag(lint_extern_without_abi)]
pub(crate) struct MissingAbi {
#[suggestion(code = "extern {default_abi}", applicability = "machine-applicable")]
pub span: Span,
pub default_abi: ExternAbi,
}
#[derive(LintDiagnostic)]
#[diag(lint_legacy_derive_helpers)]
pub(crate) struct LegacyDeriveHelpers {

View file

@ -5,7 +5,6 @@ edition = "2024"
[dependencies]
# tidy-alphabetical-start
rustc_abi = { path = "../rustc_abi" }
rustc_ast = { path = "../rustc_ast" }
rustc_data_structures = { path = "../rustc_data_structures" }
rustc_error_messages = { path = "../rustc_error_messages" }

View file

@ -1,10 +1,8 @@
use std::borrow::Cow;
use rustc_abi::ExternAbi;
use rustc_ast::AttrId;
use rustc_ast::attr::AttributeExt;
use rustc_ast::node_id::NodeId;
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
use rustc_data_structures::fx::FxIndexSet;
use rustc_data_structures::stable_hasher::{
HashStable, StableCompare, StableHasher, ToStableHashKey,
};
@ -648,7 +646,6 @@ pub enum BuiltinLintDiag {
path: String,
since_kind: DeprecatedSinceKind,
},
MissingAbi(Span, ExternAbi),
UnusedDocComment(Span),
UnusedBuiltinAttribute {
attr_name: Symbol,
@ -671,14 +668,6 @@ pub enum BuiltinLintDiag {
is_string: bool,
suggestion: Span,
},
HiddenUnicodeCodepoints {
label: String,
count: usize,
span_label: Span,
labels: Option<Vec<(char, Span)>>,
escape: bool,
spans: Vec<(char, Span)>,
},
TrailingMacro(bool, Ident),
BreakWithLabelAndLoop(Span),
UnicodeTextFlow(Span, String),
@ -803,68 +792,11 @@ pub enum BuiltinLintDiag {
suggestions: Vec<String>,
docs: Option<&'static str>,
},
InnerAttributeUnstable {
is_macro: bool,
},
OutOfScopeMacroCalls {
span: Span,
path: String,
location: String,
},
UnexpectedBuiltinCfg {
cfg: String,
cfg_name: Symbol,
controlled_by: &'static str,
},
}
/// Lints that are buffered up early on in the `Session` before the
/// `LintLevels` is calculated.
#[derive(Debug)]
pub struct BufferedEarlyLint {
/// The span of code that we are linting on.
pub span: Option<MultiSpan>,
/// The `NodeId` of the AST node that generated the lint.
pub node_id: NodeId,
/// A lint Id that can be passed to
/// `rustc_lint::early::EarlyContextAndPass::check_id`.
pub lint_id: LintId,
/// Customization of the `Diag<'_>` for the lint.
pub diagnostic: BuiltinLintDiag,
}
#[derive(Default, Debug)]
pub struct LintBuffer {
pub map: FxIndexMap<NodeId, Vec<BufferedEarlyLint>>,
}
impl LintBuffer {
pub fn add_early_lint(&mut self, early_lint: BufferedEarlyLint) {
self.map.entry(early_lint.node_id).or_default().push(early_lint);
}
pub fn take(&mut self, id: NodeId) -> Vec<BufferedEarlyLint> {
// FIXME(#120456) - is `swap_remove` correct?
self.map.swap_remove(&id).unwrap_or_default()
}
pub fn buffer_lint(
&mut self,
lint: &'static Lint,
node_id: NodeId,
span: impl Into<MultiSpan>,
diagnostic: BuiltinLintDiag,
) {
self.add_early_lint(BufferedEarlyLint {
lint_id: LintId::of(lint),
node_id,
span: Some(span.into()),
diagnostic,
});
}
}
pub type RegisteredTools = FxIndexSet<Ident>;

View file

@ -4,7 +4,7 @@
use std::num::NonZero;
use rustc_ast::NodeId;
use rustc_errors::{Applicability, Diag, EmissionGuarantee};
use rustc_errors::{Applicability, Diag, EmissionGuarantee, LintBuffer};
use rustc_feature::GateIssue;
use rustc_hir::attrs::{DeprecatedSince, Deprecation};
use rustc_hir::def_id::{DefId, LocalDefId};
@ -12,7 +12,7 @@ use rustc_hir::{self as hir, ConstStability, DefaultBodyStability, HirId, Stabil
use rustc_macros::{Decodable, Encodable, HashStable, Subdiagnostic};
use rustc_session::Session;
use rustc_session::lint::builtin::{DEPRECATED, DEPRECATED_IN_FUTURE, SOFT_UNSTABLE};
use rustc_session::lint::{BuiltinLintDiag, DeprecatedSinceKind, Level, Lint, LintBuffer};
use rustc_session::lint::{BuiltinLintDiag, DeprecatedSinceKind, Level, Lint};
use rustc_session::parse::feature_err_issue;
use rustc_span::{Span, Symbol, sym};
use tracing::debug;

View file

@ -32,7 +32,7 @@ use rustc_data_structures::intern::Interned;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_data_structures::steal::Steal;
use rustc_data_structures::unord::{UnordMap, UnordSet};
use rustc_errors::{Diag, ErrorGuaranteed};
use rustc_errors::{Diag, ErrorGuaranteed, LintBuffer};
use rustc_hir::attrs::{AttributeKind, StrippedCfgItem};
use rustc_hir::def::{CtorKind, CtorOf, DefKind, DocLinkResMap, LifetimeRes, Res};
use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LocalDefIdMap};
@ -46,7 +46,6 @@ use rustc_macros::{
};
use rustc_query_system::ich::StableHashingContext;
use rustc_serialize::{Decodable, Encodable};
use rustc_session::lint::LintBuffer;
pub use rustc_session::lint::RegisteredTools;
use rustc_span::hygiene::MacroKind;
use rustc_span::{DUMMY_SP, ExpnId, ExpnKind, Ident, Span, Symbol, sym};

View file

@ -359,6 +359,20 @@ parse_generics_in_path = unexpected generic arguments in path
parse_help_set_edition_cargo = set `edition = "{$edition}"` in `Cargo.toml`
parse_help_set_edition_standalone = pass `--edition {$edition}` to `rustc`
parse_hidden_unicode_codepoints = unicode codepoint changing visible direction of text present in {$label}
.label = this {$label} contains {$count ->
[one] an invisible
*[other] invisible
} unicode text flow control {$count ->
[one] codepoint
*[other] codepoints
}
.note = these kind of unicode codepoints change the way text flows on applications that support them, but can cause confusion because they change the order of characters on the screen
.suggestion_remove = if their presence wasn't intentional, you can remove them
.suggestion_escape = if you want to keep them but make them visible in your source code, you can escape them
.no_suggestion_note_escape = if you want to keep them but make them visible in your source code, you can escape them: {$escaped}
parse_if_expression_missing_condition = missing condition for `if` expression
.condition_label = expected condition here
.block_label = if this block is the condition of the `if` expression, then it must be followed by another block

View file

@ -8,8 +8,9 @@ use rustc_ast::{Path, Visibility};
use rustc_errors::codes::*;
use rustc_errors::{
Applicability, Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level, Subdiagnostic,
SuggestionStyle,
};
use rustc_macros::{Diagnostic, Subdiagnostic};
use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
use rustc_session::errors::ExprParenthesesNeeded;
use rustc_span::edition::{Edition, LATEST_STABLE_EDITION};
use rustc_span::{Ident, Span, Symbol};
@ -3601,3 +3602,76 @@ pub(crate) struct ExpectedRegisterClassOrExplicitRegister {
#[primary_span]
pub(crate) span: Span,
}
#[derive(LintDiagnostic)]
#[diag(parse_hidden_unicode_codepoints)]
#[note]
pub(crate) struct HiddenUnicodeCodepointsDiag {
pub label: String,
pub count: usize,
#[label]
pub span_label: Span,
#[subdiagnostic]
pub labels: Option<HiddenUnicodeCodepointsDiagLabels>,
#[subdiagnostic]
pub sub: HiddenUnicodeCodepointsDiagSub,
}
pub(crate) struct HiddenUnicodeCodepointsDiagLabels {
pub spans: Vec<(char, Span)>,
}
impl Subdiagnostic for HiddenUnicodeCodepointsDiagLabels {
fn add_to_diag<G: EmissionGuarantee>(self, diag: &mut Diag<'_, G>) {
for (c, span) in self.spans {
diag.span_label(span, format!("{c:?}"));
}
}
}
pub(crate) enum HiddenUnicodeCodepointsDiagSub {
Escape { spans: Vec<(char, Span)> },
NoEscape { spans: Vec<(char, Span)> },
}
// Used because of multiple multipart_suggestion and note
impl Subdiagnostic for HiddenUnicodeCodepointsDiagSub {
fn add_to_diag<G: EmissionGuarantee>(self, diag: &mut Diag<'_, G>) {
match self {
HiddenUnicodeCodepointsDiagSub::Escape { spans } => {
diag.multipart_suggestion_with_style(
fluent::parse_suggestion_remove,
spans.iter().map(|(_, span)| (*span, "".to_string())).collect(),
Applicability::MachineApplicable,
SuggestionStyle::HideCodeAlways,
);
diag.multipart_suggestion(
fluent::parse_suggestion_escape,
spans
.into_iter()
.map(|(c, span)| {
let c = format!("{c:?}");
(span, c[1..c.len() - 1].to_string())
})
.collect(),
Applicability::MachineApplicable,
);
}
HiddenUnicodeCodepointsDiagSub::NoEscape { spans } => {
// FIXME: in other suggestions we've reversed the inner spans of doc comments. We
// should do the same here to provide the same good suggestions as we do for
// literals above.
diag.arg(
"escaped",
spans
.into_iter()
.map(|(c, _)| format!("{c:?}"))
.collect::<Vec<String>>()
.join(", "),
);
diag.note(fluent::parse_suggestion_remove);
diag.note(fluent::parse_no_suggestion_note_escape);
}
}
}
}

View file

@ -543,21 +543,21 @@ impl<'psess, 'src> Lexer<'psess, 'src> {
})
.collect();
let label = label.to_string();
let count = spans.len();
let labels = point_at_inner_spans.then_some(spans.clone());
let labels = point_at_inner_spans
.then_some(errors::HiddenUnicodeCodepointsDiagLabels { spans: spans.clone() });
let sub = if point_at_inner_spans && !spans.is_empty() {
errors::HiddenUnicodeCodepointsDiagSub::Escape { spans }
} else {
errors::HiddenUnicodeCodepointsDiagSub::NoEscape { spans }
};
self.psess.buffer_lint(
TEXT_DIRECTION_CODEPOINT_IN_LITERAL,
span,
ast::CRATE_NODE_ID,
BuiltinLintDiag::HiddenUnicodeCodepoints {
label: label.to_string(),
count,
span_label: span,
labels,
escape: point_at_inner_spans && !spans.is_empty(),
spans,
},
errors::HiddenUnicodeCodepointsDiag { label, count, span_label: span, labels, sub },
);
}

View file

@ -49,7 +49,7 @@ use rustc_data_structures::intern::Interned;
use rustc_data_structures::steal::Steal;
use rustc_data_structures::sync::{FreezeReadGuard, FreezeWriteGuard};
use rustc_data_structures::unord::{UnordMap, UnordSet};
use rustc_errors::{Applicability, Diag, ErrCode, ErrorGuaranteed};
use rustc_errors::{Applicability, Diag, ErrCode, ErrorGuaranteed, LintBuffer};
use rustc_expand::base::{DeriveResolution, SyntaxExtension, SyntaxExtensionKind};
use rustc_feature::BUILTIN_ATTRIBUTES;
use rustc_hir::attrs::StrippedCfgItem;
@ -72,8 +72,8 @@ use rustc_middle::ty::{
ResolverGlobalCtxt, TyCtxt, TyCtxtFeed, Visibility,
};
use rustc_query_system::ich::StableHashingContext;
use rustc_session::lint::BuiltinLintDiag;
use rustc_session::lint::builtin::PRIVATE_MACRO_USE;
use rustc_session::lint::{BuiltinLintDiag, LintBuffer};
use rustc_span::hygiene::{ExpnId, LocalExpnId, MacroKind, SyntaxContext, Transparency};
use rustc_span::{DUMMY_SP, Ident, Macros20NormalizedIdent, Span, Symbol, kw, sym};
use smallvec::{SmallVec, smallvec};

View file

@ -131,6 +131,10 @@ session_target_small_data_threshold_not_supported = `-Z small-data-threshold` is
session_target_stack_protector_not_supported = `-Z stack-protector={$stack_protector}` is not supported for target {$target_triple} and will be ignored
session_unexpected_builtin_cfg = unexpected `--cfg {$cfg}` flag
.controlled_by = config `{$cfg_name}` is only supposed to be controlled by `{$controlled_by}`
.incoherent = manually setting a built-in cfg can and does create incoherent behaviors
session_unleashed_feature_help_named = skipping check for `{$gate}` feature
session_unleashed_feature_help_unnamed = skipping check that does not even have a feature gate

View file

@ -26,13 +26,12 @@ use std::iter;
use rustc_abi::Align;
use rustc_ast::ast;
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet};
use rustc_lint_defs::BuiltinLintDiag;
use rustc_lint_defs::builtin::EXPLICIT_BUILTIN_CFGS_IN_FLAGS;
use rustc_span::{Symbol, sym};
use rustc_target::spec::{PanicStrategy, RelocModel, SanitizerSet, Target};
use crate::Session;
use crate::config::{CrateType, FmtDebug};
use crate::{Session, errors};
/// The parsed `--cfg` options that define the compilation environment of the
/// crate, used to drive conditional compilation.
@ -99,7 +98,7 @@ pub(crate) fn disallow_cfgs(sess: &Session, user_cfgs: &Cfg) {
EXPLICIT_BUILTIN_CFGS_IN_FLAGS,
None,
ast::CRATE_NODE_ID,
BuiltinLintDiag::UnexpectedBuiltinCfg { cfg, cfg_name, controlled_by },
errors::UnexpectedBuiltinCfg { cfg, cfg_name, controlled_by }.into(),
)
};

View file

@ -7,7 +7,7 @@ use rustc_errors::{
Diag, DiagCtxtHandle, DiagMessage, Diagnostic, EmissionGuarantee, ErrorGuaranteed, Level,
MultiSpan,
};
use rustc_macros::{Diagnostic, Subdiagnostic};
use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
use rustc_span::{Span, Symbol};
use rustc_target::spec::{SplitDebuginfo, StackProtector, TargetTuple};
@ -505,3 +505,13 @@ pub(crate) struct SoftFloatIgnored;
#[note]
#[note(session_soft_float_deprecated_issue)]
pub(crate) struct SoftFloatDeprecated;
#[derive(LintDiagnostic)]
#[diag(session_unexpected_builtin_cfg)]
#[note(session_controlled_by)]
#[note(session_incoherent)]
pub(crate) struct UnexpectedBuiltinCfg {
pub(crate) cfg: String,
pub(crate) cfg_name: Symbol,
pub(crate) controlled_by: &'static str,
}

View file

@ -11,8 +11,8 @@ use rustc_data_structures::sync::{AppendOnlyVec, Lock};
use rustc_errors::emitter::{FatalOnlyEmitter, HumanEmitter, stderr_destination};
use rustc_errors::translation::Translator;
use rustc_errors::{
ColorConfig, Diag, DiagCtxt, DiagCtxtHandle, DiagMessage, EmissionGuarantee, MultiSpan,
StashKey,
BufferedEarlyLint, ColorConfig, DecorateDiagCompat, Diag, DiagCtxt, DiagCtxtHandle,
DiagMessage, EmissionGuarantee, MultiSpan, StashKey,
};
use rustc_feature::{GateIssue, UnstableFeatures, find_feature_issue};
use rustc_span::edition::Edition;
@ -27,7 +27,7 @@ use crate::errors::{
FeatureDiagnosticSuggestion, FeatureGateError, SuggestUpgradeCompiler,
};
use crate::lint::builtin::UNSTABLE_SYNTAX_PRE_EXPANSION;
use crate::lint::{BufferedEarlyLint, BuiltinLintDiag, Lint, LintId};
use crate::lint::{Lint, LintId};
/// Collected spans during parsing for places where a certain feature was
/// used and should be feature gated accordingly in `check_crate`.
@ -342,17 +342,17 @@ impl ParseSess {
lint: &'static Lint,
span: impl Into<MultiSpan>,
node_id: NodeId,
diagnostic: BuiltinLintDiag,
diagnostic: impl Into<DecorateDiagCompat>,
) {
self.opt_span_buffer_lint(lint, Some(span.into()), node_id, diagnostic)
self.opt_span_buffer_lint(lint, Some(span.into()), node_id, diagnostic.into())
}
pub fn opt_span_buffer_lint(
pub(crate) fn opt_span_buffer_lint(
&self,
lint: &'static Lint,
span: Option<MultiSpan>,
node_id: NodeId,
diagnostic: BuiltinLintDiag,
diagnostic: DecorateDiagCompat,
) {
self.buffered_lints.with_lock(|buffered_lints| {
buffered_lints.push(BufferedEarlyLint {