diff options
Diffstat (limited to 'compiler/rustc_parse')
-rw-r--r-- | compiler/rustc_parse/messages.ftl | 10 | ||||
-rw-r--r-- | compiler/rustc_parse/src/errors.rs | 37 | ||||
-rw-r--r-- | compiler/rustc_parse/src/parser/diagnostics.rs | 31 | ||||
-rw-r--r-- | compiler/rustc_parse/src/parser/expr.rs | 65 | ||||
-rw-r--r-- | compiler/rustc_parse/src/parser/generics.rs | 5 | ||||
-rw-r--r-- | compiler/rustc_parse/src/parser/item.rs | 55 | ||||
-rw-r--r-- | compiler/rustc_parse/src/parser/stmt.rs | 23 |
7 files changed, 205 insertions, 21 deletions
diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index cd296dca133..2d0f466e236 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -257,6 +257,10 @@ parse_invalid_literal_suffix_on_tuple_index = suffixes on a tuple index are inva .tuple_exception_line_2 = on proc macros, you'll want to use `syn::Index::from` or `proc_macro::Literal::*_unsuffixed` for code that will desugar to tuple field access .tuple_exception_line_3 = see issue #60210 <https://github.com/rust-lang/rust/issues/60210> for more information +parse_expected_builtin_ident = expected identifier after `builtin #` + +parse_unknown_builtin_construct = unknown `builtin #` construct `{$name}` + parse_non_string_abi_literal = non-string ABI literal .suggestion = specify the ABI with a string literal @@ -339,6 +343,7 @@ parse_expected_identifier = expected identifier parse_sugg_escape_identifier = escape `{$ident_name}` to use it as an identifier parse_sugg_remove_comma = remove this comma +parse_sugg_add_let_for_stmt = you might have meant to introduce a new binding parse_expected_semi_found_reserved_identifier_str = expected `;`, found reserved identifier `{$token}` parse_expected_semi_found_keyword_str = expected `;`, found keyword `{$token}` @@ -473,6 +478,11 @@ parse_missing_for_in_trait_impl = missing `for` in a trait impl parse_expected_trait_in_trait_impl_found_type = expected a trait, found type +parse_extra_impl_keyword_in_trait_impl = unexpected `impl` keyword + .suggestion = remove the extra `impl` + .note = this is parsed as an `impl Trait` type, but a trait is expected at this position + + parse_non_item_in_item_list = non-item in item list .suggestion_use_const_not_let = consider using `const` instead of `let` for associated const .label_list_start = item list starts here diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 010a13aefa4..84494eab855 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -907,6 +907,18 @@ pub(crate) struct SuggRemoveComma { } #[derive(Subdiagnostic)] +#[suggestion( + parse_sugg_add_let_for_stmt, + style = "verbose", + applicability = "maybe-incorrect", + code = "let " +)] +pub(crate) struct SuggAddMissingLetStmt { + #[primary_span] + pub span: Span, +} + +#[derive(Subdiagnostic)] pub(crate) enum ExpectedIdentifierFound { #[label(parse_expected_identifier_found_reserved_identifier)] ReservedIdentifier(#[primary_span] Span), @@ -1508,6 +1520,16 @@ pub(crate) struct ExpectedTraitInTraitImplFoundType { } #[derive(Diagnostic)] +#[diag(parse_extra_impl_keyword_in_trait_impl)] +pub(crate) struct ExtraImplKeywordInTraitImpl { + #[primary_span] + #[suggestion(code = "", applicability = "maybe-incorrect")] + pub extra_impl_kw: Span, + #[note] + pub impl_trait_span: Span, +} + +#[derive(Diagnostic)] #[diag(parse_bounds_not_allowed_on_trait_aliases)] pub(crate) struct BoundsNotAllowedOnTraitAliases { #[primary_span] @@ -2644,3 +2666,18 @@ pub(crate) struct MalformedCfgAttr { pub span: Span, pub sugg: &'static str, } + +#[derive(Diagnostic)] +#[diag(parse_unknown_builtin_construct)] +pub(crate) struct UnknownBuiltinConstruct { + #[primary_span] + pub span: Span, + pub name: Symbol, +} + +#[derive(Diagnostic)] +#[diag(parse_expected_builtin_ident)] +pub(crate) struct ExpectedBuiltinIdent { + #[primary_span] + pub span: Span, +} diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 36883bd2172..3002f23da75 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -13,7 +13,7 @@ use crate::errors::{ IncorrectUseOfAwait, ParenthesesInForHead, ParenthesesInForHeadSugg, PatternMethodParamWithoutBody, QuestionMarkInType, QuestionMarkInTypeSugg, SelfParamNotFirst, StructLiteralBodyWithoutPath, StructLiteralBodyWithoutPathSugg, StructLiteralNeedingParens, - StructLiteralNeedingParensSugg, SuggEscapeIdentifier, SuggRemoveComma, + StructLiteralNeedingParensSugg, SuggAddMissingLetStmt, SuggEscapeIdentifier, SuggRemoveComma, UnexpectedConstInGenericParam, UnexpectedConstParamDeclaration, UnexpectedConstParamDeclarationSugg, UnmatchedAngleBrackets, UseEqInstead, }; @@ -32,8 +32,8 @@ use rustc_ast::{ use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{ - pluralize, Applicability, Diagnostic, DiagnosticBuilder, DiagnosticMessage, ErrorGuaranteed, - FatalError, Handler, IntoDiagnostic, MultiSpan, PResult, + pluralize, AddToDiagnostic, Applicability, Diagnostic, DiagnosticBuilder, DiagnosticMessage, + ErrorGuaranteed, FatalError, Handler, IntoDiagnostic, MultiSpan, PResult, }; use rustc_session::errors::ExprParenthesesNeeded; use rustc_span::source_map::Spanned; @@ -1006,6 +1006,31 @@ impl<'a> Parser<'a> { Err(e) } + /// Suggest add the missing `let` before the identifier in stmt + /// `a: Ty = 1` -> `let a: Ty = 1` + pub(super) fn suggest_add_missing_let_for_stmt( + &mut self, + err: &mut DiagnosticBuilder<'a, ErrorGuaranteed>, + ) { + if self.token == token::Colon { + let prev_span = self.prev_token.span.shrink_to_lo(); + let snapshot = self.create_snapshot_for_diagnostic(); + self.bump(); + match self.parse_ty() { + Ok(_) => { + if self.token == token::Eq { + let sugg = SuggAddMissingLetStmt { span: prev_span }; + sugg.add_to_diagnostic(err); + } + } + Err(e) => { + e.cancel(); + } + } + self.restore_snapshot(snapshot); + } + } + /// Check to see if a pair of chained operators looks like an attempt at chained comparison, /// e.g. `1 < x <= 3`. If so, suggest either splitting the comparison into two, or /// parenthesising the leftmost comparison. diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 018eddea4b0..ee712a8e1b5 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1180,6 +1180,10 @@ impl<'a> Parser<'a> { self.restore_snapshot(snapshot); let close_paren = self.prev_token.span; let span = lo.to(close_paren); + // filter shorthand fields + let fields: Vec<_> = + fields.into_iter().filter(|field| !field.is_shorthand).collect(); + if !fields.is_empty() && // `token.kind` should not be compared here. // This is because the `snapshot.token.kind` is treated as the same as @@ -1300,6 +1304,8 @@ impl<'a> Parser<'a> { }) } else if self.check(&token::OpenDelim(Delimiter::Bracket)) { self.parse_expr_array_or_repeat(Delimiter::Bracket) + } else if self.is_builtin() { + self.parse_expr_builtin() } else if self.check_path() { self.parse_expr_path_start() } else if self.check_keyword(kw::Move) @@ -1766,6 +1772,61 @@ impl<'a> Parser<'a> { self.maybe_recover_from_bad_qpath(expr) } + /// Parse `builtin # ident(args,*)`. + fn parse_expr_builtin(&mut self) -> PResult<'a, P<Expr>> { + self.parse_builtin(|this, lo, ident| { + if ident.name == sym::offset_of { + return Ok(Some(this.parse_expr_offset_of(lo)?)); + } + + Ok(None) + }) + } + + pub(crate) fn parse_builtin<T>( + &mut self, + parse: impl FnOnce(&mut Parser<'a>, Span, Ident) -> PResult<'a, Option<T>>, + ) -> PResult<'a, T> { + let lo = self.token.span; + + self.bump(); // `builtin` + self.bump(); // `#` + + let Some((ident, false)) = self.token.ident() else { + let err = errors::ExpectedBuiltinIdent { span: self.token.span } + .into_diagnostic(&self.sess.span_diagnostic); + return Err(err); + }; + self.sess.gated_spans.gate(sym::builtin_syntax, ident.span); + self.bump(); + + self.expect(&TokenKind::OpenDelim(Delimiter::Parenthesis))?; + let ret = if let Some(res) = parse(self, lo, ident)? { + Ok(res) + } else { + let err = errors::UnknownBuiltinConstruct { span: lo.to(ident.span), name: ident.name } + .into_diagnostic(&self.sess.span_diagnostic); + return Err(err); + }; + self.expect(&TokenKind::CloseDelim(Delimiter::Parenthesis))?; + + ret + } + + pub(crate) fn parse_expr_offset_of(&mut self, lo: Span) -> PResult<'a, P<Expr>> { + let container = self.parse_ty()?; + self.expect(&TokenKind::Comma)?; + + let seq_sep = SeqSep { sep: Some(token::Dot), trailing_sep_allowed: false }; + let (fields, _trailing, _recovered) = self.parse_seq_to_before_end( + &TokenKind::CloseDelim(Delimiter::Parenthesis), + seq_sep, + Parser::parse_field_name, + )?; + let span = lo.to(self.token.span); + Ok(self.mk_expr(span, ExprKind::OffsetOf(container, fields.to_vec().into()))) + } + /// Returns a string literal if the next token is a string literal. /// In case of error returns `Some(lit)` if the next token is a literal with a wrong kind, /// and returns `None` if the next token is not literal at all. @@ -2835,6 +2896,10 @@ impl<'a> Parser<'a> { }) } + pub(crate) fn is_builtin(&self) -> bool { + self.token.is_keyword(kw::Builtin) && self.look_ahead(1, |t| *t == token::Pound) + } + /// Parses a `try {...}` expression (`try` token already eaten). fn parse_try_block(&mut self, span_lo: Span) -> PResult<'a, P<Expr>> { let (attrs, body) = self.parse_inner_attrs_and_block()?; diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs index e6d0f9fbc76..cd779b0b43e 100644 --- a/compiler/rustc_parse/src/parser/generics.rs +++ b/compiler/rustc_parse/src/parser/generics.rs @@ -453,6 +453,8 @@ impl<'a> Parser<'a> { // `<` (LIFETIME|IDENT) `:` - generic parameter with bounds // `<` (LIFETIME|IDENT) `=` - generic parameter with a default // `<` const - generic const parameter + // `<` IDENT `?` - RECOVERY for `impl<T ?Bound` missing a `:`, meant to + // avoid the `T?` to `Option<T>` recovery for types. // The only truly ambiguous case is // `<` IDENT `>` `::` IDENT ... // we disambiguate it in favor of generics (`impl<T> ::absolute::Path<T> { ... }`) @@ -463,6 +465,9 @@ impl<'a> Parser<'a> { || self.look_ahead(start + 1, |t| t.is_lifetime() || t.is_ident()) && self.look_ahead(start + 2, |t| { matches!(t.kind, token::Gt | token::Comma | token::Colon | token::Eq) + // Recovery-only branch -- this could be removed, + // since it only affects diagnostics currently. + || matches!(t.kind, token::Question) }) || self.is_keyword_ahead(start + 1, &[kw::Const])) } diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 6ca88200dc5..dc18d400f1e 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -265,6 +265,9 @@ impl<'a> Parser<'a> { // UNION ITEM self.bump(); // `union` self.parse_item_union()? + } else if self.is_builtin() { + // BUILTIN# ITEM + return self.parse_item_builtin(); } else if self.eat_keyword(kw::Macro) { // MACROS 2.0 ITEM self.parse_item_decl_macro(lo)? @@ -434,6 +437,11 @@ impl<'a> Parser<'a> { } } + fn parse_item_builtin(&mut self) -> PResult<'a, Option<ItemInfo>> { + // To be expanded + return Ok(None); + } + /// Parses an item macro, e.g., `item!();`. fn parse_item_macro(&mut self, vis: &Visibility) -> PResult<'a, MacCall> { let path = self.parse_path(PathStyle::Mod)?; // `foo::bar` @@ -595,10 +603,24 @@ impl<'a> Parser<'a> { let path = match ty_first.kind { // This notably includes paths passed through `ty` macro fragments (#46438). TyKind::Path(None, path) => path, - _ => { - self.sess.emit_err(errors::ExpectedTraitInTraitImplFoundType { - span: ty_first.span, - }); + other => { + if let TyKind::ImplTrait(_, bounds) = other + && let [bound] = bounds.as_slice() + { + // Suggest removing extra `impl` keyword: + // `impl<T: Default> impl Default for Wrapper<T>` + // ^^^^^ + let extra_impl_kw = ty_first.span.until(bound.span()); + self.sess + .emit_err(errors::ExtraImplKeywordInTraitImpl { + extra_impl_kw, + impl_trait_span: ty_first.span + }); + } else { + self.sess.emit_err(errors::ExpectedTraitInTraitImplFoundType { + span: ty_first.span, + }); + } err_path(ty_first.span) } }; @@ -1262,6 +1284,7 @@ impl<'a> Parser<'a> { } } + let prev_span = self.prev_token.span; let id = self.parse_ident()?; let mut generics = self.parse_generics()?; generics.where_clause = self.parse_where_clause()?; @@ -1273,10 +1296,28 @@ impl<'a> Parser<'a> { (thin_vec![], false) } else { self.parse_delim_comma_seq(Delimiter::Brace, |p| p.parse_enum_variant()).map_err( - |mut e| { - e.span_label(id.span, "while parsing this enum"); + |mut err| { + err.span_label(id.span, "while parsing this enum"); + if self.token == token::Colon { + let snapshot = self.create_snapshot_for_diagnostic(); + self.bump(); + match self.parse_ty() { + Ok(_) => { + err.span_suggestion_verbose( + prev_span, + "perhaps you meant to use `struct` here", + "struct".to_string(), + Applicability::MaybeIncorrect, + ); + } + Err(e) => { + e.cancel(); + } + } + self.restore_snapshot(snapshot); + } self.recover_stmt(); - e + err }, )? }; diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index 1c17de337e8..03279124177 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -90,7 +90,11 @@ impl<'a> Parser<'a> { attrs, errors::InvalidVariableDeclarationSub::UseLetNotVar, )? - } else if self.check_path() && !self.token.is_qpath_start() && !self.is_path_start_item() { + } else if self.check_path() + && !self.token.is_qpath_start() + && !self.is_path_start_item() + && !self.is_builtin() + { // We have avoided contextual keywords like `union`, items with `crate` visibility, // or `auto trait` items. We aim to parse an arbitrary path `a::b` but not something // that starts like a path (1 token), but it fact not a path. @@ -99,7 +103,13 @@ impl<'a> Parser<'a> { ForceCollect::Yes => { self.collect_tokens_no_attrs(|this| this.parse_stmt_path_start(lo, attrs))? } - ForceCollect::No => self.parse_stmt_path_start(lo, attrs)?, + ForceCollect::No => match self.parse_stmt_path_start(lo, attrs) { + Ok(stmt) => stmt, + Err(mut err) => { + self.suggest_add_missing_let_for_stmt(&mut err); + return Err(err); + } + }, } } else if let Some(item) = self.parse_item_common( attrs.clone(), @@ -555,7 +565,6 @@ impl<'a> Parser<'a> { if self.token == token::Colon { // if next token is following a colon, it's likely a path // and we can suggest a path separator - let ident_span = self.prev_token.span; self.bump(); if self.token.span.lo() == self.prev_token.span.hi() { err.span_suggestion_verbose( @@ -565,14 +574,6 @@ impl<'a> Parser<'a> { Applicability::MaybeIncorrect, ); } - if self.look_ahead(1, |token| token == &token::Eq) { - err.span_suggestion_verbose( - ident_span.shrink_to_lo(), - "you might have meant to introduce a new binding", - "let ", - Applicability::MaybeIncorrect, - ); - } if self.sess.unstable_features.is_nightly_build() { // FIXME(Nilstrieb): Remove this again after a few months. err.note("type ascription syntax has been removed, see issue #101728 <https://github.com/rust-lang/rust/issues/101728>"); |