From f13b8fb686ca5ae178de211cf8ae2dcc1f134526 Mon Sep 17 00:00:00 2001 From: Janusz Marcinkiewicz Date: Sat, 30 Nov 2019 18:59:51 +0100 Subject: [PATCH 1/4] Suggest calling method when first argument is `self` --- src/doc/rustc-guide | 2 +- src/librustc_resolve/build_reduced_graph.rs | 10 ++--- src/librustc_resolve/late.rs | 2 +- src/librustc_resolve/late/diagnostics.rs | 45 +++++++++++++++++---- src/test/ui/self/suggest-self-2.rs | 22 ++++++++++ src/test/ui/self/suggest-self-2.stderr | 28 +++++++++++++ 6 files changed, 95 insertions(+), 14 deletions(-) create mode 100644 src/test/ui/self/suggest-self-2.rs create mode 100644 src/test/ui/self/suggest-self-2.stderr diff --git a/src/doc/rustc-guide b/src/doc/rustc-guide index 7c56708aab798..934380b7cfcea 160000 --- a/src/doc/rustc-guide +++ b/src/doc/rustc-guide @@ -1 +1 @@ -Subproject commit 7c56708aab7986ca390221e8e8902f7de7f9b076 +Subproject commit 934380b7cfceaaa4e1b9bb0de4a372f32725520b diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs index 566ba12907437..55b20b31ad8d4 100644 --- a/src/librustc_resolve/build_reduced_graph.rs +++ b/src/librustc_resolve/build_reduced_graph.rs @@ -719,8 +719,8 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { // These items live in both the type and value namespaces. ItemKind::Struct(ref vdata, _) => { // Define a name in the type namespace. - let def_id = self.r.definitions.local_def_id(item.id); - let res = Res::Def(DefKind::Struct, def_id); + let item_def_id = self.r.definitions.local_def_id(item.id); + let res = Res::Def(DefKind::Struct, item_def_id); self.r.define(parent, ident, TypeNS, (res, vis, sp, expansion)); // Record field names for error reporting. @@ -757,12 +757,12 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { } ItemKind::Union(ref vdata, _) => { - let def_id = self.r.definitions.local_def_id(item.id); - let res = Res::Def(DefKind::Union, def_id); + let item_def_id = self.r.definitions.local_def_id(item.id); + let res = Res::Def(DefKind::Union, item_def_id); self.r.define(parent, ident, TypeNS, (res, vis, sp, expansion)); // Record field names for error reporting. - self.insert_field_names_local(def_id, vdata); + self.insert_field_names_local(item_def_id, vdata); } ItemKind::Impl(.., ref impl_items) => { diff --git a/src/librustc_resolve/late.rs b/src/librustc_resolve/late.rs index 4f95d6fe70f17..064000438607d 100644 --- a/src/librustc_resolve/late.rs +++ b/src/librustc_resolve/late.rs @@ -345,7 +345,7 @@ struct DiagnosticMetadata { /// The current self item if inside an ADT (used for better errors). current_self_item: Option, - /// The current enclosing funciton (used for better errors). + /// The current enclosing function (used for better errors). current_function: Option, /// A list of labels as of yet unused. Labels will be removed from this map when diff --git a/src/librustc_resolve/late/diagnostics.rs b/src/librustc_resolve/late/diagnostics.rs index 3be033697d286..fd0f6b01d4ce7 100644 --- a/src/librustc_resolve/late/diagnostics.rs +++ b/src/librustc_resolve/late/diagnostics.rs @@ -253,6 +253,37 @@ impl<'a> LateResolutionVisitor<'a, '_> { } return (err, candidates); } + + // Check if the first argument is `self` and suggest calling a method. + let mut has_self_arg = false; + if let PathSource::Expr(parent) = source { + match &parent.map(|p| &p.kind) { + Some(ExprKind::Call(_, args)) if args.len() > 0 => { + let mut expr_kind = &args.first().unwrap().kind; + loop { + match expr_kind { + ExprKind::Path(_, arg_name) if arg_name.segments.len() == 1 => { + has_self_arg = arg_name.segments[0].ident.name == kw::SelfLower; + break; + }, + ExprKind::AddrOf(_, _, expr) => { expr_kind = &expr.kind; } + _ => break, + } + } + } + _ => (), + } + }; + + if has_self_arg { + err.span_suggestion( + span, + &"try calling method instead of passing `self` as parameter", + format!("self.{}", path_str), + Applicability::MachineApplicable, + ); + return (err, candidates); + } } // Try Levenshtein algorithm. @@ -553,13 +584,13 @@ impl<'a> LateResolutionVisitor<'a, '_> { // Look for associated items in the current trait. if let Some((module, _)) = self.current_trait_ref { if let Ok(binding) = self.r.resolve_ident_in_module( - ModuleOrUniformRoot::Module(module), - ident, - ns, - &self.parent_scope, - false, - module.span, - ) { + ModuleOrUniformRoot::Module(module), + ident, + ns, + &self.parent_scope, + false, + module.span, + ) { let res = binding.res(); if filter_fn(res) { return Some(if self.r.has_self.contains(&res.def_id()) { diff --git a/src/test/ui/self/suggest-self-2.rs b/src/test/ui/self/suggest-self-2.rs new file mode 100644 index 0000000000000..1926ebe4b8317 --- /dev/null +++ b/src/test/ui/self/suggest-self-2.rs @@ -0,0 +1,22 @@ +struct Foo {} + +impl Foo { + fn foo(&self) { + bar(self); + //~^ ERROR cannot find function `bar` in this scope + //~| HELP try calling method instead of passing `self` as parameter + + + bar(&self); + //~^ ERROR cannot find function `bar` in this scope + //~| HELP try calling method instead of passing `self` as parameter + + bar(); + //~^ ERROR cannot find function `bar` in this scope + + self.bar(); + //~^ ERROR no method named `bar` found for type + } +} + +fn main() {} diff --git a/src/test/ui/self/suggest-self-2.stderr b/src/test/ui/self/suggest-self-2.stderr new file mode 100644 index 0000000000000..84dbaa9637898 --- /dev/null +++ b/src/test/ui/self/suggest-self-2.stderr @@ -0,0 +1,28 @@ +error[E0425]: cannot find function `bar` in this scope + --> $DIR/suggest-self-2.rs:5:9 + | +LL | bar(self); + | ^^^ help: try calling method instead of passing `self` as parameter: `self.bar` + +error[E0425]: cannot find function `bar` in this scope + --> $DIR/suggest-self-2.rs:10:9 + | +LL | bar(&self); + | ^^^ help: try calling method instead of passing `self` as parameter: `self.bar` + +error[E0425]: cannot find function `bar` in this scope + --> $DIR/suggest-self-2.rs:14:9 + | +LL | bar(); + | ^^^ not found in this scope + +error[E0599]: no method named `bar` found for type `&Foo` in the current scope + --> $DIR/suggest-self-2.rs:17:14 + | +LL | self.bar(); + | ^^^ method not found in `&Foo` + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0425, E0599. +For more information about an error, try `rustc --explain E0425`. From 4b6305cfa49a7d9e764baacc640d519383cac8c2 Mon Sep 17 00:00:00 2001 From: Janusz Marcinkiewicz Date: Mon, 9 Dec 2019 16:05:45 +0100 Subject: [PATCH 2/4] Add more detailed suggestion --- src/doc/rustc-guide | 2 +- src/librustc_resolve/build_reduced_graph.rs | 10 +++++----- src/librustc_resolve/late/diagnostics.rs | 6 +++--- src/test/ui/self/suggest-self-2.rs | 9 ++++++--- src/test/ui/self/suggest-self-2.stderr | 20 +++++++++++++------- 5 files changed, 28 insertions(+), 19 deletions(-) diff --git a/src/doc/rustc-guide b/src/doc/rustc-guide index 934380b7cfcea..7c56708aab798 160000 --- a/src/doc/rustc-guide +++ b/src/doc/rustc-guide @@ -1 +1 @@ -Subproject commit 934380b7cfceaaa4e1b9bb0de4a372f32725520b +Subproject commit 7c56708aab7986ca390221e8e8902f7de7f9b076 diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs index 55b20b31ad8d4..566ba12907437 100644 --- a/src/librustc_resolve/build_reduced_graph.rs +++ b/src/librustc_resolve/build_reduced_graph.rs @@ -719,8 +719,8 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { // These items live in both the type and value namespaces. ItemKind::Struct(ref vdata, _) => { // Define a name in the type namespace. - let item_def_id = self.r.definitions.local_def_id(item.id); - let res = Res::Def(DefKind::Struct, item_def_id); + let def_id = self.r.definitions.local_def_id(item.id); + let res = Res::Def(DefKind::Struct, def_id); self.r.define(parent, ident, TypeNS, (res, vis, sp, expansion)); // Record field names for error reporting. @@ -757,12 +757,12 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { } ItemKind::Union(ref vdata, _) => { - let item_def_id = self.r.definitions.local_def_id(item.id); - let res = Res::Def(DefKind::Union, item_def_id); + let def_id = self.r.definitions.local_def_id(item.id); + let res = Res::Def(DefKind::Union, def_id); self.r.define(parent, ident, TypeNS, (res, vis, sp, expansion)); // Record field names for error reporting. - self.insert_field_names_local(item_def_id, vdata); + self.insert_field_names_local(def_id, vdata); } ItemKind::Impl(.., ref impl_items) => { diff --git a/src/librustc_resolve/late/diagnostics.rs b/src/librustc_resolve/late/diagnostics.rs index fd0f6b01d4ce7..1df8ff02fcebe 100644 --- a/src/librustc_resolve/late/diagnostics.rs +++ b/src/librustc_resolve/late/diagnostics.rs @@ -259,14 +259,14 @@ impl<'a> LateResolutionVisitor<'a, '_> { if let PathSource::Expr(parent) = source { match &parent.map(|p| &p.kind) { Some(ExprKind::Call(_, args)) if args.len() > 0 => { - let mut expr_kind = &args.first().unwrap().kind; + let mut expr_kind = &args[0].kind; loop { match expr_kind { ExprKind::Path(_, arg_name) if arg_name.segments.len() == 1 => { has_self_arg = arg_name.segments[0].ident.name == kw::SelfLower; break; }, - ExprKind::AddrOf(_, _, expr) => { expr_kind = &expr.kind; } + ExprKind::AddrOf(_, _, expr) => expr_kind = &expr.kind, _ => break, } } @@ -278,7 +278,7 @@ impl<'a> LateResolutionVisitor<'a, '_> { if has_self_arg { err.span_suggestion( span, - &"try calling method instead of passing `self` as parameter", + &format!("try calling `{}` as a method", ident), format!("self.{}", path_str), Applicability::MachineApplicable, ); diff --git a/src/test/ui/self/suggest-self-2.rs b/src/test/ui/self/suggest-self-2.rs index 1926ebe4b8317..d6bf543352701 100644 --- a/src/test/ui/self/suggest-self-2.rs +++ b/src/test/ui/self/suggest-self-2.rs @@ -4,12 +4,15 @@ impl Foo { fn foo(&self) { bar(self); //~^ ERROR cannot find function `bar` in this scope - //~| HELP try calling method instead of passing `self` as parameter + //~| HELP try calling `bar` as a method + bar(&&self, 102); + //~^ ERROR cannot find function `bar` in this scope + //~| HELP try calling `bar` as a method - bar(&self); + bar(&mut self, 102, &"str"); //~^ ERROR cannot find function `bar` in this scope - //~| HELP try calling method instead of passing `self` as parameter + //~| HELP try calling `bar` as a method bar(); //~^ ERROR cannot find function `bar` in this scope diff --git a/src/test/ui/self/suggest-self-2.stderr b/src/test/ui/self/suggest-self-2.stderr index 84dbaa9637898..ba71498fae656 100644 --- a/src/test/ui/self/suggest-self-2.stderr +++ b/src/test/ui/self/suggest-self-2.stderr @@ -2,27 +2,33 @@ error[E0425]: cannot find function `bar` in this scope --> $DIR/suggest-self-2.rs:5:9 | LL | bar(self); - | ^^^ help: try calling method instead of passing `self` as parameter: `self.bar` + | ^^^ help: try calling `bar` as a method: `self.bar` error[E0425]: cannot find function `bar` in this scope - --> $DIR/suggest-self-2.rs:10:9 + --> $DIR/suggest-self-2.rs:9:9 | -LL | bar(&self); - | ^^^ help: try calling method instead of passing `self` as parameter: `self.bar` +LL | bar(&&self, 102); + | ^^^ help: try calling `bar` as a method: `self.bar` error[E0425]: cannot find function `bar` in this scope - --> $DIR/suggest-self-2.rs:14:9 + --> $DIR/suggest-self-2.rs:13:9 + | +LL | bar(&mut self, 102, &"str"); + | ^^^ help: try calling `bar` as a method: `self.bar` + +error[E0425]: cannot find function `bar` in this scope + --> $DIR/suggest-self-2.rs:17:9 | LL | bar(); | ^^^ not found in this scope error[E0599]: no method named `bar` found for type `&Foo` in the current scope - --> $DIR/suggest-self-2.rs:17:14 + --> $DIR/suggest-self-2.rs:20:14 | LL | self.bar(); | ^^^ method not found in `&Foo` -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors Some errors have detailed explanations: E0425, E0599. For more information about an error, try `rustc --explain E0425`. From e047368d80519c35e42fea283baba85f9c3a919e Mon Sep 17 00:00:00 2001 From: Janusz Marcinkiewicz Date: Sat, 21 Dec 2019 19:13:12 +0100 Subject: [PATCH 3/4] Add arguments to suggestion method call --- src/librustc_resolve/late/diagnostics.rs | 17 ++++++++++++++++- src/test/ui/self/suggest-self-2.stderr | 6 +++--- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/librustc_resolve/late/diagnostics.rs b/src/librustc_resolve/late/diagnostics.rs index 1df8ff02fcebe..859307c51ed9f 100644 --- a/src/librustc_resolve/late/diagnostics.rs +++ b/src/librustc_resolve/late/diagnostics.rs @@ -256,6 +256,7 @@ impl<'a> LateResolutionVisitor<'a, '_> { // Check if the first argument is `self` and suggest calling a method. let mut has_self_arg = false; + let mut args_span = None; if let PathSource::Expr(parent) = source { match &parent.map(|p| &p.kind) { Some(ExprKind::Call(_, args)) if args.len() > 0 => { @@ -264,6 +265,13 @@ impl<'a> LateResolutionVisitor<'a, '_> { match expr_kind { ExprKind::Path(_, arg_name) if arg_name.segments.len() == 1 => { has_self_arg = arg_name.segments[0].ident.name == kw::SelfLower; + if args.len() > 1 { + args_span = Some(Span::new( + args[1].span.lo(), + args.last().unwrap().span.hi(), + parent.unwrap().span.ctxt(), + )); + } break; }, ExprKind::AddrOf(_, _, expr) => expr_kind = &expr.kind, @@ -276,10 +284,17 @@ impl<'a> LateResolutionVisitor<'a, '_> { }; if has_self_arg { + let mut args_snippet: String = String::from(""); + if let Some(args_span) = args_span { + if let Ok(snippet) = self.r.session.source_map().span_to_snippet(args_span) { + args_snippet = snippet; + } + } + err.span_suggestion( span, &format!("try calling `{}` as a method", ident), - format!("self.{}", path_str), + format!("self.{}({})", path_str, args_snippet), Applicability::MachineApplicable, ); return (err, candidates); diff --git a/src/test/ui/self/suggest-self-2.stderr b/src/test/ui/self/suggest-self-2.stderr index ba71498fae656..6148012ac0d92 100644 --- a/src/test/ui/self/suggest-self-2.stderr +++ b/src/test/ui/self/suggest-self-2.stderr @@ -2,19 +2,19 @@ error[E0425]: cannot find function `bar` in this scope --> $DIR/suggest-self-2.rs:5:9 | LL | bar(self); - | ^^^ help: try calling `bar` as a method: `self.bar` + | ^^^ help: try calling `bar` as a method: `self.bar()` error[E0425]: cannot find function `bar` in this scope --> $DIR/suggest-self-2.rs:9:9 | LL | bar(&&self, 102); - | ^^^ help: try calling `bar` as a method: `self.bar` + | ^^^ help: try calling `bar` as a method: `self.bar(102)` error[E0425]: cannot find function `bar` in this scope --> $DIR/suggest-self-2.rs:13:9 | LL | bar(&mut self, 102, &"str"); - | ^^^ help: try calling `bar` as a method: `self.bar` + | ^^^ help: try calling `bar` as a method: `self.bar(102, &"str")` error[E0425]: cannot find function `bar` in this scope --> $DIR/suggest-self-2.rs:17:9 From 9273f59aa140e62163dc2f2787fd9163d3d7fee6 Mon Sep 17 00:00:00 2001 From: Janusz Marcinkiewicz Date: Sun, 22 Dec 2019 22:48:45 +0100 Subject: [PATCH 4/4] Extend suggestion span to whole method call --- src/librustc_resolve/late/diagnostics.rs | 26 ++++++++++++++---------- src/test/ui/self/suggest-self-2.stderr | 12 ++++++++--- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/src/librustc_resolve/late/diagnostics.rs b/src/librustc_resolve/late/diagnostics.rs index 859307c51ed9f..bbe62758e3ae1 100644 --- a/src/librustc_resolve/late/diagnostics.rs +++ b/src/librustc_resolve/late/diagnostics.rs @@ -255,8 +255,7 @@ impl<'a> LateResolutionVisitor<'a, '_> { } // Check if the first argument is `self` and suggest calling a method. - let mut has_self_arg = false; - let mut args_span = None; + let mut has_self_arg = None; if let PathSource::Expr(parent) = source { match &parent.map(|p| &p.kind) { Some(ExprKind::Call(_, args)) if args.len() > 0 => { @@ -264,13 +263,18 @@ impl<'a> LateResolutionVisitor<'a, '_> { loop { match expr_kind { ExprKind::Path(_, arg_name) if arg_name.segments.len() == 1 => { - has_self_arg = arg_name.segments[0].ident.name == kw::SelfLower; - if args.len() > 1 { - args_span = Some(Span::new( - args[1].span.lo(), - args.last().unwrap().span.hi(), - parent.unwrap().span.ctxt(), - )); + if arg_name.segments[0].ident.name == kw::SelfLower { + let call_span = parent.unwrap().span; + let args_span = if args.len() > 1 { + Some(Span::new( + args[1].span.lo(), + args.last().unwrap().span.hi(), + call_span.ctxt(), + )) + } else { + None + }; + has_self_arg = Some((call_span, args_span)); } break; }, @@ -283,7 +287,7 @@ impl<'a> LateResolutionVisitor<'a, '_> { } }; - if has_self_arg { + if let Some((call_span, args_span)) = has_self_arg { let mut args_snippet: String = String::from(""); if let Some(args_span) = args_span { if let Ok(snippet) = self.r.session.source_map().span_to_snippet(args_span) { @@ -292,7 +296,7 @@ impl<'a> LateResolutionVisitor<'a, '_> { } err.span_suggestion( - span, + call_span, &format!("try calling `{}` as a method", ident), format!("self.{}({})", path_str, args_snippet), Applicability::MachineApplicable, diff --git a/src/test/ui/self/suggest-self-2.stderr b/src/test/ui/self/suggest-self-2.stderr index 6148012ac0d92..452c31275153a 100644 --- a/src/test/ui/self/suggest-self-2.stderr +++ b/src/test/ui/self/suggest-self-2.stderr @@ -2,19 +2,25 @@ error[E0425]: cannot find function `bar` in this scope --> $DIR/suggest-self-2.rs:5:9 | LL | bar(self); - | ^^^ help: try calling `bar` as a method: `self.bar()` + | ^^^------ + | | + | help: try calling `bar` as a method: `self.bar()` error[E0425]: cannot find function `bar` in this scope --> $DIR/suggest-self-2.rs:9:9 | LL | bar(&&self, 102); - | ^^^ help: try calling `bar` as a method: `self.bar(102)` + | ^^^------------- + | | + | help: try calling `bar` as a method: `self.bar(102)` error[E0425]: cannot find function `bar` in this scope --> $DIR/suggest-self-2.rs:13:9 | LL | bar(&mut self, 102, &"str"); - | ^^^ help: try calling `bar` as a method: `self.bar(102, &"str")` + | ^^^------------------------ + | | + | help: try calling `bar` as a method: `self.bar(102, &"str")` error[E0425]: cannot find function `bar` in this scope --> $DIR/suggest-self-2.rs:17:9