Migrate more diagnostics in rustc_parse to diagnostic structs

This commit is contained in:
Xiretza 2022-08-24 22:41:51 +02:00
parent 4d02892acf
commit ab7c7dc7ce
6 changed files with 463 additions and 218 deletions

View File

@ -71,6 +71,8 @@ parser_field_expression_with_generic = field expressions cannot have generic arg
parser_macro_invocation_with_qualified_path = macros cannot use qualified paths 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_unexpected_token_after_label = expected `while`, `for`, `loop` or `{"{"}` after a label
.suggestion_remove_label = consider removing the label
.suggestion_enclose_in_block = consider enclosing expression in a block
parser_require_colon_after_labeled_expression = labeled expression must be followed by `:` 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 .note = labels are used before loops and blocks, allowing e.g., `break 'label` to them
@ -161,3 +163,62 @@ parser_use_eq_instead = unexpected `==`
parser_use_empty_block_not_semi = expected { "`{}`" }, found `;` parser_use_empty_block_not_semi = expected { "`{}`" }, found `;`
.suggestion = try using { "`{}`" } instead .suggestion = try using { "`{}`" } instead
parser_comparison_interpreted_as_generic =
`<` is interpreted as a start of generic arguments for `{$typename}`, not a comparison
.label_args = interpreted as generic arguments
.label_comparison = not interpreted as comparison
.suggestion = try comparing the cast value
parser_shift_interpreted_as_generic =
`<<` is interpreted as a start of generic arguments for `{$typename}`, not a shift
.label_args = interpreted as generic arguments
.label_comparison = not interpreted as shift
.suggestion = try shifting the cast value
parser_found_expr_would_be_stmt = expected expression, found `{$token}`
.label = expected expression
parser_leading_plus_not_supported = leading `+` is not supported
.label = unexpected `+`
.suggestion_remove_plus = try removing the `+`
parser_parentheses_with_struct_fields = invalid `struct` delimiters or `fn` call arguments
.suggestion_braces_for_struct = if `{$name}` is a struct, use braces as delimiters
.suggestion_no_fields_for_fn = if `{$name}` is a function, use the arguments directly
parser_labeled_loop_in_break = parentheses are required around this expression to avoid confusion with a labeled break expression
.suggestion = wrap the expression in parentheses
parser_array_brackets_instead_of_braces = this is a block expression, not an array
.suggestion = to make an array, use square brackets instead of curly braces
parser_match_arm_body_without_braces = `match` arm body without braces
.label_statements = {$num_statements ->
[one] this statement is not surrounded by a body
*[other] these statements are not surrounded by a body
}
.label_arrow = while parsing the `match` arm starting here
.suggestion_add_braces = surround the {$num_statements ->
[one] statement
*[other] statements
} with a body
.suggestion_use_comma_not_semicolon = use a comma to end a `match` arm expression
parser_struct_literal_not_allowed_here = struct literals are not allowed here
.suggestion = surround the struct literal with parentheses
parser_invalid_interpolated_expression = invalid interpolated expression
parser_hexadecimal_float_literal_not_supported = hexadecimal float literal is not supported
parser_octal_float_literal_not_supported = octal float literal is not supported
parser_binary_float_literal_not_supported = binary float literal is not supported
parser_not_supported = not supported
parser_non_string_abi_literal = non-string ABI literal
.suggestion = specify the ABI with a string literal
parser_mismatched_closing_delimiter = mismatched closing delimiter: `{$delimiter}`
.label_unmatched = mismatched closing delimiter
.label_opening_candidate = closing delimiter possibly meant for this
.label_unclosed = unclosed delimiter

View File

