diff options
| author | bors <bors@rust-lang.org> | 2018-04-10 03:27:43 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2018-04-10 03:27:43 +0000 |
| commit | 5d7f892356906ed8f7f2c7f2a878b9447e49d4f1 (patch) | |
| tree | 2736c7c9baae7abacb552cc55a049dbfd0a42e0a /src/libsyntax/parse | |
| parent | a8a8d6b5bf3ed8ca61adca172252bea7d1f1166e (diff) | |
| parent | ba0dd8eb026e2dcff27a7ee3b29514a53cc5c1d9 (diff) | |
| download | rust-5d7f892356906ed8f7f2c7f2a878b9447e49d4f1.tar.gz rust-5d7f892356906ed8f7f2c7f2a878b9447e49d4f1.zip | |
Auto merge of #49258 - zackmdavis:not_going_to_recover, r=petrochenkov
suggest `!` for erroneous identifier `not`  This supersedes #48858. r? @petrochenkov
Diffstat (limited to 'src/libsyntax/parse')
| -rw-r--r-- | src/libsyntax/parse/parser.rs | 48 | ||||
| -rw-r--r-- | src/libsyntax/parse/token.rs | 11 |
2 files changed, 57 insertions, 2 deletions
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index e6da5bcaa3a..61de50e8e6a 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -2830,7 +2830,48 @@ impl<'a> Parser<'a> { let (span, e) = self.interpolated_or_expr_span(e)?; (lo.to(span), ExprKind::Box(e)) } - _ => return self.parse_dot_or_call_expr(Some(attrs)) + token::Ident(..) if self.token.is_ident_named("not") => { + // `not` is just an ordinary identifier in Rust-the-language, + // but as `rustc`-the-compiler, we can issue clever diagnostics + // for confused users who really want to say `!` + let token_cannot_continue_expr = |t: &token::Token| match *t { + // These tokens can start an expression after `!`, but + // can't continue an expression after an ident + token::Ident(ident, is_raw) => token::ident_can_begin_expr(ident, is_raw), + token::Literal(..) | token::Pound => true, + token::Interpolated(ref nt) => match nt.0 { + token::NtIdent(..) | token::NtExpr(..) | + token::NtBlock(..) | token::NtPath(..) => true, + _ => false, + }, + _ => false + }; + let cannot_continue_expr = self.look_ahead(1, token_cannot_continue_expr); + if cannot_continue_expr { + self.bump(); + // Emit the error ... + let mut err = self.diagnostic() + .struct_span_err(self.span, + &format!("unexpected {} after identifier", + self.this_token_descr())); + // span the `not` plus trailing whitespace to avoid + // trailing whitespace after the `!` in our suggestion + let to_replace = self.sess.codemap() + .span_until_non_whitespace(lo.to(self.span)); + err.span_suggestion_short(to_replace, + "use `!` to perform logical negation", + "!".to_owned()); + err.emit(); + // —and recover! (just as if we were in the block + // for the `token::Not` arm) + let e = self.parse_prefix_expr(None); + let (span, e) = self.interpolated_or_expr_span(e)?; + (lo.to(span), self.mk_unary(UnOp::Not, e)) + } else { + return self.parse_dot_or_call_expr(Some(attrs)); + } + } + _ => { return self.parse_dot_or_call_expr(Some(attrs)); } }; return Ok(self.mk_expr(lo.to(hi), ex, attrs)); } @@ -4486,6 +4527,11 @@ impl<'a> Parser<'a> { // Which is valid in other languages, but not Rust. match self.parse_stmt_without_recovery(false) { Ok(Some(stmt)) => { + if self.look_ahead(1, |t| t == &token::OpenDelim(token::Brace)) { + // if the next token is an open brace (e.g., `if a b {`), the place- + // inside-a-block suggestion would be more likely wrong than right + return Err(e); + } let mut stmt_span = stmt.span; // expand the span to include the semicolon, if it exists if self.eat(&token::Semi) { diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index 6544619af9c..df0ea05005c 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -91,7 +91,7 @@ impl Lit { } } -fn ident_can_begin_expr(ident: ast::Ident, is_raw: bool) -> bool { +pub(crate) fn ident_can_begin_expr(ident: ast::Ident, is_raw: bool) -> bool { let ident_token: Token = Ident(ident, is_raw); !ident_token.is_reserved_ident() || @@ -348,6 +348,15 @@ impl Token { self.lifetime().is_some() } + /// Returns `true` if the token is a identifier whose name is the given + /// string slice. + pub fn is_ident_named(&self, name: &str) -> bool { + match self.ident() { + Some((ident, _)) => ident.name.as_str() == name, + None => false + } + } + /// Returns `true` if the token is a documentation comment. pub fn is_doc_comment(&self) -> bool { match *self { |
