Migrate diagnostics in parser/expr to SessionDiagnostic

This commit is contained in:
Xiretza 2022-08-17 19:05:49 +02:00
parent 4b695f7c4e
commit ffcaa0dee2
3 changed files with 596 additions and 282 deletions

View File

@ -41,3 +41,112 @@ parser_switch_mut_let_order =
parser_missing_let_before_mut = missing keyword
parser_use_let_not_auto = write `let` instead of `auto` to introduce a new variable
parser_use_let_not_var = write `let` instead of `var` to introduce a new variable
parser_invalid_comparison_operator = invalid comparison operator `{$invalid}`
.use_instead = `{$invalid}` is not a valid comparison operator, use `{$correct}`
.spaceship_operator_invalid = `<=>` is not a valid comparison operator, use `std::cmp::Ordering`
parser_invalid_logical_operator = `{$incorrect}` is not a logical operator
.note = unlike in e.g., python and PHP, `&&` and `||` are used for logical operators
.use_amp_amp_for_conjunction = use `&&` to perform logical conjunction
.use_pipe_pipe_for_disjunction = use `||` to perform logical disjunction
parser_tilde_is_not_unary_operator = `~` cannot be used as a unary operator
.suggestion = use `!` to perform bitwise not
parser_unexpected_token_after_not = unexpected {$negated_desc} after identifier
.suggestion = use `!` to perform logical negation
parser_malformed_loop_label = malformed loop label
.suggestion = use the correct loop label format
parser_lifetime_in_borrow_expression = borrow expressions cannot be annotated with lifetimes
.suggestion = remove the lifetime annotation
.label = annotated with lifetime here
parser_field_expression_with_generic = field expressions cannot have generic arguments
parser_macro_invocation_with_qualified_path = macros cannot use qualified paths
parser_unexpected_token_after_label = expected `while`, `for`, `loop` or `{"{"}` after a label
parser_require_colon_after_labeled_expression = labeled expression must be followed by `:`
.note = labels are used before loops and blocks, allowing e.g., `break 'label` to them
.label = the label
.suggestion = add `:` after the label
parser_do_catch_syntax_removed = found removed `do catch` syntax
.note = following RFC #2388, the new non-placeholder syntax is `try`
.suggestion = replace with the new syntax
parser_float_literal_requires_integer_part = float literals must have an integer part
.suggestion = must have an integer part
parser_invalid_int_literal_width = invalid width `{$width}` for integer literal
.help = valid widths are 8, 16, 32, 64 and 128
parser_invalid_num_literal_base_prefix = invalid base prefix for number literal
.note = base prefixes (`0xff`, `0b1010`, `0o755`) are lowercase
.suggestion = try making the prefix lowercase
parser_invalid_num_literal_suffix = invalid suffix `{$suffix}` for number literal
.label = invalid suffix `{$suffix}`
.help = the suffix must be one of the numeric types (`u32`, `isize`, `f32`, etc.)
parser_invalid_float_literal_width = invalid width `{$width}` for float literal
.help = valid widths are 32 and 64
parser_invalid_float_literal_suffix = invalid suffix `{$suffix}` for float literal
.label = invalid suffix `{$suffix}`
.help = valid suffixes are `f32` and `f64`
parser_int_literal_too_large = integer literal is too large
parser_missing_semicolon_before_array = expected `;`, found `[`
.suggestion = consider adding `;` here
parser_invalid_block_macro_segment = cannot use a `block` macro fragment here
.label = the `block` fragment is within this context
parser_if_expression_missing_then_block = this `if` expression is missing a block after the condition
.add_then_block = add a block here
.condition_possibly_unfinished = this binary operation is possibly unfinished
parser_if_expression_missing_condition = missing condition for `if` expression
.condition_label = expected condition here
.block_label = if this block is the condition of the `if` expression, then it must be followed by another block
parser_expected_expression_found_let = expected expression, found `let` statement
parser_expected_else_block = expected `{"{"}`, found {$first_tok}
.label = expected an `if` or a block after this `else`
.suggestion = add an `if` if this is the condition of a chained `else if` statement
parser_outer_attribute_not_allowed_on_if_else = outer attributes are not allowed on `if` and `else` branches
.branch_label = the attributes are attached to this branch
.ctx_label = the branch belongs to this `{$ctx}`
.suggestion = remove the attributes
parser_missing_in_in_for_loop = missing `in` in `for` loop
.use_in_not_of = try using `in` here instead
.add_in = try adding `in` here
parser_missing_comma_after_match_arm = expected `,` following `match` arm
.suggestion = missing a comma here to end this `match` arm
parser_catch_after_try = keyword `catch` cannot follow a `try` block
.help = try using `match` on the result of the `try` block instead
parser_comma_after_base_struct = cannot use a comma after the base struct
.note = the base struct must always be the last field
.suggestion = remove this comma
parser_eq_field_init = expected `:`, found `=`
.suggestion = replace equals symbol with a colon
parser_dotdotdot = unexpected token: `...`
.suggest_exclusive_range = use `..` for an exclusive range
.suggest_inclusive_range = or `..=` for an inclusive range
parser_left_arrow_operator = unexpected token: `<-`
.suggestion = if you meant to write a comparison against a negative value, add a space in between `<` and `-`

View File

