From e3fe0ee97b95abd13e257fa92c70248c0746165c Mon Sep 17 00:00:00 2001 From: Andy Russell Date: Wed, 2 Jan 2019 17:01:03 -0500 Subject: [PATCH] use structured suggestion for method calls Furthermore, don't suggest calling the method if it is part of a place expression, as this is invalid syntax. --- src/librustc_typeck/check/method/mod.rs | 37 +++++++++++++ src/librustc_typeck/check/mod.rs | 52 ++++++++++++++++--- src/test/ui/assign-to-method.rs | 1 + src/test/ui/assign-to-method.stderr | 12 ++++- src/test/ui/did_you_mean/issue-40396.stderr | 8 +-- src/test/ui/error-codes/E0615.stderr | 4 +- src/test/ui/implicit-method-bind.stderr | 4 +- src/test/ui/issues/issue-13853-2.stderr | 4 +- src/test/ui/issues/issue-26472.rs | 4 +- src/test/ui/issues/issue-26472.stderr | 14 +++-- .../ui/methods/method-missing-call.stderr | 8 +-- src/test/ui/union/union-suggest-field.rs | 3 +- src/test/ui/union/union-suggest-field.stderr | 4 +- 13 files changed, 116 insertions(+), 39 deletions(-) diff --git a/src/librustc_typeck/check/method/mod.rs b/src/librustc_typeck/check/method/mod.rs index 1758f76252456..02687df6a94fc 100644 --- a/src/librustc_typeck/check/method/mod.rs +++ b/src/librustc_typeck/check/method/mod.rs @@ -11,6 +11,7 @@ pub use self::CandidateSource::*; pub use self::suggest::{SelfSource, TraitInfo}; use check::FnCtxt; +use errors::{Applicability, DiagnosticBuilder}; use namespace::Namespace; use rustc_data_structures::sync::Lrc; use rustc::hir; @@ -123,6 +124,42 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } } + /// Add a suggestion to call the given method to the provided diagnostic. + crate fn suggest_method_call( + &self, + err: &mut DiagnosticBuilder<'a>, + msg: &str, + method_name: ast::Ident, + self_ty: Ty<'tcx>, + call_expr_id: ast::NodeId, + ) { + let has_params = self + .probe_for_name( + method_name.span, + probe::Mode::MethodCall, + method_name, + IsSuggestion(false), + self_ty, + call_expr_id, + ProbeScope::TraitsInScope, + ) + .and_then(|pick| { + let sig = self.tcx.fn_sig(pick.item.def_id); + Ok(sig.inputs().skip_binder().len() > 1) + }); + + let (suggestion, applicability) = if has_params.unwrap_or_default() { + ( + format!("{}(...)", method_name), + Applicability::HasPlaceholders, + ) + } else { + (format!("{}()", method_name), Applicability::MaybeIncorrect) + }; + + err.span_suggestion_with_applicability(method_name.span, msg, suggestion, applicability); + } + /// Performs method lookup. If lookup is successful, it will return the callee /// and store an appropriate adjustment for the self-expr. In some cases it may /// report an error (e.g., invoking the `drop` method). diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index d78d7273a36e6..781f4eae42676 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -3412,19 +3412,37 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { "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.id, false) { - err.note(&format!("a method `{}` also exists, perhaps you wish to call it", field)); + if self.method_exists(field, expr_t, expr.id, false) && !self.expr_in_place(expr.id) { + self.suggest_method_call( + &mut err, + &format!("a method `{}` also exists, call it with parentheses", field), + field, + expr_t, + expr.id, + ); } err.emit(); field_ty } else if field.name == keywords::Invalid.name() { self.tcx().types.err } else if self.method_exists(field, expr_t, expr.id, true) { - type_error_struct!(self.tcx().sess, field.span, expr_t, E0615, + 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) - .help("maybe a `()` to call it is missing?") - .emit(); + field, expr_t); + + if !self.expr_in_place(expr.id) { + self.suggest_method_call( + &mut err, + "use parentheses to call the method", + field, + expr_t, + expr.id + ); + } else { + err.help("methods are immutable and cannot be assigned to"); + } + + err.emit(); self.tcx().types.err } else { if !expr_t.is_primitive_ty() { @@ -5435,6 +5453,28 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { original_values, query_result) } + + /// Returns whether an expression is contained inside the LHS of an assignment expression. + fn expr_in_place(&self, mut expr_id: ast::NodeId) -> bool { + let mut contained_in_place = false; + + while let hir::Node::Expr(parent_expr) = + self.tcx.hir().get(self.tcx.hir().get_parent_node(expr_id)) + { + match &parent_expr.node { + hir::ExprKind::Assign(lhs, ..) | hir::ExprKind::AssignOp(_, lhs, ..) => { + if lhs.id == expr_id { + contained_in_place = true; + break; + } + } + _ => (), + } + expr_id = parent_expr.id; + } + + contained_in_place + } } pub fn check_bounds_are_used<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, diff --git a/src/test/ui/assign-to-method.rs b/src/test/ui/assign-to-method.rs index a7f3ab6d80dbd..95f066c382c8d 100644 --- a/src/test/ui/assign-to-method.rs +++ b/src/test/ui/assign-to-method.rs @@ -18,4 +18,5 @@ fn cat(in_x : usize, in_y : isize) -> Cat { fn main() { let nyan : Cat = cat(52, 99); nyan.speak = || println!("meow"); //~ ERROR attempted to take value of method + nyan.speak += || println!("meow"); //~ ERROR attempted to take value of method } diff --git a/src/test/ui/assign-to-method.stderr b/src/test/ui/assign-to-method.stderr index a08ac192a2282..f79f0750d8904 100644 --- a/src/test/ui/assign-to-method.stderr +++ b/src/test/ui/assign-to-method.stderr @@ -4,8 +4,16 @@ error[E0615]: attempted to take value of method `speak` on type `Cat` LL | nyan.speak = || println!("meow"); //~ ERROR attempted to take value of method | ^^^^^ | - = help: maybe a `()` to call it is missing? + = help: methods are immutable and cannot be assigned to -error: aborting due to previous error +error[E0615]: attempted to take value of method `speak` on type `Cat` + --> $DIR/assign-to-method.rs:21:8 + | +LL | nyan.speak += || println!("meow"); //~ ERROR attempted to take value of method + | ^^^^^ + | + = help: methods are immutable and cannot be assigned to + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0615`. diff --git a/src/test/ui/did_you_mean/issue-40396.stderr b/src/test/ui/did_you_mean/issue-40396.stderr index 3f6886bc3f699..86bbf2bf27ae6 100644 --- a/src/test/ui/did_you_mean/issue-40396.stderr +++ b/src/test/ui/did_you_mean/issue-40396.stderr @@ -80,17 +80,13 @@ error[E0615]: attempted to take value of method `collect` on type `std::ops::Ran --> $DIR/issue-40396.rs:2:13 | LL | (0..13).collect>(); - | ^^^^^^^ - | - = help: maybe a `()` to call it is missing? + | ^^^^^^^ help: use parentheses to call the method: `collect()` error[E0615]: attempted to take value of method `collect` on type `std::ops::Range<{integer}>` --> $DIR/issue-40396.rs:18:13 | LL | (0..13).collect(); - | ^^^^^^^ - | - = help: maybe a `()` to call it is missing? + | ^^^^^^^ help: use parentheses to call the method: `collect()` error[E0308]: mismatched types --> $DIR/issue-40396.rs:18:29 diff --git a/src/test/ui/error-codes/E0615.stderr b/src/test/ui/error-codes/E0615.stderr index 423948666db68..d86ee5b9b4b96 100644 --- a/src/test/ui/error-codes/E0615.stderr +++ b/src/test/ui/error-codes/E0615.stderr @@ -2,9 +2,7 @@ error[E0615]: attempted to take value of method `method` on type `Foo` --> $DIR/E0615.rs:11:7 | LL | f.method; //~ ERROR E0615 - | ^^^^^^ - | - = help: maybe a `()` to call it is missing? + | ^^^^^^ help: use parentheses to call the method: `method()` error: aborting due to previous error diff --git a/src/test/ui/implicit-method-bind.stderr b/src/test/ui/implicit-method-bind.stderr index 6f0ed4c5b5822..7c70709c6b978 100644 --- a/src/test/ui/implicit-method-bind.stderr +++ b/src/test/ui/implicit-method-bind.stderr @@ -2,9 +2,7 @@ error[E0615]: attempted to take value of method `abs` on type `i32` --> $DIR/implicit-method-bind.rs:2:20 | LL | let _f = 10i32.abs; //~ ERROR attempted to take value of method - | ^^^ - | - = help: maybe a `()` to call it is missing? + | ^^^ help: use parentheses to call the method: `abs()` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-13853-2.stderr b/src/test/ui/issues/issue-13853-2.stderr index 522042d3a6551..3e711243b4e17 100644 --- a/src/test/ui/issues/issue-13853-2.stderr +++ b/src/test/ui/issues/issue-13853-2.stderr @@ -2,9 +2,7 @@ error[E0615]: attempted to take value of method `get` on type `std::boxed::Box<( --> $DIR/issue-13853-2.rs:5:39 | LL | fn foo(res : Box) { res.get } //~ ERROR attempted to take value of method - | ^^^ - | - = help: maybe a `()` to call it is missing? + | ^^^ help: use parentheses to call the method: `get()` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-26472.rs b/src/test/ui/issues/issue-26472.rs index 4659c0d690242..4eb38d10a7044 100644 --- a/src/test/ui/issues/issue-26472.rs +++ b/src/test/ui/issues/issue-26472.rs @@ -8,6 +8,6 @@ mod sub { fn main() { let s = sub::S::new(); - let v = s.len; - //~^ ERROR field `len` of struct `sub::S` is private + let v = s.len; //~ ERROR field `len` of struct `sub::S` is private + s.len = v; //~ ERROR field `len` of struct `sub::S` is private } diff --git a/src/test/ui/issues/issue-26472.stderr b/src/test/ui/issues/issue-26472.stderr index dd7941e9a6e5f..8c261d2a3f3eb 100644 --- a/src/test/ui/issues/issue-26472.stderr +++ b/src/test/ui/issues/issue-26472.stderr @@ -1,11 +1,17 @@ error[E0616]: field `len` of struct `sub::S` is private --> $DIR/issue-26472.rs:11:13 | -LL | let v = s.len; - | ^^^^^ +LL | let v = s.len; //~ ERROR field `len` of struct `sub::S` is private + | ^^--- + | | + | help: a method `len` also exists, call it with parentheses: `len()` + +error[E0616]: field `len` of struct `sub::S` is private + --> $DIR/issue-26472.rs:12:5 | - = note: a method `len` also exists, perhaps you wish to call it +LL | s.len = v; //~ ERROR field `len` of struct `sub::S` is private + | ^^^^^ -error: aborting due to previous error +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0616`. diff --git a/src/test/ui/methods/method-missing-call.stderr b/src/test/ui/methods/method-missing-call.stderr index 22c907ed0024e..886d92aa9253d 100644 --- a/src/test/ui/methods/method-missing-call.stderr +++ b/src/test/ui/methods/method-missing-call.stderr @@ -2,17 +2,13 @@ error[E0615]: attempted to take value of method `get_x` on type `Point` --> $DIR/method-missing-call.rs:22:26 | LL | .get_x;//~ ERROR attempted to take value of method `get_x` on type `Point` - | ^^^^^ - | - = help: maybe a `()` to call it is missing? + | ^^^^^ help: use parentheses to call the method: `get_x()` error[E0615]: attempted to take value of method `filter_map` on type `std::iter::Filter, [closure@$DIR/method-missing-call.rs:27:20: 27:25]>, [closure@$DIR/method-missing-call.rs:28:23: 28:35]>` --> $DIR/method-missing-call.rs:29:16 | LL | .filter_map; //~ ERROR attempted to take value of method `filter_map` on type - | ^^^^^^^^^^ - | - = help: maybe a `()` to call it is missing? + | ^^^^^^^^^^ help: use parentheses to call the method: `filter_map(...)` error: aborting due to 2 previous errors diff --git a/src/test/ui/union/union-suggest-field.rs b/src/test/ui/union/union-suggest-field.rs index b2d8c0efea8fe..d84a22cee5ab2 100644 --- a/src/test/ui/union/union-suggest-field.rs +++ b/src/test/ui/union/union-suggest-field.rs @@ -16,5 +16,6 @@ fn main() { //~| SUGGESTION principal let y = u.calculate; //~ ERROR attempted to take value of method `calculate` on type `U` - //~| HELP maybe a `()` to call it is missing + //~| HELP use parentheses to call the method + //~| SUGGESTION calculate() } diff --git a/src/test/ui/union/union-suggest-field.stderr b/src/test/ui/union/union-suggest-field.stderr index 91d6b30ea0267..8ea07360d0f22 100644 --- a/src/test/ui/union/union-suggest-field.stderr +++ b/src/test/ui/union/union-suggest-field.stderr @@ -14,9 +14,7 @@ error[E0615]: attempted to take value of method `calculate` on type `U` --> $DIR/union-suggest-field.rs:18:15 | LL | let y = u.calculate; //~ ERROR attempted to take value of method `calculate` on type `U` - | ^^^^^^^^^ - | - = help: maybe a `()` to call it is missing? + | ^^^^^^^^^ help: use parentheses to call the method: `calculate()` error: aborting due to 3 previous errors