diff options
| author | Jacob Pratt <jacob@jhpratt.dev> | 2025-08-22 22:00:59 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-08-22 22:00:59 -0400 |
| commit | d3c9908a8a91e8d732201caac2c809d4e96d0027 (patch) | |
| tree | c5836250da69e430ad7c61b9aa57c337c87fc16e /compiler/rustc_parse | |
| parent | 561656db6789caface445d993b015d709ddbb635 (diff) | |
| parent | 52fadd8f9623589143a4c2e5725c3b3c91e160c4 (diff) | |
| download | rust-d3c9908a8a91e8d732201caac2c809d4e96d0027.tar.gz rust-d3c9908a8a91e8d732201caac2c809d4e96d0027.zip | |
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.
Diffstat (limited to 'compiler/rustc_parse')
| -rw-r--r-- | compiler/rustc_parse/messages.ftl | 14 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/errors.rs | 76 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/lexer/mod.rs | 18 |
3 files changed, 98 insertions, 10 deletions
diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index 958bf1fde77..4ca2f57bd87 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -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 diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 7f32f1cf030..797d4830c2f 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -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); + } + } + } +} diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index 7c7e7e50b27..9792240a548 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -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 }, ); } |
