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:
commit
3b9543c89d
@ -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_standalone = pass `--edition {$edition}` to `rustc`
|
||||
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
|
||||
}
|
||||
|
@ -1368,6 +1368,14 @@ pub(crate) struct SelfArgumentPointer {
|
||||
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)]
|
||||
#[diag(parse_visibility_not_followed_by_item)]
|
||||
#[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)]
|
||||
pub(crate) enum TopLevelOrPatternNotAllowedSugg {
|
||||
#[suggestion(
|
||||
|
@ -1,11 +1,10 @@
|
||||
use crate::errors;
|
||||
use crate::lexer::unicode_chars::UNICODE_ARRAY;
|
||||
use rustc_ast::ast::{self, AttrStyle};
|
||||
use rustc_ast::token::{self, CommentKind, Delimiter, Token, TokenKind};
|
||||
use rustc_ast::tokenstream::TokenStream;
|
||||
use rustc_ast::util::unicode::contains_text_flow_control_chars;
|
||||
use rustc_errors::{
|
||||
error_code, Applicability, DiagnosticBuilder, ErrorGuaranteed, PResult, StashKey,
|
||||
};
|
||||
use rustc_errors::{error_code, Applicability, DiagnosticBuilder, PResult, StashKey};
|
||||
use rustc_lexer::unescape::{self, Mode};
|
||||
use rustc_lexer::Cursor;
|
||||
use rustc_lexer::{Base, DocStyle, RawStrError};
|
||||
@ -151,7 +150,7 @@ fn next_token(&mut self) -> (Token, bool) {
|
||||
let span = self.mk_sp(start, self.pos);
|
||||
self.sess.symbol_gallery.insert(sym, span);
|
||||
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);
|
||||
token::Ident(sym, true)
|
||||
@ -262,27 +261,24 @@ fn next_token(&mut self) -> (Token, bool) {
|
||||
self.nbsp_is_whitespace = true;
|
||||
}
|
||||
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
|
||||
// homoglyphs, instead of keeping a table in `check_for_substitution`into the
|
||||
// token. Ideally, this should be inside `rustc_lexer`. However, we should
|
||||
// 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
|
||||
// way.
|
||||
let token = unicode_chars::check_for_substitution(self, start, c, &mut err, repeats+1);
|
||||
if c == '\x00' {
|
||||
err.help("source files must contain UTF-8 encoded text, unexpected null bytes might occur when a different encoding is used");
|
||||
}
|
||||
if repeats > 0 {
|
||||
if repeats == 1 {
|
||||
err.note(format!("character appears once more"));
|
||||
} else {
|
||||
err.note(format!("character appears {repeats} more times"));
|
||||
}
|
||||
swallow_next_invalid = repeats;
|
||||
}
|
||||
err.emit();
|
||||
let (token, sugg) = unicode_chars::check_for_substitution(self, start, c, repeats+1);
|
||||
self.sess.emit_err(errors::UnknownTokenStart {
|
||||
span: self.mk_sp(start, self.pos + Pos::from_usize(repeats * c.len_utf8())),
|
||||
escaped: escaped_char(c),
|
||||
sugg,
|
||||
null: if c == '\x00' {Some(errors::UnknownTokenNull)} else {None},
|
||||
repeat: if repeats > 0 {
|
||||
swallow_next_invalid = repeats;
|
||||
Some(errors::UnknownTokenRepeat { repeats })
|
||||
} else {None}
|
||||
});
|
||||
|
||||
if let Some(token) = token {
|
||||
token
|
||||
} else {
|
||||
@ -297,26 +293,6 @@ fn next_token(&mut self) -> (Token, bool) {
|
||||
}
|
||||
}
|
||||
|
||||
/// 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(
|
||||
&self,
|
||||
from_pos: BytePos,
|
||||
@ -329,18 +305,6 @@ fn struct_fatal_span_char(
|
||||
.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
|
||||
/// complain about it.
|
||||
fn lint_unicode_text_flow(&self, start: BytePos) {
|
||||
@ -368,14 +332,12 @@ fn cook_doc_comment(
|
||||
) -> TokenKind {
|
||||
if content.contains('\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 + 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 @@ fn cook_lexer_literal(
|
||||
}
|
||||
rustc_lexer::LiteralKind::Int { base, empty_int } => {
|
||||
if empty_int {
|
||||
self.sess
|
||||
.span_diagnostic
|
||||
.struct_span_err_with_code(
|
||||
self.mk_sp(start, end),
|
||||
"no valid digits found for number",
|
||||
error_code!(E0768),
|
||||
)
|
||||
.emit();
|
||||
let span = self.mk_sp(start, end);
|
||||
self.sess.emit_err(errors::NoDigitsLiteral { span });
|
||||
(token::Integer, sym::integer(0))
|
||||
} else {
|
||||
if matches!(base, Base::Binary | Base::Octal) {
|
||||
let base = base as u32;
|
||||
let s = self.str_from_to(start + BytePos(2), end);
|
||||
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() {
|
||||
self.err_span_(
|
||||
start + BytePos::from_usize(2 + idx),
|
||||
start + BytePos::from_usize(2 + idx + c.len_utf8()),
|
||||
&format!("invalid digit for a base {} literal", base),
|
||||
);
|
||||
self.sess.emit_err(errors::InvalidDigitLiteral { span, base });
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -482,19 +438,18 @@ fn cook_lexer_literal(
|
||||
}
|
||||
rustc_lexer::LiteralKind::Float { base, 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 {
|
||||
Base::Hexadecimal => {
|
||||
self.err_span_(start, end, "hexadecimal float literal is not supported")
|
||||
}
|
||||
Base::Octal => {
|
||||
self.err_span_(start, end, "octal float literal is not supported")
|
||||
}
|
||||
Base::Binary => {
|
||||
self.err_span_(start, end, "binary float literal is not supported")
|
||||
}
|
||||
_ => {}
|
||||
let base = match base {
|
||||
Base::Hexadecimal => Some("hexadecimal"),
|
||||
Base::Octal => Some("octal"),
|
||||
Base::Binary => Some("binary"),
|
||||
_ => None,
|
||||
};
|
||||
if let Some(base) = base {
|
||||
let span = self.mk_sp(start, end);
|
||||
self.sess.emit_err(errors::FloatLiteralUnsupportedBase { span, base });
|
||||
}
|
||||
(token::Float, self.symbol_from_to(start, end))
|
||||
}
|
||||
@ -644,54 +599,34 @@ fn report_unterminated_block_comment(&self, start: BytePos, doc_style: Option<Do
|
||||
// identifier tokens.
|
||||
fn report_unknown_prefix(&self, start: BytePos) {
|
||||
let prefix_span = self.mk_sp(start, self.pos);
|
||||
let prefix_str = self.str_from_to(start, self.pos);
|
||||
let msg = format!("prefix `{}` is unknown", prefix_str);
|
||||
let prefix = self.str_from_to(start, self.pos);
|
||||
|
||||
let expn_data = prefix_span.ctxt().outer_expn_data();
|
||||
|
||||
if expn_data.edition >= Edition::Edition2021 {
|
||||
// In Rust 2021, this is a hard error.
|
||||
let mut err = self.sess.span_diagnostic.struct_span_err(prefix_span, &msg);
|
||||
err.span_label(prefix_span, "unknown prefix");
|
||||
if prefix_str == "rb" {
|
||||
err.span_suggestion_verbose(
|
||||
prefix_span,
|
||||
"use `br` for a raw byte string",
|
||||
"br",
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
let sugg = if prefix == "rb" {
|
||||
Some(errors::UnknownPrefixSugg::UseBr(prefix_span))
|
||||
} else if expn_data.is_root() {
|
||||
err.span_suggestion_verbose(
|
||||
prefix_span.shrink_to_hi(),
|
||||
"consider inserting whitespace here",
|
||||
" ",
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
err.note("prefixed identifiers and literals are reserved since Rust 2021");
|
||||
err.emit();
|
||||
Some(errors::UnknownPrefixSugg::Whitespace(prefix_span.shrink_to_hi()))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
self.sess.emit_err(errors::UnknownPrefix { span: prefix_span, prefix, sugg });
|
||||
} else {
|
||||
// Before Rust 2021, only emit a lint for migration.
|
||||
self.sess.buffer_lint_with_diagnostic(
|
||||
&RUST_2021_PREFIXES_INCOMPATIBLE_SYNTAX,
|
||||
prefix_span,
|
||||
ast::CRATE_NODE_ID,
|
||||
&msg,
|
||||
&format!("prefix `{prefix}` is unknown"),
|
||||
BuiltinLintDiagnostics::ReservedPrefix(prefix_span),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn report_too_many_hashes(&self, start: BytePos, found: u32) -> ! {
|
||||
self.fatal_span_(
|
||||
start,
|
||||
self.pos,
|
||||
&format!(
|
||||
"too many `#` symbols: raw strings may be delimited \
|
||||
by up to 255 `#` symbols, but found {}",
|
||||
found
|
||||
),
|
||||
)
|
||||
fn report_too_many_hashes(&self, start: BytePos, num: u32) -> ! {
|
||||
self.sess.emit_fatal(errors::TooManyHashes { span: self.mk_sp(start, self.pos), num });
|
||||
}
|
||||
|
||||
fn cook_quoted(
|
||||
|
@ -3,10 +3,12 @@
|
||||
use std::iter::once;
|
||||
use std::ops::Range;
|
||||
|
||||
use rustc_errors::{pluralize, Applicability, Handler};
|
||||
use rustc_errors::{Applicability, Handler};
|
||||
use rustc_lexer::unescape::{EscapeError, Mode};
|
||||
use rustc_span::{BytePos, Span};
|
||||
|
||||
use crate::errors::{MoreThanOneCharNote, MoreThanOneCharSugg, NoBraceUnicodeSub, UnescapeError};
|
||||
|
||||
pub(crate) fn emit_unescape_error(
|
||||
handler: &Handler,
|
||||
// interior part of the literal, without quotes
|
||||
@ -31,53 +33,32 @@ pub(crate) fn emit_unescape_error(
|
||||
};
|
||||
match error {
|
||||
EscapeError::LoneSurrogateUnicodeEscape => {
|
||||
handler
|
||||
.struct_span_err(span, "invalid unicode character escape")
|
||||
.span_label(span, "invalid escape")
|
||||
.help("unicode escape must not be a surrogate")
|
||||
.emit();
|
||||
handler.emit_err(UnescapeError::InvalidUnicodeEscape { span, surrogate: true });
|
||||
}
|
||||
EscapeError::OutOfRangeUnicodeEscape => {
|
||||
handler
|
||||
.struct_span_err(span, "invalid unicode character escape")
|
||||
.span_label(span, "invalid escape")
|
||||
.help("unicode escape must be at most 10FFFF")
|
||||
.emit();
|
||||
handler.emit_err(UnescapeError::InvalidUnicodeEscape { span, surrogate: false });
|
||||
}
|
||||
EscapeError::MoreThanOneChar => {
|
||||
use unicode_normalization::{char::is_combining_mark, UnicodeNormalization};
|
||||
let mut sugg = None;
|
||||
let mut note = None;
|
||||
|
||||
let mut has_help = false;
|
||||
let mut handler = handler.struct_span_err(
|
||||
span_with_quotes,
|
||||
"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 lit_chars = lit.chars().collect::<Vec<_>>();
|
||||
let (first, rest) = lit_chars.split_first().unwrap();
|
||||
if rest.iter().copied().all(is_combining_mark) {
|
||||
let normalized = lit.nfc().to_string();
|
||||
if normalized.chars().count() == 1 {
|
||||
has_help = true;
|
||||
handler.span_suggestion(
|
||||
span,
|
||||
&format!(
|
||||
"consider using the normalized form `{}` of this character",
|
||||
normalized.chars().next().unwrap().escape_default()
|
||||
),
|
||||
normalized,
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
let ch = normalized.chars().next().unwrap().escape_default().to_string();
|
||||
sugg = Some(MoreThanOneCharSugg::NormalizedForm { span, ch, normalized });
|
||||
}
|
||||
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 {
|
||||
let printable: Vec<char> = lit
|
||||
.chars()
|
||||
@ -87,32 +68,18 @@ pub(crate) fn emit_unescape_error(
|
||||
})
|
||||
.collect();
|
||||
|
||||
if let [ch] = printable.as_slice() {
|
||||
has_help = true;
|
||||
|
||||
handler.span_note(
|
||||
if let &[ch] = printable.as_slice() {
|
||||
sugg =
|
||||
Some(MoreThanOneCharSugg::RemoveNonPrinting { span, ch: ch.to_string() });
|
||||
note = Some(MoreThanOneCharNote::NonPrinting {
|
||||
span,
|
||||
&format!(
|
||||
"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,
|
||||
);
|
||||
escaped: lit.escape_default().to_string(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if !has_help {
|
||||
let (prefix, msg) = if mode.is_byte() {
|
||||
("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 sugg = sugg.unwrap_or_else(|| {
|
||||
let is_byte = mode.is_byte();
|
||||
let prefix = if is_byte { "b" } else { "" };
|
||||
let mut escaped = String::with_capacity(lit.len());
|
||||
let mut chrs = lit.chars().peekable();
|
||||
while let Some(first) = chrs.next() {
|
||||
@ -129,54 +96,32 @@ pub(crate) fn emit_unescape_error(
|
||||
(c, _) => escaped.push(c),
|
||||
};
|
||||
}
|
||||
handler.span_suggestion(
|
||||
span_with_quotes,
|
||||
msg,
|
||||
format!("{prefix}\"{escaped}\""),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
|
||||
handler.emit();
|
||||
let sugg = format!("{prefix}\"{escaped}\"");
|
||||
MoreThanOneCharSugg::Quotes { span: span_with_quotes, is_byte, sugg }
|
||||
});
|
||||
handler.emit_err(UnescapeError::MoreThanOneChar {
|
||||
span: span_with_quotes,
|
||||
note,
|
||||
suggestion: sugg,
|
||||
});
|
||||
}
|
||||
EscapeError::EscapeOnlyChar => {
|
||||
let (c, char_span) = last_char();
|
||||
|
||||
let msg = if mode.is_byte() {
|
||||
"byte constant must be escaped"
|
||||
} else {
|
||||
"character constant must be escaped"
|
||||
};
|
||||
handler
|
||||
.struct_span_err(span, &format!("{}: `{}`", msg, escaped_char(c)))
|
||||
.span_suggestion(
|
||||
char_span,
|
||||
"escape the character",
|
||||
c.escape_default(),
|
||||
Applicability::MachineApplicable,
|
||||
)
|
||||
.emit();
|
||||
handler.emit_err(UnescapeError::EscapeOnlyChar {
|
||||
span,
|
||||
char_span,
|
||||
escaped_sugg: c.escape_default().to_string(),
|
||||
escaped_msg: escaped_char(c),
|
||||
byte: mode.is_byte(),
|
||||
});
|
||||
}
|
||||
EscapeError::BareCarriageReturn => {
|
||||
let msg = if mode.in_double_quotes() {
|
||||
"bare CR not allowed in string, use `\\r` instead"
|
||||
} else {
|
||||
"character constant must be escaped: `\\r`"
|
||||
};
|
||||
handler
|
||||
.struct_span_err(span, msg)
|
||||
.span_suggestion(
|
||||
span,
|
||||
"escape the character",
|
||||
"\\r",
|
||||
Applicability::MachineApplicable,
|
||||
)
|
||||
.emit();
|
||||
let double_quotes = mode.in_double_quotes();
|
||||
handler.emit_err(UnescapeError::BareCr { span, double_quotes });
|
||||
}
|
||||
EscapeError::BareCarriageReturnInRawString => {
|
||||
assert!(mode.in_double_quotes());
|
||||
let msg = "bare CR not allowed in raw string";
|
||||
handler.span_err(span, msg);
|
||||
handler.emit_err(UnescapeError::BareCrRawString(span));
|
||||
}
|
||||
EscapeError::InvalidEscape => {
|
||||
let (c, span) = last_char();
|
||||
@ -213,22 +158,13 @@ pub(crate) fn emit_unescape_error(
|
||||
diag.emit();
|
||||
}
|
||||
EscapeError::TooShortHexEscape => {
|
||||
handler.span_err(span, "numeric character escape is too short");
|
||||
handler.emit_err(UnescapeError::TooShortHexEscape(span));
|
||||
}
|
||||
EscapeError::InvalidCharInHexEscape | EscapeError::InvalidCharInUnicodeEscape => {
|
||||
let (c, span) = last_char();
|
||||
|
||||
let msg = if error == EscapeError::InvalidCharInHexEscape {
|
||||
"invalid character in numeric character escape"
|
||||
} else {
|
||||
"invalid character in unicode escape"
|
||||
};
|
||||
let c = escaped_char(c);
|
||||
|
||||
handler
|
||||
.struct_span_err(span, &format!("{}: `{}`", msg, c))
|
||||
.span_label(span, msg)
|
||||
.emit();
|
||||
let is_hex = error == EscapeError::InvalidCharInHexEscape;
|
||||
let ch = escaped_char(c);
|
||||
handler.emit_err(UnescapeError::InvalidCharInEscape { span, is_hex, ch });
|
||||
}
|
||||
EscapeError::NonAsciiCharInByte => {
|
||||
let (c, span) = last_char();
|
||||
@ -278,41 +214,22 @@ pub(crate) fn emit_unescape_error(
|
||||
err.emit();
|
||||
}
|
||||
EscapeError::OutOfRangeHexEscape => {
|
||||
handler
|
||||
.struct_span_err(span, "out of range hex escape")
|
||||
.span_label(span, "must be a character in the range [\\x00-\\x7f]")
|
||||
.emit();
|
||||
handler.emit_err(UnescapeError::OutOfRangeHexEscape(span));
|
||||
}
|
||||
EscapeError::LeadingUnderscoreUnicodeEscape => {
|
||||
let (c, span) = last_char();
|
||||
let msg = "invalid start of unicode escape";
|
||||
handler
|
||||
.struct_span_err(span, &format!("{}: `{}`", msg, c))
|
||||
.span_label(span, msg)
|
||||
.emit();
|
||||
handler.emit_err(UnescapeError::LeadingUnderscoreUnicodeEscape {
|
||||
span,
|
||||
ch: escaped_char(c),
|
||||
});
|
||||
}
|
||||
EscapeError::OverlongUnicodeEscape => {
|
||||
handler
|
||||
.struct_span_err(span, "overlong unicode escape")
|
||||
.span_label(span, "must have at most 6 hex digits")
|
||||
.emit();
|
||||
handler.emit_err(UnescapeError::OverlongUnicodeEscape(span));
|
||||
}
|
||||
EscapeError::UnclosedUnicodeEscape => {
|
||||
handler
|
||||
.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();
|
||||
handler.emit_err(UnescapeError::UnclosedUnicodeEscape(span, span.shrink_to_hi()));
|
||||
}
|
||||
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_len = 0;
|
||||
let (c, char_span) = last_char();
|
||||
@ -322,54 +239,37 @@ pub(crate) fn emit_unescape_error(
|
||||
suggestion_len += c.len_utf8();
|
||||
}
|
||||
|
||||
if suggestion_len > 0 {
|
||||
let (label, sub) = if suggestion_len > 0 {
|
||||
suggestion.push('}');
|
||||
let hi = char_span.lo() + BytePos(suggestion_len as u32);
|
||||
diag.span_suggestion(
|
||||
span.with_hi(hi),
|
||||
"format of unicode escape sequences uses braces",
|
||||
suggestion,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
(None, NoBraceUnicodeSub::Suggestion { span: span.with_hi(hi), suggestion })
|
||||
} else {
|
||||
diag.span_label(span, msg);
|
||||
diag.help("format of unicode escape sequences is `\\u{...}`");
|
||||
}
|
||||
|
||||
diag.emit();
|
||||
(Some(span), NoBraceUnicodeSub::Help)
|
||||
};
|
||||
handler.emit_err(UnescapeError::NoBraceInUnicodeEscape { span, label, sub });
|
||||
}
|
||||
EscapeError::UnicodeEscapeInByte => {
|
||||
let msg = "unicode escape in byte string";
|
||||
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();
|
||||
handler.emit_err(UnescapeError::UnicodeEscapeInByte(span));
|
||||
}
|
||||
EscapeError::EmptyUnicodeEscape => {
|
||||
handler
|
||||
.struct_span_err(span, "empty unicode escape")
|
||||
.span_label(span, "this escape must have at least 1 hex digit")
|
||||
.emit();
|
||||
handler.emit_err(UnescapeError::EmptyUnicodeEscape(span));
|
||||
}
|
||||
EscapeError::ZeroChars => {
|
||||
let msg = "empty character literal";
|
||||
handler.struct_span_err(span, msg).span_label(span, msg).emit();
|
||||
handler.emit_err(UnescapeError::ZeroChars(span));
|
||||
}
|
||||
EscapeError::LoneSlash => {
|
||||
let msg = "invalid trailing slash in literal";
|
||||
handler.struct_span_err(span, msg).span_label(span, msg).emit();
|
||||
handler.emit_err(UnescapeError::LoneSlash(span));
|
||||
}
|
||||
EscapeError::UnskippedWhitespaceWarning => {
|
||||
let (c, char_span) = last_char();
|
||||
let msg =
|
||||
format!("non-ASCII whitespace symbol '{}' is not skipped", c.escape_unicode());
|
||||
handler.struct_span_warn(span, &msg).span_label(char_span, &msg).emit();
|
||||
handler.emit_warning(UnescapeError::UnskippedWhitespace {
|
||||
span,
|
||||
ch: escaped_char(c),
|
||||
char_span,
|
||||
});
|
||||
}
|
||||
EscapeError::MultipleSkippedLinesWarning => {
|
||||
let msg = "multiple lines skipped by escaped newline";
|
||||
let bottom_msg = "skipping everything up to and including this point";
|
||||
handler.struct_span_warn(span, msg).span_label(span, bottom_msg).emit();
|
||||
handler.emit_warning(UnescapeError::MultipleSkippedLinesWarning(span));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,8 +2,10 @@
|
||||
//! <https://www.unicode.org/Public/security/10.0.0/confusables.txt>
|
||||
|
||||
use super::StringReader;
|
||||
use crate::token::{self, Delimiter};
|
||||
use rustc_errors::{Applicability, Diagnostic};
|
||||
use crate::{
|
||||
errors::TokenSubstitution,
|
||||
token::{self, Delimiter},
|
||||
};
|
||||
use rustc_span::{symbol::kw, BytePos, Pos, Span};
|
||||
|
||||
#[rustfmt::skip] // for line breaks
|
||||
@ -338,48 +340,44 @@ pub(super) fn check_for_substitution<'a>(
|
||||
reader: &StringReader<'a>,
|
||||
pos: BytePos,
|
||||
ch: char,
|
||||
err: &mut Diagnostic,
|
||||
count: usize,
|
||||
) -> Option<token::TokenKind> {
|
||||
let &(_, u_name, ascii_str) = UNICODE_ARRAY.iter().find(|&&(c, _, _)| c == ch)?;
|
||||
) -> (Option<token::TokenKind>, Option<TokenSubstitution>) {
|
||||
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 Some((_, ascii_name, token)) = ASCII_ARRAY.iter().find(|&&(s, _, _)| s == ascii_str) else {
|
||||
let msg = format!("substitution character not found for '{}'", ch);
|
||||
reader.sess.span_diagnostic.span_bug_no_panic(span, &msg);
|
||||
return None;
|
||||
return (None, None);
|
||||
};
|
||||
|
||||
// special help suggestion for "directed" double quotes
|
||||
if let Some(s) = peek_delimited(&reader.src[reader.src_index(pos)..], '“', '”') {
|
||||
let msg = format!(
|
||||
"Unicode characters '“' (Left Double Quotation Mark) and \
|
||||
'”' (Right Double Quotation Mark) look like '{}' ({}), but are not",
|
||||
ascii_str, ascii_name
|
||||
let sugg = if let Some(s) = peek_delimited(&reader.src[reader.src_index(pos)..], '“', '”') {
|
||||
let span = Span::with_root_ctxt(
|
||||
pos,
|
||||
pos + Pos::from_usize('“'.len_utf8() + s.len() + '”'.len_utf8()),
|
||||
);
|
||||
err.span_suggestion(
|
||||
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(
|
||||
Some(TokenSubstitution::DirectedQuotes {
|
||||
span,
|
||||
&msg,
|
||||
ascii_str.to_string().repeat(count),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
token.clone()
|
||||
suggestion: format!("\"{s}\""),
|
||||
ascii_str,
|
||||
ascii_name,
|
||||
})
|
||||
} else {
|
||||
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
|
||||
|
@ -5,6 +5,7 @@
|
||||
AttrWrapper, BlockMode, ClosureSpans, ForceCollect, Parser, PathStyle, Restrictions,
|
||||
SemiColonMode, SeqSep, TokenExpectType, TokenType, TrailingToken,
|
||||
};
|
||||
|
||||
use crate::errors;
|
||||
use crate::maybe_recover_from_interpolated_ty_qpath;
|
||||
use core::mem;
|
||||
@ -1017,7 +1018,7 @@ fn parse_dot_suffix_expr(&mut self, lo: Span, base: P<Expr>) -> PResult<'a, P<Ex
|
||||
fn error_unexpected_after_dot(&self) {
|
||||
// FIXME Could factor this out into non_fatal_unexpected or something.
|
||||
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.
|
||||
|
@ -1,18 +1,8 @@
|
||||
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 crate::errors;
|
||||
|
||||
use super::diagnostics::{dummy_arg, ConsumeClosingDelim};
|
||||
use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign};
|
||||
use super::{AttrWrapper, FollowedByType, ForceCollect, Parser, PathStyle, TrailingToken};
|
||||
use crate::errors::FnTypoWithImpl;
|
||||
use rustc_ast::ast::*;
|
||||
use rustc_ast::ptr::P;
|
||||
use rustc_ast::token::{self, Delimiter, TokenKind};
|
||||
@ -177,11 +167,11 @@ fn parse_item_common_(
|
||||
|
||||
// At this point, we have failed to parse an item.
|
||||
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 {
|
||||
self.sess.emit_err(DefaultNotFollowedByItem { span });
|
||||
self.sess.emit_err(errors::DefaultNotFollowedByItem { span });
|
||||
}
|
||||
|
||||
if !attrs_allowed {
|
||||
@ -403,7 +393,7 @@ fn recover_missing_kw_before_item(&mut self) -> PResult<'a, ()> {
|
||||
|
||||
let err = if self.check(&token::OpenDelim(Delimiter::Brace)) {
|
||||
// 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)) {
|
||||
// possible public function or tuple struct definition where `fn`/`struct` was
|
||||
// forgotten
|
||||
@ -412,34 +402,36 @@ fn recover_missing_kw_before_item(&mut self) -> PResult<'a, ()> {
|
||||
|
||||
self.consume_block(Delimiter::Parenthesis, ConsumeClosingDelim::Yes);
|
||||
|
||||
let err = if self.check(&token::RArrow)
|
||||
|| self.check(&token::OpenDelim(Delimiter::Brace))
|
||||
{
|
||||
self.eat_to_tokens(&[&token::OpenDelim(Delimiter::Brace)]);
|
||||
self.bump(); // `{`
|
||||
self.consume_block(Delimiter::Brace, ConsumeClosingDelim::Yes);
|
||||
if is_method {
|
||||
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 })
|
||||
let err =
|
||||
if self.check(&token::RArrow) || self.check(&token::OpenDelim(Delimiter::Brace)) {
|
||||
self.eat_to_tokens(&[&token::OpenDelim(Delimiter::Brace)]);
|
||||
self.bump(); // `{`
|
||||
self.consume_block(Delimiter::Brace, ConsumeClosingDelim::Yes);
|
||||
if is_method {
|
||||
errors::MissingKeywordForItemDefinition::Method { span: sp, ident }
|
||||
} 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)
|
||||
} else if found_generics {
|
||||
Some(MissingKeywordForItemDefinition::Ambiguous { span: sp, subdiag: None })
|
||||
Some(errors::MissingKeywordForItemDefinition::Ambiguous { span: sp, subdiag: None })
|
||||
} else {
|
||||
None
|
||||
};
|
||||
@ -567,8 +559,10 @@ fn parse_item_impl(
|
||||
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);
|
||||
self.sess
|
||||
.emit_err(MissingTraitInTraitImpl { span, for_span: span.to(self.token.span) });
|
||||
self.sess.emit_err(errors::MissingTraitInTraitImpl {
|
||||
span,
|
||||
for_span: span.to(self.token.span),
|
||||
});
|
||||
|
||||
P(Ty {
|
||||
kind: TyKind::Path(None, err_path(span)),
|
||||
@ -602,7 +596,7 @@ fn parse_item_impl(
|
||||
Some(ty_second) => {
|
||||
// impl Trait for Type
|
||||
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();
|
||||
@ -610,8 +604,9 @@ fn parse_item_impl(
|
||||
// This notably includes paths passed through `ty` macro fragments (#46438).
|
||||
TyKind::Path(None, path) => path,
|
||||
_ => {
|
||||
self.sess
|
||||
.emit_err(ExpectedTraitInTraitImplFoundType { span: ty_first.span });
|
||||
self.sess.emit_err(errors::ExpectedTraitInTraitImplFoundType {
|
||||
span: ty_first.span,
|
||||
});
|
||||
err_path(ty_first.span)
|
||||
}
|
||||
};
|
||||
@ -655,7 +650,7 @@ fn parse_item_list<T>(
|
||||
|
||||
// Recover `impl Ty;` instead of `impl Ty {}`
|
||||
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();
|
||||
return Ok(vec![]);
|
||||
}
|
||||
@ -812,7 +807,7 @@ fn parse_item_trait(&mut self, attrs: &mut AttrVec, lo: Span) -> PResult<'a, Ite
|
||||
// It's a trait alias.
|
||||
if had_colon {
|
||||
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)?;
|
||||
@ -821,10 +816,10 @@ fn parse_item_trait(&mut self, attrs: &mut AttrVec, lo: Span) -> PResult<'a, Ite
|
||||
|
||||
let whole_span = lo.to(self.prev_token.span);
|
||||
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 {
|
||||
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);
|
||||
@ -870,7 +865,7 @@ fn parse_assoc_item(
|
||||
Ok(kind) => kind,
|
||||
Err(kind) => match kind {
|
||||
ItemKind::Static(a, _, b) => {
|
||||
self.sess.emit_err(AssociatedStaticItemNotAllowed { span });
|
||||
self.sess.emit_err(errors::AssociatedStaticItemNotAllowed { span });
|
||||
AssocItemKind::Const(Defaultness::Final, a, b)
|
||||
}
|
||||
_ => return self.error_bad_item_kind(span, &kind, "`trait`s or `impl`s"),
|
||||
@ -1069,9 +1064,9 @@ fn parse_crate_name_with_dashes(&mut self) -> PResult<'a, Ident> {
|
||||
write!(fixed_name, "_{}", part.name).unwrap();
|
||||
}
|
||||
|
||||
self.sess.emit_err(ExternCrateNameWithDashes {
|
||||
self.sess.emit_err(errors::ExternCrateNameWithDashes {
|
||||
span: fixed_name_sp,
|
||||
sugg: ExternCrateNameWithDashesSugg { dashes },
|
||||
sugg: errors::ExternCrateNameWithDashesSugg { dashes },
|
||||
});
|
||||
|
||||
Ok(Ident::from_str_and_span(&fixed_name, fixed_name_sp))
|
||||
@ -1122,7 +1117,7 @@ pub fn parse_foreign_item(
|
||||
Ok(kind) => kind,
|
||||
Err(kind) => match kind {
|
||||
ItemKind::Const(_, a, b) => {
|
||||
self.sess.emit_err(ExternItemCannotBeConst {
|
||||
self.sess.emit_err(errors::ExternItemCannotBeConst {
|
||||
ident_span: ident.span,
|
||||
const_span: span.with_hi(ident.span.lo()),
|
||||
});
|
||||
@ -1173,10 +1168,10 @@ fn is_static_global(&mut self) -> bool {
|
||||
fn recover_const_mut(&mut self, const_span: Span) {
|
||||
if self.eat_keyword(kw::Mut) {
|
||||
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) {
|
||||
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 @@ fn recover_missing_const_type(&mut self, colon_present: bool, m: Option<Mutabili
|
||||
|
||||
let span = self.prev_token.span.shrink_to_hi();
|
||||
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);
|
||||
|
||||
// The user intended that the type be inferred,
|
||||
@ -1274,7 +1270,7 @@ fn recover_missing_const_type(&mut self, colon_present: bool, m: Option<Mutabili
|
||||
fn parse_item_enum(&mut self) -> PResult<'a, ItemInfo> {
|
||||
if self.token.is_keyword(kw::Struct) {
|
||||
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()) {
|
||||
self.bump();
|
||||
self.sess.emit_err(err);
|
||||
@ -1289,7 +1285,7 @@ fn parse_item_enum(&mut self) -> PResult<'a, ItemInfo> {
|
||||
|
||||
// Possibly recover `enum Foo;` instead of `enum Foo {}`
|
||||
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();
|
||||
(vec![], false)
|
||||
} else {
|
||||
@ -1415,7 +1411,8 @@ fn parse_item_struct(&mut self) -> PResult<'a, ItemInfo> {
|
||||
self.expect_semi()?;
|
||||
body
|
||||
} 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));
|
||||
};
|
||||
|
||||
@ -1593,7 +1590,7 @@ fn parse_single_struct_field(
|
||||
token::CloseDelim(Delimiter::Brace) => {}
|
||||
token::DocComment(..) => {
|
||||
let previous_span = self.prev_token.span;
|
||||
let mut err = DocCommentDoesNotDocumentAnything {
|
||||
let mut err = errors::DocCommentDoesNotDocumentAnything {
|
||||
span: self.token.span,
|
||||
missing_comma: None,
|
||||
};
|
||||
@ -2103,7 +2100,7 @@ fn parse_fn(
|
||||
// If we see `for Ty ...` then user probably meant `impl` item.
|
||||
if self.token.is_keyword(kw::For) {
|
||||
old_err.cancel();
|
||||
return Err(self.sess.create_err(FnTypoWithImpl { fn_span }));
|
||||
return Err(self.sess.create_err(errors::FnTypoWithImpl { fn_span }));
|
||||
} else {
|
||||
return Err(old_err);
|
||||
}
|
||||
@ -2248,7 +2245,10 @@ pub(super) fn parse_fn_front_matter(
|
||||
|
||||
if let Async::Yes { span, .. } = asyncness {
|
||||
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 @@ fn parse_self_param(&mut self) -> PResult<'a, Option<Param>> {
|
||||
};
|
||||
// Recover for the grammar `*self`, `*const self`, and `*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))
|
||||
};
|
||||
|
6
tests/ui/parser/raw/too-many-hash.rs
Normal file
6
tests/ui/parser/raw/too-many-hash.rs
Normal 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
|
||||
}
|
8
tests/ui/parser/raw/too-many-hash.stderr
Normal file
8
tests/ui/parser/raw/too-many-hash.stderr
Normal 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
|
||||
|
Loading…
Reference in New Issue
Block a user