Rollup merge of #62791 - estebank:type-ascription, r=petrochenkov

Handle more cases of typos misinterpreted as type ascription

Fix #60933, #54516.

CC #47666, #34255, #48016.
This commit is contained in:
Mark Rousskov 2019-07-23 12:51:07 -04:00 committed by GitHub
commit ab7149bdc5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 225 additions and 118 deletions

View File

@ -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};
@ -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 `;`.
@ -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]) {
@ -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(),
@ -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.
@ -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(

View File

@ -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>,
}
@ -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,
};
@ -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);
@ -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 {
@ -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);
}
@ -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(_)) => {
@ -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 didnt have to handle `x..`/`x..=`, it would be pretty easy to
@ -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)
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -2,15 +2,12 @@ error: expected type, found `3`
--> $DIR/recover-from-bad-variant.rs:7:26
|
LL | let x = Enum::Foo(a: 3, b: 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/recover-from-bad-variant.rs:7:23
|
LL | let x = Enum::Foo(a: 3, b: 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[E0532]: expected tuple struct/variant, found struct variant `Enum::Foo`
--> $DIR/recover-from-bad-variant.rs:10:9

View File

@ -2,7 +2,7 @@ error: expected type, found keyword `mut`
--> $DIR/removed-syntax-mut-vec-ty.rs:1:11
|
LL | type v = [mut isize];
| ^^^
| ^^^ expected type
error: aborting due to previous error

View File

@ -2,7 +2,7 @@ error: expected type, found `{`
--> $DIR/removed-syntax-record.rs:1:10
|
LL | type t = { f: () };
| ^
| ^ expected type
error: aborting due to previous error

View File

@ -29,7 +29,7 @@ error: expected type, found `'a`
--> $DIR/trait-object-lifetime-parens.rs:9:17
|
LL | let _: Box<('a) + Trait>;
| - ^^
| - ^^ expected type
| |
| while parsing the type for `_`

View File

@ -2,9 +2,12 @@ error: expected type, found `"foo"`
--> $DIR/type-ascription-instead-of-method.rs:2:13
|
LL | Box:new("foo".to_string())
| - ^^^^^ expecting a type here because of type ascription
| - ^^^^^ expected type
| |
| help: maybe you meant to write a path separator here: `::`
| help: maybe write a path separator here: `::`
|
= 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 previous error

View File

@ -2,9 +2,12 @@ error: expected type, found `""`
--> $DIR/type-ascription-instead-of-variant.rs:2:25
|
LL | let _ = Option:Some("");
| - ^^ expecting a type here because of type ascription
| - ^^ expected type
| |
| help: maybe you meant to write a path separator here: `::`
| help: maybe write a path separator here: `::`
|
= 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 previous error

View File

@ -0,0 +1,15 @@
struct Reactor {
input_cells: Vec<usize>,
}
impl Reactor {
pub fn new() -> Self {
input_cells: Vec::new()
//~^ ERROR cannot find value `input_cells` in this scope
//~| ERROR parenthesized type parameters may only be used with a `Fn` trait
//~| ERROR wrong number of type arguments: expected 1, found 0
//~| WARNING this was previously accepted by the compiler but is being phased out
}
}
// This case isn't currently being handled gracefully, including for completeness.

View File

@ -0,0 +1,30 @@
error[E0425]: cannot find value `input_cells` in this scope
--> $DIR/issue-34255-1.rs:7:9
|
LL | input_cells: Vec::new()
| ^^^^^^^^^^^ a field by this name exists in `Self`
error: parenthesized type parameters may only be used with a `Fn` trait
--> $DIR/issue-34255-1.rs:7:30
|
LL | input_cells: Vec::new()
| ^^
|
= note: `#[deny(parenthesized_params_in_types_and_modules)]` on by default
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #42238 <https://github.com/rust-lang/rust/issues/42238>
error[E0601]: `main` function not found in crate `issue_34255_1`
|
= note: consider adding a `main` function to `$DIR/issue-34255-1.rs`
error[E0107]: wrong number of type arguments: expected 1, found 0
--> $DIR/issue-34255-1.rs:7:22
|
LL | input_cells: Vec::new()
| ^^^^^^^^^^ expected 1 type argument
error: aborting due to 4 previous errors
Some errors have detailed explanations: E0107, E0425, E0601.
For more information about an error, try `rustc --explain E0107`.

View File

@ -0,0 +1,5 @@
fn main() {
let _ = Option:Some(vec![0, 1]); //~ ERROR expected type, found
}
// This case isn't currently being handled gracefully due to the macro invocation.

View File

@ -0,0 +1,13 @@
error: expected type, found reserved keyword `box`
--> $DIR/issue-47666.rs:2:25
|
LL | let _ = Option:Some(vec![0, 1]);
| ^^^^^^^^^^
| |
| expected type
| in this macro invocation
|
= note: this warning originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
error: aborting due to previous error

View File

@ -0,0 +1,6 @@
use std::collections::BTreeMap;
fn main() {
println!("{}", std::mem:size_of::<BTreeMap<u32, u32>>());
//~^ ERROR expected token: `,`
}

View File

@ -0,0 +1,13 @@
error: expected token: `,`
--> $DIR/issue-54516.rs:4:58
|
LL | println!("{}", std::mem:size_of::<BTreeMap<u32, u32>>());
| - ^ expected `,`
| |
| help: maybe write a path separator here: `::`
|
= 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 previous error

View File

@ -0,0 +1,4 @@
fn main() {
let u: usize = std::mem:size_of::<u32>();
//~^ ERROR expected one of
}

View File

@ -0,0 +1,13 @@
error: expected one of `!`, `::`, or `;`, found `(`
--> $DIR/issue-60933.rs:2:43
|
LL | let u: usize = std::mem:size_of::<u32>();
| - ^ expected one of `!`, `::`, or `;` here
| |
| help: maybe write a path separator here: `::`
|
= 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 previous error

View File

@ -2,7 +2,7 @@ error: expected type, found `10`
--> $DIR/type-ascription-instead-of-initializer.rs:2:31
|
LL | let x: Vec::with_capacity(10, 20);
| -- ^^
| -- ^^ expected type
| ||
| |help: use `=` if you meant to assign
| while parsing the type for `x`

View File

@ -4,21 +4,21 @@ error: expected type, found `0`
LL | println!("test"):
| - help: try using a semicolon: `;`
LL | 0;
| ^ 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: expected type, found `0`
--> $DIR/type-ascription-instead-of-statement-end.rs:9:23
|
LL | println!("test"): 0;
| ^ 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/type-ascription-instead-of-statement-end.rs:9:5
|
LL | println!("test"): 0;
| ^^^^^^^^^^^^^^^^
= 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