Skip to content

Commit e628196

Browse files
committed
Auto merge of #57291 - euclio:method-call-suggestion, r=estebank
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. I'm thinking it might be worth putting a label on the method assignment span like "this is a method" and removing the span from the "methods are immutable" text so it isn't reported twice. The suggestions in `src/test/ui/did_you_mean/issue-40396.stderr` are suboptimal. I could check if the containing expression is `BinOp`, but I'm not sure if that's general enough. Any ideas? r? @estebank
2 parents af2c159 + e3fe0ee commit e628196

13 files changed

+116
-39
lines changed

src/librustc_typeck/check/method/mod.rs

+37
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ pub use self::CandidateSource::*;
1111
pub use self::suggest::{SelfSource, TraitInfo};
1212

1313
use check::FnCtxt;
14+
use errors::{Applicability, DiagnosticBuilder};
1415
use namespace::Namespace;
1516
use rustc_data_structures::sync::Lrc;
1617
use rustc::hir;
@@ -123,6 +124,42 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
123124
}
124125
}
125126

127+
/// Add a suggestion to call the given method to the provided diagnostic.
128+
crate fn suggest_method_call(
129+
&self,
130+
err: &mut DiagnosticBuilder<'a>,
131+
msg: &str,
132+
method_name: ast::Ident,
133+
self_ty: Ty<'tcx>,
134+
call_expr_id: ast::NodeId,
135+
) {
136+
let has_params = self
137+
.probe_for_name(
138+
method_name.span,
139+
probe::Mode::MethodCall,
140+
method_name,
141+
IsSuggestion(false),
142+
self_ty,
143+
call_expr_id,
144+
ProbeScope::TraitsInScope,
145+
)
146+
.and_then(|pick| {
147+
let sig = self.tcx.fn_sig(pick.item.def_id);
148+
Ok(sig.inputs().skip_binder().len() > 1)
149+
});
150+
151+
let (suggestion, applicability) = if has_params.unwrap_or_default() {
152+
(
153+
format!("{}(...)", method_name),
154+
Applicability::HasPlaceholders,
155+
)
156+
} else {
157+
(format!("{}()", method_name), Applicability::MaybeIncorrect)
158+
};
159+
160+
err.span_suggestion_with_applicability(method_name.span, msg, suggestion, applicability);
161+
}
162+
126163
/// Performs method lookup. If lookup is successful, it will return the callee
127164
/// and store an appropriate adjustment for the self-expr. In some cases it may
128165
/// report an error (e.g., invoking the `drop` method).

src/librustc_typeck/check/mod.rs

+46-6
Original file line numberDiff line numberDiff line change
@@ -3442,19 +3442,37 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
34423442
"field `{}` of struct `{}` is private",
34433443
field, struct_path);
34443444
// Also check if an accessible method exists, which is often what is meant.
3445-
if self.method_exists(field, expr_t, expr.id, false) {
3446-
err.note(&format!("a method `{}` also exists, perhaps you wish to call it", field));
3445+
if self.method_exists(field, expr_t, expr.id, false) && !self.expr_in_place(expr.id) {
3446+
self.suggest_method_call(
3447+
&mut err,
3448+
&format!("a method `{}` also exists, call it with parentheses", field),
3449+
field,
3450+
expr_t,
3451+
expr.id,
3452+
);
34473453
}
34483454
err.emit();
34493455
field_ty
34503456
} else if field.name == keywords::Invalid.name() {
34513457
self.tcx().types.err
34523458
} else if self.method_exists(field, expr_t, expr.id, true) {
3453-
type_error_struct!(self.tcx().sess, field.span, expr_t, E0615,
3459+
let mut err = type_error_struct!(self.tcx().sess, field.span, expr_t, E0615,
34543460
"attempted to take value of method `{}` on type `{}`",
3455-
field, expr_t)
3456-
.help("maybe a `()` to call it is missing?")
3457-
.emit();
3461+
field, expr_t);
3462+
3463+
if !self.expr_in_place(expr.id) {
3464+
self.suggest_method_call(
3465+
&mut err,
3466+
"use parentheses to call the method",
3467+
field,
3468+
expr_t,
3469+
expr.id
3470+
);
3471+
} else {
3472+
err.help("methods are immutable and cannot be assigned to");
3473+
}
3474+
3475+
err.emit();
34583476
self.tcx().types.err
34593477
} else {
34603478
if !expr_t.is_primitive_ty() {
@@ -5507,6 +5525,28 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
55075525
original_values,
55085526
query_result)
55095527
}
5528+
5529+
/// Returns whether an expression is contained inside the LHS of an assignment expression.
5530+
fn expr_in_place(&self, mut expr_id: ast::NodeId) -> bool {
5531+
let mut contained_in_place = false;
5532+
5533+
while let hir::Node::Expr(parent_expr) =
5534+
self.tcx.hir().get(self.tcx.hir().get_parent_node(expr_id))
5535+
{
5536+
match &parent_expr.node {
5537+
hir::ExprKind::Assign(lhs, ..) | hir::ExprKind::AssignOp(_, lhs, ..) => {
5538+
if lhs.id == expr_id {
5539+
contained_in_place = true;
5540+
break;
5541+
}
5542+
}
5543+
_ => (),
5544+
}
5545+
expr_id = parent_expr.id;
5546+
}
5547+
5548+
contained_in_place
5549+
}
55105550
}
55115551

