Skip to content

Commit 5d7f892

Browse files
committed
Auto merge of #49258 - zackmdavis:not_going_to_recover, r=petrochenkov
suggest `!` for erroneous identifier `not` ![not_recovery](https://user-images.githubusercontent.com/1076988/37753255-3b669c42-2d59-11e8-9071-efad8eaf3086.png) This supersedes #48858. r? @petrochenkov
2 parents a8a8d6b + ba0dd8e commit 5d7f892

File tree

4 files changed

+151
-2
lines changed

4 files changed

+151
-2
lines changed

src/libsyntax/parse/parser.rs

+47-1
Original file line numberDiff line numberDiff line change
@@ -2830,7 +2830,48 @@ impl<'a> Parser<'a> {
28302830
let (span, e) = self.interpolated_or_expr_span(e)?;
28312831
(lo.to(span), ExprKind::Box(e))
28322832
}
2833-
_ => return self.parse_dot_or_call_expr(Some(attrs))
2833+
token::Ident(..) if self.token.is_ident_named("not") => {
2834+
// `not` is just an ordinary identifier in Rust-the-language,
2835+
// but as `rustc`-the-compiler, we can issue clever diagnostics
2836+
// for confused users who really want to say `!`
2837+
let token_cannot_continue_expr = |t: &token::Token| match *t {
2838+
// These tokens can start an expression after `!`, but
2839+
// can't continue an expression after an ident
2840+
token::Ident(ident, is_raw) => token::ident_can_begin_expr(ident, is_raw),
2841+
token::Literal(..) | token::Pound => true,
2842+
token::Interpolated(ref nt) => match nt.0 {
2843+
token::NtIdent(..) | token::NtExpr(..) |
2844+
token::NtBlock(..) | token::NtPath(..) => true,
2845+
_ => false,
2846+
},
2847+
_ => false
2848+
};
2849+
let cannot_continue_expr = self.look_ahead(1, token_cannot_continue_expr);
2850+
if cannot_continue_expr {
2851+
self.bump();
2852+
// Emit the error ...
2853+
let mut err = self.diagnostic()
2854+
.struct_span_err(self.span,
2855+
&format!("unexpected {} after identifier",
2856+
self.this_token_descr()));
2857+
// span the `not` plus trailing whitespace to avoid
2858+
// trailing whitespace after the `!` in our suggestion
2859+
let to_replace = self.sess.codemap()
2860+
.span_until_non_whitespace(lo.to(self.span));
2861+
err.span_suggestion_short(to_replace,
2862+
"use `!` to perform logical negation",
2863+
"!".to_owned());
2864+
err.emit();
2865+
// —and recover! (just as if we were in the block
2866+
// for the `token::Not` arm)
2867+
let e = self.parse_prefix_expr(None);
2868+
let (span, e) = self.interpolated_or_expr_span(e)?;
2869+
(lo.to(span), self.mk_unary(UnOp::Not, e))
2870+
} else {
2871+
return self.parse_dot_or_call_expr(Some(attrs));
2872+
}
2873+
}
2874+
_ => { return self.parse_dot_or_call_expr(Some(attrs)); }
28342875
};
28352876
return Ok(self.mk_expr(lo.to(hi), ex, attrs));
28362877
}
@@ -4486,6 +4527,11 @@ impl<'a> Parser<'a> {
44864527
// Which is valid in other languages, but not Rust.
44874528
match self.parse_stmt_without_recovery(false) {
44884529
Ok(Some(stmt)) => {
4530+
if self.look_ahead(1, |t| t == &token::OpenDelim(token::Brace)) {
4531+
// if the next token is an open brace (e.g., `if a b {`), the place-
4532+
// inside-a-block suggestion would be more likely wrong than right
4533+
return Err(e);
4534+
}
44894535
let mut stmt_span = stmt.span;
44904536
// expand the span to include the semicolon, if it exists
44914537
if self.eat(&token::Semi) {

src/libsyntax/parse/token.rs

+10-1
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ impl Lit {
9191
}
9292
}
9393

94-
fn ident_can_begin_expr(ident: ast::Ident, is_raw: bool) -> bool {
94+
pub(crate) fn ident_can_begin_expr(ident: ast::Ident, is_raw: bool) -> bool {
9595
let ident_token: Token = Ident(ident, is_raw);
9696

9797
!ident_token.is_reserved_ident() ||
@@ -348,6 +348,15 @@ impl Token {
348348
self.lifetime().is_some()
349349
}
350350

351+
/// Returns `true` if the token is a identifier whose name is the given
352+
/// string slice.
353+
pub fn is_ident_named(&self, name: &str) -> bool {
354+
match self.ident() {
355+
Some((ident, _)) => ident.name.as_str() == name,
356+
None => false
357+
}
358+
}
359+
351360
/// Returns `true` if the token is a documentation comment.
352361
pub fn is_doc_comment(&self) -> bool {
353362
match *self {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
fn gratitude() {
12+
let for_you = false;
13+
if not for_you {
14+
//~^ ERROR unexpected `for_you` after identifier
15+
println!("I couldn't");
16+
}
17+
}
18+
19+
fn qualification() {
20+
let the_worst = true;
21+
while not the_worst {
22+
//~^ ERROR unexpected `the_worst` after identifier
23+
println!("still pretty bad");
24+
}
25+
}
26+
27+
fn should_we() {
28+
let not = true;
29+
if not // lack of braces is [sic]
30+
println!("Then when?");
31+
//~^ ERROR expected `{`, found `;
32+
//~| ERROR unexpected `println` after identifier
33+
}
34+
35+
fn sleepy() {
36+
let resource = not 2;
37+
//~^ ERROR unexpected `2` after identifier
38+
}
39+
40+
fn main() {
41+
let be_smothered_out_before = true;
42+
let young_souls = not be_smothered_out_before;
43+
//~^ ERROR unexpected `be_smothered_out_before` after identifier
44+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
error: unexpected `for_you` after identifier
2+
--> $DIR/issue-46836-identifier-not-instead-of-negation.rs:13:12
3+
|
4+
LL | if not for_you {
5+
| ----^^^^^^^
6+
| |
7+
| help: use `!` to perform logical negation
8+
9+
error: unexpected `the_worst` after identifier
10+
--> $DIR/issue-46836-identifier-not-instead-of-negation.rs:21:15
11+
|
12+
LL | while not the_worst {
13+
| ----^^^^^^^^^
14+
| |
15+
| help: use `!` to perform logical negation
16+
17+
error: unexpected `println` after identifier
18+
--> $DIR/issue-46836-identifier-not-instead-of-negation.rs:30:9
19+
|
20+
LL | if not // lack of braces is [sic]
21+
| ----- help: use `!` to perform logical negation
22+
LL | println!("Then when?");
23+
| ^^^^^^^
24+
25+
error: expected `{`, found `;`
26+
--> $DIR/issue-46836-identifier-not-instead-of-negation.rs:30:31
27+
|
28+
LL | if not // lack of braces is [sic]
29+
| -- this `if` statement has a condition, but no block
30+
LL | println!("Then when?");
31+
| ^
32+
33+
error: unexpected `2` after identifier
34+
--> $DIR/issue-46836-identifier-not-instead-of-negation.rs:36:24
35+
|
36+
LL | let resource = not 2;
37+
| ----^
38+
| |
39+
| help: use `!` to perform logical negation
40+
41+
error: unexpected `be_smothered_out_before` after identifier
42+
--> $DIR/issue-46836-identifier-not-instead-of-negation.rs:42:27
43+
|
44+
LL | let young_souls = not be_smothered_out_before;
45+
| ----^^^^^^^^^^^^^^^^^^^^^^^
46+
| |
47+
| help: use `!` to perform logical negation
48+
49+
error: aborting due to 6 previous errors
50+

0 commit comments

Comments
 (0)