1
Fork 0

Auto merge of #74210 - estebank:type-ascriptomatic, r=petrochenkov

Deduplicate `::` -> `:` typo errors

Deduplicate errors caused by the same type ascription typo, including
ones suggested during parsing that would get reported again during
resolve. Fix #70382.
This commit is contained in:
bors 2020-08-02 12:20:09 +00:00
commit fd4d151aed
28 changed files with 212 additions and 181 deletions

View file

@ -333,6 +333,7 @@ impl<'a> Parser<'a> {
Applicability::MachineApplicable Applicability::MachineApplicable
}, },
); );
self.sess.type_ascription_path_suggestions.borrow_mut().insert(sp);
} else if op_pos.line != next_pos.line && maybe_expected_semicolon { } else if op_pos.line != next_pos.line && maybe_expected_semicolon {
err.span_suggestion( err.span_suggestion(
sp, sp,

View file

@ -2241,8 +2241,15 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
self.resolve_expr(argument, None); self.resolve_expr(argument, None);
} }
} }
ExprKind::Type(ref type_expr, _) => { ExprKind::Type(ref type_expr, ref ty) => {
self.diagnostic_metadata.current_type_ascription.push(type_expr.span); // `ParseSess::type_ascription_path_suggestions` keeps spans of colon tokens in
// type ascription. Here we are trying to retrieve the span of the colon token as
// well, but only if it's written without spaces `expr:Ty` and therefore confusable
// with `expr::Ty`, only in this case it will match the span from
// `type_ascription_path_suggestions`.
self.diagnostic_metadata
.current_type_ascription
.push(type_expr.span.between(ty.span));
visit::walk_expr(self, expr); visit::walk_expr(self, expr);
self.diagnostic_metadata.current_type_ascription.pop(); self.diagnostic_metadata.current_type_ascription.pop();
} }

View file