55125552
pub fn check_bounds_are_used<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,

src/test/ui/assign-to-method.rs

+1
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,5 @@ fn cat(in_x : usize, in_y : isize) -> Cat {
1818
fn main() {
1919
let nyan : Cat = cat(52, 99);
2020
nyan.speak = || println!("meow"); //~ ERROR attempted to take value of method
21+
nyan.speak += || println!("meow"); //~ ERROR attempted to take value of method
2122
}

src/test/ui/assign-to-method.stderr

+10-2
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,16 @@ error[E0615]: attempted to take value of method `speak` on type `Cat`
44
LL | nyan.speak = || println!("meow"); //~ ERROR attempted to take value of method
55
| ^^^^^
66
|
7-
= help: maybe a `()` to call it is missing?
7+
= help: methods are immutable and cannot be assigned to
88

9-
error: aborting due to previous error
9+
error[E0615]: attempted to take value of method `speak` on type `Cat`
10+
--> $DIR/assign-to-method.rs:21:8
11+
|
12+
LL | nyan.speak += || println!("meow"); //~ ERROR attempted to take value of method
13+
| ^^^^^
14+
|
15+
= help: methods are immutable and cannot be assigned to
16+
17+
error: aborting due to 2 previous errors
1018

1119
For more information about this error, try `rustc --explain E0615`.

src/test/ui/did_you_mean/issue-40396.stderr

+2-6
Original file line numberDiff line numberDiff line change
@@ -80,17 +80,13 @@ error[E0615]: attempted to take value of method `collect` on type `std::ops::Ran
8080
--> $DIR/issue-40396.rs:2:13
8181
|
8282
LL | (0..13).collect<Vec<i32>>();
83-
| ^^^^^^^
84-
|
85-
= help: maybe a `()` to call it is missing?
83+
| ^^^^^^^ help: use parentheses to call the method: `collect()`
8684

8785
error[E0615]: attempted to take value of method `collect` on type `std::ops::Range<{integer}>`
8886
--> $DIR/issue-40396.rs:18:13
8987
|
9088
LL | (0..13).collect<Vec<i32>();
91-
| ^^^^^^^
92-
|
93-
= help: maybe a `()` to call it is missing?
89+
| ^^^^^^^ help: use parentheses to call the method: `collect()`
9490

9591
error[E0308]: mismatched types
9692
--> $DIR/issue-40396.rs:18:29

src/test/ui/error-codes/E0615.stderr

+1-3
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,7 @@ error[E0615]: attempted to take value of method `method` on type `Foo`
22
--> $DIR/E0615.rs:11:7
33
|
44
LL | f.method; //~ ERROR E0615
5-
| ^^^^^^
6-
|
7-
= help: maybe a `()` to call it is missing?
5+
| ^^^^^^ help: use parentheses to call the method: `method()`
86

97
error: aborting due to previous error
108

src/test/ui/implicit-method-bind.stderr

+1-3
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,7 @@ error[E0615]: attempted to take value of method `abs` on type `i32`
22
--> $DIR/implicit-method-bind.rs:2:20
33
|
44
LL | let _f = 10i32.abs; //~ ERROR attempted to take value of method
5-
| ^^^
6-
|
7-
= help: maybe a `()` to call it is missing?
5+
| ^^^ help: use parentheses to call the method: `abs()`
86

97
error: aborting due to previous error
108

src/test/ui/issues/issue-13853-2.stderr

+1-3
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,7 @@ error[E0615]: attempted to take value of method `get` on type `std::boxed::Box<(
22
--> $DIR/issue-13853-2.rs:5:39
33
|
44
LL | fn foo(res : Box<ResponseHook>) { res.get } //~ ERROR attempted to take value of method
5-
| ^^^
6-
|
7-
= help: maybe a `()` to call it is missing?
5+
| ^^^ help: use parentheses to call the method: `get()`
86

97
error: aborting due to previous error
108

src/test/ui/issues/issue-26472.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@ mod sub {
88

99
fn main() {
1010
let s = sub::S::new();
11-
let v = s.len;
12-
//~^ ERROR field `len` of struct `sub::S` is private
11+
let v = s.len; //~ ERROR field `len` of struct `sub::S` is private
12+
s.len = v; //~ ERROR field `len` of struct `sub::S` is private
1313
}

src/test/ui/issues/issue-26472.stderr

+10-4
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
11
error[E0616]: field `len` of struct `sub::S` is private
22
--> $DIR/issue-26472.rs:11:13
33
|
4-
LL | let v = s.len;
5-
| ^^^^^
4+
LL | let v = s.len; //~ ERROR field `len` of struct `sub::S` is private
5+
| ^^---
6+
| |
7+
| help: a method `len` also exists, call it with parentheses: `len()`
8+
9+
error[E0616]: field `len` of struct `sub::S` is private
10+
--> $DIR/issue-26472.rs:12:5
611
|
7-
= note: a method `len` also exists, perhaps you wish to call it
12+
LL | s.len = v; //~ ERROR field `len` of struct `sub::S` is private
13+
| ^^^^^
814

9-
error: aborting due to previous error
15+
error: aborting due to 2 previous errors
1016

1117
For more information about this error, try `rustc --explain E0616`.

src/test/ui/methods/method-missing-call.stderr

+2-6
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,13 @@ error[E0615]: attempted to take value of method `get_x` on type `Point`
22
--> $DIR/method-missing-call.rs:22:26
33
|
44
LL | .get_x;//~ ERROR attempted to take value of method `get_x` on type `Point`
5-
| ^^^^^
6-
|
7-
= help: maybe a `()` to call it is missing?
5+
| ^^^^^ help: use parentheses to call the method: `get_x()`
86

97
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]>`
108
--> $DIR/method-missing-call.rs:29:16
119
|
1210
LL | .filter_map; //~ ERROR attempted to take value of method `filter_map` on type
13-
| ^^^^^^^^^^
14-
|
15-
= help: maybe a `()` to call it is missing?
11+
| ^^^^^^^^^^ help: use parentheses to call the method: `filter_map(...)`
1612

1713
error: aborting due to 2 previous errors
1814

src/test/ui/union/union-suggest-field.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,6 @@ fn main() {
1616
//~| SUGGESTION principal
1717

1818
let y = u.calculate; //~ ERROR attempted to take value of method `calculate` on type `U`
19-
//~| HELP maybe a `()` to call it is missing
19+
//~| HELP use parentheses to call the method
20+
//~| SUGGESTION calculate()
2021
}

src/test/ui/union/union-suggest-field.stderr

+1-3
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,7 @@ error[E0615]: attempted to take value of method `calculate` on type `U`
1414
--> $DIR/union-suggest-field.rs:18:15
1515
|
1616
LL | let y = u.calculate; //~ ERROR attempted to take value of method `calculate` on type `U`
17-
| ^^^^^^^^^
18-
|
19-
= help: maybe a `()` to call it is missing?
17+
| ^^^^^^^^^ help: use parentheses to call the method: `calculate()`
2018

2119
error: aborting due to 3 previous errors
2220

0 commit comments

Comments
 (0)