Migrate rustc_parse to derive diagnostics

This commit is contained in:
clubby789 2023-01-29 13:37:05 +00:00
parent e7813fee92
commit 521c5f36d6
9 changed files with 656 additions and 381 deletions

View File

@ -582,3 +582,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
}

View File

@ -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]
@ -1650,6 +1658,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(

View File

@ -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 @@ impl<'a> StringReader<'a> {
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 @@ impl<'a> StringReader<'a> {
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 @@ 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(
&self,
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)))
}
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 @@ impl<'a> StringReader<'a> {
) -> 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 @@ impl<'a> StringReader<'a> {
}
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 @@ impl<'a> StringReader<'a> {
}
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 @@ impl<'a> StringReader<'a> {
// 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(

View File

@ -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));
}
}
}

View File

@ -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

View File

@ -5,6 +5,7 @@ use super::{
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 @@ impl<'a> Parser<'a> {
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.

View File

@ -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 @@ impl<'a> Parser<'a> {
// 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 @@ impl<'a> Parser<'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 @@ impl<'a> Parser<'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 @@ impl<'a> Parser<'a> {
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 @@ impl<'a> Parser<'a> {
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 @@ impl<'a> Parser<'a> {
// 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 @@ impl<'a> Parser<'a> {
// 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 @@ impl<'a> Parser<'a> {
// 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 @@ impl<'a> Parser<'a> {
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 @@ impl<'a> Parser<'a> {
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 @@ impl<'a> Parser<'a> {
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 @@ impl<'a> Parser<'a> {
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 @@ impl<'a> Parser<'a> {
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 @@ impl<'a> Parser<'a> {
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 @@ impl<'a> Parser<'a> {
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 @@ impl<'a> Parser<'a> {
// 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 @@ impl<'a> Parser<'a> {
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 @@ impl<'a> Parser<'a> {
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 @@ impl<'a> Parser<'a> {
// 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 @@ impl<'a> Parser<'a> {
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 @@ impl<'a> Parser<'a> {
};
// 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))
};

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