@ -363,6 +363,349 @@ pub enum InvalidVariableDeclarationSub {
UseLetNotVar(#[primary_span] Span),
}
#[derive(SessionDiagnostic)]
#[diag(parser::invalid_comparison_operator)]
pub(crate) struct InvalidComparisonOperator {
#[primary_span]
pub span: Span,
pub invalid: String,
#[subdiagnostic]
pub sub: InvalidComparisonOperatorSub,
}
#[derive(SessionSubdiagnostic)]
pub(crate) enum InvalidComparisonOperatorSub {
#[suggestion_short(
parser::use_instead,
applicability = "machine-applicable",
code = "{correct}"
)]
Correctable {
#[primary_span]
span: Span,
invalid: String,
correct: String,
},
#[label(parser::spaceship_operator_invalid)]
Spaceship(#[primary_span] Span),
}
#[derive(SessionDiagnostic)]
#[diag(parser::invalid_logical_operator)]
#[note]
pub(crate) struct InvalidLogicalOperator {
#[primary_span]
pub span: Span,
pub incorrect: String,
#[subdiagnostic]
pub sub: InvalidLogicalOperatorSub,
}
#[derive(SessionSubdiagnostic)]
pub(crate) enum InvalidLogicalOperatorSub {
#[suggestion_short(
parser::use_amp_amp_for_conjunction,
applicability = "machine-applicable",
code = "&&"
)]
Conjunction(#[primary_span] Span),
#[suggestion_short(
parser::use_pipe_pipe_for_disjunction,
applicability = "machine-applicable",
code = "||"
)]
Disjunction(#[primary_span] Span),
}
#[derive(SessionDiagnostic)]
#[diag(parser::tilde_is_not_unary_operator)]
pub(crate) struct TildeAsUnaryOperator(
#[primary_span]
#[suggestion_short(applicability = "machine-applicable", code = "!")]
pub Span,
);
#[derive(SessionDiagnostic)]
#[diag(parser::unexpected_token_after_not)]
pub(crate) struct NotAsNegationOperator {
#[primary_span]
pub negated: Span,
pub negated_desc: String,
#[suggestion_short(applicability = "machine-applicable", code = "!")]
pub not: Span,
}
#[derive(SessionDiagnostic)]
#[diag(parser::malformed_loop_label)]
pub(crate) struct MalformedLoopLabel {
#[primary_span]
#[suggestion(applicability = "machine-applicable", code = "{correct_label}")]
pub span: Span,
pub correct_label: Ident,
}
#[derive(SessionDiagnostic)]
#[diag(parser::lifetime_in_borrow_expression)]
pub(crate) struct LifetimeInBorrowExpression {
#[primary_span]
pub span: Span,
#[suggestion(applicability = "machine-applicable", code = "")]
#[label]
pub lifetime_span: Span,
}
#[derive(SessionDiagnostic)]
#[diag(parser::field_expression_with_generic)]
pub(crate) struct FieldExpressionWithGeneric(#[primary_span] pub Span);
#[derive(SessionDiagnostic)]
#[diag(parser::macro_invocation_with_qualified_path)]
pub(crate) struct MacroInvocationWithQualifiedPath(#[primary_span] pub Span);
#[derive(SessionDiagnostic)]
#[diag(parser::unexpected_token_after_label)]
pub(crate) struct UnexpectedTokenAfterLabel(
#[primary_span]
#[label(parser::unexpected_token_after_label)]
pub Span,
);
#[derive(SessionDiagnostic)]
#[diag(parser::require_colon_after_labeled_expression)]
#[note]
pub(crate) struct RequireColonAfterLabeledExpression {
#[primary_span]
pub span: Span,
#[label]
pub label: Span,
#[suggestion_short(applicability = "machine-applicable", code = ": ")]
pub label_end: Span,
}
#[derive(SessionDiagnostic)]
#[diag(parser::do_catch_syntax_removed)]
#[note]
pub(crate) struct DoCatchSyntaxRemoved {
#[primary_span]
#[suggestion(applicability = "machine-applicable", code = "try")]
pub span: Span,
}
#[derive(SessionDiagnostic)]
#[diag(parser::float_literal_requires_integer_part)]
pub(crate) struct FloatLiteralRequiresIntegerPart {
#[primary_span]
#[suggestion(applicability = "machine-applicable", code = "{correct}")]
pub span: Span,
pub correct: String,
}
#[derive(SessionDiagnostic)]
#[diag(parser::invalid_int_literal_width)]
#[help]
pub(crate) struct InvalidIntLiteralWidth {
#[primary_span]
pub span: Span,
pub width: String,
}
#[derive(SessionDiagnostic)]
#[diag(parser::invalid_num_literal_base_prefix)]
#[note]
pub(crate) struct InvalidNumLiteralBasePrefix {
#[primary_span]
#[suggestion(applicability = "maybe-incorrect", code = "{fixed}")]
pub span: Span,
pub fixed: String,
}
#[derive(SessionDiagnostic)]
#[diag(parser::invalid_num_literal_suffix)]
#[help]
pub(crate) struct InvalidNumLiteralSuffix {
#[primary_span]
#[label]
pub span: Span,
pub suffix: String,
}
#[derive(SessionDiagnostic)]
#[diag(parser::invalid_float_literal_width)]
#[help]
pub(crate) struct InvalidFloatLiteralWidth {
#[primary_span]
pub span: Span,
pub width: String,
}
#[derive(SessionDiagnostic)]
#[diag(parser::invalid_float_literal_suffix)]
#[help]
pub(crate) struct InvalidFloatLiteralSuffix {
#[primary_span]
#[label]
pub span: Span,
pub suffix: String,
}
#[derive(SessionDiagnostic)]
#[diag(parser::int_literal_too_large)]
pub(crate) struct IntLiteralTooLarge {
#[primary_span]
pub span: Span,
}
#[derive(SessionDiagnostic)]
#[diag(parser::missing_semicolon_before_array)]
pub(crate) struct MissingSemicolonBeforeArray {
#[primary_span]
pub open_delim: Span,
#[suggestion_verbose(applicability = "maybe-incorrect", code = ";")]
pub semicolon: Span,
}
#[derive(SessionDiagnostic)]
#[diag(parser::invalid_block_macro_segment)]
pub(crate) struct InvalidBlockMacroSegment {
#[primary_span]
pub span: Span,
#[label]
pub context: Span,
}
#[derive(SessionDiagnostic)]
#[diag(parser::if_expression_missing_then_block)]
pub(crate) struct IfExpressionMissingThenBlock {
#[primary_span]
pub if_span: Span,
#[subdiagnostic]
pub sub: IfExpressionMissingThenBlockSub,
}
#[derive(SessionSubdiagnostic)]
pub(crate) enum IfExpressionMissingThenBlockSub {
#[help(parser::condition_possibly_unfinished)]
UnfinishedCondition(#[primary_span] Span),
#[help(parser::add_then_block)]
AddThenBlock(#[primary_span] Span),
}
#[derive(SessionDiagnostic)]
#[diag(parser::if_expression_missing_condition)]
pub(crate) struct IfExpressionMissingCondition {
#[primary_span]
#[label(parser::condition_label)]
pub if_span: Span,
#[label(parser::block_label)]
pub block_span: Span,
}
#[derive(SessionDiagnostic)]
#[diag(parser::expected_expression_found_let)]
pub(crate) struct ExpectedExpressionFoundLet {
#[primary_span]
pub span: Span,
}
#[derive(SessionDiagnostic)]
#[diag(parser::expected_else_block)]
pub(crate) struct ExpectedElseBlock {
#[primary_span]
pub first_tok_span: Span,
pub first_tok: String,
#[label]
pub else_span: Span,
#[suggestion(applicability = "maybe-incorrect", code = "if ")]
pub condition_start: Span,
}
#[derive(SessionDiagnostic)]
#[diag(parser::outer_attribute_not_allowed_on_if_else)]
pub(crate) struct OuterAttributeNotAllowedOnIfElse {
#[primary_span]
pub last: Span,
#[label(parser::branch_label)]
pub branch_span: Span,
#[label(parser::ctx_label)]
pub ctx_span: Span,
pub ctx: String,
#[suggestion(applicability = "machine-applicable", code = "")]
pub attributes: Span,
}
#[derive(SessionDiagnostic)]
#[diag(parser::missing_in_in_for_loop)]
pub(crate) struct MissingInInForLoop {
#[primary_span]
pub span: Span,
#[subdiagnostic]
pub sub: MissingInInForLoopSub,
}
#[derive(SessionSubdiagnostic)]
pub(crate) enum MissingInInForLoopSub {
// Has been misleading, at least in the past (closed Issue #48492), thus maybe-incorrect
#[suggestion_short(parser::use_in_not_of, applicability = "maybe-incorrect", code = "in")]
InNotOf(#[primary_span] Span),
#[suggestion_short(parser::add_in, applicability = "maybe-incorrect", code = " in ")]
AddIn(#[primary_span] Span),
}
#[derive(SessionDiagnostic)]
#[diag(parser::missing_comma_after_match_arm)]
pub(crate) struct MissingCommaAfterMatchArm {
#[primary_span]
#[suggestion(applicability = "machine-applicable", code = ",")]
pub span: Span,
}
#[derive(SessionDiagnostic)]
#[diag(parser::catch_after_try)]
#[help]
pub(crate) struct CatchAfterTry {
#[primary_span]
pub span: Span,
}
#[derive(SessionDiagnostic)]
#[diag(parser::comma_after_base_struct)]
#[note]
pub(crate) struct CommaAfterBaseStruct {
#[primary_span]
pub span: Span,
#[suggestion_short(applicability = "machine-applicable", code = "")]
pub comma: Span,
}
#[derive(SessionDiagnostic)]
#[diag(parser::eq_field_init)]
pub(crate) struct EqFieldInit {
#[primary_span]
pub span: Span,
#[suggestion(applicability = "machine-applicable", code = ":")]
pub eq: Span,
}
#[derive(SessionDiagnostic)]
#[diag(parser::dotdotdot)]
pub(crate) struct DotDotDot {
#[primary_span]
#[suggestion(parser::suggest_exclusive_range, applicability = "maybe-incorrect", code = "..")]
#[suggestion(parser::suggest_inclusive_range, applicability = "maybe-incorrect", code = "..=")]
pub span: Span,
}
#[derive(SessionDiagnostic)]
#[diag(parser::left_arrow_operator)]
pub(crate) struct LeftArrowOperator {
#[primary_span]
#[suggestion(applicability = "maybe-incorrect", code = "< -")]
pub span: Span,
}
// SnapshotParser is used to create a snapshot of the parser
// without causing duplicate errors being emitted when the `Parser`
// is dropped.

View File

@ -1,4 +1,14 @@
use super::diagnostics::SnapshotParser;
use super::diagnostics::{
CatchAfterTry, CommaAfterBaseStruct, DoCatchSyntaxRemoved, DotDotDot, EqFieldInit,
ExpectedElseBlock, ExpectedExpressionFoundLet, FieldExpressionWithGeneric,
FloatLiteralRequiresIntegerPart, IfExpressionMissingCondition, IfExpressionMissingThenBlock,
IfExpressionMissingThenBlockSub, InvalidBlockMacroSegment, InvalidComparisonOperator,
InvalidComparisonOperatorSub, InvalidLogicalOperator, InvalidLogicalOperatorSub,
LeftArrowOperator, LifetimeInBorrowExpression, MacroInvocationWithQualifiedPath,
MalformedLoopLabel, MissingInInForLoop, MissingInInForLoopSub, MissingSemicolonBeforeArray,
NotAsNegationOperator, OuterAttributeNotAllowedOnIfElse, RequireColonAfterLabeledExpression,
SnapshotParser, TildeAsUnaryOperator, UnexpectedTokenAfterLabel,
};
use super::pat::{CommaRecoveryMode, RecoverColon, RecoverComma, PARAM_EXPECTED};
use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign};
use super::{
@ -6,6 +16,11 @@
SemiColonMode, SeqSep, TokenExpectType, TokenType, TrailingToken,
};
use crate::maybe_recover_from_interpolated_ty_qpath;
use crate::parser::diagnostics::{
IntLiteralTooLarge, InvalidFloatLiteralSuffix, InvalidFloatLiteralWidth,
InvalidIntLiteralWidth, InvalidNumLiteralBasePrefix, InvalidNumLiteralSuffix,
MissingCommaAfterMatchArm,
};
use core::mem;
use rustc_ast::ptr::P;
@ -20,9 +35,10 @@
use rustc_ast::{Arm, Async, BlockCheckMode, Expr, ExprKind, Label, Movability, RangeLimits};
use rustc_ast::{ClosureBinder, StmtKind};
use rustc_ast_pretty::pprust;
use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, PResult};
use rustc_errors::{Applicability, Diagnostic, PResult};
use rustc_session::lint::builtin::BREAK_WITH_LABEL_AND_LOOP;
use rustc_session::lint::BuiltinLintDiagnostics;
use rustc_session::SessionDiagnostic;
use rustc_span::source_map::{self, Span, Spanned};
use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::{BytePos, Pos};
@ -216,15 +232,18 @@ pub(super) fn parse_assoc_expr_with(
AssocOp::Equal => "==",
AssocOp::NotEqual => "!=",
_ => unreachable!(),
};
self.struct_span_err(sp, &format!("invalid comparison operator `{sugg}=`"))
.span_suggestion_short(
sp,
&format!("`{s}=` is not a valid comparison operator, use `{s}`", s = sugg),
sugg,
Applicability::MachineApplicable,
)
.emit();
}
.into();
let invalid = format!("{}=", &sugg);
self.sess.emit_err(InvalidComparisonOperator {
span: sp,
invalid: invalid.clone(),
sub: InvalidComparisonOperatorSub::Correctable {
span: sp,
invalid,
correct: sugg,
},
});
self.bump();
}
@ -234,14 +253,15 @@ pub(super) fn parse_assoc_expr_with(
&& self.prev_token.span.hi() == self.token.span.lo()
{
let sp = op.span.to(self.token.span);
self.struct_span_err(sp, "invalid comparison operator `<>`")
.span_suggestion_short(
sp,
"`<>` is not a valid comparison operator, use `!=`",
"!=",
Applicability::MachineApplicable,
)
.emit();
self.sess.emit_err(InvalidComparisonOperator {
span: sp,
invalid: "<>".into(),
sub: InvalidComparisonOperatorSub::Correctable {
span: sp,
invalid: "<>".into(),
correct: "!=".into(),
},
});
self.bump();
}
@ -251,12 +271,11 @@ pub(super) fn parse_assoc_expr_with(
&& self.prev_token.span.hi() == self.token.span.lo()
{
let sp = op.span.to(self.token.span);
self.struct_span_err(sp, "invalid comparison operator `<=>`")
.span_label(
sp,
"`<=>` is not a valid comparison operator, use `std::cmp::Ordering`",
)
.emit();
self.sess.emit_err(InvalidComparisonOperator {
span: sp,
invalid: "<=>".into(),
sub: InvalidComparisonOperatorSub::Spaceship(sp),
});
self.bump();
}
@ -430,11 +449,19 @@ fn check_assoc_op(&self) -> Option<Spanned<AssocOp>> {
}
(Some(op), _) => (op, self.token.span),
(None, Some((Ident { name: sym::and, span }, false))) => {
self.error_bad_logical_op("and", "&&", "conjunction");
self.sess.emit_err(InvalidLogicalOperator {
span: self.token.span,
incorrect: "and".into(),
sub: InvalidLogicalOperatorSub::Conjunction(self.token.span),
});
(AssocOp::LAnd, span)
}
(None, Some((Ident { name: sym::or, span }, false))) => {
self.error_bad_logical_op("or", "||", "disjunction");
self.sess.emit_err(InvalidLogicalOperator {
span: self.token.span,
incorrect: "or".into(),
sub: InvalidLogicalOperatorSub::Disjunction(self.token.span),
});
(AssocOp::LOr, span)
}
_ => return None,
@ -442,19 +469,6 @@ fn check_assoc_op(&self) -> Option<Spanned<AssocOp>> {
Some(source_map::respan(span, op))
}
/// Error on `and` and `or` suggesting `&&` and `||` respectively.
fn error_bad_logical_op(&self, bad: &str, good: &str, english: &str) {
self.struct_span_err(self.token.span, &format!("`{bad}` is not a logical operator"))
.span_suggestion_short(
self.token.span,
&format!("use `{good}` to perform logical {english}"),
good,
Applicability::MachineApplicable,
)
.note("unlike in e.g., python and PHP, `&&` and `||` are used for logical operators")
.emit();
}
/// Checks if this expression is a successfully parsed statement.
fn expr_is_complete(&self, e: &Expr) -> bool {
self.restrictions.contains(Restrictions::STMT_EXPR)
@ -619,14 +633,7 @@ fn parse_unary_expr(&mut self, lo: Span, op: UnOp) -> PResult<'a, (Span, ExprKin
// Recover on `!` suggesting for bitwise negation instead.
fn recover_tilde_expr(&mut self, lo: Span) -> PResult<'a, (Span, ExprKind)> {
self.struct_span_err(lo, "`~` cannot be used as a unary operator")
.span_suggestion_short(
lo,
"use `!` to perform bitwise not",
"!",
Applicability::MachineApplicable,
)
.emit();
self.sess.emit_err(TildeAsUnaryOperator(lo));
self.parse_unary_expr(lo, UnOp::Not)
}
@ -652,20 +659,14 @@ fn is_mistaken_not_ident_negation(&self) -> bool {
/// Recover on `not expr` in favor of `!expr`.
fn recover_not_expr(&mut self, lo: Span) -> PResult<'a, (Span, ExprKind)> {
// Emit the error...
let not_token = self.look_ahead(1, |t| t.clone());
self.struct_span_err(
not_token.span,
&format!("unexpected {} after identifier", super::token_descr(&not_token)),
)
.span_suggestion_short(
let negated_token = self.look_ahead(1, |t| t.clone());
self.sess.emit_err(NotAsNegationOperator {
negated: negated_token.span,
negated_desc: super::token_descr(&negated_token),
// Span the `not` plus trailing whitespace to avoid
// trailing whitespace after the `!` in our suggestion
self.sess.source_map().span_until_non_whitespace(lo.to(not_token.span)),
"use `!` to perform logical negation",
"!",
Applicability::MachineApplicable,
)
.emit();
not: self.sess.source_map().span_until_non_whitespace(lo.to(negated_token.span)),
});
// ...and recover!
self.parse_unary_expr(lo, UnOp::Not)
@ -725,14 +726,10 @@ fn parse_assoc_op_cast(
match self.parse_labeled_expr(label, false) {
Ok(expr) => {
type_err.cancel();
self.struct_span_err(label.ident.span, "malformed loop label")
.span_suggestion(
label.ident.span,
"use the correct loop label format",
label.ident,
Applicability::MachineApplicable,
)
.emit();
self.sess.emit_err(MalformedLoopLabel {
span: label.ident.span,
correct_label: label.ident,
});
return Ok(expr);
}
Err(err) => {
@ -910,15 +907,7 @@ fn parse_borrow_expr(&mut self, lo: Span) -> PResult<'a, (Span, ExprKind)> {
}
fn error_remove_borrow_lifetime(&self, span: Span, lt_span: Span) {
self.struct_span_err(span, "borrow expressions cannot be annotated with lifetimes")
.span_label(lt_span, "annotated with lifetime here")
.span_suggestion(
lt_span,
"remove the lifetime annotation",
"",
Applicability::MachineApplicable,
)
.emit();
self.sess.emit_err(LifetimeInBorrowExpression { span, lifetime_span: lt_span });
}
/// Parse `mut?` or `raw [ const | mut ]`.
@ -1272,11 +1261,7 @@ fn parse_dot_suffix(&mut self, self_arg: P<Expr>, lo: Span) -> PResult<'a, P<Exp
} else {
// Field access `expr.f`
if let Some(args) = segment.args {
self.struct_span_err(
args.span(),
"field expressions cannot have generic arguments",
)
.emit();
self.sess.emit_err(FieldExpressionWithGeneric(args.span()));
}
let span = lo.to(self.prev_token.span);
@ -1489,7 +1474,7 @@ fn parse_path_start_expr(&mut self) -> PResult<'a, P<Expr>> {
let (span, kind) = if self.eat(&token::Not) {
// MACRO INVOCATION expression
if qself.is_some() {
self.struct_span_err(path.span, "macros cannot use qualified paths").emit();
self.sess.emit_err(MacroInvocationWithQualifiedPath(path.span));
}
let lo = path.span;
let mac = P(MacCall {
@ -1535,11 +1520,11 @@ fn parse_labeled_expr(
&& (self.check_noexpect(&TokenKind::Comma) || self.check_noexpect(&TokenKind::Gt))
{
// We're probably inside of a `Path<'a>` that needs a turbofish
let msg = "expected `while`, `for`, `loop` or `{` after a label";
self.struct_span_err(self.token.span, msg).span_label(self.token.span, msg).emit();
self.sess.emit_err(UnexpectedTokenAfterLabel(self.token.span));
consume_colon = false;
Ok(self.mk_expr_err(lo))
} else {
// FIXME: use UnexpectedTokenAfterLabel, needs multipart suggestions
let msg = "expected `while`, `for`, `loop` or `{` after a label";
let mut err = self.struct_span_err(self.token.span, msg);
@ -1604,25 +1589,16 @@ fn visit_expr_post(&mut self, ex: &'ast Expr) {
}?;
if !ate_colon && consume_colon {
self.error_labeled_expr_must_be_followed_by_colon(lo, expr.span);
self.sess.emit_err(RequireColonAfterLabeledExpression {
span: expr.span,
label: lo,
label_end: lo.shrink_to_hi(),
});
}
Ok(expr)
}
fn error_labeled_expr_must_be_followed_by_colon(&self, lo: Span, span: Span) {
self.struct_span_err(span, "labeled expression must be followed by `:`")
.span_label(lo, "the label")
.span_suggestion_short(
lo.shrink_to_hi(),
"add `:` after the label",
": ",
Applicability::MachineApplicable,
)
.note("labels are used before loops and blocks, allowing e.g., `break 'label` to them")
.emit();
}
/// Recover on the syntax `do catch { ... }` suggesting `try { ... }` instead.
fn recover_do_catch(&mut self) -> PResult<'a, P<Expr>> {
let lo = self.token.span;
@ -1630,16 +1606,8 @@ fn recover_do_catch(&mut self) -> PResult<'a, P<Expr>> {
self.bump(); // `do`
self.bump(); // `catch`
let span_dc = lo.to(self.prev_token.span);
self.struct_span_err(span_dc, "found removed `do catch` syntax")
.span_suggestion(
span_dc,
"replace with the new syntax",
"try",
Applicability::MachineApplicable,
)
.note("following RFC #2388, the new non-placeholder syntax is `try`")
.emit();
let span = lo.to(self.prev_token.span);
self.sess.emit_err(DoCatchSyntaxRemoved { span });
self.parse_try_block(lo)
}
@ -1834,14 +1802,10 @@ pub(super) fn parse_opt_lit(&mut self) -> Option<Lit> {
}
fn error_float_lits_must_have_int_part(&self, token: &Token) {
self.struct_span_err(token.span, "float literals must have an integer part")
.span_suggestion(
token.span,
"must have an integer part",
pprust::token_to_string(token),
Applicability::MachineApplicable,
)
.emit();
self.sess.emit_err(FloatLiteralRequiresIntegerPart {
span: token.span,
correct: pprust::token_to_string(token).into_owned(),
});
}
fn report_lit_error(&self, err: LitError, lit: token::Lit, span: Span) {
@ -1883,28 +1847,11 @@ fn fix_base_capitalisation(s: &str) -> Option<String> {
let suf = suf.as_str();
if looks_like_width_suffix(&['i', 'u'], &suf) {
// If it looks like a width, try to be helpful.
let msg = format!("invalid width `{}` for integer literal", &suf[1..]);
self.struct_span_err(span, &msg)
.help("valid widths are 8, 16, 32, 64 and 128")
.emit();
self.sess.emit_err(InvalidIntLiteralWidth { span, width: suf[1..].into() });
} else if let Some(fixed) = fix_base_capitalisation(suf) {
let msg = "invalid base prefix for number literal";
self.struct_span_err(span, msg)
.note("base prefixes (`0xff`, `0b1010`, `0o755`) are lowercase")
.span_suggestion(
span,
"try making the prefix lowercase",
fixed,
Applicability::MaybeIncorrect,
)
.emit();
self.sess.emit_err(InvalidNumLiteralBasePrefix { span, fixed });
} else {
let msg = format!("invalid suffix `{suf}` for number literal");
self.struct_span_err(span, &msg)
.span_label(span, format!("invalid suffix `{suf}`"))
.help("the suffix must be one of the numeric types (`u32`, `isize`, `f32`, etc.)")
.emit();
self.sess.emit_err(InvalidNumLiteralSuffix { span, suffix: suf.to_string() });
}
}
LitError::InvalidFloatSuffix => {
@ -1912,14 +1859,10 @@ fn fix_base_capitalisation(s: &str) -> Option<String> {
let suf = suf.as_str();
if looks_like_width_suffix(&['f'], suf) {
// If it looks like a width, try to be helpful.
let msg = format!("invalid width `{}` for float literal", &suf[1..]);
self.struct_span_err(span, &msg).help("valid widths are 32 and 64").emit();
self.sess
.emit_err(InvalidFloatLiteralWidth { span, width: suf[1..].to_string() });
} else {
let msg = format!("invalid suffix `{suf}` for float literal");
self.struct_span_err(span, &msg)
.span_label(span, format!("invalid suffix `{suf}`"))
.help("valid suffixes are `f32` and `f64`")
.emit();
self.sess.emit_err(InvalidFloatLiteralSuffix { span, suffix: suf.to_string() });
}
}
LitError::NonDecimalFloat(base) => {
@ -1934,7 +1877,7 @@ fn fix_base_capitalisation(s: &str) -> Option<String> {
.emit();
}
LitError::IntTooLarge => {
self.struct_span_err(span, "integer literal is too large").emit();
self.sess.emit_err(IntLiteralTooLarge { span });
}
}
}
@ -2046,14 +1989,10 @@ fn suggest_missing_semicolon_before_array(
.span_to_snippet(snapshot.token.span)
.map_or(false, |snippet| snippet == "]") =>
{
let mut err = self.struct_span_err(open_delim_span, "expected `;`, found `[`");
err.span_suggestion_verbose(
prev_span.shrink_to_hi(),
"consider adding `;` here",
';',
Applicability::MaybeIncorrect,
);
return Err(err);
return Err(MissingSemicolonBeforeArray {
open_delim: open_delim_span,
semicolon: prev_span.shrink_to_hi(),
}.into_diagnostic(self.sess));
}
Ok(_) => (),
Err(err) => err.cancel(),
@ -2080,9 +2019,10 @@ pub(super) fn parse_block_expr(
}
if self.token.is_whole_block() {
self.struct_span_err(self.token.span, "cannot use a `block` macro fragment here")
.span_label(lo.to(self.token.span), "the `block` fragment is within this context")
.emit();
self.sess.emit_err(InvalidBlockMacroSegment {
span: self.token.span,
context: lo.to(self.token.span),
});
}
let (attrs, blk) = self.parse_block_common(lo, blk_mode)?;
@ -2252,11 +2192,19 @@ fn parse_if_after_cond(&mut self, lo: Span, mut cond: P<Expr>) -> PResult<'a, P<
let block = match &mut cond.kind {
ExprKind::Binary(Spanned { span: binop_span, .. }, _, right)
if let ExprKind::Block(_, None) = right.kind => {
this.error_missing_if_then_block(lo, cond_span.shrink_to_lo().to(*binop_span), true).emit();
self.sess.emit_err(IfExpressionMissingThenBlock {
if_span: lo,
sub: IfExpressionMissingThenBlockSub::UnfinishedCondition(
cond_span.shrink_to_lo().to(*binop_span)
),
});
std::mem::replace(right, this.mk_expr_err(binop_span.shrink_to_hi()))
},
ExprKind::Block(_, None) => {
this.error_missing_if_cond(lo, cond_span).emit();
self.sess.emit_err(IfExpressionMissingCondition {
if_span: self.sess.source_map().next_point(lo),
block_span: self.sess.source_map().start_point(cond_span),
});
std::mem::replace(&mut cond, this.mk_expr_err(cond_span.shrink_to_hi()))
}
_ => {
@ -2274,7 +2222,10 @@ fn parse_if_after_cond(&mut self, lo: Span, mut cond: P<Expr>) -> PResult<'a, P<
if let Some(block) = recover_block_from_condition(self) {
block
} else {
self.error_missing_if_then_block(lo, cond_span, false).emit();
self.sess.emit_err(IfExpressionMissingThenBlock {
if_span: lo,
sub: IfExpressionMissingThenBlockSub::AddThenBlock(cond_span.shrink_to_hi()),
});
self.mk_block_err(cond_span.shrink_to_hi())
}
} else {
@ -2302,39 +2253,6 @@ fn parse_if_after_cond(&mut self, lo: Span, mut cond: P<Expr>) -> PResult<'a, P<
Ok(self.mk_expr(lo.to(self.prev_token.span), ExprKind::If(cond, thn, els)))
}
fn error_missing_if_then_block(
&self,
if_span: Span,
cond_span: Span,
is_unfinished: bool,
) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
let mut err = self.struct_span_err(
if_span,
"this `if` expression is missing a block after the condition",
);
if is_unfinished {
err.span_help(cond_span, "this binary operation is possibly unfinished");
} else {
err.span_help(cond_span.shrink_to_hi(), "add a block here");
}
err
}
fn error_missing_if_cond(
&self,
lo: Span,
span: Span,
) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
let next_span = self.sess.source_map().next_point(lo);
let mut err = self.struct_span_err(next_span, "missing condition for `if` expression");
err.span_label(next_span, "expected condition here");
err.span_label(
self.sess.source_map().start_point(span),
"if this block is the condition of the `if` expression, then it must be followed by another block"
);
err
}
/// Parses the condition of a `if` or `while` expression.
fn parse_cond_expr(&mut self) -> PResult<'a, P<Expr>> {
self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL | Restrictions::ALLOW_LET, None)
@ -2350,8 +2268,7 @@ fn parse_let_expr(&mut self) -> PResult<'a, P<Expr>> {
TokenKind::AndAnd | TokenKind::Ident(kw::If, _) | TokenKind::Ident(kw::While, _)
);
if !self.restrictions.contains(Restrictions::ALLOW_LET) || not_in_chain {
self.struct_span_err(self.token.span, "expected expression, found `let` statement")
.emit();
self.sess.emit_err(ExpectedExpressionFoundLet { span: self.token.span });
}
self.bump(); // Eat `let` token
@ -2389,15 +2306,12 @@ fn parse_else_expr(&mut self) -> PResult<'a, P<Expr>> {
if self.check(&TokenKind::OpenDelim(Delimiter::Brace))
&& classify::expr_requires_semi_to_be_stmt(&cond) =>
{
self.struct_span_err(first_tok_span, format!("expected `{{`, found {first_tok}"))
.span_label(else_span, "expected an `if` or a block after this `else`")
.span_suggestion(
cond.span.shrink_to_lo(),
"add an `if` if this is the condition of a chained `else if` statement",
"if ",
Applicability::MaybeIncorrect,
)
.emit();
self.sess.emit_err(ExpectedElseBlock {
first_tok_span,
first_tok,
else_span,
condition_start: cond.span.shrink_to_lo(),
});
self.parse_if_after_cond(cond.span.shrink_to_lo(), cond)?
}
Err(e) => {
@ -2422,16 +2336,18 @@ fn error_on_if_block_attrs(
branch_span: Span,
attrs: &[ast::Attribute],
) {
let (span, last) = match attrs {
let (attributes, last) = match attrs {
[] => return,
[x0 @ xn] | [x0, .., xn] => (x0.span.to(xn.span), xn.span),
};
let ctx = if is_ctx_else { "else" } else { "if" };
self.struct_span_err(last, "outer attributes are not allowed on `if` and `else` branches")
.span_label(branch_span, "the attributes are attached to this branch")
.span_label(ctx_span, format!("the branch belongs to this `{ctx}`"))
.span_suggestion(span, "remove the attributes", "", Applicability::MachineApplicable)
.emit();
self.sess.emit_err(OuterAttributeNotAllowedOnIfElse {
last,
branch_span,
ctx_span,
ctx: ctx.to_string(),
attributes,
});
}
/// Parses `for <src_pat> in <src_expr> <src_loop_block>` (`for` token already eaten).
@ -2465,23 +2381,16 @@ fn parse_for_expr(&mut self, opt_label: Option<Label>, lo: Span) -> PResult<'a,
}
fn error_missing_in_for_loop(&mut self) {
let (span, msg, sugg) = if self.token.is_ident_named(sym::of) {
let (span, sub): (_, fn(_) -> _) = if self.token.is_ident_named(sym::of) {
// Possibly using JS syntax (#75311).
let span = self.token.span;
self.bump();
(span, "try using `in` here instead", "in")
(span, MissingInInForLoopSub::InNotOf)
} else {
(self.prev_token.span.between(self.token.span), "try adding `in` here", " in ")
(self.prev_token.span.between(self.token.span), MissingInInForLoopSub::AddIn)
};
self.struct_span_err(span, "missing `in` in `for` loop")
.span_suggestion_short(
span,
msg,
sugg,
// Has been misleading, at least in the past (closed Issue #48492).
Applicability::MaybeIncorrect,
)
.emit();
self.sess.emit_err(MissingInInForLoop { span, sub: sub(span) });
}
/// Parses a `while` or `while let` expression (`while` token already eaten).
@ -2787,17 +2696,9 @@ fn check_let_expr(expr: &Expr) -> bool {
.is_ok();
if pattern_follows && snapshot.check(&TokenKind::FatArrow) {
err.cancel();
this.struct_span_err(
hi.shrink_to_hi(),
"expected `,` following `match` arm",
)
.span_suggestion(
hi.shrink_to_hi(),
"missing a comma here to end this `match` arm",
",",
Applicability::MachineApplicable,
)
.emit();
this.sess.emit_err(MissingCommaAfterMatchArm {
span: hi.shrink_to_hi(),
});
return Ok(true);
}
}
@ -2827,13 +2728,7 @@ fn check_let_expr(expr: &Expr) -> bool {
fn parse_try_block(&mut self, span_lo: Span) -> PResult<'a, P<Expr>> {
let (attrs, body) = self.parse_inner_attrs_and_block()?;
if self.eat_keyword(kw::Catch) {
let mut error = self.struct_span_err(
self.prev_token.span,
"keyword `catch` cannot follow a `try` block",
);
error.help("try using `match` on the result of the `try` block instead");
error.emit();
Err(error)
Err(CatchAfterTry { span: self.prev_token.span }.into_diagnostic(self.sess))
} else {
let span = span_lo.to(body.span);
self.sess.gated_spans.gate(sym::try_blocks, span);
@ -3082,18 +2977,10 @@ fn recover_struct_comma_after_dotdot(&mut self, span: Span) {
if self.token != token::Comma {
return;
}
self.struct_span_err(
span.to(self.prev_token.span),
"cannot use a comma after the base struct",
)
.span_suggestion_short(
self.token.span,
"remove this comma",
"",
Applicability::MachineApplicable,
)
.note("the base struct must always be the last field")
.emit();
self.sess.emit_err(CommaAfterBaseStruct {
span: span.to(self.prev_token.span),
comma: self.token.span,
});
self.recover_stmt();
}
@ -3139,43 +3026,18 @@ fn error_on_eq_field_init(&self, field_name: Ident) {
return;
}
self.struct_span_err(self.token.span, "expected `:`, found `=`")
.span_suggestion(
field_name.span.shrink_to_hi().to(self.token.span),
"replace equals symbol with a colon",
":",
Applicability::MachineApplicable,
)
.emit();
self.sess.emit_err(EqFieldInit {
span: self.token.span,
eq: field_name.span.shrink_to_hi().to(self.token.span),
});
}
fn err_dotdotdot_syntax(&self, span: Span) {
self.struct_span_err(span, "unexpected token: `...`")
.span_suggestion(
span,
"use `..` for an exclusive range",
"..",
Applicability::MaybeIncorrect,
)
.span_suggestion(
span,
"or `..=` for an inclusive range",
"..=",
Applicability::MaybeIncorrect,
)
.emit();
self.sess.emit_err(DotDotDot { span });
}
fn err_larrow_operator(&self, span: Span) {
self.struct_span_err(span, "unexpected token: `<-`")
.span_suggestion(
span,
"if you meant to write a comparison against a negative value, add a \
space in between `<` and `-`",
"< -",
Applicability::MaybeIncorrect,
)
.emit();
self.sess.emit_err(LeftArrowOperator { span });
}
fn mk_assign_op(&self, binop: BinOp, lhs: P<Expr>, rhs: P<Expr>) -> ExprKind {