Rollup merge of #107446 - clubby789:rustc-parse-diag-migrate, r=compiler-errors

Migrate some of `rustc_parse` to derive diagnostics

`@rustbot` label +A-translation
r? rust-lang/diagnostics
cc #100717
This commit is contained in:
Matthias Krüger 2023-02-09 11:21:57 +01:00 committed by GitHub
commit 3b9543c89d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 656 additions and 381 deletions

View file

@ -585,3 +585,118 @@ parse_negative_bounds_not_supported = negative bounds are not supported
parse_help_set_edition_cargo = set `edition = "{$edition}"` in `Cargo.toml` parse_help_set_edition_cargo = set `edition = "{$edition}"` in `Cargo.toml`
parse_help_set_edition_standalone = pass `--edition {$edition}` to `rustc` parse_help_set_edition_standalone = pass `--edition {$edition}` to `rustc`
parse_note_edition_guide = for more on editions, read https://doc.rust-lang.org/edition-guide parse_note_edition_guide = for more on editions, read https://doc.rust-lang.org/edition-guide
parse_unexpected_token_after_dot = unexpected token: `{$actual}`
parse_cannot_be_raw_ident = `{$ident}` cannot be a raw identifier
parse_cr_doc_comment = bare CR not allowed in {$block ->
[true] block doc-comment
*[false] doc-comment
}
parse_no_digits_literal = no valid digits found for number
parse_invalid_digit_literal = invalid digit for a base {$base} literal
parse_empty_exponent_float = expected at least one digit in exponent
parse_float_literal_unsupported_base = {$base} float literal is not supported
parse_more_than_one_char = character literal may only contain one codepoint
.followed_by = this `{$chr}` is followed by the combining {$len ->
[one] mark
*[other] marks
} `{$escaped_marks}`
.non_printing = there are non-printing characters, the full sequence is `{$escaped}`
.consider_normalized = consider using the normalized form `{$ch}` of this character
.remove_non = consider removing the non-printing characters
.use_double_quotes = if you meant to write a {$is_byte ->
[true] byte string
*[false] `str`
} literal, use double quotes
parse_no_brace_unicode_escape = incorrect unicode escape sequence
.label = {parse_no_brace_unicode_escape}
.use_braces = format of unicode escape sequences uses braces
.format_of_unicode = format of unicode escape sequences is `\u{"{...}"}`
parse_invalid_unicode_escape = invalid unicode character escape
.label = invalid escape
.help = unicode escape must {$surrogate ->
[true] not be a surrogate
*[false] be at most 10FFFF
}
parse_escape_only_char = {$byte ->
[true] byte
*[false] character
} constant must be escaped: `{$escaped_msg}`
.escape = escape the character
parse_bare_cr = {$double_quotes ->
[true] bare CR not allowed in string, use `\r` instead
*[false] character constant must be escaped: `\r`
}
.escape = escape the character
parse_bare_cr_in_raw_string = bare CR not allowed in raw string
parse_too_short_hex_escape = numeric character escape is too short
parse_invalid_char_in_escape = {parse_invalid_char_in_escape_msg}: `{$ch}`
.label = {parse_invalid_char_in_escape_msg}
parse_invalid_char_in_escape_msg = invalid character in {$is_hex ->
[true] numeric character
*[false] unicode
} escape
parse_out_of_range_hex_escape = out of range hex escape
.label = must be a character in the range [\x00-\x7f]
parse_leading_underscore_unicode_escape = {parse_leading_underscore_unicode_escape_label}: `_`
parse_leading_underscore_unicode_escape_label = invalid start of unicode escape
parse_overlong_unicode_escape = overlong unicode escape
.label = must have at most 6 hex digits
parse_unclosed_unicode_escape = unterminated unicode escape
.label = missing a closing `{"}"}`
.terminate = terminate the unicode escape
parse_unicode_escape_in_byte = unicode escape in byte string
.label = {parse_unicode_escape_in_byte}
.help = unicode escape sequences cannot be used as a byte or in a byte string
parse_empty_unicode_escape = empty unicode escape
.label = this escape must have at least 1 hex digit
parse_zero_chars = empty character literal
.label = {parse_zero_chars}
parse_lone_slash = invalid trailing slash in literal
.label = {parse_lone_slash}
parse_unskipped_whitespace = non-ASCII whitespace symbol '{$ch}' is not skipped
.label = {parse_unskipped_whitespace}
parse_multiple_skipped_lines = multiple lines skipped by escaped newline
.label = skipping everything up to and including this point
parse_unknown_prefix = prefix `{$prefix}` is unknown
.label = unknown prefix
.note = prefixed identifiers and literals are reserved since Rust 2021
.suggestion_br = use `br` for a raw byte string
.suggestion_whitespace = consider inserting whitespace here
parse_too_many_hashes = too many `#` symbols: raw strings may be delimited by up to 255 `#` symbols, but found {$num}
parse_unknown_start_of_token = unknown start of token: {$escaped}
.sugg_quotes = Unicode characters '“' (Left Double Quotation Mark) and '”' (Right Double Quotation Mark) look like '{$ascii_str}' ({$ascii_name}), but are not
.sugg_other = Unicode character '{$ch}' ({$u_name}) looks like '{$ascii_str}' ({$ascii_name}), but it is not
.help_null = source files must contain UTF-8 encoded text, unexpected null bytes might occur when a different encoding is used
.note_repeats = character appears {$repeats ->
[one] once more
*[other] {$repeats} more times
}

View file

