Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle more cases of typos misinterpreted as type ascription #62791

Merged
merged 2 commits into from
Jul 23, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
99 changes: 53 additions & 46 deletions src/libsyntax/parse/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::ast::{
self, Arg, BinOpKind, BindingMode, BlockCheckMode, Expr, ExprKind, Ident, Item, ItemKind,
Mutability, Pat, PatKind, PathSegment, QSelf, Ty, TyKind, VariantData,
};
use crate::feature_gate::{feature_err, UnstableFeatures};
use crate::parse::{SeqSep, PResult, Parser, ParseSess};
use crate::parse::parser::{BlockMode, PathStyle, SemiColonMode, TokenType, TokenExpectType};
use crate::parse::token::{self, TokenKind};
Expand Down Expand Up @@ -326,8 +327,8 @@ impl<'a> Parser<'a> {
self.token.is_keyword(kw::Return) ||
self.token.is_keyword(kw::While)
);
let cm = self.sess.source_map();
match (cm.lookup_line(self.token.span.lo()), cm.lookup_line(sp.lo())) {
let sm = self.sess.source_map();
match (sm.lookup_line(self.token.span.lo()), sm.lookup_line(sp.lo())) {
(Ok(ref a), Ok(ref b)) if a.line != b.line && is_semi_suggestable => {
// The spans are in different lines, expected `;` and found `let` or `return`.
// High likelihood that it is only a missing `;`.
Expand Down Expand Up @@ -365,9 +366,53 @@ impl<'a> Parser<'a> {
err.span_label(self.token.span, "unexpected token");
}
}
self.maybe_annotate_with_ascription(&mut err, false);
Err(err)
}

pub fn maybe_annotate_with_ascription(
&self,
err: &mut DiagnosticBuilder<'_>,
maybe_expected_semicolon: bool,
) {
if let Some((sp, likely_path)) = self.last_type_ascription {
let sm = self.sess.source_map();
let next_pos = sm.lookup_char_pos(self.token.span.lo());
let op_pos = sm.lookup_char_pos(sp.hi());

if likely_path {
err.span_suggestion(
sp,
"maybe write a path separator here",
"::".to_string(),
match self.sess.unstable_features {
UnstableFeatures::Disallow => Applicability::MachineApplicable,
_ => Applicability::MaybeIncorrect,
},
);
} else if op_pos.line != next_pos.line && maybe_expected_semicolon {
err.span_suggestion(
sp,
"try using a semicolon",
";".to_string(),
Applicability::MaybeIncorrect,
);
} else if let UnstableFeatures::Disallow = self.sess.unstable_features {
err.span_label(sp, "tried to parse a type due to this");
} else {
err.span_label(sp, "tried to parse a type due to this type ascription");
}
if let UnstableFeatures::Disallow = self.sess.unstable_features {
// Give extra information about type ascription only if it's a nightly compiler.
} else {
err.note("`#![feature(type_ascription)]` lets you annotate an expression with a \
type: `<expr>: <type>`");
err.note("for more information, see \
https://github.com/rust-lang/rust/issues/23416");
}
}
}

/// Eats and discards tokens until one of `kets` is encountered. Respects token trees,
/// passes through any errors encountered. Used for error recovery.
crate fn eat_to_tokens(&mut self, kets: &[&TokenKind]) {
Expand Down Expand Up @@ -556,7 +601,7 @@ impl<'a> Parser<'a> {
.collect::<Vec<_>>();

if !discriminant_spans.is_empty() && has_fields {
let mut err = crate::feature_gate::feature_err(
let mut err = feature_err(
sess,
sym::arbitrary_enum_discriminant,
discriminant_spans.clone(),
Expand Down Expand Up @@ -769,8 +814,8 @@ impl<'a> Parser<'a> {
return Ok(recovered);
}
}
let cm = self.sess.source_map();
match (cm.lookup_line(prev_sp.lo()), cm.lookup_line(sp.lo())) {
let sm = self.sess.source_map();
match (sm.lookup_line(prev_sp.lo()), sm.lookup_line(sp.lo())) {
(Ok(ref a), Ok(ref b)) if a.line == b.line => {
// When the spans are in the same line, it means that the only content
// between them is whitespace, point only at the found token.
Expand Down Expand Up @@ -887,47 +932,9 @@ impl<'a> Parser<'a> {
self.look_ahead(2, |t| t.is_ident()) ||
self.look_ahead(1, |t| t == &token::Colon) && // `foo:bar:baz`
self.look_ahead(2, |t| t.is_ident()) ||
self.look_ahead(1, |t| t == &token::ModSep) && // `foo:bar::baz`
self.look_ahead(2, |t| t.is_ident())
}

crate fn bad_type_ascription(
&self,
err: &mut DiagnosticBuilder<'a>,
lhs_span: Span,
cur_op_span: Span,
next_sp: Span,
maybe_path: bool,
) {
err.span_label(self.token.span, "expecting a type here because of type ascription");
let cm = self.sess.source_map();
let next_pos = cm.lookup_char_pos(next_sp.lo());
let op_pos = cm.lookup_char_pos(cur_op_span.hi());
if op_pos.line != next_pos.line {
err.span_suggestion(
cur_op_span,
"try using a semicolon",
";".to_string(),
Applicability::MaybeIncorrect,
);
} else {
if maybe_path {
err.span_suggestion(
cur_op_span,
"maybe you meant to write a path separator here",
"::".to_string(),
Applicability::MaybeIncorrect,
);
} else {
err.note("`#![feature(type_ascription)]` lets you annotate an \
expression with a type: `<expr>: <type>`")
.span_note(
lhs_span,
"this expression expects an ascribed type after the colon",
)
.help("this might be indicative of a syntax error elsewhere");
}
}
self.look_ahead(1, |t| t == &token::ModSep) &&
(self.look_ahead(2, |t| t.is_ident()) || // `foo:bar::baz`
self.look_ahead(2, |t| t == &token::Lt)) // `foo:bar::<baz>`
}

crate fn recover_seq_parse_error(
Expand Down
39 changes: 20 additions & 19 deletions src/libsyntax/parse/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ pub struct Parser<'a> {
/// error.
crate unclosed_delims: Vec<UnmatchedBrace>,
crate last_unexpected_token_span: Option<Span>,
crate last_type_ascription: Option<(Span, bool /* likely path typo */)>,
/// If present, this `Parser` is not parsing Rust code but rather a macro call.
crate subparser_name: Option<&'static str>,
}
Expand Down Expand Up @@ -502,6 +503,7 @@ impl<'a> Parser<'a> {
max_angle_bracket_count: 0,
unclosed_delims: Vec::new(),
last_unexpected_token_span: None,
last_type_ascription: None,
subparser_name,
};

Expand Down Expand Up @@ -1422,7 +1424,10 @@ impl<'a> Parser<'a> {
}
} else {
let msg = format!("expected type, found {}", self.this_token_descr());
return Err(self.fatal(&msg));
let mut err = self.fatal(&msg);
err.span_label(self.token.span, "expected type");
self.maybe_annotate_with_ascription(&mut err, true);
return Err(err);
};

let span = lo.to(self.prev_span);
Expand Down Expand Up @@ -2823,10 +2828,11 @@ impl<'a> Parser<'a> {
}

/// Parses an associative expression with operators of at least `min_prec` precedence.
fn parse_assoc_expr_with(&mut self,
min_prec: usize,
lhs: LhsExpr)
-> PResult<'a, P<Expr>> {
fn parse_assoc_expr_with(
&mut self,
min_prec: usize,
lhs: LhsExpr,
) -> PResult<'a, P<Expr>> {
let mut lhs = if let LhsExpr::AlreadyParsed(expr) = lhs {
expr
} else {
Expand All @@ -2840,9 +2846,11 @@ impl<'a> Parser<'a> {
self.parse_prefix_expr(attrs)?
}
};
let last_type_ascription_set = self.last_type_ascription.is_some();
estebank marked this conversation as resolved.
Show resolved Hide resolved

match (self.expr_is_complete(&lhs), AssocOp::from_token(&self.token)) {
(true, None) => {
self.last_type_ascription = None;
// Semi-statement forms are odd. See https://github.com/rust-lang/rust/issues/29071
return Ok(lhs);
}
Expand All @@ -2857,12 +2865,14 @@ impl<'a> Parser<'a> {
// If the next token is a keyword, then the tokens above *are* unambiguously incorrect:
// `if x { a } else { b } && if y { c } else { d }`
if !self.look_ahead(1, |t| t.is_reserved_ident()) => {
self.last_type_ascription = None;
// These cases are ambiguous and can't be identified in the parser alone
let sp = self.sess.source_map().start_point(self.token.span);
self.sess.ambiguous_block_expr_parse.borrow_mut().insert(sp, lhs.span);
return Ok(lhs);
}
(true, Some(ref op)) if !op.can_continue_expr_unambiguously() => {
self.last_type_ascription = None;
return Ok(lhs);
}
(true, Some(_)) => {
Expand Down Expand Up @@ -2921,21 +2931,9 @@ impl<'a> Parser<'a> {
continue
} else if op == AssocOp::Colon {
let maybe_path = self.could_ascription_be_path(&lhs.node);
let next_sp = self.token.span;
self.last_type_ascription = Some((self.prev_span, maybe_path));

lhs = match self.parse_assoc_op_cast(lhs, lhs_span, ExprKind::Type) {
Ok(lhs) => lhs,
Err(mut err) => {
self.bad_type_ascription(
&mut err,
lhs_span,
cur_op_span,
next_sp,
maybe_path,
);
return Err(err);
}
};
lhs = self.parse_assoc_op_cast(lhs, lhs_span, ExprKind::Type)?;
continue
} else if op == AssocOp::DotDot || op == AssocOp::DotDotEq {
// If we didn’t have to handle `x..`/`x..=`, it would be pretty easy to
Expand Down Expand Up @@ -3020,6 +3018,9 @@ impl<'a> Parser<'a> {

if let Fixity::None = fixity { break }
}
if last_type_ascription_set {
self.last_type_ascription = None;
}
Ok(lhs)
}

Expand Down
5 changes: 4 additions & 1 deletion src/libsyntax_ext/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,10 @@ fn parse_args<'a>(

while p.token != token::Eof {
if !p.eat(&token::Comma) {
return Err(ecx.struct_span_err(p.token.span, "expected token: `,`"));
let mut err = ecx.struct_span_err(p.token.span, "expected token: `,`");
err.span_label(p.token.span, "expected `,`");
p.maybe_annotate_with_ascription(&mut err, false);
return Err(err);
}
if p.token == token::Eof {
break;
Expand Down
4 changes: 2 additions & 2 deletions src/test/ui/codemap_tests/bad-format-args.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ error: expected token: `,`
--> $DIR/bad-format-args.rs:3:16
|
LL | format!("" 1);
| ^
| ^ expected `,`

error: expected token: `,`
--> $DIR/bad-format-args.rs:4:19
|
LL | format!("", 1 1);
| ^
| ^ expected `,`

error: aborting due to 3 previous errors

11 changes: 4 additions & 7 deletions src/test/ui/issues/issue-22644.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -87,15 +87,12 @@ error: expected type, found `4`
--> $DIR/issue-22644.rs:34:28
|
LL | println!("{}", a: &mut 4);
| ^ expecting a type here because of type ascription
| - ^ expected type
| |
| tried to parse a type due to this type ascription
|
= note: `#![feature(type_ascription)]` lets you annotate an expression with a type: `<expr>: <type>`
note: this expression expects an ascribed type after the colon
--> $DIR/issue-22644.rs:34:20
|
LL | println!("{}", a: &mut 4);
| ^
= help: this might be indicative of a syntax error elsewhere
= note: for more information, see https://github.com/rust-lang/rust/issues/23416

error: aborting due to 9 previous errors

11 changes: 4 additions & 7 deletions src/test/ui/issues/issue-34255-1.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,12 @@ error: expected type, found `42`
--> $DIR/issue-34255-1.rs:8:24
|
LL | Test::Drill(field: 42);
| ^^ expecting a type here because of type ascription
| - ^^ expected type
| |
| tried to parse a type due to this type ascription
|
= note: `#![feature(type_ascription)]` lets you annotate an expression with a type: `<expr>: <type>`
note: this expression expects an ascribed type after the colon
--> $DIR/issue-34255-1.rs:8:17
|
LL | Test::Drill(field: 42);
| ^^^^^
= help: this might be indicative of a syntax error elsewhere
= note: for more information, see https://github.com/rust-lang/rust/issues/23416

error: aborting due to previous error

2 changes: 1 addition & 1 deletion src/test/ui/issues/issue-39616.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ error: expected type, found `0`
--> $DIR/issue-39616.rs:1:12
|
LL | fn foo(a: [0; 1]) {}
| ^
| ^ expected type

error: expected one of `)`, `,`, `->`, `where`, or `{`, found `]`
--> $DIR/issue-39616.rs:1:16
Expand Down
5 changes: 4 additions & 1 deletion src/test/ui/issues/issue-44406.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ LL | bar(baz: $rest)
| - help: try using a semicolon: `;`
...
LL | foo!(true);
| ^^^^ expecting a type here because of type ascription
| ^^^^ expected type
|
= note: `#![feature(type_ascription)]` lets you annotate an expression with a type: `<expr>: <type>`
= note: for more information, see https://github.com/rust-lang/rust/issues/23416

error: aborting due to 2 previous errors

11 changes: 4 additions & 7 deletions src/test/ui/lifetime_starts_expressions.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,12 @@ error: expected type, found keyword `loop`
--> $DIR/lifetime_starts_expressions.rs:6:26
|
LL | loop { break 'label: loop { break 'label 42; }; }
| ^^^^ expecting a type here because of type ascription
| - ^^^^ expected type
| |
| tried to parse a type due to this type ascription
|
= note: `#![feature(type_ascription)]` lets you annotate an expression with a type: `<expr>: <type>`
note: this expression expects an ascribed type after the colon
--> $DIR/lifetime_starts_expressions.rs:6:12
|
LL | loop { break 'label: loop { break 'label 42; }; }
| ^^^^^^^^^^^^
= help: this might be indicative of a syntax error elsewhere
= note: for more information, see https://github.com/rust-lang/rust/issues/23416

error: aborting due to 2 previous errors

2 changes: 1 addition & 1 deletion src/test/ui/macros/missing-comma.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ error: expected token: `,`
--> $DIR/missing-comma.rs:19:19
|
LL | println!("{}" a);
| ^
| ^ expected `,`

error: no rules expected the token `b`
--> $DIR/missing-comma.rs:21:12
Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/parser/issue-33262.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ error: expected type, found `{`
--> $DIR/issue-33262.rs:4:22
|
LL | for i in 0..a as { }
| ^
| ^ expected type

error: aborting due to previous error

2 changes: 1 addition & 1 deletion src/test/ui/parser/macro/trait-object-macro-matcher.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ error: expected type, found `'static`
--> $DIR/trait-object-macro-matcher.rs:9:8
|
LL | m!('static);
| ^^^^^^^
| ^^^^^^^ expected type

error: aborting due to previous error

2 changes: 1 addition & 1 deletion src/test/ui/parser/recover-enum2.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ error: expected type, found `{`
--> $DIR/recover-enum2.rs:6:18
|
LL | abc: {},
| ^
| ^ expected type

error: expected one of `!`, `(`, `)`, `+`, `,`, `::`, or `<`, found `{`
--> $DIR/recover-enum2.rs:25:22
Expand Down