Skip to content

Commit d0fe179

Browse files
authored
Rollup merge of rust-lang#66913 - VirrageS:help-self, r=varkor,Centril
Suggest calling method when first argument is `self` Closes: rust-lang#66782 I've explored different approaches for this MR but I think the most straightforward is the best one. I've tried to find out if the methods for given type exist (to maybe have a better suggestion), but we don't collect them anywhere and collecting them is quite problematic. Moreover, collecting all the methods would require rewriting big part of the code and also could potentially include performance degradation, which I don't think is necessary for this simple case.
2 parents c5840f9 + 7b91ef8 commit d0fe179

File tree

4 files changed

+121
-1
lines changed

4 files changed

+121
-1
lines changed

src/librustc_resolve/late.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -345,7 +345,7 @@ struct DiagnosticMetadata {
345345
/// The current self item if inside an ADT (used for better errors).
346346
current_self_item: Option<NodeId>,
347347

348-
/// The current enclosing funciton (used for better errors).
348+
/// The current enclosing function (used for better errors).
349349
current_function: Option<Span>,
350350

351351
/// A list of labels as of yet unused. Labels will be removed from this map when

src/librustc_resolve/late/diagnostics.rs

+55
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,24 @@ impl<'a> LateResolutionVisitor<'a, '_> {
259259
}
260260
return (err, candidates);
261261
}
262+
263+
// If the first argument in call is `self` suggest calling a method.
264+
if let Some((call_span, args_span)) = self.call_has_self_arg(source) {
265+
let mut args_snippet = String::new();
266+
if let Some(args_span) = args_span {
267+
if let Ok(snippet) = self.r.session.source_map().span_to_snippet(args_span) {
268+
args_snippet = snippet;
269+
}
270+
}
271+
272+
err.span_suggestion(
273+
call_span,
274+
&format!("try calling `{}` as a method", ident),
275+
format!("self.{}({})", path_str, args_snippet),
276+
Applicability::MachineApplicable,
277+
);
278+
return (err, candidates);
279+
}
262280
}
263281

264282
// Try Levenshtein algorithm.
@@ -298,6 +316,43 @@ impl<'a> LateResolutionVisitor<'a, '_> {
298316
(err, candidates)
299317
}
300318

319+
/// Check if the source is call expression and the first argument is `self`. If true,
320+
/// return the span of whole call and the span for all arguments expect the first one (`self`).
321+
fn call_has_self_arg(&self, source: PathSource<'_>) -> Option<(Span, Option<Span>)> {
322+
let mut has_self_arg = None;
323+
if let PathSource::Expr(parent) = source {
324+
match &parent?.kind {
325+
ExprKind::Call(_, args) if args.len() > 0 => {
326+
let mut expr_kind = &args[0].kind;
327+
loop {
328+
match expr_kind {
329+
ExprKind::Path(_, arg_name) if arg_name.segments.len() == 1 => {
330+
if arg_name.segments[0].ident.name == kw::SelfLower {
331+
let call_span = parent.unwrap().span;
332+
let tail_args_span = if args.len() > 1 {
333+
Some(Span::new(
334+
args[1].span.lo(),
335+
args.last().unwrap().span.hi(),
336+
call_span.ctxt(),
337+
))
338+
} else {
339+
None
340+
};
341+
has_self_arg = Some((call_span, tail_args_span));
342+
}
343+
break;
344+
}
345+
ExprKind::AddrOf(_, _, expr) => expr_kind = &expr.kind,
346+
_ => break,
347+
}
348+
}
349+
}
350+
_ => (),
351+
}
352+
};
353+
return has_self_arg;
354+
}
355+
301356
fn followed_by_brace(&self, span: Span) -> (bool, Option<(Span, String)>) {
302357
// HACK(estebank): find a better way to figure out that this was a
303358
// parser issue where a struct literal is being used on an expression

src/test/ui/self/suggest-self-2.rs

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
struct Foo {}
2+
3+
impl Foo {
4+
fn foo(&self) {
5+
bar(self);
6+
//~^ ERROR cannot find function `bar` in this scope
7+
//~| HELP try calling `bar` as a method
8+
9+
bar(&&self, 102);
10+
//~^ ERROR cannot find function `bar` in this scope
11+
//~| HELP try calling `bar` as a method
12+
13+
bar(&mut self, 102, &"str");
14+
//~^ ERROR cannot find function `bar` in this scope
15+
//~| HELP try calling `bar` as a method
16+
17+
bar();
18+
//~^ ERROR cannot find function `bar` in this scope
19+
20+
self.bar();
21+
//~^ ERROR no method named `bar` found for type
22+
}
23+
}
24+
25+
fn main() {}
+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
error[E0425]: cannot find function `bar` in this scope
2+
--> $DIR/suggest-self-2.rs:5:9
3+
|
4+
LL | bar(self);
5+
| ^^^------
6+
| |
7+
| help: try calling `bar` as a method: `self.bar()`
8+
9+
error[E0425]: cannot find function `bar` in this scope
10+
--> $DIR/suggest-self-2.rs:9:9
11+
|
12+
LL | bar(&&self, 102);
13+
| ^^^-------------
14+
| |
15+
| help: try calling `bar` as a method: `self.bar(102)`
16+
17+
error[E0425]: cannot find function `bar` in this scope
18+
--> $DIR/suggest-self-2.rs:13:9
19+
|
20+
LL | bar(&mut self, 102, &"str");
21+
| ^^^------------------------
22+
| |
23+
| help: try calling `bar` as a method: `self.bar(102, &"str")`
24+
25+
error[E0425]: cannot find function `bar` in this scope
26+
--> $DIR/suggest-self-2.rs:17:9
27+
|
28+
LL | bar();
29+
| ^^^ not found in this scope
30+
31+
error[E0599]: no method named `bar` found for type `&Foo` in the current scope
32+
--> $DIR/suggest-self-2.rs:20:14
33+
|
34+
LL | self.bar();
35+
| ^^^ method not found in `&Foo`
36+
37+
error: aborting due to 5 previous errors
38+
39+
Some errors have detailed explanations: E0425, E0599.
40+
For more information about an error, try `rustc --explain E0425`.

0 commit comments

Comments
 (0)