@ -17,7 +17,7 @@ use rustc_hir::PrimTy;
use rustc_session::config::nightly_options; use rustc_session::config::nightly_options;
use rustc_span::hygiene::MacroKind; use rustc_span::hygiene::MacroKind;
use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::symbol::{kw, sym, Ident};
use rustc_span::Span; use rustc_span::{BytePos, Span};
use log::debug; use log::debug;
@ -223,13 +223,31 @@ impl<'a> LateResolutionVisitor<'a, '_, '_> {
if candidates.is_empty() && is_expected(Res::Def(DefKind::Enum, crate_def_id)) { if candidates.is_empty() && is_expected(Res::Def(DefKind::Enum, crate_def_id)) {
let enum_candidates = let enum_candidates =
self.r.lookup_import_candidates(ident, ns, &self.parent_scope, is_enum_variant); self.r.lookup_import_candidates(ident, ns, &self.parent_scope, is_enum_variant);
let mut enum_candidates = enum_candidates
.iter()
.map(|suggestion| import_candidate_to_enum_paths(&suggestion))
.collect::<Vec<_>>();
enum_candidates.sort();
if !enum_candidates.is_empty() { if !enum_candidates.is_empty() {
if let (PathSource::Type, Some(span)) =
(source, self.diagnostic_metadata.current_type_ascription.last())
{
if self
.r
.session
.parse_sess
.type_ascription_path_suggestions
.borrow()
.contains(span)
{
// Already reported this issue on the lhs of the type ascription.
err.delay_as_bug();
return (err, candidates);
}
}
let mut enum_candidates = enum_candidates
.iter()
.map(|suggestion| import_candidate_to_enum_paths(&suggestion))
.collect::<Vec<_>>();
enum_candidates.sort();
// Contextualize for E0412 "cannot find type", but don't belabor the point // Contextualize for E0412 "cannot find type", but don't belabor the point
// (that it's a variant) for E0573 "expected type, found variant". // (that it's a variant) for E0573 "expected type, found variant".
let preamble = if res.is_none() { let preamble = if res.is_none() {
@ -484,10 +502,21 @@ impl<'a> LateResolutionVisitor<'a, '_, '_> {
match source { match source {
PathSource::Expr(Some( PathSource::Expr(Some(
parent @ Expr { kind: ExprKind::Field(..) | ExprKind::MethodCall(..), .. }, parent @ Expr { kind: ExprKind::Field(..) | ExprKind::MethodCall(..), .. },
)) => { )) if path_sep(err, &parent) => {}
path_sep(err, &parent); PathSource::Expr(
} None
PathSource::Expr(None) if followed_by_brace => { | Some(Expr {
kind:
ExprKind::Path(..)
| ExprKind::Binary(..)
| ExprKind::Unary(..)
| ExprKind::If(..)
| ExprKind::While(..)
| ExprKind::ForLoop(..)
| ExprKind::Match(..),
..
}),
) if followed_by_brace => {
if let Some(sp) = closing_brace { if let Some(sp) = closing_brace {
err.multipart_suggestion( err.multipart_suggestion(
"surround the struct literal with parentheses", "surround the struct literal with parentheses",
@ -508,11 +537,7 @@ impl<'a> LateResolutionVisitor<'a, '_, '_> {
); );
} }
} }
PathSource::Expr( PathSource::Expr(_) | PathSource::TupleStruct(_) | PathSource::Pat => {
None | Some(Expr { kind: ExprKind::Call(..) | ExprKind::Path(..), .. }),
)
| PathSource::TupleStruct(_)
| PathSource::Pat => {
let span = match &source { let span = match &source {
PathSource::Expr(Some(Expr { PathSource::Expr(Some(Expr {
span, kind: ExprKind::Call(_, _), .. span, kind: ExprKind::Call(_, _), ..
@ -593,6 +618,24 @@ impl<'a> LateResolutionVisitor<'a, '_, '_> {
Res::Def(DefKind::Enum, def_id), Res::Def(DefKind::Enum, def_id),
PathSource::TupleStruct(_) | PathSource::Expr(..), PathSource::TupleStruct(_) | PathSource::Expr(..),
) => { ) => {
if self
.diagnostic_metadata
.current_type_ascription
.last()
.map(|sp| {
self.r
.session
.parse_sess
.type_ascription_path_suggestions
.borrow()
.contains(&sp)
})
.unwrap_or(false)
{
err.delay_as_bug();
// We already suggested changing `:` into `::` during parsing.
return false;
}
if let Some(variants) = self.collect_enum_variants(def_id) { if let Some(variants) = self.collect_enum_variants(def_id) {
if !variants.is_empty() { if !variants.is_empty() {
let msg = if variants.len() == 1 { let msg = if variants.len() == 1 {
@ -609,7 +652,7 @@ impl<'a> LateResolutionVisitor<'a, '_, '_> {
); );
} }
} else { } else {
err.note("did you mean to use one of the enum's variants?"); err.note("you might have meant to use one of the enum's variants");
} }
} }
(Res::Def(DefKind::Struct, def_id), _) if ns == ValueNS => { (Res::Def(DefKind::Struct, def_id), _) if ns == ValueNS => {
@ -829,77 +872,73 @@ impl<'a> LateResolutionVisitor<'a, '_, '_> {
fn type_ascription_suggestion(&self, err: &mut DiagnosticBuilder<'_>, base_span: Span) { fn type_ascription_suggestion(&self, err: &mut DiagnosticBuilder<'_>, base_span: Span) {
let sm = self.r.session.source_map(); let sm = self.r.session.source_map();
let base_snippet = sm.span_to_snippet(base_span); let base_snippet = sm.span_to_snippet(base_span);
if let Some(sp) = self.diagnostic_metadata.current_type_ascription.last() { if let Some(&sp) = self.diagnostic_metadata.current_type_ascription.last() {
let mut sp = *sp; if let Ok(snippet) = sm.span_to_snippet(sp) {
loop { let len = snippet.trim_end().len() as u32;
// Try to find the `:`; bail on first non-':' / non-whitespace. if snippet.trim() == ":" {
sp = sm.next_point(sp); let colon_sp =
if let Ok(snippet) = sm.span_to_snippet(sp.to(sm.next_point(sp))) { sp.with_lo(sp.lo() + BytePos(len - 1)).with_hi(sp.lo() + BytePos(len));
let line_sp = sm.lookup_char_pos(sp.hi()).line; let mut show_label = true;
let line_base_sp = sm.lookup_char_pos(base_span.lo()).line; if sm.is_multiline(sp) {
if snippet == ":" { err.span_suggestion_short(
let mut show_label = true; colon_sp,
if line_sp != line_base_sp { "maybe you meant to write `;` here",
err.span_suggestion_short( ";".to_string(),
sp, Applicability::MaybeIncorrect,
"did you mean to use `;` here instead?", );
";".to_string(), } else {
let after_colon_sp =
self.get_colon_suggestion_span(colon_sp.shrink_to_hi());
if snippet.len() == 1 {
// `foo:bar`
err.span_suggestion(
colon_sp,
"maybe you meant to write a path separator here",
"::".to_string(),
Applicability::MaybeIncorrect, Applicability::MaybeIncorrect,
); );
} else { show_label = false;
let colon_sp = self.get_colon_suggestion_span(sp); if !self
let after_colon_sp = .r
self.get_colon_suggestion_span(colon_sp.shrink_to_hi()); .session
if !sm .parse_sess
.span_to_snippet(after_colon_sp) .type_ascription_path_suggestions
.map(|s| s == " ") .borrow_mut()
.unwrap_or(false) .insert(colon_sp)
{ {
err.span_suggestion( err.delay_as_bug();
colon_sp,
"maybe you meant to write a path separator here",
"::".to_string(),
Applicability::MaybeIncorrect,
);
show_label = false;
} }
if let Ok(base_snippet) = base_snippet { }
let mut sp = after_colon_sp; if let Ok(base_snippet) = base_snippet {
for _ in 0..100 { let mut sp = after_colon_sp;
// Try to find an assignment for _ in 0..100 {
sp = sm.next_point(sp); // Try to find an assignment
let snippet = sm.span_to_snippet(sp.to(sm.next_point(sp))); sp = sm.next_point(sp);
match snippet { let snippet = sm.span_to_snippet(sp.to(sm.next_point(sp)));
Ok(ref x) if x.as_str() == "=" => { match snippet {
err.span_suggestion( Ok(ref x) if x.as_str() == "=" => {
base_span, err.span_suggestion(
"maybe you meant to write an assignment here", base_span,
format!("let {}", base_snippet), "maybe you meant to write an assignment here",
Applicability::MaybeIncorrect, format!("let {}", base_snippet),
); Applicability::MaybeIncorrect,
show_label = false; );
break; show_label = false;
} break;
Ok(ref x) if x.as_str() == "\n" => break,
Err(_) => break,
Ok(_) => {}
} }
Ok(ref x) if x.as_str() == "\n" => break,
Err(_) => break,
Ok(_) => {}
} }
} }
} }
if show_label {
err.span_label(
base_span,
"expecting a type here because of type ascription",
);
}
break;
} else if !snippet.trim().is_empty() {
debug!("tried to find type ascription `:` token, couldn't find it");
break;
} }
} else { if show_label {
break; err.span_label(
base_span,
"expecting a type here because of type ascription",
);
}
} }
} }
} }

View file

@ -138,6 +138,8 @@ pub struct ParseSess {
pub reached_eof: Lock<bool>, pub reached_eof: Lock<bool>,
/// Environment variables accessed during the build and their values when they exist. /// Environment variables accessed during the build and their values when they exist.
pub env_depinfo: Lock<FxHashSet<(Symbol, Option<Symbol>)>>, pub env_depinfo: Lock<FxHashSet<(Symbol, Option<Symbol>)>>,
/// All the type ascriptions expressions that have had a suggestion for likely path typo.
pub type_ascription_path_suggestions: Lock<FxHashSet<Span>>,
} }
impl ParseSess { impl ParseSess {
@ -164,6 +166,7 @@ impl ParseSess {
symbol_gallery: SymbolGallery::default(), symbol_gallery: SymbolGallery::default(),
reached_eof: Lock::new(false), reached_eof: Lock::new(false),
env_depinfo: Default::default(), env_depinfo: Default::default(),
type_ascription_path_suggestions: Default::default(),
} }
} }

View file

@ -0,0 +1,6 @@
// run-rustfix
struct X {}
fn main() {
let _ = vec![X {}]; //…
//~^ ERROR expected value, found struct `X`
}

View file

@ -1,5 +1,6 @@
// run-rustfix
struct X {} struct X {}
fn main() { fn main() {
vec![X]; //… let _ = vec![X]; //…
//~^ ERROR expected value, found struct `X` //~^ ERROR expected value, found struct `X`
} }

View file

@ -1,11 +1,11 @@
error[E0423]: expected value, found struct `X` error[E0423]: expected value, found struct `X`
--> $DIR/issue-61226.rs:3:10 --> $DIR/issue-61226.rs:4:18
| |
LL | struct X {} LL | struct X {}
| ----------- `X` defined here | ----------- `X` defined here
LL | fn main() { LL | fn main() {
LL | vec![X]; //… LL | let _ = vec![X]; //…
| ^ help: use struct literal syntax instead: `X {}` | ^ help: use struct literal syntax instead: `X {}`
error: aborting due to previous error error: aborting due to previous error

View file

@ -0,0 +1,5 @@
// run-rustfix
fn main() {
let _ = Box::new("foo".to_string());
//~^ ERROR expected type, found
}

View file

@ -1,4 +1,5 @@
// run-rustfix
fn main() { fn main() {
Box:new("foo".to_string()) let _ = Box:new("foo".to_string());
//~^ ERROR expected type, found //~^ ERROR expected type, found
} }

View file

@ -1,10 +1,10 @@
error: expected type, found `"foo"` error: expected type, found `"foo"`
--> $DIR/type-ascription-instead-of-method.rs:2:13 --> $DIR/type-ascription-instead-of-method.rs:3:21
| |
LL | Box:new("foo".to_string()) LL | let _ = Box:new("foo".to_string());
| - ^^^^^ expected type | - ^^^^^ expected type
| | | |
| help: maybe 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: `#![feature(type_ascription)]` lets you annotate an expression with a type: `<expr>: <type>`

View file

@ -0,0 +1,6 @@
// run-rustfix
fn main() -> Result<(), ()> {
let _ = vec![Ok(2)].into_iter().collect::<Result<Vec<_>,_>>()?;
//~^ ERROR expected `::`, found `(`
Ok(())
}

View file

@ -1,5 +1,6 @@
// run-rustfix
fn main() -> Result<(), ()> { fn main() -> Result<(), ()> {
vec![Ok(2)].into_iter().collect:<Result<Vec<_>,_>>()?; let _ = vec![Ok(2)].into_iter().collect:<Result<Vec<_>,_>>()?;
//~^ ERROR expected `::`, found `(` //~^ ERROR expected `::`, found `(`
Ok(()) Ok(())
} }

View file

@ -1,10 +1,10 @@
error: expected `::`, found `(` error: expected `::`, found `(`
--> $DIR/type-ascription-instead-of-path-2.rs:2:55 --> $DIR/type-ascription-instead-of-path-2.rs:3:63
| |
LL | vec![Ok(2)].into_iter().collect:<Result<Vec<_>,_>>()?; LL | let _ = vec![Ok(2)].into_iter().collect:<Result<Vec<_>,_>>()?;
| - ^ expected `::` | - ^ expected `::`
| | | |
| help: maybe 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: `#![feature(type_ascription)]` lets you annotate an expression with a type: `<expr>: <type>`

View file

@ -0,0 +1,5 @@
// run-rustfix
fn main() {
let _ = Option::Some("");
//~^ ERROR expected type, found
}

View file

@ -1,3 +1,4 @@
// run-rustfix
fn main() { fn main() {
let _ = Option:Some(""); let _ = Option:Some("");
//~^ ERROR expected type, found //~^ ERROR expected type, found

View file

@ -1,5 +1,5 @@
error: expected type, found `""` error: expected type, found `""`
--> $DIR/type-ascription-instead-of-variant.rs:2:25 --> $DIR/type-ascription-instead-of-variant.rs:3:25
| |
LL | let _ = Option:Some(""); LL | let _ = Option:Some("");
| - ^^ expected type | - ^^ expected type

View file

@ -0,0 +1,4 @@
// run-rustfix
fn main() {
let _ = Option::Some(vec![0, 1]); //~ ERROR expected type, found
}

View file

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

View file

@ -1,5 +1,5 @@
error: expected type, found reserved keyword `box` error: expected type, found reserved keyword `box`
--> $DIR/issue-47666.rs:2:25 --> $DIR/issue-47666.rs:3:25
| |
LL | let _ = Option:Some(vec![0, 1]); LL | let _ = Option:Some(vec![0, 1]);
| - ^^^^^^^^^^ | - ^^^^^^^^^^
@ -12,35 +12,5 @@ LL | let _ = Option:Some(vec![0, 1]);
= note: `#![feature(type_ascription)]` lets you annotate an expression with a type: `<expr>: <type>` = note: `#![feature(type_ascription)]` lets you annotate an expression with a type: `<expr>: <type>`
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0423]: expected value, found enum `Option` error: aborting due to previous error
--> $DIR/issue-47666.rs:2:13
|
LL | let _ = Option:Some(vec![0, 1]);
| ^^^^^^
|
help: try using one of the enum's variants
|
LL | let _ = std::option::Option::None:Some(vec![0, 1]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^
LL | let _ = std::option::Option::Some:Some(vec![0, 1]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0573]: expected type, found variant `Some`
--> $DIR/issue-47666.rs:2:20
|
LL | let _ = Option:Some(vec![0, 1]);
| ^^^^^^^^^^^^^^^^ not a type
|
help: try using the variant's enum
|
LL | let _ = Option:std::option::Option;
| ^^^^^^^^^^^^^^^^^^^
help: maybe you meant to write a path separator here
|
LL | let _ = Option::Some(vec![0, 1]);
| ^^
error: aborting due to 3 previous errors
Some errors have detailed explanations: E0423, E0573.
For more information about an error, try `rustc --explain E0423`.

View file

@ -0,0 +1,7 @@
// run-rustfix
use std::collections::BTreeMap;
fn main() {
println!("{}", std::mem::size_of::<BTreeMap<u32, u32>>());
//~^ ERROR casts cannot be followed by a function call
}

View file

@ -1,8 +1,7 @@
// run-rustfix
use std::collections::BTreeMap; use std::collections::BTreeMap;
fn main() { fn main() {
println!("{}", std::mem:size_of::<BTreeMap<u32, u32>>()); println!("{}", std::mem:size_of::<BTreeMap<u32, u32>>());
//~^ ERROR casts cannot be followed by a function call //~^ ERROR casts cannot be followed by a function call
//~| ERROR expected value, found module `std::mem` [E0423]
//~| ERROR cannot find type `size_of` in this scope [E0412]
} }

View file

@ -1,5 +1,5 @@
error: casts cannot be followed by a function call error: casts cannot be followed by a function call
--> $DIR/issue-54516.rs:4:20 --> $DIR/issue-54516.rs:5:20
| |
LL | println!("{}", std::mem:size_of::<BTreeMap<u32, u32>>()); LL | println!("{}", std::mem:size_of::<BTreeMap<u32, u32>>());
| ^^^^^^^^-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -8,23 +8,5 @@ LL | println!("{}", std::mem:size_of::<BTreeMap<u32, u32>>());
| |
= note: `#![feature(type_ascription)]` lets you annotate an expression with a type: `<expr>: <type>` = note: `#![feature(type_ascription)]` lets you annotate an expression with a type: `<expr>: <type>`
error[E0423]: expected value, found module `std::mem` error: aborting due to previous error
--> $DIR/issue-54516.rs:4:20
|
LL | println!("{}", std::mem:size_of::<BTreeMap<u32, u32>>());
| ^^^^^^^^- help: maybe you meant to write a path separator here: `::`
| |
| not a value
error[E0412]: cannot find type `size_of` in this scope
--> $DIR/issue-54516.rs:4:29
|
LL | println!("{}", std::mem:size_of::<BTreeMap<u32, u32>>());
| -^^^^^^^ not found in this scope
| |
| help: maybe you meant to write a path separator here: `::`
error: aborting due to 3 previous errors
Some errors have detailed explanations: E0412, E0423.
For more information about an error, try `rustc --explain E0412`.

View file

@ -0,0 +1,5 @@
// run-rustfix
fn main() {
let _: usize = std::mem::size_of::<u32>();
//~^ ERROR casts cannot be followed by a function call
}

View file

@ -1,6 +1,5 @@
// run-rustfix
fn main() { fn main() {
let u: usize = std::mem:size_of::<u32>(); let _: usize = std::mem:size_of::<u32>();
//~^ ERROR casts cannot be followed by a function call //~^ ERROR casts cannot be followed by a function call
//~| ERROR expected value, found module `std::mem` [E0423]
//~| ERROR cannot find type `size_of` in this scope [E0412]
} }

View file

@ -1,30 +1,12 @@
error: casts cannot be followed by a function call error: casts cannot be followed by a function call
--> $DIR/issue-60933.rs:2:20 --> $DIR/issue-60933.rs:3:20
| |
LL | let u: usize = std::mem:size_of::<u32>(); LL | let _: usize = std::mem:size_of::<u32>();
| ^^^^^^^^-^^^^^^^^^^^^^^ | ^^^^^^^^-^^^^^^^^^^^^^^
| | | |
| help: maybe 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: `#![feature(type_ascription)]` lets you annotate an expression with a type: `<expr>: <type>`
error[E0423]: expected value, found module `std::mem` error: aborting due to previous error
--> $DIR/issue-60933.rs:2:20
|
LL | let u: usize = std::mem:size_of::<u32>();
| ^^^^^^^^- help: maybe you meant to write a path separator here: `::`
| |
| not a value
error[E0412]: cannot find type `size_of` in this scope
--> $DIR/issue-60933.rs:2:29
|
LL | let u: usize = std::mem:size_of::<u32>();
| -^^^^^^^ not found in this scope
| |
| help: maybe you meant to write a path separator here: `::`
error: aborting due to 3 previous errors
Some errors have detailed explanations: E0412, E0423.
For more information about an error, try `rustc --explain E0412`.

View file

@ -0,0 +1,9 @@
// run-rustfix
#![feature(type_ascription)]
fn main() {
f() ;
f(); //~ ERROR expected type, found function
}
fn f() {}

View file

@ -1,3 +1,4 @@
// run-rustfix
#![feature(type_ascription)] #![feature(type_ascription)]
fn main() { fn main() {

View file

@ -1,8 +1,8 @@
error[E0573]: expected type, found function `f` error[E0573]: expected type, found function `f`
--> $DIR/type-ascription-with-fn-call.rs:5:5 --> $DIR/type-ascription-with-fn-call.rs:6:5
| |
LL | f() : LL | f() :
| - help: did you mean to use `;` here instead? | - help: maybe you meant to write `;` here
LL | f(); LL | f();
| ^^^ | ^^^
| | | |