diff options
| author | Mazdak Farrokhzad <twingoow@gmail.com> | 2019-08-29 05:32:48 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2019-08-29 05:32:48 +0200 |
| commit | 52c3846d51b8efabebb02bd2587a87905e068290 (patch) | |
| tree | d34b77611677dd3f0e9081a4417262ff3abd1307 /src/libsyntax/parse/parser | |
| parent | eb4ac32c5944e5c690f8731a8b25eaae8cbad0a7 (diff) | |
| parent | 42e895d4d99ec7724f3efd632f52170f3f99a5aa (diff) | |
| download | rust-52c3846d51b8efabebb02bd2587a87905e068290.tar.gz rust-52c3846d51b8efabebb02bd2587a87905e068290.zip | |
Rollup merge of #63945 - Centril:recover-mut-pat, r=estebank
Recover `mut $pat` and other improvements - Recover on e.g. `mut Foo(x, y)` and suggest `Foo(mut x, mut y)`. Fixes https://github.com/rust-lang/rust/issues/63764. - Recover on e.g. `let mut mut x;` - Recover on e.g. `let keyword` and `let keyword(...)`. - Cleanups in `token.rs` with `fn is_non_raw_ident_where` and friends.
Diffstat (limited to 'src/libsyntax/parse/parser')
| -rw-r--r-- | src/libsyntax/parse/parser/pat.rs | 155 | ||||
| -rw-r--r-- | src/libsyntax/parse/parser/path.rs | 2 |
2 files changed, 127 insertions, 30 deletions
diff --git a/src/libsyntax/parse/parser/pat.rs b/src/libsyntax/parse/parser/pat.rs index 4c6b6ce38f6..823f880337d 100644 --- a/src/libsyntax/parse/parser/pat.rs +++ b/src/libsyntax/parse/parser/pat.rs @@ -4,6 +4,7 @@ use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole}; use crate::ptr::P; use crate::ast::{self, Attribute, Pat, PatKind, FieldPat, RangeEnd, RangeSyntax, Mac}; use crate::ast::{BindingMode, Ident, Mutability, Path, QSelf, Expr, ExprKind}; +use crate::mut_visit::{noop_visit_pat, MutVisitor}; use crate::parse::token::{self}; use crate::print::pprust; use crate::source_map::{respan, Span, Spanned}; @@ -273,7 +274,7 @@ impl<'a> Parser<'a> { // Parse _ PatKind::Wild } else if self.eat_keyword(kw::Mut) { - self.recover_pat_ident_mut_first()? + self.parse_pat_ident_mut()? } else if self.eat_keyword(kw::Ref) { // Parse ref ident @ pat / ref mut ident @ pat let mutbl = self.parse_mutability(); @@ -281,13 +282,12 @@ impl<'a> Parser<'a> { } else if self.eat_keyword(kw::Box) { // Parse `box pat` PatKind::Box(self.parse_pat_with_range_pat(false, None)?) - } else if self.token.is_ident() && !self.token.is_reserved_ident() && - self.parse_as_ident() { + } else if self.can_be_ident_pat() { // Parse `ident @ pat` // This can give false positives and parse nullary enums, // they are dealt with later in resolve. self.parse_pat_ident(BindingMode::ByValue(Mutability::Immutable))? - } else if self.token.is_path_start() { + } else if self.is_start_of_pat_with_path() { // Parse pattern starting with a path let (qself, path) = if self.eat_lt() { // Parse a qualified path @@ -384,24 +384,108 @@ impl<'a> Parser<'a> { }) } + /// Parse a mutable binding with the `mut` token already eaten. + fn parse_pat_ident_mut(&mut self) -> PResult<'a, PatKind> { + let mut_span = self.prev_span; + + if self.eat_keyword(kw::Ref) { + return self.recover_mut_ref_ident(mut_span) + } + + self.recover_additional_muts(); + + // Make sure we don't allow e.g. `let mut $p;` where `$p:pat`. + if let token::Interpolated(ref nt) = self.token.kind { + if let token::NtPat(_) = **nt { + self.expected_ident_found().emit(); + } + } + + // Parse the pattern we hope to be an identifier. + let mut pat = self.parse_pat(Some("identifier"))?; + + // Add `mut` to any binding in the parsed pattern. + let changed_any_binding = Self::make_all_value_bindings_mutable(&mut pat); + + // Unwrap; If we don't have `mut $ident`, error. + let pat = pat.into_inner(); + match &pat.node { + PatKind::Ident(..) => {} + _ => self.ban_mut_general_pat(mut_span, &pat, changed_any_binding), + } + + Ok(pat.node) + } + /// Recover on `mut ref? ident @ pat` and suggest /// that the order of `mut` and `ref` is incorrect. - fn recover_pat_ident_mut_first(&mut self) -> PResult<'a, PatKind> { - let mutref_span = self.prev_span.to(self.token.span); - let binding_mode = if self.eat_keyword(kw::Ref) { - self.struct_span_err(mutref_span, "the order of `mut` and `ref` is incorrect") - .span_suggestion( - mutref_span, - "try switching the order", - "ref mut".into(), - Applicability::MachineApplicable - ) - .emit(); - BindingMode::ByRef(Mutability::Mutable) + fn recover_mut_ref_ident(&mut self, lo: Span) -> PResult<'a, PatKind> { + let mutref_span = lo.to(self.prev_span); + self.struct_span_err(mutref_span, "the order of `mut` and `ref` is incorrect") + .span_suggestion( + mutref_span, + "try switching the order", + "ref mut".into(), + Applicability::MachineApplicable + ) + .emit(); + + self.parse_pat_ident(BindingMode::ByRef(Mutability::Mutable)) + } + + /// Turn all by-value immutable bindings in a pattern into mutable bindings. + /// Returns `true` if any change was made. + fn make_all_value_bindings_mutable(pat: &mut P<Pat>) -> bool { + struct AddMut(bool); + impl MutVisitor for AddMut { + fn visit_pat(&mut self, pat: &mut P<Pat>) { + if let PatKind::Ident(BindingMode::ByValue(ref mut m @ Mutability::Immutable), ..) + = pat.node + { + *m = Mutability::Mutable; + self.0 = true; + } + noop_visit_pat(pat, self); + } + } + + let mut add_mut = AddMut(false); + add_mut.visit_pat(pat); + add_mut.0 + } + + /// Error on `mut $pat` where `$pat` is not an ident. + fn ban_mut_general_pat(&self, lo: Span, pat: &Pat, changed_any_binding: bool) { + let span = lo.to(pat.span); + let fix = pprust::pat_to_string(&pat); + let (problem, suggestion) = if changed_any_binding { + ("`mut` must be attached to each individual binding", "add `mut` to each binding") } else { - BindingMode::ByValue(Mutability::Mutable) + ("`mut` must be followed by a named binding", "remove the `mut` prefix") }; - self.parse_pat_ident(binding_mode) + self.struct_span_err(span, problem) + .span_suggestion(span, suggestion, fix, Applicability::MachineApplicable) + .note("`mut` may be followed by `variable` and `variable @ pattern`") + .emit() + } + + /// Eat any extraneous `mut`s and error + recover if we ate any. + fn recover_additional_muts(&mut self) { + let lo = self.token.span; + while self.eat_keyword(kw::Mut) {} + if lo == self.token.span { + return; + } + + let span = lo.to(self.prev_span); + self.struct_span_err(span, "`mut` on a binding may not be repeated") + .span_suggestion( + span, + "remove the additional `mut`s", + String::new(), + Applicability::MachineApplicable, + ) + .emit(); } /// Parse macro invocation @@ -479,17 +563,6 @@ impl<'a> Parser<'a> { Err(err) } - // Helper function to decide whether to parse as ident binding - // or to try to do something more complex like range patterns. - fn parse_as_ident(&mut self) -> bool { - self.look_ahead(1, |t| match t.kind { - token::OpenDelim(token::Paren) | token::OpenDelim(token::Brace) | - token::DotDotDot | token::DotDotEq | token::DotDot | - token::ModSep | token::Not => false, - _ => true, - }) - } - /// Is the current token suitable as the start of a range patterns end? fn is_pat_range_end_start(&self) -> bool { self.token.is_path_start() // e.g. `MY_CONST`; @@ -563,6 +636,30 @@ impl<'a> Parser<'a> { } } + /// Is this the start of a pattern beginning with a path? + fn is_start_of_pat_with_path(&mut self) -> bool { + self.check_path() + // Just for recovery (see `can_be_ident`). + || self.token.is_ident() && !self.token.is_bool_lit() && !self.token.is_keyword(kw::In) + } + + /// Would `parse_pat_ident` be appropriate here? + fn can_be_ident_pat(&mut self) -> bool { + self.check_ident() + && !self.token.is_bool_lit() // Avoid `true` or `false` as a binding as it is a literal. + && !self.token.is_path_segment_keyword() // Avoid e.g. `Self` as it is a path. + // Avoid `in`. Due to recovery in the list parser this messes with `for ( $pat in $expr )`. + && !self.token.is_keyword(kw::In) + && self.look_ahead(1, |t| match t.kind { // Try to do something more complex? + token::OpenDelim(token::Paren) // A tuple struct pattern. + | token::OpenDelim(token::Brace) // A struct pattern. + | token::DotDotDot | token::DotDotEq | token::DotDot // A range pattern. + | token::ModSep // A tuple / struct variant pattern. + | token::Not => false, // A macro expanding to a pattern. + _ => true, + }) + } + /// Parses `ident` or `ident @ pat`. /// Used by the copy foo and ref foo patterns to give a good /// error message when parsing mistakes like `ref foo(a, b)`. diff --git a/src/libsyntax/parse/parser/path.rs b/src/libsyntax/parse/parser/path.rs index 3eb4d45045a..d4b13cc2e01 100644 --- a/src/libsyntax/parse/parser/path.rs +++ b/src/libsyntax/parse/parser/path.rs @@ -423,7 +423,7 @@ impl<'a> Parser<'a> { // FIXME(const_generics): to distinguish between idents for types and consts, // we should introduce a GenericArg::Ident in the AST and distinguish when // lowering to the HIR. For now, idents for const args are not permitted. - if self.token.is_keyword(kw::True) || self.token.is_keyword(kw::False) { + if self.token.is_bool_lit() { self.parse_literal_maybe_minus()? } else { return Err( |