@ -1368,6 +1368,14 @@ pub(crate) struct SelfArgumentPointer {
pub span: Span, pub span: Span,
} }
#[derive(Diagnostic)]
#[diag(parse_unexpected_token_after_dot)]
pub struct UnexpectedTokenAfterDot<'a> {
#[primary_span]
pub span: Span,
pub actual: Cow<'a, str>,
}
#[derive(Diagnostic)] #[derive(Diagnostic)]
#[diag(parse_visibility_not_followed_by_item)] #[diag(parse_visibility_not_followed_by_item)]
#[help] #[help]
@ -1658,6 +1666,310 @@ pub(crate) enum TopLevelOrPatternNotAllowed {
}, },
} }
#[derive(Diagnostic)]
#[diag(parse_cannot_be_raw_ident)]
pub struct CannotBeRawIdent {
#[primary_span]
pub span: Span,
pub ident: Symbol,
}
#[derive(Diagnostic)]
#[diag(parse_cr_doc_comment)]
pub struct CrDocComment {
#[primary_span]
pub span: Span,
pub block: bool,
}
#[derive(Diagnostic)]
#[diag(parse_no_digits_literal, code = "E0768")]
pub struct NoDigitsLiteral {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(parse_invalid_digit_literal)]
pub struct InvalidDigitLiteral {
#[primary_span]
pub span: Span,
pub base: u32,
}
#[derive(Diagnostic)]
#[diag(parse_empty_exponent_float)]
pub struct EmptyExponentFloat {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(parse_float_literal_unsupported_base)]
pub struct FloatLiteralUnsupportedBase {
#[primary_span]
pub span: Span,
pub base: &'static str,
}
#[derive(Diagnostic)]
#[diag(parse_unknown_prefix)]
#[note]
pub struct UnknownPrefix<'a> {
#[primary_span]
#[label]
pub span: Span,
pub prefix: &'a str,
#[subdiagnostic]
pub sugg: Option<UnknownPrefixSugg>,
}
#[derive(Subdiagnostic)]
pub enum UnknownPrefixSugg {
#[suggestion(suggestion_br, code = "br", applicability = "maybe-incorrect", style = "verbose")]
UseBr(#[primary_span] Span),
#[suggestion(
suggestion_whitespace,
code = " ",
applicability = "maybe-incorrect",
style = "verbose"
)]
Whitespace(#[primary_span] Span),
}
#[derive(Diagnostic)]
#[diag(parse_too_many_hashes)]
pub struct TooManyHashes {
#[primary_span]
pub span: Span,
pub num: u32,
}
#[derive(Diagnostic)]
#[diag(parse_unknown_start_of_token)]
pub struct UnknownTokenStart {
#[primary_span]
pub span: Span,
pub escaped: String,
#[subdiagnostic]
pub sugg: Option<TokenSubstitution>,
#[subdiagnostic]
pub null: Option<UnknownTokenNull>,
#[subdiagnostic]
pub repeat: Option<UnknownTokenRepeat>,
}
#[derive(Subdiagnostic)]
pub enum TokenSubstitution {
#[suggestion(sugg_quotes, code = "{suggestion}", applicability = "maybe-incorrect")]
DirectedQuotes {
#[primary_span]
span: Span,
suggestion: String,
ascii_str: &'static str,
ascii_name: &'static str,
},
#[suggestion(sugg_other, code = "{suggestion}", applicability = "maybe-incorrect")]
Other {
#[primary_span]
span: Span,
suggestion: String,
ch: String,
u_name: &'static str,
ascii_str: &'static str,
ascii_name: &'static str,
},
}
#[derive(Subdiagnostic)]
#[note(note_repeats)]
pub struct UnknownTokenRepeat {
pub repeats: usize,
}
#[derive(Subdiagnostic)]
#[help(help_null)]
pub struct UnknownTokenNull;
#[derive(Diagnostic)]
pub enum UnescapeError {
#[diag(parse_invalid_unicode_escape)]
#[help]
InvalidUnicodeEscape {
#[primary_span]
#[label]
span: Span,
surrogate: bool,
},
#[diag(parse_escape_only_char)]
EscapeOnlyChar {
#[primary_span]
span: Span,
#[suggestion(escape, applicability = "machine-applicable", code = "{escaped_sugg}")]
char_span: Span,
escaped_sugg: String,
escaped_msg: String,
byte: bool,
},
#[diag(parse_bare_cr)]
BareCr {
#[primary_span]
#[suggestion(escape, applicability = "machine-applicable", code = "\\r")]
span: Span,
double_quotes: bool,
},
#[diag(parse_bare_cr_in_raw_string)]
BareCrRawString(#[primary_span] Span),
#[diag(parse_too_short_hex_escape)]
TooShortHexEscape(#[primary_span] Span),
#[diag(parse_invalid_char_in_escape)]
InvalidCharInEscape {
#[primary_span]
#[label]
span: Span,
is_hex: bool,
ch: String,
},
#[diag(parse_out_of_range_hex_escape)]
OutOfRangeHexEscape(
#[primary_span]
#[label]
Span,
),
#[diag(parse_leading_underscore_unicode_escape)]
LeadingUnderscoreUnicodeEscape {
#[primary_span]
#[label(parse_leading_underscore_unicode_escape_label)]
span: Span,
ch: String,
},
#[diag(parse_overlong_unicode_escape)]
OverlongUnicodeEscape(
#[primary_span]
#[label]
Span,
),
#[diag(parse_unclosed_unicode_escape)]
UnclosedUnicodeEscape(
#[primary_span]
#[label]
Span,
#[suggestion(terminate, code = "}}", applicability = "maybe-incorrect", style = "verbose")]
Span,
),
#[diag(parse_no_brace_unicode_escape)]
NoBraceInUnicodeEscape {
#[primary_span]
span: Span,
#[label]
label: Option<Span>,
#[subdiagnostic]
sub: NoBraceUnicodeSub,
},
#[diag(parse_unicode_escape_in_byte)]
#[help]
UnicodeEscapeInByte(
#[primary_span]
#[label]
Span,
),
#[diag(parse_empty_unicode_escape)]
EmptyUnicodeEscape(
#[primary_span]
#[label]
Span,
),
#[diag(parse_zero_chars)]
ZeroChars(
#[primary_span]
#[label]
Span,
),
#[diag(parse_lone_slash)]
LoneSlash(
#[primary_span]
#[label]
Span,
),
#[diag(parse_unskipped_whitespace)]
UnskippedWhitespace {
#[primary_span]
span: Span,
#[label]
char_span: Span,
ch: String,
},
#[diag(parse_multiple_skipped_lines)]
MultipleSkippedLinesWarning(
#[primary_span]
#[label]
Span,
),
#[diag(parse_more_than_one_char)]
MoreThanOneChar {
#[primary_span]
span: Span,
#[subdiagnostic]
note: Option<MoreThanOneCharNote>,
#[subdiagnostic]
suggestion: MoreThanOneCharSugg,
},
}
#[derive(Subdiagnostic)]
pub enum MoreThanOneCharSugg {
#[suggestion(consider_normalized, code = "{normalized}", applicability = "machine-applicable")]
NormalizedForm {
#[primary_span]
span: Span,
ch: String,
normalized: String,
},
#[suggestion(remove_non, code = "{ch}", applicability = "maybe-incorrect")]
RemoveNonPrinting {
#[primary_span]
span: Span,
ch: String,
},
#[suggestion(use_double_quotes, code = "{sugg}", applicability = "machine-applicable")]
Quotes {
#[primary_span]
span: Span,
is_byte: bool,
sugg: String,
},
}
#[derive(Subdiagnostic)]
pub enum MoreThanOneCharNote {
#[note(followed_by)]
AllCombining {
#[primary_span]
span: Span,
chr: String,
len: usize,
escaped_marks: String,
},
#[note(non_printing)]
NonPrinting {
#[primary_span]
span: Span,
escaped: String,
},
}
#[derive(Subdiagnostic)]
pub enum NoBraceUnicodeSub {
#[suggestion(use_braces, code = "{suggestion}", applicability = "maybe-incorrect")]
Suggestion {
#[primary_span]
span: Span,
suggestion: String,
},
#[help(format_of_unicode)]
Help,
}
#[derive(Subdiagnostic)] #[derive(Subdiagnostic)]
pub(crate) enum TopLevelOrPatternNotAllowedSugg { pub(crate) enum TopLevelOrPatternNotAllowedSugg {
#[suggestion( #[suggestion(

View file

@ -1,11 +1,10 @@
use crate::errors;
use crate::lexer::unicode_chars::UNICODE_ARRAY; use crate::lexer::unicode_chars::UNICODE_ARRAY;
use rustc_ast::ast::{self, AttrStyle}; use rustc_ast::ast::{self, AttrStyle};
use rustc_ast::token::{self, CommentKind, Delimiter, Token, TokenKind}; use rustc_ast::token::{self, CommentKind, Delimiter, Token, TokenKind};
use rustc_ast::tokenstream::TokenStream; use rustc_ast::tokenstream::TokenStream;
use rustc_ast::util::unicode::contains_text_flow_control_chars; use rustc_ast::util::unicode::contains_text_flow_control_chars;
use rustc_errors::{ use rustc_errors::{error_code, Applicability, DiagnosticBuilder, PResult, StashKey};
error_code, Applicability, DiagnosticBuilder, ErrorGuaranteed, PResult, StashKey,
};
use rustc_lexer::unescape::{self, Mode}; use rustc_lexer::unescape::{self, Mode};
use rustc_lexer::Cursor; use rustc_lexer::Cursor;
use rustc_lexer::{Base, DocStyle, RawStrError}; use rustc_lexer::{Base, DocStyle, RawStrError};
@ -151,7 +150,7 @@ impl<'a> StringReader<'a> {
let span = self.mk_sp(start, self.pos); let span = self.mk_sp(start, self.pos);
self.sess.symbol_gallery.insert(sym, span); self.sess.symbol_gallery.insert(sym, span);
if !sym.can_be_raw() { if !sym.can_be_raw() {
self.err_span(span, &format!("`{}` cannot be a raw identifier", sym)); self.sess.emit_err(errors::CannotBeRawIdent { span, ident: sym });
} }
self.sess.raw_identifier_spans.borrow_mut().push(span); self.sess.raw_identifier_spans.borrow_mut().push(span);
token::Ident(sym, true) token::Ident(sym, true)
@ -262,27 +261,24 @@ impl<'a> StringReader<'a> {
self.nbsp_is_whitespace = true; self.nbsp_is_whitespace = true;
} }
let repeats = it.take_while(|c1| *c1 == c).count(); let repeats = it.take_while(|c1| *c1 == c).count();
let mut err =
self.struct_err_span_char(start, self.pos + Pos::from_usize(repeats * c.len_utf8()), "unknown start of token", c);
// FIXME: the lexer could be used to turn the ASCII version of unicode // FIXME: the lexer could be used to turn the ASCII version of unicode
// homoglyphs, instead of keeping a table in `check_for_substitution`into the // homoglyphs, instead of keeping a table in `check_for_substitution`into the
// token. Ideally, this should be inside `rustc_lexer`. However, we should // token. Ideally, this should be inside `rustc_lexer`. However, we should
// first remove compound tokens like `<<` from `rustc_lexer`, and then add // first remove compound tokens like `<<` from `rustc_lexer`, and then add
// fancier error recovery to it, as there will be less overall work to do this // fancier error recovery to it, as there will be less overall work to do this
// way. // way.
let token = unicode_chars::check_for_substitution(self, start, c, &mut err, repeats+1); let (token, sugg) = unicode_chars::check_for_substitution(self, start, c, repeats+1);
if c == '\x00' { self.sess.emit_err(errors::UnknownTokenStart {
err.help("source files must contain UTF-8 encoded text, unexpected null bytes might occur when a different encoding is used"); span: self.mk_sp(start, self.pos + Pos::from_usize(repeats * c.len_utf8())),
} escaped: escaped_char(c),
if repeats > 0 { sugg,
if repeats == 1 { null: if c == '\x00' {Some(errors::UnknownTokenNull)} else {None},
err.note(format!("character appears once more")); repeat: if repeats > 0 {
} else { swallow_next_invalid = repeats;
err.note(format!("character appears {repeats} more times")); Some(errors::UnknownTokenRepeat { repeats })
} } else {None}
swallow_next_invalid = repeats; });
}
err.emit();
if let Some(token) = token { if let Some(token) = token {
token token
} else { } else {
@ -297,26 +293,6 @@ impl<'a> StringReader<'a> {
} }
} }
/// Report a fatal lexical error with a given span.
fn fatal_span(&self, sp: Span, m: &str) -> ! {
self.sess.span_diagnostic.span_fatal(sp, m)
}
/// Report a lexical error with a given span.
fn err_span(&self, sp: Span, m: &str) {
self.sess.span_diagnostic.struct_span_err(sp, m).emit();
}
/// Report a fatal error spanning [`from_pos`, `to_pos`).
fn fatal_span_(&self, from_pos: BytePos, to_pos: BytePos, m: &str) -> ! {
self.fatal_span(self.mk_sp(from_pos, to_pos), m)
}
/// Report a lexical error spanning [`from_pos`, `to_pos`).
fn err_span_(&self, from_pos: BytePos, to_pos: BytePos, m: &str) {
self.err_span(self.mk_sp(from_pos, to_pos), m)
}
fn struct_fatal_span_char( fn struct_fatal_span_char(
&self, &self,
from_pos: BytePos, from_pos: BytePos,
@ -329,18 +305,6 @@ impl<'a> StringReader<'a> {
.struct_span_fatal(self.mk_sp(from_pos, to_pos), &format!("{}: {}", m, escaped_char(c))) .struct_span_fatal(self.mk_sp(from_pos, to_pos), &format!("{}: {}", m, escaped_char(c)))
} }
fn struct_err_span_char(
&self,
from_pos: BytePos,
to_pos: BytePos,
m: &str,
c: char,
) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
self.sess
.span_diagnostic
.struct_span_err(self.mk_sp(from_pos, to_pos), &format!("{}: {}", m, escaped_char(c)))
}
/// Detect usages of Unicode codepoints changing the direction of the text on screen and loudly /// Detect usages of Unicode codepoints changing the direction of the text on screen and loudly
/// complain about it. /// complain about it.
fn lint_unicode_text_flow(&self, start: BytePos) { fn lint_unicode_text_flow(&self, start: BytePos) {
@ -368,14 +332,12 @@ impl<'a> StringReader<'a> {
) -> TokenKind { ) -> TokenKind {
if content.contains('\r') { if content.contains('\r') {
for (idx, _) in content.char_indices().filter(|&(_, c)| c == '\r') { for (idx, _) in content.char_indices().filter(|&(_, c)| c == '\r') {
self.err_span_( let span = self.mk_sp(
content_start + BytePos(idx as u32), content_start + BytePos(idx as u32),
content_start + BytePos(idx as u32 + 1), content_start + BytePos(idx as u32 + 1),
match comment_kind {
CommentKind::Line => "bare CR not allowed in doc-comment",
CommentKind::Block => "bare CR not allowed in block doc-comment",
},
); );
let block = matches!(comment_kind, CommentKind::Block);
self.sess.emit_err(errors::CrDocComment { span, block });
} }
} }
@ -454,26 +416,20 @@ impl<'a> StringReader<'a> {
} }
rustc_lexer::LiteralKind::Int { base, empty_int } => { rustc_lexer::LiteralKind::Int { base, empty_int } => {
if empty_int { if empty_int {
self.sess let span = self.mk_sp(start, end);
.span_diagnostic self.sess.emit_err(errors::NoDigitsLiteral { span });
.struct_span_err_with_code(
self.mk_sp(start, end),
"no valid digits found for number",
error_code!(E0768),
)
.emit();
(token::Integer, sym::integer(0)) (token::Integer, sym::integer(0))
} else { } else {
if matches!(base, Base::Binary | Base::Octal) { if matches!(base, Base::Binary | Base::Octal) {
let base = base as u32; let base = base as u32;
let s = self.str_from_to(start + BytePos(2), end); let s = self.str_from_to(start + BytePos(2), end);
for (idx, c) in s.char_indices() { for (idx, c) in s.char_indices() {
let span = self.mk_sp(
start + BytePos::from_usize(2 + idx),
start + BytePos::from_usize(2 + idx + c.len_utf8()),
);
if c != '_' && c.to_digit(base).is_none() { if c != '_' && c.to_digit(base).is_none() {
self.err_span_( self.sess.emit_err(errors::InvalidDigitLiteral { span, base });
start + BytePos::from_usize(2 + idx),
start + BytePos::from_usize(2 + idx + c.len_utf8()),
&format!("invalid digit for a base {} literal", base),
);
} }
} }
} }
@ -482,19 +438,18 @@ impl<'a> StringReader<'a> {
} }
rustc_lexer::LiteralKind::Float { base, empty_exponent } => { rustc_lexer::LiteralKind::Float { base, empty_exponent } => {
if empty_exponent { if empty_exponent {
self.err_span_(start, self.pos, "expected at least one digit in exponent"); let span = self.mk_sp(start, self.pos);
self.sess.emit_err(errors::EmptyExponentFloat { span });
} }
match base { let base = match base {
Base::Hexadecimal => { Base::Hexadecimal => Some("hexadecimal"),
self.err_span_(start, end, "hexadecimal float literal is not supported") Base::Octal => Some("octal"),
} Base::Binary => Some("binary"),
Base::Octal => { _ => None,
self.err_span_(start, end, "octal float literal is not supported") };
} if let Some(base) = base {
Base::Binary => { let span = self.mk_sp(start, end);
self.err_span_(start, end, "binary float literal is not supported") self.sess.emit_err(errors::FloatLiteralUnsupportedBase { span, base });
}
_ => {}
} }
(token::Float, self.symbol_from_to(start, end)) (token::Float, self.symbol_from_to(start, end))
} }
@ -644,54 +599,34 @@ impl<'a> StringReader<'a> {
// identifier tokens. // identifier tokens.
fn report_unknown_prefix(&self, start: BytePos) { fn report_unknown_prefix(&self, start: BytePos) {
let prefix_span = self.mk_sp(start, self.pos); let prefix_span = self.mk_sp(start, self.pos);
let prefix_str = self.str_from_to(start, self.pos); let prefix = self.str_from_to(start, self.pos);
let msg = format!("prefix `{}` is unknown", prefix_str);
let expn_data = prefix_span.ctxt().outer_expn_data(); let expn_data = prefix_span.ctxt().outer_expn_data();
if expn_data.edition >= Edition::Edition2021 { if expn_data.edition >= Edition::Edition2021 {
// In Rust 2021, this is a hard error. // In Rust 2021, this is a hard error.
let mut err = self.sess.span_diagnostic.struct_span_err(prefix_span, &msg); let sugg = if prefix == "rb" {
err.span_label(prefix_span, "unknown prefix"); Some(errors::UnknownPrefixSugg::UseBr(prefix_span))
if prefix_str == "rb" {
err.span_suggestion_verbose(
prefix_span,
"use `br` for a raw byte string",
"br",
Applicability::MaybeIncorrect,
);
} else if expn_data.is_root() { } else if expn_data.is_root() {
err.span_suggestion_verbose( Some(errors::UnknownPrefixSugg::Whitespace(prefix_span.shrink_to_hi()))
prefix_span.shrink_to_hi(), } else {
"consider inserting whitespace here", None
" ", };
Applicability::MaybeIncorrect, self.sess.emit_err(errors::UnknownPrefix { span: prefix_span, prefix, sugg });
);
}
err.note("prefixed identifiers and literals are reserved since Rust 2021");
err.emit();
} else { } else {
// Before Rust 2021, only emit a lint for migration. // Before Rust 2021, only emit a lint for migration.
self.sess.buffer_lint_with_diagnostic( self.sess.buffer_lint_with_diagnostic(
&RUST_2021_PREFIXES_INCOMPATIBLE_SYNTAX, &RUST_2021_PREFIXES_INCOMPATIBLE_SYNTAX,
prefix_span, prefix_span,
ast::CRATE_NODE_ID, ast::CRATE_NODE_ID,
&msg, &format!("prefix `{prefix}` is unknown"),
BuiltinLintDiagnostics::ReservedPrefix(prefix_span), BuiltinLintDiagnostics::ReservedPrefix(prefix_span),
); );
} }
} }
fn report_too_many_hashes(&self, start: BytePos, found: u32) -> ! { fn report_too_many_hashes(&self, start: BytePos, num: u32) -> ! {
self.fatal_span_( self.sess.emit_fatal(errors::TooManyHashes { span: self.mk_sp(start, self.pos), num });
start,
self.pos,
&format!(
"too many `#` symbols: raw strings may be delimited \
by up to 255 `#` symbols, but found {}",
found
),
)
} }
fn cook_quoted( fn cook_quoted(

View file

@ -3,10 +3,12 @@
use std::iter::once; use std::iter::once;
use std::ops::Range; use std::ops::Range;
use rustc_errors::{pluralize, Applicability, Handler}; use rustc_errors::{Applicability, Handler};
use rustc_lexer::unescape::{EscapeError, Mode}; use rustc_lexer::unescape::{EscapeError, Mode};
use rustc_span::{BytePos, Span}; use rustc_span::{BytePos, Span};
use crate::errors::{MoreThanOneCharNote, MoreThanOneCharSugg, NoBraceUnicodeSub, UnescapeError};
pub(crate) fn emit_unescape_error( pub(crate) fn emit_unescape_error(
handler: &Handler, handler: &Handler,
// interior part of the literal, without quotes // interior part of the literal, without quotes
@ -31,53 +33,32 @@ pub(crate) fn emit_unescape_error(
}; };
match error { match error {
EscapeError::LoneSurrogateUnicodeEscape => { EscapeError::LoneSurrogateUnicodeEscape => {
handler handler.emit_err(UnescapeError::InvalidUnicodeEscape { span, surrogate: true });
.struct_span_err(span, "invalid unicode character escape")
.span_label(span, "invalid escape")
.help("unicode escape must not be a surrogate")
.emit();
} }
EscapeError::OutOfRangeUnicodeEscape => { EscapeError::OutOfRangeUnicodeEscape => {
handler handler.emit_err(UnescapeError::InvalidUnicodeEscape { span, surrogate: false });
.struct_span_err(span, "invalid unicode character escape")
.span_label(span, "invalid escape")
.help("unicode escape must be at most 10FFFF")
.emit();
} }
EscapeError::MoreThanOneChar => { EscapeError::MoreThanOneChar => {
use unicode_normalization::{char::is_combining_mark, UnicodeNormalization}; use unicode_normalization::{char::is_combining_mark, UnicodeNormalization};
let mut sugg = None;
let mut note = None;
let mut has_help = false; let lit_chars = lit.chars().collect::<Vec<_>>();
let mut handler = handler.struct_span_err( let (first, rest) = lit_chars.split_first().unwrap();
span_with_quotes, if rest.iter().copied().all(is_combining_mark) {
"character literal may only contain one codepoint",
);
if lit.chars().skip(1).all(|c| is_combining_mark(c)) {
let escaped_marks =
lit.chars().skip(1).map(|c| c.escape_default().to_string()).collect::<Vec<_>>();
handler.span_note(
span,
&format!(
"this `{}` is followed by the combining mark{} `{}`",
lit.chars().next().unwrap(),
pluralize!(escaped_marks.len()),
escaped_marks.join(""),
),
);
let normalized = lit.nfc().to_string(); let normalized = lit.nfc().to_string();
if normalized.chars().count() == 1 { if normalized.chars().count() == 1 {
has_help = true; let ch = normalized.chars().next().unwrap().escape_default().to_string();
handler.span_suggestion( sugg = Some(MoreThanOneCharSugg::NormalizedForm { span, ch, normalized });
span,
&format!(
"consider using the normalized form `{}` of this character",
normalized.chars().next().unwrap().escape_default()
),
normalized,
Applicability::MachineApplicable,
);
} }
let escaped_marks =
rest.iter().map(|c| c.escape_default().to_string()).collect::<Vec<_>>();
note = Some(MoreThanOneCharNote::AllCombining {
span,
chr: format!("{first}"),
len: escaped_marks.len(),
escaped_marks: escaped_marks.join(""),
});
} else { } else {
let printable: Vec<char> = lit let printable: Vec<char> = lit
.chars() .chars()
@ -87,32 +68,18 @@ pub(crate) fn emit_unescape_error(
}) })
.collect(); .collect();
if let [ch] = printable.as_slice() { if let &[ch] = printable.as_slice() {
has_help = true; sugg =
Some(MoreThanOneCharSugg::RemoveNonPrinting { span, ch: ch.to_string() });
handler.span_note( note = Some(MoreThanOneCharNote::NonPrinting {
span, span,
&format!( escaped: lit.escape_default().to_string(),
"there are non-printing characters, the full sequence is `{}`", });
lit.escape_default(),
),
);
handler.span_suggestion(
span,
"consider removing the non-printing characters",
ch,
Applicability::MaybeIncorrect,
);
} }
} };
let sugg = sugg.unwrap_or_else(|| {
if !has_help { let is_byte = mode.is_byte();
let (prefix, msg) = if mode.is_byte() { let prefix = if is_byte { "b" } else { "" };
("b", "if you meant to write a byte string literal, use double quotes")
} else {
("", "if you meant to write a `str` literal, use double quotes")
};
let mut escaped = String::with_capacity(lit.len()); let mut escaped = String::with_capacity(lit.len());
let mut chrs = lit.chars().peekable(); let mut chrs = lit.chars().peekable();
while let Some(first) = chrs.next() { while let Some(first) = chrs.next() {
@ -129,54 +96,32 @@ pub(crate) fn emit_unescape_error(
(c, _) => escaped.push(c), (c, _) => escaped.push(c),
}; };
} }
handler.span_suggestion( let sugg = format!("{prefix}\"{escaped}\"");
span_with_quotes, MoreThanOneCharSugg::Quotes { span: span_with_quotes, is_byte, sugg }
msg, });
format!("{prefix}\"{escaped}\""), handler.emit_err(UnescapeError::MoreThanOneChar {
Applicability::MachineApplicable, span: span_with_quotes,
); note,
} suggestion: sugg,
});
handler.emit();
} }
EscapeError::EscapeOnlyChar => { EscapeError::EscapeOnlyChar => {
let (c, char_span) = last_char(); let (c, char_span) = last_char();
handler.emit_err(UnescapeError::EscapeOnlyChar {
let msg = if mode.is_byte() { span,
"byte constant must be escaped" char_span,
} else { escaped_sugg: c.escape_default().to_string(),
"character constant must be escaped" escaped_msg: escaped_char(c),
}; byte: mode.is_byte(),
handler });
.struct_span_err(span, &format!("{}: `{}`", msg, escaped_char(c)))
.span_suggestion(
char_span,
"escape the character",
c.escape_default(),
Applicability::MachineApplicable,
)
.emit();
} }
EscapeError::BareCarriageReturn => { EscapeError::BareCarriageReturn => {
let msg = if mode.in_double_quotes() { let double_quotes = mode.in_double_quotes();
"bare CR not allowed in string, use `\\r` instead" handler.emit_err(UnescapeError::BareCr { span, double_quotes });
} else {
"character constant must be escaped: `\\r`"
};
handler
.struct_span_err(span, msg)
.span_suggestion(
span,
"escape the character",
"\\r",
Applicability::MachineApplicable,
)
.emit();
} }
EscapeError::BareCarriageReturnInRawString => { EscapeError::BareCarriageReturnInRawString => {
assert!(mode.in_double_quotes()); assert!(mode.in_double_quotes());
let msg = "bare CR not allowed in raw string"; handler.emit_err(UnescapeError::BareCrRawString(span));
handler.span_err(span, msg);
} }
EscapeError::InvalidEscape => { EscapeError::InvalidEscape => {
let (c, span) = last_char(); let (c, span) = last_char();
@ -213,22 +158,13 @@ pub(crate) fn emit_unescape_error(
diag.emit(); diag.emit();
} }
EscapeError::TooShortHexEscape => { EscapeError::TooShortHexEscape => {
handler.span_err(span, "numeric character escape is too short"); handler.emit_err(UnescapeError::TooShortHexEscape(span));
} }
EscapeError::InvalidCharInHexEscape | EscapeError::InvalidCharInUnicodeEscape => { EscapeError::InvalidCharInHexEscape | EscapeError::InvalidCharInUnicodeEscape => {
let (c, span) = last_char(); let (c, span) = last_char();
let is_hex = error == EscapeError::InvalidCharInHexEscape;
let msg = if error == EscapeError::InvalidCharInHexEscape { let ch = escaped_char(c);
"invalid character in numeric character escape" handler.emit_err(UnescapeError::InvalidCharInEscape { span, is_hex, ch });
} else {
"invalid character in unicode escape"
};
let c = escaped_char(c);
handler
.struct_span_err(span, &format!("{}: `{}`", msg, c))
.span_label(span, msg)
.emit();
} }
EscapeError::NonAsciiCharInByte => { EscapeError::NonAsciiCharInByte => {
let (c, span) = last_char(); let (c, span) = last_char();
@ -278,41 +214,22 @@ pub(crate) fn emit_unescape_error(
err.emit(); err.emit();
} }
EscapeError::OutOfRangeHexEscape => { EscapeError::OutOfRangeHexEscape => {
handler handler.emit_err(UnescapeError::OutOfRangeHexEscape(span));
.struct_span_err(span, "out of range hex escape")
.span_label(span, "must be a character in the range [\\x00-\\x7f]")
.emit();
} }
EscapeError::LeadingUnderscoreUnicodeEscape => { EscapeError::LeadingUnderscoreUnicodeEscape => {
let (c, span) = last_char(); let (c, span) = last_char();
let msg = "invalid start of unicode escape"; handler.emit_err(UnescapeError::LeadingUnderscoreUnicodeEscape {
handler span,
.struct_span_err(span, &format!("{}: `{}`", msg, c)) ch: escaped_char(c),
.span_label(span, msg) });
.emit();
} }
EscapeError::OverlongUnicodeEscape => { EscapeError::OverlongUnicodeEscape => {
handler handler.emit_err(UnescapeError::OverlongUnicodeEscape(span));
.struct_span_err(span, "overlong unicode escape")
.span_label(span, "must have at most 6 hex digits")
.emit();
} }
EscapeError::UnclosedUnicodeEscape => { EscapeError::UnclosedUnicodeEscape => {
handler handler.emit_err(UnescapeError::UnclosedUnicodeEscape(span, span.shrink_to_hi()));
.struct_span_err(span, "unterminated unicode escape")
.span_label(span, "missing a closing `}`")
.span_suggestion_verbose(
span.shrink_to_hi(),
"terminate the unicode escape",
"}",
Applicability::MaybeIncorrect,
)
.emit();
} }
EscapeError::NoBraceInUnicodeEscape => { EscapeError::NoBraceInUnicodeEscape => {
let msg = "incorrect unicode escape sequence";
let mut diag = handler.struct_span_err(span, msg);
let mut suggestion = "\\u{".to_owned(); let mut suggestion = "\\u{".to_owned();
let mut suggestion_len = 0; let mut suggestion_len = 0;
let (c, char_span) = last_char(); let (c, char_span) = last_char();
@ -322,54 +239,37 @@ pub(crate) fn emit_unescape_error(
suggestion_len += c.len_utf8(); suggestion_len += c.len_utf8();
} }
if suggestion_len > 0 { let (label, sub) = if suggestion_len > 0 {
suggestion.push('}'); suggestion.push('}');
let hi = char_span.lo() + BytePos(suggestion_len as u32); let hi = char_span.lo() + BytePos(suggestion_len as u32);
diag.span_suggestion( (None, NoBraceUnicodeSub::Suggestion { span: span.with_hi(hi), suggestion })
span.with_hi(hi),
"format of unicode escape sequences uses braces",
suggestion,
Applicability::MaybeIncorrect,
);
} else { } else {
diag.span_label(span, msg); (Some(span), NoBraceUnicodeSub::Help)
diag.help("format of unicode escape sequences is `\\u{...}`"); };
} handler.emit_err(UnescapeError::NoBraceInUnicodeEscape { span, label, sub });
diag.emit();
} }
EscapeError::UnicodeEscapeInByte => { EscapeError::UnicodeEscapeInByte => {
let msg = "unicode escape in byte string"; handler.emit_err(UnescapeError::UnicodeEscapeInByte(span));
handler
.struct_span_err(span, msg)
.span_label(span, msg)
.help("unicode escape sequences cannot be used as a byte or in a byte string")
.emit();
} }
EscapeError::EmptyUnicodeEscape => { EscapeError::EmptyUnicodeEscape => {
handler handler.emit_err(UnescapeError::EmptyUnicodeEscape(span));
.struct_span_err(span, "empty unicode escape")
.span_label(span, "this escape must have at least 1 hex digit")
.emit();
} }
EscapeError::ZeroChars => { EscapeError::ZeroChars => {
let msg = "empty character literal"; handler.emit_err(UnescapeError::ZeroChars(span));
handler.struct_span_err(span, msg).span_label(span, msg).emit();
} }
EscapeError::LoneSlash => { EscapeError::LoneSlash => {
let msg = "invalid trailing slash in literal"; handler.emit_err(UnescapeError::LoneSlash(span));
handler.struct_span_err(span, msg).span_label(span, msg).emit();
} }
EscapeError::UnskippedWhitespaceWarning => { EscapeError::UnskippedWhitespaceWarning => {
let (c, char_span) = last_char(); let (c, char_span) = last_char();
let msg = handler.emit_warning(UnescapeError::UnskippedWhitespace {
format!("non-ASCII whitespace symbol '{}' is not skipped", c.escape_unicode()); span,
handler.struct_span_warn(span, &msg).span_label(char_span, &msg).emit(); ch: escaped_char(c),
char_span,
});
} }
EscapeError::MultipleSkippedLinesWarning => { EscapeError::MultipleSkippedLinesWarning => {
let msg = "multiple lines skipped by escaped newline"; handler.emit_warning(UnescapeError::MultipleSkippedLinesWarning(span));
let bottom_msg = "skipping everything up to and including this point";
handler.struct_span_warn(span, msg).span_label(span, bottom_msg).emit();
} }
} }
} }

View file

@ -2,8 +2,10 @@
//! <https://www.unicode.org/Public/security/10.0.0/confusables.txt> //! <https://www.unicode.org/Public/security/10.0.0/confusables.txt>
use super::StringReader; use super::StringReader;
use crate::token::{self, Delimiter}; use crate::{
use rustc_errors::{Applicability, Diagnostic}; errors::TokenSubstitution,
token::{self, Delimiter},
};
use rustc_span::{symbol::kw, BytePos, Pos, Span}; use rustc_span::{symbol::kw, BytePos, Pos, Span};
#[rustfmt::skip] // for line breaks #[rustfmt::skip] // for line breaks
@ -338,48 +340,44 @@ pub(super) fn check_for_substitution<'a>(
reader: &StringReader<'a>, reader: &StringReader<'a>,
pos: BytePos, pos: BytePos,
ch: char, ch: char,
err: &mut Diagnostic,
count: usize, count: usize,
) -> Option<token::TokenKind> { ) -> (Option<token::TokenKind>, Option<TokenSubstitution>) {
let &(_, u_name, ascii_str) = UNICODE_ARRAY.iter().find(|&&(c, _, _)| c == ch)?; let Some(&(_, u_name, ascii_str)) = UNICODE_ARRAY.iter().find(|&&(c, _, _)| c == ch) else {
return (None, None);
};
let span = Span::with_root_ctxt(pos, pos + Pos::from_usize(ch.len_utf8() * count)); let span = Span::with_root_ctxt(pos, pos + Pos::from_usize(ch.len_utf8() * count));
let Some((_, ascii_name, token)) = ASCII_ARRAY.iter().find(|&&(s, _, _)| s == ascii_str) else { let Some((_, ascii_name, token)) = ASCII_ARRAY.iter().find(|&&(s, _, _)| s == ascii_str) else {
let msg = format!("substitution character not found for '{}'", ch); let msg = format!("substitution character not found for '{}'", ch);
reader.sess.span_diagnostic.span_bug_no_panic(span, &msg); reader.sess.span_diagnostic.span_bug_no_panic(span, &msg);
return None; return (None, None);
}; };
// special help suggestion for "directed" double quotes // special help suggestion for "directed" double quotes
if let Some(s) = peek_delimited(&reader.src[reader.src_index(pos)..], '“', '”') { let sugg = if let Some(s) = peek_delimited(&reader.src[reader.src_index(pos)..], '“', '”') {
let msg = format!( let span = Span::with_root_ctxt(
"Unicode characters '“' (Left Double Quotation Mark) and \ pos,
'”' (Right Double Quotation Mark) look like '{}' ({}), but are not", pos + Pos::from_usize('“'.len_utf8() + s.len() + '”'.len_utf8()),
ascii_str, ascii_name
); );
err.span_suggestion( Some(TokenSubstitution::DirectedQuotes {
Span::with_root_ctxt(
pos,
pos + Pos::from_usize('“'.len_utf8() + s.len() + '”'.len_utf8()),
),
&msg,
format!("\"{}\"", s),
Applicability::MaybeIncorrect,
);
} else {
let msg = format!(
"Unicode character '{}' ({}) looks like '{}' ({}), but it is not",
ch, u_name, ascii_str, ascii_name
);
err.span_suggestion(
span, span,
&msg, suggestion: format!("\"{s}\""),
ascii_str.to_string().repeat(count), ascii_str,
Applicability::MaybeIncorrect, ascii_name,
); })
} } else {
token.clone() let suggestion = ascii_str.to_string().repeat(count);
Some(TokenSubstitution::Other {
span,
suggestion,
ch: ch.to_string(),
u_name,
ascii_str,
ascii_name,
})
};
(token.clone(), sugg)
} }
/// Extract string if found at current position with given delimiters /// Extract string if found at current position with given delimiters

View file

@ -5,6 +5,7 @@ use super::{
AttrWrapper, BlockMode, ClosureSpans, ForceCollect, Parser, PathStyle, Restrictions, AttrWrapper, BlockMode, ClosureSpans, ForceCollect, Parser, PathStyle, Restrictions,
SemiColonMode, SeqSep, TokenExpectType, TokenType, TrailingToken, SemiColonMode, SeqSep, TokenExpectType, TokenType, TrailingToken,
}; };
use crate::errors; use crate::errors;
use crate::maybe_recover_from_interpolated_ty_qpath; use crate::maybe_recover_from_interpolated_ty_qpath;
use core::mem; use core::mem;
@ -1017,7 +1018,7 @@ impl<'a> Parser<'a> {
fn error_unexpected_after_dot(&self) { fn error_unexpected_after_dot(&self) {
// FIXME Could factor this out into non_fatal_unexpected or something. // FIXME Could factor this out into non_fatal_unexpected or something.
let actual = pprust::token_to_string(&self.token); let actual = pprust::token_to_string(&self.token);
self.struct_span_err(self.token.span, &format!("unexpected token: `{actual}`")).emit(); self.sess.emit_err(errors::UnexpectedTokenAfterDot { span: self.token.span, actual });
} }
// We need an identifier or integer, but the next token is a float. // We need an identifier or integer, but the next token is a float.

View file

@ -1,18 +1,8 @@
use crate::errors::{ use crate::errors;
AmbiguousMissingKwForItemSub, AssociatedStaticItemNotAllowed, AsyncFnIn2015,
BoundsNotAllowedOnTraitAliases, ConstGlobalCannotBeMutable, ConstLetMutuallyExclusive,
DefaultNotFollowedByItem, DocCommentDoesNotDocumentAnything, EnumStructMutuallyExclusive,
ExpectedTraitInTraitImplFoundType, ExternCrateNameWithDashes, ExternCrateNameWithDashesSugg,
ExternItemCannotBeConst, HelpUseLatestEdition, MissingConstType, MissingForInTraitImpl,
MissingKeywordForItemDefinition, MissingTraitInTraitImpl, SelfArgumentPointer,
TraitAliasCannotBeAuto, TraitAliasCannotBeUnsafe, UnexpectedTokenAfterStructName,
UseEmptyBlockNotSemi, VisibilityNotFollowedByItem,
};
use super::diagnostics::{dummy_arg, ConsumeClosingDelim}; use super::diagnostics::{dummy_arg, ConsumeClosingDelim};
use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign}; use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign};
use super::{AttrWrapper, FollowedByType, ForceCollect, Parser, PathStyle, TrailingToken}; use super::{AttrWrapper, FollowedByType, ForceCollect, Parser, PathStyle, TrailingToken};
use crate::errors::FnTypoWithImpl;
use rustc_ast::ast::*; use rustc_ast::ast::*;
use rustc_ast::ptr::P; use rustc_ast::ptr::P;
use rustc_ast::token::{self, Delimiter, TokenKind}; use rustc_ast::token::{self, Delimiter, TokenKind};
@ -177,11 +167,11 @@ impl<'a> Parser<'a> {
// At this point, we have failed to parse an item. // At this point, we have failed to parse an item.
if !matches!(vis.kind, VisibilityKind::Inherited) { if !matches!(vis.kind, VisibilityKind::Inherited) {
self.sess.emit_err(VisibilityNotFollowedByItem { span: vis.span, vis }); self.sess.emit_err(errors::VisibilityNotFollowedByItem { span: vis.span, vis });
} }
if let Defaultness::Default(span) = def { if let Defaultness::Default(span) = def {
self.sess.emit_err(DefaultNotFollowedByItem { span }); self.sess.emit_err(errors::DefaultNotFollowedByItem { span });
} }
if !attrs_allowed { if !attrs_allowed {
@ -403,7 +393,7 @@ impl<'a> Parser<'a> {
let err = if self.check(&token::OpenDelim(Delimiter::Brace)) { let err = if self.check(&token::OpenDelim(Delimiter::Brace)) {
// possible public struct definition where `struct` was forgotten // possible public struct definition where `struct` was forgotten
Some(MissingKeywordForItemDefinition::Struct { span: sp, ident }) Some(errors::MissingKeywordForItemDefinition::Struct { span: sp, ident })
} else if self.check(&token::OpenDelim(Delimiter::Parenthesis)) { } else if self.check(&token::OpenDelim(Delimiter::Parenthesis)) {
// possible public function or tuple struct definition where `fn`/`struct` was // possible public function or tuple struct definition where `fn`/`struct` was
// forgotten // forgotten
@ -412,34 +402,36 @@ impl<'a> Parser<'a> {
self.consume_block(Delimiter::Parenthesis, ConsumeClosingDelim::Yes); self.consume_block(Delimiter::Parenthesis, ConsumeClosingDelim::Yes);
let err = if self.check(&token::RArrow) let err =
|| self.check(&token::OpenDelim(Delimiter::Brace)) if self.check(&token::RArrow) || self.check(&token::OpenDelim(Delimiter::Brace)) {
{ self.eat_to_tokens(&[&token::OpenDelim(Delimiter::Brace)]);
self.eat_to_tokens(&[&token::OpenDelim(Delimiter::Brace)]); self.bump(); // `{`
self.bump(); // `{` self.consume_block(Delimiter::Brace, ConsumeClosingDelim::Yes);
self.consume_block(Delimiter::Brace, ConsumeClosingDelim::Yes); if is_method {
if is_method { errors::MissingKeywordForItemDefinition::Method { span: sp, ident }
MissingKeywordForItemDefinition::Method { span: sp, ident }
} else {
MissingKeywordForItemDefinition::Function { span: sp, ident }
}
} else if self.check(&token::Semi) {
MissingKeywordForItemDefinition::Struct { span: sp, ident }
} else {
MissingKeywordForItemDefinition::Ambiguous {
span: sp,
subdiag: if found_generics {
None
} else if let Ok(snippet) = self.span_to_snippet(ident_sp) {
Some(AmbiguousMissingKwForItemSub::SuggestMacro { span: full_sp, snippet })
} else { } else {
Some(AmbiguousMissingKwForItemSub::HelpMacro) errors::MissingKeywordForItemDefinition::Function { span: sp, ident }
}, }
} } else if self.check(&token::Semi) {
}; errors::MissingKeywordForItemDefinition::Struct { span: sp, ident }
} else {
errors::MissingKeywordForItemDefinition::Ambiguous {
span: sp,
subdiag: if found_generics {
None
} else if let Ok(snippet) = self.span_to_snippet(ident_sp) {
Some(errors::AmbiguousMissingKwForItemSub::SuggestMacro {
span: full_sp,
snippet,
})
} else {
Some(errors::AmbiguousMissingKwForItemSub::HelpMacro)
},
}
};
Some(err) Some(err)
} else if found_generics { } else if found_generics {
Some(MissingKeywordForItemDefinition::Ambiguous { span: sp, subdiag: None }) Some(errors::MissingKeywordForItemDefinition::Ambiguous { span: sp, subdiag: None })
} else { } else {
None None
}; };
@ -567,8 +559,10 @@ impl<'a> Parser<'a> {
let ty_first = if self.token.is_keyword(kw::For) && self.look_ahead(1, |t| t != &token::Lt) let ty_first = if self.token.is_keyword(kw::For) && self.look_ahead(1, |t| t != &token::Lt)
{ {
let span = self.prev_token.span.between(self.token.span); let span = self.prev_token.span.between(self.token.span);
self.sess self.sess.emit_err(errors::MissingTraitInTraitImpl {
.emit_err(MissingTraitInTraitImpl { span, for_span: span.to(self.token.span) }); span,
for_span: span.to(self.token.span),
});
P(Ty { P(Ty {
kind: TyKind::Path(None, err_path(span)), kind: TyKind::Path(None, err_path(span)),
@ -602,7 +596,7 @@ impl<'a> Parser<'a> {
Some(ty_second) => { Some(ty_second) => {
// impl Trait for Type // impl Trait for Type
if !has_for { if !has_for {
self.sess.emit_err(MissingForInTraitImpl { span: missing_for_span }); self.sess.emit_err(errors::MissingForInTraitImpl { span: missing_for_span });
} }
let ty_first = ty_first.into_inner(); let ty_first = ty_first.into_inner();
@ -610,8 +604,9 @@ impl<'a> Parser<'a> {
// This notably includes paths passed through `ty` macro fragments (#46438). // This notably includes paths passed through `ty` macro fragments (#46438).
TyKind::Path(None, path) => path, TyKind::Path(None, path) => path,
_ => { _ => {
self.sess self.sess.emit_err(errors::ExpectedTraitInTraitImplFoundType {
.emit_err(ExpectedTraitInTraitImplFoundType { span: ty_first.span }); span: ty_first.span,
});
err_path(ty_first.span) err_path(ty_first.span)
} }
}; };
@ -655,7 +650,7 @@ impl<'a> Parser<'a> {
// Recover `impl Ty;` instead of `impl Ty {}` // Recover `impl Ty;` instead of `impl Ty {}`
if self.token == TokenKind::Semi { if self.token == TokenKind::Semi {
self.sess.emit_err(UseEmptyBlockNotSemi { span: self.token.span }); self.sess.emit_err(errors::UseEmptyBlockNotSemi { span: self.token.span });
self.bump(); self.bump();
return Ok(vec![]); return Ok(vec![]);
} }
@ -812,7 +807,7 @@ impl<'a> Parser<'a> {
// It's a trait alias. // It's a trait alias.
if had_colon { if had_colon {
let span = span_at_colon.to(span_before_eq); let span = span_at_colon.to(span_before_eq);
self.sess.emit_err(BoundsNotAllowedOnTraitAliases { span }); self.sess.emit_err(errors::BoundsNotAllowedOnTraitAliases { span });
} }
let bounds = self.parse_generic_bounds(None)?; let bounds = self.parse_generic_bounds(None)?;
@ -821,10 +816,10 @@ impl<'a> Parser<'a> {
let whole_span = lo.to(self.prev_token.span); let whole_span = lo.to(self.prev_token.span);
if is_auto == IsAuto::Yes { if is_auto == IsAuto::Yes {
self.sess.emit_err(TraitAliasCannotBeAuto { span: whole_span }); self.sess.emit_err(errors::TraitAliasCannotBeAuto { span: whole_span });
} }
if let Unsafe::Yes(_) = unsafety { if let Unsafe::Yes(_) = unsafety {
self.sess.emit_err(TraitAliasCannotBeUnsafe { span: whole_span }); self.sess.emit_err(errors::TraitAliasCannotBeUnsafe { span: whole_span });
} }
self.sess.gated_spans.gate(sym::trait_alias, whole_span); self.sess.gated_spans.gate(sym::trait_alias, whole_span);
@ -870,7 +865,7 @@ impl<'a> Parser<'a> {
Ok(kind) => kind, Ok(kind) => kind,
Err(kind) => match kind { Err(kind) => match kind {
ItemKind::Static(a, _, b) => { ItemKind::Static(a, _, b) => {
self.sess.emit_err(AssociatedStaticItemNotAllowed { span }); self.sess.emit_err(errors::AssociatedStaticItemNotAllowed { span });
AssocItemKind::Const(Defaultness::Final, a, b) AssocItemKind::Const(Defaultness::Final, a, b)
} }
_ => return self.error_bad_item_kind(span, &kind, "`trait`s or `impl`s"), _ => return self.error_bad_item_kind(span, &kind, "`trait`s or `impl`s"),
@ -1069,9 +1064,9 @@ impl<'a> Parser<'a> {
write!(fixed_name, "_{}", part.name).unwrap(); write!(fixed_name, "_{}", part.name).unwrap();
} }
self.sess.emit_err(ExternCrateNameWithDashes { self.sess.emit_err(errors::ExternCrateNameWithDashes {
span: fixed_name_sp, span: fixed_name_sp,
sugg: ExternCrateNameWithDashesSugg { dashes }, sugg: errors::ExternCrateNameWithDashesSugg { dashes },
}); });
Ok(Ident::from_str_and_span(&fixed_name, fixed_name_sp)) Ok(Ident::from_str_and_span(&fixed_name, fixed_name_sp))
@ -1122,7 +1117,7 @@ impl<'a> Parser<'a> {
Ok(kind) => kind, Ok(kind) => kind,
Err(kind) => match kind { Err(kind) => match kind {
ItemKind::Const(_, a, b) => { ItemKind::Const(_, a, b) => {
self.sess.emit_err(ExternItemCannotBeConst { self.sess.emit_err(errors::ExternItemCannotBeConst {
ident_span: ident.span, ident_span: ident.span,
const_span: span.with_hi(ident.span.lo()), const_span: span.with_hi(ident.span.lo()),
}); });
@ -1173,10 +1168,10 @@ impl<'a> Parser<'a> {
fn recover_const_mut(&mut self, const_span: Span) { fn recover_const_mut(&mut self, const_span: Span) {
if self.eat_keyword(kw::Mut) { if self.eat_keyword(kw::Mut) {
let span = self.prev_token.span; let span = self.prev_token.span;
self.sess.emit_err(ConstGlobalCannotBeMutable { ident_span: span, const_span }); self.sess.emit_err(errors::ConstGlobalCannotBeMutable { ident_span: span, const_span });
} else if self.eat_keyword(kw::Let) { } else if self.eat_keyword(kw::Let) {
let span = self.prev_token.span; let span = self.prev_token.span;
self.sess.emit_err(ConstLetMutuallyExclusive { span: const_span.to(span) }); self.sess.emit_err(errors::ConstLetMutuallyExclusive { span: const_span.to(span) });
} }
} }
@ -1262,7 +1257,8 @@ impl<'a> Parser<'a> {
let span = self.prev_token.span.shrink_to_hi(); let span = self.prev_token.span.shrink_to_hi();
let err: DiagnosticBuilder<'_, ErrorGuaranteed> = let err: DiagnosticBuilder<'_, ErrorGuaranteed> =
MissingConstType { span, colon, kind }.into_diagnostic(&self.sess.span_diagnostic); errors::MissingConstType { span, colon, kind }
.into_diagnostic(&self.sess.span_diagnostic);
err.stash(span, StashKey::ItemNoType); err.stash(span, StashKey::ItemNoType);
// The user intended that the type be inferred, // The user intended that the type be inferred,
@ -1274,7 +1270,7 @@ impl<'a> Parser<'a> {
fn parse_item_enum(&mut self) -> PResult<'a, ItemInfo> { fn parse_item_enum(&mut self) -> PResult<'a, ItemInfo> {
if self.token.is_keyword(kw::Struct) { if self.token.is_keyword(kw::Struct) {
let span = self.prev_token.span.to(self.token.span); let span = self.prev_token.span.to(self.token.span);
let err = EnumStructMutuallyExclusive { span }; let err = errors::EnumStructMutuallyExclusive { span };
if self.look_ahead(1, |t| t.is_ident()) { if self.look_ahead(1, |t| t.is_ident()) {
self.bump(); self.bump();
self.sess.emit_err(err); self.sess.emit_err(err);
@ -1289,7 +1285,7 @@ impl<'a> Parser<'a> {
// Possibly recover `enum Foo;` instead of `enum Foo {}` // Possibly recover `enum Foo;` instead of `enum Foo {}`
let (variants, _) = if self.token == TokenKind::Semi { let (variants, _) = if self.token == TokenKind::Semi {
self.sess.emit_err(UseEmptyBlockNotSemi { span: self.token.span }); self.sess.emit_err(errors::UseEmptyBlockNotSemi { span: self.token.span });
self.bump(); self.bump();
(vec![], false) (vec![], false)
} else { } else {
@ -1415,7 +1411,8 @@ impl<'a> Parser<'a> {
self.expect_semi()?; self.expect_semi()?;
body body
} else { } else {
let err = UnexpectedTokenAfterStructName::new(self.token.span, self.token.clone()); let err =
errors::UnexpectedTokenAfterStructName::new(self.token.span, self.token.clone());
return Err(err.into_diagnostic(&self.sess.span_diagnostic)); return Err(err.into_diagnostic(&self.sess.span_diagnostic));
}; };
@ -1593,7 +1590,7 @@ impl<'a> Parser<'a> {
token::CloseDelim(Delimiter::Brace) => {} token::CloseDelim(Delimiter::Brace) => {}
token::DocComment(..) => { token::DocComment(..) => {
let previous_span = self.prev_token.span; let previous_span = self.prev_token.span;
let mut err = DocCommentDoesNotDocumentAnything { let mut err = errors::DocCommentDoesNotDocumentAnything {
span: self.token.span, span: self.token.span,
missing_comma: None, missing_comma: None,
}; };
@ -2103,7 +2100,7 @@ impl<'a> Parser<'a> {
// If we see `for Ty ...` then user probably meant `impl` item. // If we see `for Ty ...` then user probably meant `impl` item.
if self.token.is_keyword(kw::For) { if self.token.is_keyword(kw::For) {
old_err.cancel(); old_err.cancel();
return Err(self.sess.create_err(FnTypoWithImpl { fn_span })); return Err(self.sess.create_err(errors::FnTypoWithImpl { fn_span }));
} else { } else {
return Err(old_err); return Err(old_err);
} }
@ -2248,7 +2245,10 @@ impl<'a> Parser<'a> {
if let Async::Yes { span, .. } = asyncness { if let Async::Yes { span, .. } = asyncness {
if span.is_rust_2015() { if span.is_rust_2015() {
self.sess.emit_err(AsyncFnIn2015 { span, help: HelpUseLatestEdition::new() }); self.sess.emit_err(errors::AsyncFnIn2015 {
span,
help: errors::HelpUseLatestEdition::new(),
});
} }
} }
@ -2501,7 +2501,7 @@ impl<'a> Parser<'a> {
}; };
// Recover for the grammar `*self`, `*const self`, and `*mut self`. // Recover for the grammar `*self`, `*const self`, and `*mut self`.
let recover_self_ptr = |this: &mut Self| { let recover_self_ptr = |this: &mut Self| {
self.sess.emit_err(SelfArgumentPointer { span: this.token.span }); self.sess.emit_err(errors::SelfArgumentPointer { span: this.token.span });
Ok((SelfKind::Value(Mutability::Not), expect_self_ident(this), this.prev_token.span)) Ok((SelfKind::Value(Mutability::Not), expect_self_ident(this), this.prev_token.span))
}; };

View file

@ -0,0 +1,6 @@
// ignore-tidy-linelength
fn main() {
let s: &str = r################################################################################################################################################################################################################################################################"very raw"################################################################################################################################################################################################################################################################;
//~^ ERROR too many `#` symbols: raw strings may be delimited by up to 255 `#` symbols, but found 256
}

View file

@ -0,0 +1,8 @@
error: too many `#` symbols: raw strings may be delimited by up to 255 `#` symbols, but found 256
--> $DIR/too-many-hash.rs:4:19
|
LL | ... = r################################################################################################################################################################################################################################################################"very raw"##############################################################################################################################################################################################################################################################...
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to previous error