@ -21,6 +21,7 @@ use rustc_errors::{
}; };
use rustc_errors::{pluralize, struct_span_err, Diagnostic, ErrorGuaranteed}; use rustc_errors::{pluralize, struct_span_err, Diagnostic, ErrorGuaranteed};
use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_macros::{Diagnostic, Subdiagnostic};
use rustc_session::errors::ExprParenthesesNeeded;
use rustc_span::source_map::Spanned; use rustc_span::source_map::Spanned;
use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::symbol::{kw, sym, Ident};
use rustc_span::{Span, SpanSnippetError, DUMMY_SP}; use rustc_span::{Span, SpanSnippetError, DUMMY_SP};
@ -487,11 +488,24 @@ pub(crate) struct MacroInvocationWithQualifiedPath(#[primary_span] pub Span);
#[derive(Diagnostic)] #[derive(Diagnostic)]
#[diag(parser::unexpected_token_after_label)] #[diag(parser::unexpected_token_after_label)]
pub(crate) struct UnexpectedTokenAfterLabel( pub(crate) struct UnexpectedTokenAfterLabel {
#[primary_span] #[primary_span]
#[label(parser::unexpected_token_after_label)] #[label(parser::unexpected_token_after_label)]
pub Span, pub span: Span,
); #[suggestion_verbose(parser::suggestion_remove_label, code = "")]
pub remove_label: Option<Span>,
#[subdiagnostic]
pub enclose_in_block: Option<UnexpectedTokenAfterLabelSugg>,
}
#[derive(Subdiagnostic)]
#[multipart_suggestion(parser::suggestion_enclose_in_block, applicability = "machine-applicable")]
pub(crate) struct UnexpectedTokenAfterLabelSugg {
#[suggestion_part(code = "{{ ")]
pub left: Span,
#[suggestion_part(code = " }}")]
pub right: Span,
}
#[derive(Diagnostic)] #[derive(Diagnostic)]
#[diag(parser::require_colon_after_labeled_expression)] #[diag(parser::require_colon_after_labeled_expression)]
@ -753,6 +767,236 @@ pub(crate) struct UseEmptyBlockNotSemi {
pub span: Span, pub span: Span,
} }
#[derive(Diagnostic)]
#[diag(parser::comparison_interpreted_as_generic)]
pub(crate) struct ComparisonInterpretedAsGeneric {
#[primary_span]
#[label(parser::label_comparison)]
pub comparison: Span,
pub typename: String,
#[label(parser::label_args)]
pub args: Span,
#[subdiagnostic]
pub suggestion: ComparisonOrShiftInterpretedAsGenericSugg,
}
#[derive(Diagnostic)]
#[diag(parser::shift_interpreted_as_generic)]
pub(crate) struct ShiftInterpretedAsGeneric {
#[primary_span]
#[label(parser::label_comparison)]
pub shift: Span,
pub typename: String,
#[label(parser::label_args)]
pub args: Span,
#[subdiagnostic]
pub suggestion: ComparisonOrShiftInterpretedAsGenericSugg,
}
#[derive(Subdiagnostic)]
#[multipart_suggestion(parser::suggestion, applicability = "machine-applicable")]
pub(crate) struct ComparisonOrShiftInterpretedAsGenericSugg {
#[suggestion_part(code = "(")]
pub left: Span,
#[suggestion_part(code = ")")]
pub right: Span,
}
#[derive(Diagnostic)]
#[diag(parser::found_expr_would_be_stmt)]
pub(crate) struct FoundExprWouldBeStmt {
#[primary_span]
#[label]
pub span: Span,
pub token: String,
#[subdiagnostic]
pub suggestion: ExprParenthesesNeeded,
}
#[derive(Diagnostic)]
#[diag(parser::leading_plus_not_supported)]
pub(crate) struct LeadingPlusNotSupported {
#[primary_span]
#[label]
pub span: Span,
#[suggestion_verbose(
parser::suggestion_remove_plus,
code = "",
applicability = "machine-applicable"
)]
pub remove_plus: Option<Span>,
#[subdiagnostic]
pub add_parentheses: Option<ExprParenthesesNeeded>,
}
#[derive(Diagnostic)]
#[diag(parser::parentheses_with_struct_fields)]
pub(crate) struct ParenthesesWithStructFields {
#[primary_span]
pub span: Span,
pub name: String,
#[subdiagnostic]
pub braces_for_struct: BracesForStructLiteral,
#[subdiagnostic]
pub no_fields_for_fn: NoFieldsForFnCall,
}
#[derive(Subdiagnostic)]
#[multipart_suggestion(parser::suggestion_braces_for_struct, applicability = "maybe-incorrect")]
pub(crate) struct BracesForStructLiteral {
#[suggestion_part(code = " {{ ")]
pub first: Span,
#[suggestion_part(code = " }}")]
pub second: Span,
}
#[derive(Subdiagnostic)]
#[multipart_suggestion(parser::suggestion_no_fields_for_fn, applicability = "maybe-incorrect")]
pub(crate) struct NoFieldsForFnCall {
#[suggestion_part(code = "")]
pub fields: Vec<Span>,
}
#[derive(Diagnostic)]
#[diag(parser::labeled_loop_in_break)]
pub(crate) struct LabeledLoopInBreak {
#[primary_span]
pub span: Span,
#[subdiagnostic]
pub sub: LabeledLoopInBreakSub,
}
#[derive(Subdiagnostic)]
#[multipart_suggestion(parser::suggestion, applicability = "machine-applicable")]
pub(crate) struct LabeledLoopInBreakSub {
#[suggestion_part(code = "(")]
pub first: Span,
#[suggestion_part(code = ")")]
pub second: Span,
}
#[derive(Diagnostic)]
#[diag(parser::array_brackets_instead_of_braces)]
pub(crate) struct ArrayBracketsInsteadOfSpaces {
#[primary_span]
pub span: Span,
#[subdiagnostic]
pub sub: ArrayBracketsInsteadOfSpacesSugg,
}
#[derive(Subdiagnostic)]
#[multipart_suggestion(parser::suggestion, applicability = "maybe-incorrect")]
pub(crate) struct ArrayBracketsInsteadOfSpacesSugg {
#[suggestion_part(code = "[")]
pub left: Span,
#[suggestion_part(code = "]")]
pub right: Span,
}
#[derive(Diagnostic)]
#[diag(parser::match_arm_body_without_braces)]
pub(crate) struct MatchArmBodyWithoutBraces {
#[primary_span]
#[label(parser::label_statements)]
pub statements: Span,
#[label(parser::label_arrow)]
pub arrow: Span,
pub num_statements: usize,
#[subdiagnostic]
pub sub: MatchArmBodyWithoutBracesSugg,
}
#[derive(Subdiagnostic)]
pub(crate) enum MatchArmBodyWithoutBracesSugg {
#[multipart_suggestion(parser::suggestion_add_braces, applicability = "machine-applicable")]
AddBraces {
#[suggestion_part(code = "{{ ")]
left: Span,
#[suggestion_part(code = " }}")]
right: Span,
},
#[suggestion(
parser::suggestion_use_comma_not_semicolon,
code = ",",
applicability = "machine-applicable"
)]
UseComma {
#[primary_span]
semicolon: Span,
},
}
#[derive(Diagnostic)]
#[diag(parser::struct_literal_not_allowed_here)]
pub(crate) struct StructLiteralNotAllowedHere {
#[primary_span]
pub span: Span,
#[subdiagnostic]
pub sub: StructLiteralNotAllowedHereSugg,
}
#[derive(Subdiagnostic)]
#[multipart_suggestion(parser::suggestion, applicability = "machine-applicable")]
pub(crate) struct StructLiteralNotAllowedHereSugg {
#[suggestion_part(code = "(")]
pub left: Span,
#[suggestion_part(code = ")")]
pub right: Span,
}
#[derive(Diagnostic)]
#[diag(parser::invalid_interpolated_expression)]
pub(crate) struct InvalidInterpolatedExpression {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(parser::hexadecimal_float_literal_not_supported)]
pub(crate) struct HexadecimalFloatLiteralNotSupported {
#[primary_span]
#[label(parser::not_supported)]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(parser::octal_float_literal_not_supported)]
pub(crate) struct OctalFloatLiteralNotSupported {
#[primary_span]
#[label(parser::not_supported)]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(parser::binary_float_literal_not_supported)]
pub(crate) struct BinaryFloatLiteralNotSupported {
#[primary_span]
#[label(parser::not_supported)]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(parser::non_string_abi_literal)]
pub(crate) struct NonStringAbiLiteral {
#[primary_span]
#[suggestion(code = "\"C\"", applicability = "maybe-incorrect")]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(parser::mismatched_closing_delimiter)]
pub(crate) struct MismatchedClosingDelimiter {
#[primary_span]
pub spans: Vec<Span>,
pub delimiter: String,
#[label(parser::label_unmatched)]
pub unmatched: Span,
#[label(parser::label_opening_candidate)]
pub opening_candidate: Option<Span>,
#[label(parser::label_unclosed)]
pub unclosed: Option<Span>,
}
// SnapshotParser is used to create a snapshot of the parser // SnapshotParser is used to create a snapshot of the parser
// without causing duplicate errors being emitted when the `Parser` // without causing duplicate errors being emitted when the `Parser`
// is dropped. // is dropped.

View File

@ -1,14 +1,19 @@
use super::diagnostics::{ use super::diagnostics::{
CatchAfterTry, CommaAfterBaseStruct, DoCatchSyntaxRemoved, DotDotDot, EqFieldInit, ArrayBracketsInsteadOfSpaces, ArrayBracketsInsteadOfSpacesSugg, BracesForStructLiteral,
CatchAfterTry, CommaAfterBaseStruct, ComparisonInterpretedAsGeneric,
ComparisonOrShiftInterpretedAsGenericSugg, DoCatchSyntaxRemoved, DotDotDot, EqFieldInit,
ExpectedElseBlock, ExpectedExpressionFoundLet, FieldExpressionWithGeneric, ExpectedElseBlock, ExpectedExpressionFoundLet, FieldExpressionWithGeneric,
FloatLiteralRequiresIntegerPart, IfExpressionMissingCondition, IfExpressionMissingThenBlock, FloatLiteralRequiresIntegerPart, FoundExprWouldBeStmt, IfExpressionMissingCondition,
IfExpressionMissingThenBlockSub, InvalidBlockMacroSegment, InvalidComparisonOperator, IfExpressionMissingThenBlock, IfExpressionMissingThenBlockSub, InvalidBlockMacroSegment,
InvalidComparisonOperatorSub, InvalidLogicalOperator, InvalidLogicalOperatorSub, InvalidComparisonOperator, InvalidComparisonOperatorSub, InvalidInterpolatedExpression,
LeftArrowOperator, LifetimeInBorrowExpression, MacroInvocationWithQualifiedPath, InvalidLogicalOperator, InvalidLogicalOperatorSub, LabeledLoopInBreak, LeftArrowOperator,
MalformedLoopLabel, MissingInInForLoop, MissingInInForLoopSub, MissingSemicolonBeforeArray, LifetimeInBorrowExpression, MacroInvocationWithQualifiedPath, MalformedLoopLabel,
NotAsNegationOperator, NotAsNegationOperatorSub, OuterAttributeNotAllowedOnIfElse, MatchArmBodyWithoutBraces, MissingInInForLoop, MissingInInForLoopSub,
RequireColonAfterLabeledExpression, SnapshotParser, TildeAsUnaryOperator, MissingSemicolonBeforeArray, NoFieldsForFnCall, NotAsNegationOperator,
UnexpectedTokenAfterLabel, NotAsNegationOperatorSub, OuterAttributeNotAllowedOnIfElse, ParenthesesWithStructFields,
RequireColonAfterLabeledExpression, ShiftInterpretedAsGeneric, SnapshotParser,
StructLiteralNotAllowedHere, TildeAsUnaryOperator, UnexpectedTokenAfterLabel,
UnexpectedTokenAfterLabelSugg,
}; };
use super::pat::{CommaRecoveryMode, RecoverColon, RecoverComma, PARAM_EXPECTED}; use super::pat::{CommaRecoveryMode, RecoverColon, RecoverComma, PARAM_EXPECTED};
use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign}; use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign};
@ -18,9 +23,11 @@ use super::{
}; };
use crate::maybe_recover_from_interpolated_ty_qpath; use crate::maybe_recover_from_interpolated_ty_qpath;
use crate::parser::diagnostics::{ use crate::parser::diagnostics::{
IntLiteralTooLarge, InvalidFloatLiteralSuffix, InvalidFloatLiteralWidth, BinaryFloatLiteralNotSupported, HexadecimalFloatLiteralNotSupported, IntLiteralTooLarge,
InvalidIntLiteralWidth, InvalidNumLiteralBasePrefix, InvalidNumLiteralSuffix, InvalidFloatLiteralSuffix, InvalidFloatLiteralWidth, InvalidIntLiteralWidth,
MissingCommaAfterMatchArm, InvalidNumLiteralBasePrefix, InvalidNumLiteralSuffix, LabeledLoopInBreakSub,
LeadingPlusNotSupported, MatchArmBodyWithoutBracesSugg, MissingCommaAfterMatchArm,
OctalFloatLiteralNotSupported, StructLiteralNotAllowedHereSugg,
}; };
use core::mem; use core::mem;
@ -38,6 +45,7 @@ use rustc_ast::{ClosureBinder, StmtKind};
use rustc_ast_pretty::pprust; use rustc_ast_pretty::pprust;
use rustc_errors::IntoDiagnostic; use rustc_errors::IntoDiagnostic;
use rustc_errors::{Applicability, Diagnostic, PResult}; use rustc_errors::{Applicability, Diagnostic, PResult};
use rustc_session::errors::ExprParenthesesNeeded;
use rustc_session::lint::builtin::BREAK_WITH_LABEL_AND_LOOP; use rustc_session::lint::builtin::BREAK_WITH_LABEL_AND_LOOP;
use rustc_session::lint::BuiltinLintDiagnostics; use rustc_session::lint::BuiltinLintDiagnostics;
use rustc_span::source_map::{self, Span, Spanned}; use rustc_span::source_map::{self, Span, Spanned};
@ -421,13 +429,12 @@ impl<'a> Parser<'a> {
/// but the next token implies this should be parsed as an expression. /// but the next token implies this should be parsed as an expression.
/// For example: `if let Some(x) = x { x } else { 0 } / 2`. /// For example: `if let Some(x) = x { x } else { 0 } / 2`.
fn error_found_expr_would_be_stmt(&self, lhs: &Expr) { fn error_found_expr_would_be_stmt(&self, lhs: &Expr) {
let mut err = self.struct_span_err( self.sess.emit_err(FoundExprWouldBeStmt {
self.token.span, span: self.token.span,
&format!("expected expression, found `{}`", pprust::token_to_string(&self.token),), // FIXME(#100717)
); token: pprust::token_to_string(&self.token).to_string(),
err.span_label(self.token.span, "expected expression"); suggestion: ExprParenthesesNeeded::surrounding(lhs.span),
self.sess.expr_parentheses_needed(&mut err, lhs.span); });
err.emit();
} }
/// Possibly translate the current token to an associative operator. /// Possibly translate the current token to an associative operator.
@ -578,21 +585,16 @@ impl<'a> Parser<'a> {
make_it!(this, attrs, |this, _| this.parse_borrow_expr(lo)) make_it!(this, attrs, |this, _| this.parse_borrow_expr(lo))
} }
token::BinOp(token::Plus) if this.look_ahead(1, |tok| tok.is_numeric_lit()) => { token::BinOp(token::Plus) if this.look_ahead(1, |tok| tok.is_numeric_lit()) => {
let mut err = this.struct_span_err(lo, "leading `+` is not supported"); let mut err =
err.span_label(lo, "unexpected `+`"); LeadingPlusNotSupported { span: lo, remove_plus: None, add_parentheses: None };
// a block on the LHS might have been intended to be an expression instead // a block on the LHS might have been intended to be an expression instead
if let Some(sp) = this.sess.ambiguous_block_expr_parse.borrow().get(&lo) { if let Some(sp) = this.sess.ambiguous_block_expr_parse.borrow().get(&lo) {
this.sess.expr_parentheses_needed(&mut err, *sp); err.add_parentheses = Some(ExprParenthesesNeeded::surrounding(*sp));
} else { } else {
err.span_suggestion_verbose( err.remove_plus = Some(lo);
lo,
"try removing the `+`",
"",
Applicability::MachineApplicable,
);
} }
err.emit(); this.sess.emit_err(err);
this.bump(); this.bump();
this.parse_prefix_expr(None) this.parse_prefix_expr(None)
@ -755,9 +757,33 @@ impl<'a> Parser<'a> {
match self.parse_path(PathStyle::Expr) { match self.parse_path(PathStyle::Expr) {
Ok(path) => { Ok(path) => {
let (op_noun, op_verb) = match self.token.kind { let typename = pprust::path_to_string(&path);
token::Lt => ("comparison", "comparing"),
token::BinOp(token::Shl) => ("shift", "shifting"), let span_after_type = parser_snapshot_after_type.token.span;
let expr =
mk_expr(self, lhs, self.mk_ty(path.span, TyKind::Path(None, path)));
let args_span = self.look_ahead(1, |t| t.span).to(span_after_type);
let suggestion = ComparisonOrShiftInterpretedAsGenericSugg {
left: expr.span.shrink_to_lo(),
right: expr.span.shrink_to_hi(),
};
match self.token.kind {
token::Lt => self.sess.emit_err(ComparisonInterpretedAsGeneric {
comparison: self.token.span,
typename,
args: args_span,
suggestion,
}),
token::BinOp(token::Shl) => {
self.sess.emit_err(ShiftInterpretedAsGeneric {
shift: self.token.span,
typename,
args: args_span,
suggestion,
})
}
_ => { _ => {
// We can end up here even without `<` being the next token, for // We can end up here even without `<` being the next token, for
// example because `parse_ty_no_plus` returns `Err` on keywords, // example because `parse_ty_no_plus` returns `Err` on keywords,
@ -771,33 +797,7 @@ impl<'a> Parser<'a> {
// Successfully parsed the type path leaving a `<` yet to parse. // Successfully parsed the type path leaving a `<` yet to parse.
type_err.cancel(); type_err.cancel();
// Report non-fatal diagnostics, keep `x as usize` as an expression // Keep `x as usize` as an expression in AST and continue parsing.
// in AST and continue parsing.
let msg = format!(
"`<` is interpreted as a start of generic arguments for `{}`, not a {}",
pprust::path_to_string(&path),
op_noun,
);
let span_after_type = parser_snapshot_after_type.token.span;
let expr =
mk_expr(self, lhs, self.mk_ty(path.span, TyKind::Path(None, path)));
self.struct_span_err(self.token.span, &msg)
.span_label(
self.look_ahead(1, |t| t.span).to(span_after_type),
"interpreted as generic arguments",
)
.span_label(self.token.span, format!("not interpreted as {op_noun}"))
.multipart_suggestion(
&format!("try {op_verb} the cast value"),
vec![
(expr.span.shrink_to_lo(), "(".to_string()),
(expr.span.shrink_to_hi(), ")".to_string()),
],
Applicability::MachineApplicable,
)
.emit();
expr expr
} }
Err(path_err) => { Err(path_err) => {
@ -1208,29 +1208,25 @@ impl<'a> Parser<'a> {
let close_paren = self.prev_token.span; let close_paren = self.prev_token.span;
let span = lo.to(self.prev_token.span); let span = lo.to(self.prev_token.span);
if !fields.is_empty() { if !fields.is_empty() {
let replacement_err = self.struct_span_err( let mut replacement_err = ParenthesesWithStructFields {
span, span,
"invalid `struct` delimiters or `fn` call arguments", name,
); braces_for_struct: BracesForStructLiteral {
mem::replace(err, replacement_err).cancel(); first: open_paren,
second: close_paren,
},
no_fields_for_fn: NoFieldsForFnCall {
fields: fields
.into_iter()
.map(|field| field.span.until(field.expr.span))
.collect(),
},
}
.into_diagnostic(&self.sess.span_diagnostic);
replacement_err.emit();
err.multipart_suggestion( let old_err = mem::replace(err, replacement_err);
&format!("if `{name}` is a struct, use braces as delimiters"), old_err.cancel();
vec![
(open_paren, " { ".to_string()),
(close_paren, " }".to_string()),
],
Applicability::MaybeIncorrect,
);
err.multipart_suggestion(
&format!("if `{name}` is a function, use the arguments directly"),
fields
.into_iter()
.map(|field| (field.span.until(field.expr.span), String::new()))
.collect(),
Applicability::MaybeIncorrect,
);
err.emit();
} else { } else {
err.emit(); err.emit();
} }
@ -1537,15 +1533,19 @@ impl<'a> Parser<'a> {
&& (self.check_noexpect(&TokenKind::Comma) || self.check_noexpect(&TokenKind::Gt)) && (self.check_noexpect(&TokenKind::Comma) || self.check_noexpect(&TokenKind::Gt))
{ {
// We're probably inside of a `Path<'a>` that needs a turbofish // We're probably inside of a `Path<'a>` that needs a turbofish
self.sess.emit_err(UnexpectedTokenAfterLabel(self.token.span)); self.sess.emit_err(UnexpectedTokenAfterLabel {
span: self.token.span,
remove_label: None,
enclose_in_block: None,
});
consume_colon = false; consume_colon = false;
Ok(self.mk_expr_err(lo)) Ok(self.mk_expr_err(lo))
} else { } else {
// FIXME: use UnexpectedTokenAfterLabel, needs multipart suggestions let mut err = UnexpectedTokenAfterLabel {
let msg = "expected `while`, `for`, `loop` or `{` after a label"; span: self.token.span,
remove_label: None,
let mut err = self.struct_span_err(self.token.span, msg); enclose_in_block: None,
err.span_label(self.token.span, msg); };
// Continue as an expression in an effort to recover on `'label: non_block_expr`. // Continue as an expression in an effort to recover on `'label: non_block_expr`.
let expr = self.parse_expr().map(|expr| { let expr = self.parse_expr().map(|expr| {
@ -1572,28 +1572,15 @@ impl<'a> Parser<'a> {
// If there are no breaks that may use this label, suggest removing the label and // If there are no breaks that may use this label, suggest removing the label and
// recover to the unmodified expression. // recover to the unmodified expression.
if !found_labeled_breaks { if !found_labeled_breaks {
let msg = "consider removing the label"; err.remove_label = Some(lo.until(span));
err.span_suggestion_verbose(
lo.until(span),
msg,
"",
Applicability::MachineApplicable,
);
return expr; return expr;
} }
let sugg_msg = "consider enclosing expression in a block"; err.enclose_in_block = Some(UnexpectedTokenAfterLabelSugg {
let suggestions = vec![ left: span.shrink_to_lo(),
(span.shrink_to_lo(), "{ ".to_owned()), right: span.shrink_to_hi(),
(span.shrink_to_hi(), " }".to_owned()), });
];
err.multipart_suggestion_verbose(
sugg_msg,
suggestions,
Applicability::MachineApplicable,
);
// Replace `'label: non_block_expr` with `'label: {non_block_expr}` in order to suppress future errors about `break 'label`. // Replace `'label: non_block_expr` with `'label: {non_block_expr}` in order to suppress future errors about `break 'label`.
let stmt = self.mk_stmt(span, StmtKind::Expr(expr)); let stmt = self.mk_stmt(span, StmtKind::Expr(expr));
@ -1601,7 +1588,7 @@ impl<'a> Parser<'a> {
self.mk_expr(span, ExprKind::Block(blk, label)) self.mk_expr(span, ExprKind::Block(blk, label))
}); });
err.emit(); self.sess.emit_err(err);
expr expr
}?; }?;
@ -1672,19 +1659,13 @@ impl<'a> Parser<'a> {
// The value expression can be a labeled loop, see issue #86948, e.g.: // The value expression can be a labeled loop, see issue #86948, e.g.:
// `loop { break 'label: loop { break 'label 42; }; }` // `loop { break 'label: loop { break 'label 42; }; }`
let lexpr = self.parse_labeled_expr(label.take().unwrap(), true)?; let lexpr = self.parse_labeled_expr(label.take().unwrap(), true)?;
self.struct_span_err( self.sess.emit_err(LabeledLoopInBreak {
lexpr.span, span: lexpr.span,
"parentheses are required around this expression to avoid confusion with a labeled break expression", sub: LabeledLoopInBreakSub {
) first: lexpr.span.shrink_to_lo(),
.multipart_suggestion( second: lexpr.span.shrink_to_hi(),
"wrap the expression in parentheses", },
vec![ });
(lexpr.span.shrink_to_lo(), "(".to_string()),
(lexpr.span.shrink_to_hi(), ")".to_string()),
],
Applicability::MachineApplicable,
)
.emit();
Some(lexpr) Some(lexpr)
} else if self.token != token::OpenDelim(Delimiter::Brace) } else if self.token != token::OpenDelim(Delimiter::Brace)
|| !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL) || !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL)
@ -1756,9 +1737,8 @@ impl<'a> Parser<'a> {
}; };
if let Some(expr) = expr { if let Some(expr) = expr {
if matches!(expr.kind, ExprKind::Err) { if matches!(expr.kind, ExprKind::Err) {
let mut err = self let mut err = InvalidInterpolatedExpression { span: self.token.span }
.diagnostic() .into_diagnostic(&self.sess.span_diagnostic);
.struct_span_err(self.token.span, "invalid interpolated expression");
err.downgrade_to_delayed_bug(); err.downgrade_to_delayed_bug();
return err; return err;
} }
@ -1790,7 +1770,10 @@ impl<'a> Parser<'a> {
}); });
if let Some(token) = &recovered { if let Some(token) = &recovered {
self.bump(); self.bump();
self.error_float_lits_must_have_int_part(&token); self.sess.emit_err(FloatLiteralRequiresIntegerPart {
span: token.span,
correct: pprust::token_to_string(token).into_owned(),
});
} }
} }
@ -1818,13 +1801,6 @@ impl<'a> Parser<'a> {
} }
} }
fn error_float_lits_must_have_int_part(&self, token: &Token) {
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) { fn report_lit_error(&self, err: LitError, lit: token::Lit, span: Span) {
// Checks if `s` looks like i32 or u1234 etc. // Checks if `s` looks like i32 or u1234 etc.
fn looks_like_width_suffix(first_chars: &[char], s: &str) -> bool { fn looks_like_width_suffix(first_chars: &[char], s: &str) -> bool {
@ -1883,15 +1859,12 @@ impl<'a> Parser<'a> {
} }
} }
LitError::NonDecimalFloat(base) => { LitError::NonDecimalFloat(base) => {
let descr = match base { match base {
16 => "hexadecimal", 16 => self.sess.emit_err(HexadecimalFloatLiteralNotSupported { span }),
8 => "octal", 8 => self.sess.emit_err(OctalFloatLiteralNotSupported { span }),
2 => "binary", 2 => self.sess.emit_err(BinaryFloatLiteralNotSupported { span }),
_ => unreachable!(), _ => unreachable!(),
}; };
self.struct_span_err(span, &format!("{descr} float literal is not supported"))
.span_label(span, "not supported")
.emit();
} }
LitError::IntTooLarge => { LitError::IntTooLarge => {
self.sess.emit_err(IntLiteralTooLarge { span }); self.sess.emit_err(IntLiteralTooLarge { span });
@ -1964,14 +1937,13 @@ impl<'a> Parser<'a> {
let mut snapshot = self.create_snapshot_for_diagnostic(); let mut snapshot = self.create_snapshot_for_diagnostic();
match snapshot.parse_array_or_repeat_expr(Delimiter::Brace) { match snapshot.parse_array_or_repeat_expr(Delimiter::Brace) {
Ok(arr) => { Ok(arr) => {
let hi = snapshot.prev_token.span; self.sess.emit_err(ArrayBracketsInsteadOfSpaces {
self.struct_span_err(arr.span, "this is a block expression, not an array") span: arr.span,
.multipart_suggestion( sub: ArrayBracketsInsteadOfSpacesSugg {
"to make an array, use square brackets instead of curly braces", left: lo,
vec![(lo, "[".to_owned()), (hi, "]".to_owned())], right: snapshot.prev_token.span,
Applicability::MaybeIncorrect, },
) });
.emit();
self.restore_snapshot(snapshot); self.restore_snapshot(snapshot);
Some(self.mk_expr_err(arr.span)) Some(self.mk_expr_err(arr.span))
@ -2515,39 +2487,22 @@ impl<'a> Parser<'a> {
self.bump(); // `;` self.bump(); // `;`
let mut stmts = let mut stmts =
vec![self.mk_stmt(first_expr.span, ast::StmtKind::Expr(first_expr.clone()))]; vec![self.mk_stmt(first_expr.span, ast::StmtKind::Expr(first_expr.clone()))];
let err = |this: &mut Parser<'_>, stmts: Vec<ast::Stmt>| { let err = |this: &Parser<'_>, stmts: Vec<ast::Stmt>| {
let span = stmts[0].span.to(stmts[stmts.len() - 1].span); let span = stmts[0].span.to(stmts[stmts.len() - 1].span);
let mut err = this.struct_span_err(span, "`match` arm body without braces");
let (these, s, are) = this.sess.emit_err(MatchArmBodyWithoutBraces {
if stmts.len() > 1 { ("these", "s", "are") } else { ("this", "", "is") }; statements: span,
err.span_label( arrow: arrow_span,
span, num_statements: stmts.len(),
&format!( sub: if stmts.len() > 1 {
"{these} statement{s} {are} not surrounded by a body", MatchArmBodyWithoutBracesSugg::AddBraces {
these = these, left: span.shrink_to_lo(),
s = s, right: span.shrink_to_hi(),
are = are }
), } else {
); MatchArmBodyWithoutBracesSugg::UseComma { semicolon: semi_sp }
err.span_label(arrow_span, "while parsing the `match` arm starting here"); },
if stmts.len() > 1 { });
err.multipart_suggestion(
&format!("surround the statement{s} with a body"),
vec![
(span.shrink_to_lo(), "{ ".to_string()),
(span.shrink_to_hi(), " }".to_string()),
],
Applicability::MachineApplicable,
);
} else {
err.span_suggestion(
semi_sp,
"use a comma to end a `match` arm expression",
",",
Applicability::MachineApplicable,
);
}
err.emit();
this.mk_expr_err(span) this.mk_expr_err(span)
}; };
// We might have either a `,` -> `;` typo, or a block without braces. We need // We might have either a `,` -> `;` typo, or a block without braces. We need
@ -2836,23 +2791,19 @@ impl<'a> Parser<'a> {
let expr = self.parse_struct_expr(qself.cloned(), path.clone(), true); let expr = self.parse_struct_expr(qself.cloned(), path.clone(), true);
if let (Ok(expr), false) = (&expr, struct_allowed) { if let (Ok(expr), false) = (&expr, struct_allowed) {
// This is a struct literal, but we don't can't accept them here. // This is a struct literal, but we don't can't accept them here.
self.error_struct_lit_not_allowed_here(path.span, expr.span); self.sess.emit_err(StructLiteralNotAllowedHere {
span: expr.span,
sub: StructLiteralNotAllowedHereSugg {
left: path.span.shrink_to_lo(),
right: expr.span.shrink_to_hi(),
},
});
} }
return Some(expr); return Some(expr);
} }
None None
} }
fn error_struct_lit_not_allowed_here(&self, lo: Span, sp: Span) {
self.struct_span_err(sp, "struct literals are not allowed here")
.multipart_suggestion(
"surround the struct literal with parentheses",
vec![(lo.shrink_to_lo(), "(".to_string()), (sp.shrink_to_hi(), ")".to_string())],
Applicability::MachineApplicable,
)
.emit();
}
pub(super) fn parse_struct_fields( pub(super) fn parse_struct_fields(
&mut self, &mut self,
pth: ast::Path, pth: ast::Path,

View File

@ -32,7 +32,8 @@ use rustc_ast_pretty::pprust;
use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::fx::FxHashMap;
use rustc_errors::PResult; use rustc_errors::PResult;
use rustc_errors::{ use rustc_errors::{
struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed, FatalError, MultiSpan, struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed, FatalError, IntoDiagnostic,
MultiSpan,
}; };
use rustc_session::parse::ParseSess; use rustc_session::parse::ParseSess;
use rustc_span::source_map::{Span, DUMMY_SP}; use rustc_span::source_map::{Span, DUMMY_SP};
@ -41,6 +42,8 @@ use rustc_span::symbol::{kw, sym, Ident, Symbol};
use std::ops::Range; use std::ops::Range;
use std::{cmp, mem, slice}; use std::{cmp, mem, slice};
use self::diagnostics::{MismatchedClosingDelimiter, NonStringAbiLiteral};
bitflags::bitflags! { bitflags::bitflags! {
struct Restrictions: u8 { struct Restrictions: u8 {
const STMT_EXPR = 1 << 0; const STMT_EXPR = 1 << 0;
@ -1384,14 +1387,7 @@ impl<'a> Parser<'a> {
Err(Some(lit)) => match lit.kind { Err(Some(lit)) => match lit.kind {
ast::LitKind::Err => None, ast::LitKind::Err => None,
_ => { _ => {
self.struct_span_err(lit.span, "non-string ABI literal") self.sess.emit_err(NonStringAbiLiteral { span: lit.span });
.span_suggestion(
lit.span,
"specify the ABI with a string literal",
"\"C\"",
Applicability::MaybeIncorrect,
)
.emit();
None None
} }
}, },
@ -1432,25 +1428,18 @@ pub(crate) fn make_unclosed_delims_error(
// `None` here means an `Eof` was found. We already emit those errors elsewhere, we add them to // `None` here means an `Eof` was found. We already emit those errors elsewhere, we add them to
// `unmatched_braces` only for error recovery in the `Parser`. // `unmatched_braces` only for error recovery in the `Parser`.
let found_delim = unmatched.found_delim?; let found_delim = unmatched.found_delim?;
let span: MultiSpan = if let Some(sp) = unmatched.unclosed_span { let mut spans = vec![unmatched.found_span];
vec![unmatched.found_span, sp].into()
} else {
unmatched.found_span.into()
};
let mut err = sess.span_diagnostic.struct_span_err(
span,
&format!(
"mismatched closing delimiter: `{}`",
pprust::token_kind_to_string(&token::CloseDelim(found_delim)),
),
);
err.span_label(unmatched.found_span, "mismatched closing delimiter");
if let Some(sp) = unmatched.candidate_span {
err.span_label(sp, "closing delimiter possibly meant for this");
}
if let Some(sp) = unmatched.unclosed_span { if let Some(sp) = unmatched.unclosed_span {
err.span_label(sp, "unclosed delimiter"); spans.push(sp);
};
let err = MismatchedClosingDelimiter {
spans,
delimiter: pprust::token_kind_to_string(&token::CloseDelim(found_delim)).to_string(),
unmatched: unmatched.found_span,
opening_candidate: unmatched.candidate_span,
unclosed: unmatched.unclosed_span,
} }
.into_diagnostic(&sess.span_diagnostic);
Some(err) Some(err)
} }

View File

@ -29,7 +29,7 @@ fn main() {
< //~ ERROR `<` is interpreted as a start of generic < //~ ERROR `<` is interpreted as a start of generic
5); 5);
println!("{}", a as usize << long_name); //~ ERROR `<` is interpreted as a start of generic println!("{}", a as usize << long_name); //~ ERROR `<<` is interpreted as a start of generic
println!("{}", a: &mut 4); //~ ERROR expected type, found `4` println!("{}", a: &mut 4); //~ ERROR expected type, found `4`
} }

View File

@ -95,7 +95,7 @@ LL |
LL ~ usize) LL ~ usize)
| |
error: `<` is interpreted as a start of generic arguments for `usize`, not a shift error: `<<` is interpreted as a start of generic arguments for `usize`, not a shift
--> $DIR/issue-22644.rs:32:31 --> $DIR/issue-22644.rs:32:31
| |
LL | println!("{}", a as usize << long_name); LL | println!("{}", a as usize << long_name);