diff --git a/compiler/rustc_lint/src/early/diagnostics.rs b/compiler/rustc_lint/src/early/diagnostics.rs index 54e18dd32207..d5cd26dd75e3 100644 --- a/compiler/rustc_lint/src/early/diagnostics.rs +++ b/compiler/rustc_lint/src/early/diagnostics.rs @@ -1,6 +1,5 @@ use std::borrow::Cow; -use rustc_ast::util::unicode::TEXT_FLOW_CONTROL_CHARS; use rustc_errors::{ Applicability, Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, Level, elided_lifetime_in_path_suggestion, @@ -10,7 +9,6 @@ use rustc_middle::ty::TyCtxt; use rustc_session::Session; use rustc_session::lint::BuiltinLintDiag; -use rustc_span::BytePos; use tracing::debug; use crate::lints; @@ -28,32 +26,6 @@ pub struct DecorateBuiltinLint<'sess, 'tcx> { impl<'a> Diagnostic<'a, ()> for DecorateBuiltinLint<'_, '_> { fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { match self.diagnostic { - BuiltinLintDiag::UnicodeTextFlow(comment_span, content) => { - let spans: Vec<_> = content - .char_indices() - .filter_map(|(i, c)| { - TEXT_FLOW_CONTROL_CHARS.contains(&c).then(|| { - let lo = comment_span.lo() + BytePos(2 + i as u32); - (c, comment_span.with_lo(lo).with_hi(lo + BytePos(c.len_utf8() as u32))) - }) - }) - .collect(); - let characters = spans - .iter() - .map(|&(c, span)| lints::UnicodeCharNoteSub { span, c_debug: format!("{c:?}") }) - .collect(); - let suggestions = (!spans.is_empty()).then_some(lints::UnicodeTextFlowSuggestion { - spans: spans.iter().map(|(_c, span)| *span).collect(), - }); - - lints::UnicodeTextFlow { - comment_span, - characters, - suggestions, - num_codepoints: spans.len(), - } - .into_diag(dcx, level) - } BuiltinLintDiag::AbsPathWithModule(mod_span) => { let (replacement, applicability) = match self.sess.source_map().span_to_snippet(mod_span) { @@ -153,23 +125,6 @@ fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { } .into_diag(dcx, level) } - BuiltinLintDiag::ReservedPrefix(label_span, prefix) => lints::ReservedPrefix { - label: label_span, - suggestion: label_span.shrink_to_hi(), - prefix, - } - .into_diag(dcx, level), - BuiltinLintDiag::RawPrefix(label_span) => { - lints::RawPrefix { label: label_span, suggestion: label_span.shrink_to_hi() } - .into_diag(dcx, level) - } - BuiltinLintDiag::ReservedString { is_string, suggestion } => { - if is_string { - lints::ReservedString { suggestion }.into_diag(dcx, level) - } else { - lints::ReservedMultihash { suggestion }.into_diag(dcx, level) - } - } BuiltinLintDiag::DeprecatedWhereclauseLocation(left_sp, sugg) => { let suggestion = match sugg { Some((right_sp, sugg)) => lints::DeprecatedWhereClauseLocationSugg::MoveToEnd { diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 2d1a8fae1d40..d5b5a50d281a 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -3019,46 +3019,6 @@ pub(crate) struct IllFormedAttributeInput { pub docs: &'static str, } -#[derive(Diagnostic)] -#[diag("unicode codepoint changing visible direction of text present in comment")] -#[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" -)] -pub(crate) struct UnicodeTextFlow { - #[label( - "{$num_codepoints -> - [1] this comment contains an invisible unicode text flow control codepoint - *[other] this comment contains invisible unicode text flow control codepoints - }" - )] - pub comment_span: Span, - #[subdiagnostic] - pub characters: Vec, - #[subdiagnostic] - pub suggestions: Option, - - pub num_codepoints: usize, -} - -#[derive(Subdiagnostic)] -#[label("{$c_debug}")] -pub(crate) struct UnicodeCharNoteSub { - #[primary_span] - pub span: Span, - pub c_debug: String, -} - -#[derive(Subdiagnostic)] -#[multipart_suggestion( - "if their presence wasn't intentional, you can remove them", - applicability = "machine-applicable", - style = "hidden" -)] -pub(crate) struct UnicodeTextFlowSuggestion { - #[suggestion_part(code = "")] - pub spans: Vec, -} - #[derive(Diagnostic)] #[diag( "absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition" @@ -3192,34 +3152,6 @@ pub(crate) struct PatternsInFnsWithoutBodySub { pub ident: Ident, } -#[derive(Diagnostic)] -#[diag("prefix `{$prefix}` is unknown")] -pub(crate) struct ReservedPrefix { - #[label("unknown prefix")] - pub label: Span, - #[suggestion( - "insert whitespace here to avoid this being parsed as a prefix in Rust 2021", - code = " ", - applicability = "machine-applicable" - )] - pub suggestion: Span, - - pub prefix: String, -} - -#[derive(Diagnostic)] -#[diag("prefix `'r` is reserved")] -pub(crate) struct RawPrefix { - #[label("reserved prefix")] - pub label: Span, - #[suggestion( - "insert whitespace here to avoid this being parsed as a prefix in Rust 2021", - code = " ", - applicability = "machine-applicable" - )] - pub suggestion: Span, -} - #[derive(Diagnostic)] #[diag("where clause not allowed here")] #[note("see issue #89122 for more information")] @@ -3405,28 +3337,6 @@ pub(crate) enum MutRefSugg { #[diag("`use` of a local item without leading `self::`, `super::`, or `crate::`")] pub(crate) struct UnqualifiedLocalImportsDiag; -#[derive(Diagnostic)] -#[diag("will be parsed as a guarded string in Rust 2024")] -pub(crate) struct ReservedString { - #[suggestion( - "insert whitespace here to avoid this being parsed as a guarded string in Rust 2024", - code = " ", - applicability = "machine-applicable" - )] - pub suggestion: Span, -} - -#[derive(Diagnostic)] -#[diag("reserved token in Rust 2024")] -pub(crate) struct ReservedMultihash { - #[suggestion( - "insert whitespace here to avoid this being parsed as a forbidden token in Rust 2024", - code = " ", - applicability = "machine-applicable" - )] - pub suggestion: Span, -} - #[derive(Diagnostic)] #[diag("direct cast of function item into an integer")] pub(crate) struct FunctionCastsAsIntegerDiag<'tcx> { diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 2ac75f9dd94c..e2e4ffc64bea 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -678,15 +678,7 @@ pub enum BuiltinLintDiag { ident: Ident, is_foreign: bool, }, - ReservedPrefix(Span, String), - /// `'r#` in edition < 2021. - RawPrefix(Span), /// `##` or `#"` in edition < 2024. - ReservedString { - is_string: bool, - suggestion: Span, - }, - UnicodeTextFlow(Span, String), DeprecatedWhereclauseLocation(Span, Option<(Span, String)>), SingleUseLifetime { /// Span of the parameter which declares this lifetime. diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 45dc29d059ba..0e852d2abda0 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -4517,3 +4517,93 @@ pub(crate) struct BreakWithLabelAndLoopSub { #[suggestion_part(code = ")")] pub right: Span, } + +#[derive(Diagnostic)] +#[diag("prefix `'r` is reserved")] +pub(crate) struct RawPrefix { + #[label("reserved prefix")] + pub label: Span, + #[suggestion( + "insert whitespace here to avoid this being parsed as a prefix in Rust 2021", + code = " ", + applicability = "machine-applicable" + )] + pub suggestion: Span, +} + +#[derive(Diagnostic)] +#[diag("unicode codepoint changing visible direction of text present in comment")] +#[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" +)] +pub(crate) struct UnicodeTextFlow { + #[label( + "{$num_codepoints -> + [1] this comment contains an invisible unicode text flow control codepoint + *[other] this comment contains invisible unicode text flow control codepoints + }" + )] + pub comment_span: Span, + #[subdiagnostic] + pub characters: Vec, + #[subdiagnostic] + pub suggestions: Option, + + pub num_codepoints: usize, +} + +#[derive(Subdiagnostic)] +#[label("{$c_debug}")] +pub(crate) struct UnicodeCharNoteSub { + #[primary_span] + pub span: Span, + pub c_debug: String, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion( + "if their presence wasn't intentional, you can remove them", + applicability = "machine-applicable", + style = "hidden" +)] +pub(crate) struct UnicodeTextFlowSuggestion { + #[suggestion_part(code = "")] + pub spans: Vec, +} + +#[derive(Diagnostic)] +#[diag("prefix `{$prefix}` is unknown")] +pub(crate) struct ReservedPrefix { + #[label("unknown prefix")] + pub label: Span, + #[suggestion( + "insert whitespace here to avoid this being parsed as a prefix in Rust 2021", + code = " ", + applicability = "machine-applicable" + )] + pub suggestion: Span, + + pub prefix: String, +} + +#[derive(Diagnostic)] +#[diag("will be parsed as a guarded string in Rust 2024")] +pub(crate) struct ReservedStringLint { + #[suggestion( + "insert whitespace here to avoid this being parsed as a guarded string in Rust 2024", + code = " ", + applicability = "machine-applicable" + )] + pub suggestion: Span, +} + +#[derive(Diagnostic)] +#[diag("reserved token in Rust 2024")] +pub(crate) struct ReservedMultihashLint { + #[suggestion( + "insert whitespace here to avoid this being parsed as a forbidden token in Rust 2024", + code = " ", + applicability = "machine-applicable" + )] + pub suggestion: Span, +} diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index cd90655125b2..2223e6a26575 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -4,12 +4,11 @@ use rustc_ast::tokenstream::TokenStream; use rustc_ast::util::unicode::{TEXT_FLOW_CONTROL_CHARS, contains_text_flow_control_chars}; use rustc_errors::codes::*; -use rustc_errors::{Applicability, Diag, DiagCtxtHandle, StashKey}; +use rustc_errors::{Applicability, Diag, DiagCtxtHandle, Diagnostic, StashKey}; use rustc_lexer::{ Base, Cursor, DocStyle, FrontmatterAllowed, LiteralKind, RawStrError, is_horizontal_whitespace, }; use rustc_literal_escaper::{EscapeError, Mode, check_for_errors}; -use rustc_session::lint::BuiltinLintDiag; use rustc_session::lint::builtin::{ RUST_2021_PREFIXES_INCOMPATIBLE_SYNTAX, RUST_2024_GUARDED_STRING_INCOMPATIBLE_SYNTAX, TEXT_DIRECTION_CODEPOINT_IN_COMMENT, TEXT_DIRECTION_CODEPOINT_IN_LITERAL, @@ -388,7 +387,10 @@ fn next_token_from_cursor(&mut self) -> (Token, bool) { RUST_2021_PREFIXES_INCOMPATIBLE_SYNTAX, prefix_span, ast::CRATE_NODE_ID, - BuiltinLintDiag::RawPrefix(prefix_span), + errors::RawPrefix { + label: prefix_span, + suggestion: prefix_span.shrink_to_hi() + }, ); // Reset the state so we just lex the `'r`. @@ -498,11 +500,41 @@ fn lint_unicode_text_flow(&self, start: BytePos) { let content = self.str_from(content_start); if contains_text_flow_control_chars(content) { let span = self.mk_sp(start, self.pos); - self.psess.buffer_lint( + let content = content.to_string(); + self.psess.dyn_buffer_lint( TEXT_DIRECTION_CODEPOINT_IN_COMMENT, span, ast::CRATE_NODE_ID, - BuiltinLintDiag::UnicodeTextFlow(span, content.to_string()), + move |dcx, level| { + let spans: Vec<_> = content + .char_indices() + .filter_map(|(i, c)| { + TEXT_FLOW_CONTROL_CHARS.contains(&c).then(|| { + let lo = span.lo() + BytePos(2 + i as u32); + (c, span.with_lo(lo).with_hi(lo + BytePos(c.len_utf8() as u32))) + }) + }) + .collect(); + let characters = spans + .iter() + .map(|&(c, span)| errors::UnicodeCharNoteSub { + span, + c_debug: format!("{c:?}"), + }) + .collect(); + let suggestions = + (!spans.is_empty()).then_some(errors::UnicodeTextFlowSuggestion { + spans: spans.iter().map(|(_c, span)| *span).collect(), + }); + + errors::UnicodeTextFlow { + comment_span: span, + characters, + suggestions, + num_codepoints: spans.len(), + } + .into_diag(dcx, level) + }, ); } } @@ -1038,7 +1070,11 @@ fn report_unknown_prefix(&self, start: BytePos) { RUST_2021_PREFIXES_INCOMPATIBLE_SYNTAX, prefix_span, ast::CRATE_NODE_ID, - BuiltinLintDiag::ReservedPrefix(prefix_span, prefix.to_string()), + errors::ReservedPrefix { + label: prefix_span, + suggestion: prefix_span.shrink_to_hi(), + prefix: prefix.to_string(), + }, ); } } @@ -1112,11 +1148,18 @@ fn maybe_report_guarded_str(&mut self, start: BytePos, str_before: &'src str) -> }) } else { // Before Rust 2024, only emit a lint for migration. - self.psess.buffer_lint( + self.psess.dyn_buffer_lint( RUST_2024_GUARDED_STRING_INCOMPATIBLE_SYNTAX, span, ast::CRATE_NODE_ID, - BuiltinLintDiag::ReservedString { is_string, suggestion: space_span }, + move |dcx, level| { + if is_string { + errors::ReservedStringLint { suggestion: space_span }.into_diag(dcx, level) + } else { + errors::ReservedMultihashLint { suggestion: space_span } + .into_diag(dcx, level) + } + }, ); // For backwards compatibility, roll back to after just the first `#` diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index f1d9b9e7212d..7d1a0661aaab 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -19,7 +19,7 @@ StmtKind, Ty, TyKind, UnOp, UnsafeBinderCastKind, YieldKind, }; use rustc_data_structures::stack::ensure_sufficient_stack; -use rustc_errors::{Applicability, Diag, Diagnostic, PResult, StashKey, Subdiagnostic}; +use rustc_errors::{Applicability, Diag, PResult, StashKey, Subdiagnostic}; use rustc_literal_escaper::unescape_char; use rustc_session::errors::{ExprParenthesesNeeded, report_lit_error}; use rustc_session::lint::builtin::BREAK_WITH_LABEL_AND_LOOP; @@ -1921,18 +1921,15 @@ fn parse_expr_break(&mut self) -> PResult<'a, Box> { } { let span = expr.span; - self.psess.dyn_buffer_lint( + self.psess.buffer_lint( BREAK_WITH_LABEL_AND_LOOP, lo.to(expr.span), ast::CRATE_NODE_ID, - move |dcx, level| { - errors::BreakWithLabelAndLoop { - sub: errors::BreakWithLabelAndLoopSub { - left: span.shrink_to_lo(), - right: span.shrink_to_hi(), - }, - } - .into_diag(dcx, level) + errors::BreakWithLabelAndLoop { + sub: errors::BreakWithLabelAndLoopSub { + left: span.shrink_to_lo(), + right: span.shrink_to_hi(), + }, }, ); }