From 0b713ae919705bfbc2ec5bf5eea74570f7ecc19a Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Tue, 13 Aug 2019 23:54:20 +0200 Subject: [PATCH 1/8] typeck: extract ban_private_field_access --- src/librustc_typeck/check/expr.rs | 50 ++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 17 deletions(-) diff --git a/src/librustc_typeck/check/expr.rs b/src/librustc_typeck/check/expr.rs index 9680f61d69903..b4154c15d6739 100644 --- a/src/librustc_typeck/check/expr.rs +++ b/src/librustc_typeck/check/expr.rs @@ -24,6 +24,7 @@ use syntax::source_map::Span; use syntax::util::lev_distance::find_best_match_for_name; use rustc::hir; use rustc::hir::{ExprKind, QPath}; +use rustc::hir::def_id::DefId; use rustc::hir::def::{CtorKind, Res, DefKind}; use rustc::hir::ptr::P; use rustc::infer; @@ -1336,23 +1337,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { autoderef.unambiguous_final_ty(self); if let Some((did, field_ty)) = private_candidate { - let struct_path = self.tcx().def_path_str(did); - let mut err = struct_span_err!(self.tcx().sess, expr.span, E0616, - "field `{}` of struct `{}` is private", - field, struct_path); - // Also check if an accessible method exists, which is often what is meant. - if self.method_exists(field, expr_t, expr.hir_id, false) - && !self.expr_in_place(expr.hir_id) - { - self.suggest_method_call( - &mut err, - &format!("a method `{}` also exists, call it with parentheses", field), - field, - expr_t, - expr.hir_id, - ); - } - err.emit(); + self.ban_private_field_access(expr, expr_t, field, did); field_ty } else if field.name == kw::Invalid { self.tcx().types.err @@ -1446,6 +1431,37 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + fn ban_private_field_access( + &self, + expr: &'tcx hir::Expr, + expr_t: Ty<'tcx>, + field: ast::Ident, + def_id: DefId, + ) { + let struct_path = self.tcx().def_path_str(def_id); + let mut err = struct_span_err!( + self.tcx().sess, + expr.span, + E0616, + "field `{}` of struct `{}` is private", + field, + struct_path + ); + // Also check if an accessible method exists, which is often what is meant. + if self.method_exists(field, expr_t, expr.hir_id, false) + && !self.expr_in_place(expr.hir_id) + { + self.suggest_method_call( + &mut err, + &format!("a method `{}` also exists, call it with parentheses", field), + field, + expr_t, + expr.hir_id, + ); + } + err.emit(); + } + fn no_such_field_err(&self, span: Span, field: T, expr_t: &ty::TyS<'_>) -> DiagnosticBuilder<'_> { type_error_struct!(self.tcx().sess, span, expr_t, E0609, From 5e019def0d8c277ef7b82d9348350ed1fc8747db Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Tue, 13 Aug 2019 23:59:22 +0200 Subject: [PATCH 2/8] typeck: extract ban_take_value_of_method --- src/librustc_typeck/check/expr.rs | 48 +++++++++++++++++++------------ 1 file changed, 29 insertions(+), 19 deletions(-) diff --git a/src/librustc_typeck/check/expr.rs b/src/librustc_typeck/check/expr.rs index b4154c15d6739..c25a60d065d6b 100644 --- a/src/librustc_typeck/check/expr.rs +++ b/src/librustc_typeck/check/expr.rs @@ -1342,23 +1342,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else if field.name == kw::Invalid { self.tcx().types.err } else if self.method_exists(field, expr_t, expr.hir_id, true) { - let mut err = type_error_struct!(self.tcx().sess, field.span, expr_t, E0615, - "attempted to take value of method `{}` on type `{}`", - field, expr_t); - - if !self.expr_in_place(expr.hir_id) { - self.suggest_method_call( - &mut err, - "use parentheses to call the method", - field, - expr_t, - expr.hir_id - ); - } else { - err.help("methods are immutable and cannot be assigned to"); - } - - err.emit(); + self.ban_take_value_of_method(expr, expr_t, field); self.tcx().types.err } else { if !expr_t.is_primitive_ty() { @@ -1436,9 +1420,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr: &'tcx hir::Expr, expr_t: Ty<'tcx>, field: ast::Ident, - def_id: DefId, + base_did: DefId, ) { - let struct_path = self.tcx().def_path_str(def_id); + let struct_path = self.tcx().def_path_str(base_did); let mut err = struct_span_err!( self.tcx().sess, expr.span, @@ -1462,6 +1446,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.emit(); } + fn ban_take_value_of_method(&self, expr: &'tcx hir::Expr, expr_t: Ty<'tcx>, field: ast::Ident) { + let mut err = type_error_struct!( + self.tcx().sess, + field.span, + expr_t, + E0615, + "attempted to take value of method `{}` on type `{}`", + field, + expr_t + ); + + if !self.expr_in_place(expr.hir_id) { + self.suggest_method_call( + &mut err, + "use parentheses to call the method", + field, + expr_t, + expr.hir_id + ); + } else { + err.help("methods are immutable and cannot be assigned to"); + } + + err.emit(); + } + fn no_such_field_err(&self, span: Span, field: T, expr_t: &ty::TyS<'_>) -> DiagnosticBuilder<'_> { type_error_struct!(self.tcx().sess, span, expr_t, E0609, From 98058468815a62be36875ed0e990bbcbcd4a42b4 Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Wed, 14 Aug 2019 00:30:06 +0200 Subject: [PATCH 3/8] typeck: extract maybe_suggest_array_indexing --- src/librustc_typeck/check/expr.rs | 50 ++++++++++++++++++------------- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/src/librustc_typeck/check/expr.rs b/src/librustc_typeck/check/expr.rs index c25a60d065d6b..20a47441375ba 100644 --- a/src/librustc_typeck/check/expr.rs +++ b/src/librustc_typeck/check/expr.rs @@ -1370,25 +1370,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; } ty::Array(_, len) => { - if let (Some(len), Ok(user_index)) = ( - len.try_eval_usize(self.tcx, self.param_env), - field.as_str().parse::() - ) { - let base = self.tcx.sess.source_map() - .span_to_snippet(base.span) - .unwrap_or_else(|_| - self.tcx.hir().hir_to_pretty_string(base.hir_id)); - let help = "instead of using tuple indexing, use array indexing"; - let suggestion = format!("{}[{}]", base, field); - let applicability = if len < user_index { - Applicability::MachineApplicable - } else { - Applicability::MaybeIncorrect - }; - err.span_suggestion( - expr.span, help, suggestion, applicability - ); - } + self.maybe_suggest_array_indexing(&mut err, expr, base, field, len); } ty::RawPtr(..) => { let base = self.tcx.sess.source_map() @@ -1417,7 +1399,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn ban_private_field_access( &self, - expr: &'tcx hir::Expr, + expr: &hir::Expr, expr_t: Ty<'tcx>, field: ast::Ident, base_did: DefId, @@ -1446,7 +1428,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.emit(); } - fn ban_take_value_of_method(&self, expr: &'tcx hir::Expr, expr_t: Ty<'tcx>, field: ast::Ident) { + fn ban_take_value_of_method(&self, expr: &hir::Expr, expr_t: Ty<'tcx>, field: ast::Ident) { let mut err = type_error_struct!( self.tcx().sess, field.span, @@ -1472,6 +1454,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.emit(); } + fn maybe_suggest_array_indexing( + &self, + err: &mut DiagnosticBuilder<'_>, + expr: &hir::Expr, + base: &hir::Expr, + field: ast::Ident, + len: &ty::Const<'tcx>, + ) { + if let (Some(len), Ok(user_index)) = ( + len.try_eval_usize(self.tcx, self.param_env), + field.as_str().parse::() + ) { + let base = self.tcx.sess.source_map() + .span_to_snippet(base.span) + .unwrap_or_else(|_| self.tcx.hir().hir_to_pretty_string(base.hir_id)); + let help = "instead of using tuple indexing, use array indexing"; + let suggestion = format!("{}[{}]", base, field); + let applicability = if len < user_index { + Applicability::MachineApplicable + } else { + Applicability::MaybeIncorrect + }; + err.span_suggestion(expr.span, help, suggestion, applicability); + } + } + fn no_such_field_err(&self, span: Span, field: T, expr_t: &ty::TyS<'_>) -> DiagnosticBuilder<'_> { type_error_struct!(self.tcx().sess, span, expr_t, E0609, From 039c78932564c38c79f08745ab6e02bceb1eb468 Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Wed, 14 Aug 2019 00:31:08 +0200 Subject: [PATCH 4/8] typeck: extract suggest_first_deref_field --- src/librustc_typeck/check/expr.rs | 32 ++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/src/librustc_typeck/check/expr.rs b/src/librustc_typeck/check/expr.rs index 20a47441375ba..2b696613c8b55 100644 --- a/src/librustc_typeck/check/expr.rs +++ b/src/librustc_typeck/check/expr.rs @@ -1373,17 +1373,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.maybe_suggest_array_indexing(&mut err, expr, base, field, len); } ty::RawPtr(..) => { - let base = self.tcx.sess.source_map() - .span_to_snippet(base.span) - .unwrap_or_else(|_| self.tcx.hir().hir_to_pretty_string(base.hir_id)); - let msg = format!("`{}` is a raw pointer; try dereferencing it", base); - let suggestion = format!("(*{}).{}", base, field); - err.span_suggestion( - expr.span, - &msg, - suggestion, - Applicability::MaybeIncorrect, - ); + self.suggest_first_deref_field(&mut err, expr, base, field); } _ => {} } @@ -1480,6 +1470,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + fn suggest_first_deref_field( + &self, + err: &mut DiagnosticBuilder<'_>, + expr: &hir::Expr, + base: &hir::Expr, + field: ast::Ident, + ) { + let base = self.tcx.sess.source_map() + .span_to_snippet(base.span) + .unwrap_or_else(|_| self.tcx.hir().hir_to_pretty_string(base.hir_id)); + let msg = format!("`{}` is a raw pointer; try dereferencing it", base); + let suggestion = format!("(*{}).{}", base, field); + err.span_suggestion( + expr.span, + &msg, + suggestion, + Applicability::MaybeIncorrect, + ); + } + fn no_such_field_err(&self, span: Span, field: T, expr_t: &ty::TyS<'_>) -> DiagnosticBuilder<'_> { type_error_struct!(self.tcx().sess, span, expr_t, E0609, From 01e96dc5832b85f87acb1a651e33d12ac9b37cc5 Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Wed, 14 Aug 2019 00:40:44 +0200 Subject: [PATCH 5/8] typeck: extract suggest_fields_on_recordish --- src/librustc_typeck/check/expr.rs | 45 ++++++++++++++++++------------- 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/src/librustc_typeck/check/expr.rs b/src/librustc_typeck/check/expr.rs index 2b696613c8b55..e4582424d0fca 100644 --- a/src/librustc_typeck/check/expr.rs +++ b/src/librustc_typeck/check/expr.rs @@ -1350,24 +1350,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match expr_t.sty { ty::Adt(def, _) if !def.is_enum() => { - if let Some(suggested_field_name) = - Self::suggest_field_name(def.non_enum_variant(), - &field.as_str(), vec![]) { - err.span_suggestion( - field.span, - "a field with a similar name exists", - suggested_field_name.to_string(), - Applicability::MaybeIncorrect, - ); - } else { - err.span_label(field.span, "unknown field"); - let struct_variant_def = def.non_enum_variant(); - let field_names = self.available_field_names(struct_variant_def); - if !field_names.is_empty() { - err.note(&format!("available fields are: {}", - self.name_series_display(field_names))); - } - }; + self.suggest_fields_on_recordish(&mut err, def, field); } ty::Array(_, len) => { self.maybe_suggest_array_indexing(&mut err, expr, base, field, len); @@ -1444,6 +1427,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.emit(); } + fn suggest_fields_on_recordish( + &self, + err: &mut DiagnosticBuilder<'_>, + def: &'tcx ty::AdtDef, + field: ast::Ident, + ) { + if let Some(suggested_field_name) = + Self::suggest_field_name(def.non_enum_variant(), &field.as_str(), vec![]) + { + err.span_suggestion( + field.span, + "a field with a similar name exists", + suggested_field_name.to_string(), + Applicability::MaybeIncorrect, + ); + } else { + err.span_label(field.span, "unknown field"); + let struct_variant_def = def.non_enum_variant(); + let field_names = self.available_field_names(struct_variant_def); + if !field_names.is_empty() { + err.note(&format!("available fields are: {}", + self.name_series_display(field_names))); + } + } + } + fn maybe_suggest_array_indexing( &self, err: &mut DiagnosticBuilder<'_>, From 07414417c58cf9e1ad1d75730007c683bd7dfae7 Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Wed, 14 Aug 2019 00:50:39 +0200 Subject: [PATCH 6/8] typeck: restructure check_field a bit --- src/librustc_typeck/check/expr.rs | 56 +++++++++++++++++-------------- 1 file changed, 31 insertions(+), 25 deletions(-) diff --git a/src/librustc_typeck/check/expr.rs b/src/librustc_typeck/check/expr.rs index e4582424d0fca..57e82714150e8 100644 --- a/src/librustc_typeck/check/expr.rs +++ b/src/librustc_typeck/check/expr.rs @@ -1338,36 +1338,42 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Some((did, field_ty)) = private_candidate { self.ban_private_field_access(expr, expr_t, field, did); - field_ty - } else if field.name == kw::Invalid { - self.tcx().types.err + return field_ty; + } + + if field.name == kw::Invalid { } else if self.method_exists(field, expr_t, expr.hir_id, true) { self.ban_take_value_of_method(expr, expr_t, field); - self.tcx().types.err - } else { - if !expr_t.is_primitive_ty() { - let mut err = self.no_such_field_err(field.span, field, expr_t); + } else if !expr_t.is_primitive_ty() { + let mut err = self.no_such_field_err(field.span, field, expr_t); - match expr_t.sty { - ty::Adt(def, _) if !def.is_enum() => { - self.suggest_fields_on_recordish(&mut err, def, field); - } - ty::Array(_, len) => { - self.maybe_suggest_array_indexing(&mut err, expr, base, field, len); - } - ty::RawPtr(..) => { - self.suggest_first_deref_field(&mut err, expr, base, field); - } - _ => {} + match expr_t.sty { + ty::Adt(def, _) if !def.is_enum() => { + self.suggest_fields_on_recordish(&mut err, def, field); } - err - } else { - type_error_struct!(self.tcx().sess, field.span, expr_t, E0610, - "`{}` is a primitive type and therefore doesn't have fields", - expr_t) - }.emit(); - self.tcx().types.err + ty::Array(_, len) => { + self.maybe_suggest_array_indexing(&mut err, expr, base, field, len); + } + ty::RawPtr(..) => { + self.suggest_first_deref_field(&mut err, expr, base, field); + } + _ => {} + } + + err.emit(); + } else { + type_error_struct!( + self.tcx().sess, + field.span, + expr_t, + E0610, + "`{}` is a primitive type and therefore doesn't have fields", + expr_t + ) + .emit(); } + + self.tcx().types.err } fn ban_private_field_access( From 88398a429c04ac915fbd314da22c6dc813c275c9 Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Wed, 14 Aug 2019 01:51:41 +0200 Subject: [PATCH 7/8] typeck: on wrong .await suggest -> 2018 --- src/librustc_typeck/check/expr.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/librustc_typeck/check/expr.rs b/src/librustc_typeck/check/expr.rs index 57e82714150e8..d139cd4264c86 100644 --- a/src/librustc_typeck/check/expr.rs +++ b/src/librustc_typeck/check/expr.rs @@ -1360,6 +1360,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => {} } + if field.name == kw::Await { + // We know by construction that `.await` is either on Rust 2015 + // or results in `ExprKind::Await`. Suggest switching the edition to 2018. + err.note("to `.await` a `Future`, switch to Rust 2018"); + err.help("set `edition = \"2018\"` in `Cargo.toml`"); + err.note("for more on editions, read https://doc.rust-lang.org/edition-guide"); + } + err.emit(); } else { type_error_struct!( From 9287eb647f7168a65950965044bd5e22d1b05faf Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Wed, 14 Aug 2019 01:52:16 +0200 Subject: [PATCH 8/8] typeck: add tests for suggesting -> 2018 on wrong .await --- .../suggest-switching-edition-on-await.rs | 45 +++++++++++++++++++ .../suggest-switching-edition-on-await.stderr | 43 ++++++++++++++++++ 2 files changed, 88 insertions(+) create mode 100644 src/test/ui/async-await/suggest-switching-edition-on-await.rs create mode 100644 src/test/ui/async-await/suggest-switching-edition-on-await.stderr diff --git a/src/test/ui/async-await/suggest-switching-edition-on-await.rs b/src/test/ui/async-await/suggest-switching-edition-on-await.rs new file mode 100644 index 0000000000000..1402f1ca92ba8 --- /dev/null +++ b/src/test/ui/async-await/suggest-switching-edition-on-await.rs @@ -0,0 +1,45 @@ +use std::pin::Pin; +use std::future::Future; + +fn main() {} + +fn await_on_struct_missing() { + struct S; + let x = S; + x.await; + //~^ ERROR no field `await` on type + //~| NOTE unknown field + //~| NOTE to `.await` a `Future`, switch to Rust 2018 + //~| HELP set `edition = "2018"` in `Cargo.toml` + //~| NOTE for more on editions, read https://doc.rust-lang.org/edition-guide +} + +fn await_on_struct_similar() { + struct S { + awai: u8, + } + let x = S { awai: 42 }; + x.await; + //~^ ERROR no field `await` on type + //~| HELP a field with a similar name exists + //~| NOTE to `.await` a `Future`, switch to Rust 2018 + //~| HELP set `edition = "2018"` in `Cargo.toml` + //~| NOTE for more on editions, read https://doc.rust-lang.org/edition-guide +} + +fn await_on_63533(x: Pin<&mut dyn Future>) { + x.await; + //~^ ERROR no field `await` on type + //~| NOTE unknown field + //~| NOTE to `.await` a `Future`, switch to Rust 2018 + //~| HELP set `edition = "2018"` in `Cargo.toml` + //~| NOTE for more on editions, read https://doc.rust-lang.org/edition-guide +} + +fn await_on_apit(x: impl Future) { + x.await; + //~^ ERROR no field `await` on type + //~| NOTE to `.await` a `Future`, switch to Rust 2018 + //~| HELP set `edition = "2018"` in `Cargo.toml` + //~| NOTE for more on editions, read https://doc.rust-lang.org/edition-guide +} diff --git a/src/test/ui/async-await/suggest-switching-edition-on-await.stderr b/src/test/ui/async-await/suggest-switching-edition-on-await.stderr new file mode 100644 index 0000000000000..f623511c0eb23 --- /dev/null +++ b/src/test/ui/async-await/suggest-switching-edition-on-await.stderr @@ -0,0 +1,43 @@ +error[E0609]: no field `await` on type `await_on_struct_missing::S` + --> $DIR/suggest-switching-edition-on-await.rs:9:7 + | +LL | x.await; + | ^^^^^ unknown field + | + = note: to `.await` a `Future`, switch to Rust 2018 + = help: set `edition = "2018"` in `Cargo.toml` + = note: for more on editions, read https://doc.rust-lang.org/edition-guide + +error[E0609]: no field `await` on type `await_on_struct_similar::S` + --> $DIR/suggest-switching-edition-on-await.rs:22:7 + | +LL | x.await; + | ^^^^^ help: a field with a similar name exists: `awai` + | + = note: to `.await` a `Future`, switch to Rust 2018 + = help: set `edition = "2018"` in `Cargo.toml` + = note: for more on editions, read https://doc.rust-lang.org/edition-guide + +error[E0609]: no field `await` on type `std::pin::Pin<&mut dyn std::future::Future>` + --> $DIR/suggest-switching-edition-on-await.rs:31:7 + | +LL | x.await; + | ^^^^^ unknown field + | + = note: to `.await` a `Future`, switch to Rust 2018 + = help: set `edition = "2018"` in `Cargo.toml` + = note: for more on editions, read https://doc.rust-lang.org/edition-guide + +error[E0609]: no field `await` on type `impl Future` + --> $DIR/suggest-switching-edition-on-await.rs:40:7 + | +LL | x.await; + | ^^^^^ + | + = note: to `.await` a `Future`, switch to Rust 2018 + = help: set `edition = "2018"` in `Cargo.toml` + = note: for more on editions, read https://doc.rust-lang.org/edition-guide + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0609`.