about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMazdak Farrokhzad <twingoow@gmail.com>2020-01-04 02:19:44 +0100
committerGitHub <noreply@github.com>2020-01-04 02:19:44 +0100
commita436293994b7ff8c15f515faf1aa42623769cf5d (patch)
treeb27f43db717b9ef14634dac8aa26e44ef516c8c6
parente845e691c988303ea54cb9de9639a8577b5a9d6b (diff)
parent7b91ef8837cb5e926418aeb4a675313ba928c877 (diff)
downloadrust-a436293994b7ff8c15f515faf1aa42623769cf5d.tar.gz
rust-a436293994b7ff8c15f515faf1aa42623769cf5d.zip
Rollup merge of #66913 - VirrageS:help-self, r=varkor,Centril
Suggest calling method when first argument is `self`

Closes: #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.
-rw-r--r--src/librustc_resolve/late.rs2
-rw-r--r--src/librustc_resolve/late/diagnostics.rs55
-rw-r--r--src/test/ui/self/suggest-self-2.rs25
-rw-r--r--src/test/ui/self/suggest-self-2.stderr40
4 files changed, 121 insertions, 1 deletions
diff --git a/src/librustc_resolve/late.rs b/src/librustc_resolve/late.rs
index a0d59fa4829..325921b5982 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<NodeId>,
 
-    /// The current enclosing funciton (used for better errors).
+    /// The current enclosing function (used for better errors).
     current_function: Option<Span>,
 
     /// 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 5e59efac536..3a6bb37269f 100644
--- a/src/librustc_resolve/late/diagnostics.rs
+++ b/src/librustc_resolve/late/diagnostics.rs
@@ -259,6 +259,24 @@ impl<'a> LateResolutionVisitor<'a, '_> {
                 }
                 return (err, candidates);
             }
+
+            // If the first argument in call is `self` suggest calling a method.
+            if let Some((call_span, args_span)) = self.call_has_self_arg(source) {
+                let mut args_snippet = String::new();
+                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(
+                    call_span,
+                    &format!("try calling `{}` as a method", ident),
+                    format!("self.{}({})", path_str, args_snippet),
+                    Applicability::MachineApplicable,
+                );
+                return (err, candidates);
+            }
         }
 
         // Try Levenshtein algorithm.
@@ -298,6 +316,43 @@ impl<'a> LateResolutionVisitor<'a, '_> {
         (err, candidates)
     }
 
+    /// Check if the source is call expression and the first argument is `self`. If true,
+    /// return the span of whole call and the span for all arguments expect the first one (`self`).
+    fn call_has_self_arg(&self, source: PathSource<'_>) -> Option<(Span, Option<Span>)> {
+        let mut has_self_arg = None;
+        if let PathSource::Expr(parent) = source {
+            match &parent?.kind {
+                ExprKind::Call(_, args) if args.len() > 0 => {
+                    let mut expr_kind = &args[0].kind;
+                    loop {
+                        match expr_kind {
+                            ExprKind::Path(_, arg_name) if arg_name.segments.len() == 1 => {
+                                if arg_name.segments[0].ident.name == kw::SelfLower {
+                                    let call_span = parent.unwrap().span;
+                                    let tail_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, tail_args_span));
+                                }
+                                break;
+                            }
+                            ExprKind::AddrOf(_, _, expr) => expr_kind = &expr.kind,
+                            _ => break,
+                        }
+                    }
+                }
+                _ => (),
+            }
+        };
+        return has_self_arg;
+    }
+
     fn followed_by_brace(&self, span: Span) -> (bool, Option<(Span, String)>) {
         // HACK(estebank): find a better way to figure out that this was a
         // parser issue where a struct literal is being used on an expression
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 00000000000..d6bf5433527
--- /dev/null
+++ b/src/test/ui/self/suggest-self-2.rs
@@ -0,0 +1,25 @@
+struct Foo {}
+
+impl Foo {
+    fn foo(&self) {
+        bar(self);
+        //~^ ERROR cannot find function `bar` in this scope
+        //~| 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(&mut self, 102, &"str");
+        //~^ ERROR cannot find function `bar` in this scope
+        //~| HELP try calling `bar` as a method
+
+        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 00000000000..452c3127515
--- /dev/null
+++ b/src/test/ui/self/suggest-self-2.stderr
@@ -0,0 +1,40 @@
+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()`
+
+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)`
+
+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")`
+
+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:20:14
+   |
+LL |         self.bar();
+   |              ^^^ method not found in `&Foo`
+
+error: aborting due to 5 previous errors
+
+Some errors have detailed explanations: E0425, E0599.
+For more information about an error, try `rustc --explain E0425`.