Skip to content

Commit

Permalink
Rollup merge of rust-lang#62791 - estebank:type-ascription, r=petroch…
Browse files Browse the repository at this point in the history
…enkov

Handle more cases of typos misinterpreted as type ascription

Fix rust-lang#60933, rust-lang#54516.

CC rust-lang#47666, rust-lang#34255, rust-lang#48016.
  • Loading branch information
Centril committed Jul 21, 2019
2 parents 8d91abc + 9dbe2e7 commit 38532ae
Show file tree
Hide file tree
Showing 29 changed files with 225 additions and 118 deletions.
99 changes: 53 additions & 46 deletions src/libsyntax/parse/diagnostics.rs
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
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();

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
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
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
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
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
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
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
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
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
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
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
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

0 comments on commit 38532ae

Please sign in to comment.