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.
This commit is contained in:
Andy Russell 2019-01-02 17:01:03 -05:00
parent cae164753f
commit e3fe0ee97b
No known key found for this signature in database
GPG Key ID: BE2221033EDBC374
13 changed files with 117 additions and 40 deletions

View File

@ -11,6 +11,7 @@ pub use self::CandidateSource::*;
pub use self::suggest::{SelfSource, TraitInfo}; pub use self::suggest::{SelfSource, TraitInfo};
use check::FnCtxt; use check::FnCtxt;
use errors::{Applicability, DiagnosticBuilder};
use namespace::Namespace; use namespace::Namespace;
use rustc_data_structures::sync::Lrc; use rustc_data_structures::sync::Lrc;
use rustc::hir; 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 /// 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 /// and store an appropriate adjustment for the self-expr. In some cases it may
/// report an error (e.g., invoking the `drop` method). /// report an error (e.g., invoking the `drop` method).

View File

@ -3412,19 +3412,37 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
"field `{}` of struct `{}` is private", "field `{}` of struct `{}` is private",
field, struct_path); field, struct_path);
// Also check if an accessible method exists, which is often what is meant. // Also check if an accessible method exists, which is often what is meant.
if self.method_exists(field, expr_t, expr.id, false) { if self.method_exists(field, expr_t, expr.id, false) && !self.expr_in_place(expr.id) {
err.note(&format!("a method `{}` also exists, perhaps you wish to call it", field)); self.suggest_method_call(
&mut err,
&format!("a method `{}` also exists, call it with parentheses", field),
field,
expr_t,
expr.id,
);
} }
err.emit(); err.emit();
field_ty field_ty
} else if field.name == keywords::Invalid.name() { } else if field.name == keywords::Invalid.name() {
self.tcx().types.err self.tcx().types.err
} else if self.method_exists(field, expr_t, expr.id, true) { } 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 `{}`", "attempted to take value of method `{}` on type `{}`",
field, expr_t) field, expr_t);
.help("maybe a `()` to call it is missing?")
.emit(); 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 self.tcx().types.err
} else { } else {
if !expr_t.is_primitive_ty() { if !expr_t.is_primitive_ty() {
@ -5435,6 +5453,28 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
original_values, original_values,
query_result) 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>, pub fn check_bounds_are_used<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,

View File

@ -18,4 +18,5 @@ fn cat(in_x : usize, in_y : isize) -> Cat {
fn main() { fn main() {
let nyan : Cat = cat(52, 99); 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
nyan.speak += || println!("meow"); //~ ERROR attempted to take value of method
} }

View File

@ -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 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`. For more information about this error, try `rustc --explain E0615`.

View File

@ -80,17 +80,13 @@ error[E0615]: attempted to take value of method `collect` on type `std::ops::Ran
--> $DIR/issue-40396.rs:2:13 --> $DIR/issue-40396.rs:2:13
| |
LL | (0..13).collect<Vec<i32>>(); LL | (0..13).collect<Vec<i32>>();
| ^^^^^^^ | ^^^^^^^ help: use parentheses to call the method: `collect()`
|
= help: maybe a `()` to call it is missing?
error[E0615]: attempted to take value of method `collect` on type `std::ops::Range<{integer}>` error[E0615]: attempted to take value of method `collect` on type `std::ops::Range<{integer}>`
--> $DIR/issue-40396.rs:18:13 --> $DIR/issue-40396.rs:18:13
| |
LL | (0..13).collect<Vec<i32>(); LL | (0..13).collect<Vec<i32>();
| ^^^^^^^ | ^^^^^^^ help: use parentheses to call the method: `collect()`
|
= help: maybe a `()` to call it is missing?
error[E0308]: mismatched types error[E0308]: mismatched types
--> $DIR/issue-40396.rs:18:29 --> $DIR/issue-40396.rs:18:29

View File

@ -2,9 +2,7 @@ error[E0615]: attempted to take value of method `method` on type `Foo`
--> $DIR/E0615.rs:11:7 --> $DIR/E0615.rs:11:7
| |
LL | f.method; //~ ERROR E0615 LL | f.method; //~ ERROR E0615
| ^^^^^^ | ^^^^^^ help: use parentheses to call the method: `method()`
|
= help: maybe a `()` to call it is missing?
error: aborting due to previous error error: aborting due to previous error

View File

@ -2,9 +2,7 @@ error[E0615]: attempted to take value of method `abs` on type `i32`
--> $DIR/implicit-method-bind.rs:2:20 --> $DIR/implicit-method-bind.rs:2:20
| |
LL | let _f = 10i32.abs; //~ ERROR attempted to take value of method LL | let _f = 10i32.abs; //~ ERROR attempted to take value of method
| ^^^ | ^^^ help: use parentheses to call the method: `abs()`
|
= help: maybe a `()` to call it is missing?
error: aborting due to previous error error: aborting due to previous error

View File

@ -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 --> $DIR/issue-13853-2.rs:5:39
| |
LL | fn foo(res : Box<ResponseHook>) { res.get } //~ ERROR attempted to take value of method LL | fn foo(res : Box<ResponseHook>) { res.get } //~ ERROR attempted to take value of method
| ^^^ | ^^^ help: use parentheses to call the method: `get()`
|
= help: maybe a `()` to call it is missing?
error: aborting due to previous error error: aborting due to previous error

View File

@ -8,6 +8,6 @@ mod sub {
fn main() { fn main() {
let s = sub::S::new(); let s = sub::S::new();
let v = s.len; let v = s.len; //~ ERROR field `len` of struct `sub::S` is private
//~^ ERROR field `len` of struct `sub::S` is private s.len = v; //~ ERROR field `len` of struct `sub::S` is private
} }

View File

@ -1,11 +1,17 @@
error[E0616]: field `len` of struct `sub::S` is private error[E0616]: field `len` of struct `sub::S` is private
--> $DIR/issue-26472.rs:11:13 --> $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
| ^^^^^ | ^^---
| | |
= note: a method `len` also exists, perhaps you wish to call it | help: a method `len` also exists, call it with parentheses: `len()`
error: aborting due to previous error error[E0616]: field `len` of struct `sub::S` is private
--> $DIR/issue-26472.rs:12:5
|
LL | s.len = v; //~ ERROR field `len` of struct `sub::S` is private
| ^^^^^
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0616`. For more information about this error, try `rustc --explain E0616`.

View File

@ -2,17 +2,13 @@ error[E0615]: attempted to take value of method `get_x` on type `Point`
--> $DIR/method-missing-call.rs:22:26 --> $DIR/method-missing-call.rs:22:26
| |
LL | .get_x;//~ ERROR attempted to take value of method `get_x` on type `Point` LL | .get_x;//~ ERROR attempted to take value of method `get_x` on type `Point`
| ^^^^^ | ^^^^^ help: use parentheses to call the method: `get_x()`
|
= help: maybe a `()` to call it is missing?
error[E0615]: attempted to take value of method `filter_map` on type `std::iter::Filter<std::iter::Map<std::slice::Iter<'_, {integer}>, [closure@$DIR/method-missing-call.rs:27:20: 27:25]>, [closure@$DIR/method-missing-call.rs:28:23: 28:35]>` error[E0615]: attempted to take value of method `filter_map` on type `std::iter::Filter<std::iter::Map<std::slice::Iter<'_, {integer}>, [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 --> $DIR/method-missing-call.rs:29:16
| |
LL | .filter_map; //~ ERROR attempted to take value of method `filter_map` on type LL | .filter_map; //~ ERROR attempted to take value of method `filter_map` on type
| ^^^^^^^^^^ | ^^^^^^^^^^ help: use parentheses to call the method: `filter_map(...)`
|
= help: maybe a `()` to call it is missing?
error: aborting due to 2 previous errors error: aborting due to 2 previous errors

View File

@ -16,5 +16,6 @@ fn main() {
//~| SUGGESTION principal //~| SUGGESTION principal
let y = u.calculate; //~ ERROR attempted to take value of method `calculate` on type `U` 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()
} }

View File

@ -14,9 +14,7 @@ error[E0615]: attempted to take value of method `calculate` on type `U`
--> $DIR/union-suggest-field.rs:18:15 --> $DIR/union-suggest-field.rs:18:15
| |
LL | let y = u.calculate; //~ ERROR attempted to take value of method `calculate` on type `U` LL | let y = u.calculate; //~ ERROR attempted to take value of method `calculate` on type `U`
| ^^^^^^^^^ | ^^^^^^^^^ help: use parentheses to call the method: `calculate()`
|
= help: maybe a `()` to call it is missing?
error: aborting due to 3 previous errors error: aborting due to 3 previous errors