about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEsteban Küber <esteban@kuber.com.ar>2020-10-26 16:28:56 -0700
committerEsteban Küber <esteban@kuber.com.ar>2020-10-26 16:31:11 -0700
commit9e16213610f606de0e44ac2b26ab9666bf0e83bf (patch)
tree2e97213beb4edc887d4d325a94f66bb3cff28375
parent9f6c670c4b4273b2c7c0af07a524d4240c926bfc (diff)
downloadrust-9e16213610f606de0e44ac2b26ab9666bf0e83bf.tar.gz
rust-9e16213610f606de0e44ac2b26ab9666bf0e83bf.zip
Suggest calling associated `fn` inside `trait`s
When calling a function that doesn't exist inside of a trait's
associated `fn`, and another associated `fn` in that trait has that
name, suggest calling it with the appropriate fully-qualified path.

Expand the label to be more descriptive.

Prompted by the following user experience:
https://users.rust-lang.org/t/cannot-find-function/50663
-rw-r--r--compiler/rustc_resolve/src/late.rs24
-rw-r--r--compiler/rustc_resolve/src/late/diagnostics.rs58
-rw-r--r--src/test/ui/resolve/associated-fn-called-as-fn.rs32
-rw-r--r--src/test/ui/resolve/associated-fn-called-as-fn.stderr15
-rw-r--r--src/test/ui/resolve/issue-14254.stderr30
-rw-r--r--src/test/ui/resolve/issue-2356.stderr6
-rw-r--r--src/test/ui/resolve/resolve-assoc-suggestions.stderr4
-rw-r--r--src/test/ui/resolve/resolve-speculative-adjustment.stderr2
-rw-r--r--src/test/ui/suggestions/assoc-type-in-method-return.stderr2
9 files changed, 124 insertions, 49 deletions
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index 7517ab66170..9fe42590d3e 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -353,8 +353,8 @@ impl<'a> PathSource<'a> {
 
 #[derive(Default)]
 struct DiagnosticMetadata<'ast> {
-    /// The current trait's associated types' ident, used for diagnostic suggestions.
-    current_trait_assoc_types: Vec<Ident>,
+    /// The current trait's associated items' ident, used for diagnostic suggestions.
+    current_trait_assoc_items: Option<&'ast [P<AssocItem>]>,
 
     /// The current self type if inside an impl (used for better errors).
     current_self_type: Option<Ty>,
@@ -1148,26 +1148,18 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
         result
     }
 
-    /// When evaluating a `trait` use its associated types' idents for suggestionsa in E0412.
+    /// When evaluating a `trait` use its associated types' idents for suggestions in E0412.
     fn with_trait_items<T>(
         &mut self,
-        trait_items: &Vec<P<AssocItem>>,
+        trait_items: &'ast Vec<P<AssocItem>>,
         f: impl FnOnce(&mut Self) -> T,
     ) -> T {
-        let trait_assoc_types = replace(
-            &mut self.diagnostic_metadata.current_trait_assoc_types,
-            trait_items
-                .iter()
-                .filter_map(|item| match &item.kind {
-                    AssocItemKind::TyAlias(_, _, bounds, _) if bounds.is_empty() => {
-                        Some(item.ident)
-                    }
-                    _ => None,
-                })
-                .collect(),
+        let trait_assoc_items = replace(
+            &mut self.diagnostic_metadata.current_trait_assoc_items,
+            Some(&trait_items[..]),
         );
         let result = f(self);
-        self.diagnostic_metadata.current_trait_assoc_types = trait_assoc_types;
+        self.diagnostic_metadata.current_trait_assoc_items = trait_assoc_items;
         result
     }
 
diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs
index c24b383f3b8..75dc54e4d65 100644
--- a/compiler/rustc_resolve/src/late/diagnostics.rs
+++ b/compiler/rustc_resolve/src/late/diagnostics.rs
@@ -30,7 +30,21 @@ type Res = def::Res<ast::NodeId>;
 enum AssocSuggestion {
     Field,
     MethodWithSelf,
-    AssocItem,
+    AssocFn,
+    AssocType,
+    AssocConst,
+}
+
+impl AssocSuggestion {
+    fn action(&self) -> &'static str {
+        match self {
+            AssocSuggestion::Field => "use the available field",
+            AssocSuggestion::MethodWithSelf => "call the method with the fully-qualified path",
+            AssocSuggestion::AssocFn => "call the associated function",
+            AssocSuggestion::AssocConst => "use the associated `const`",
+            AssocSuggestion::AssocType => "use the associated type",
+        }
+    }
 }
 
 crate enum MissingLifetimeSpot<'tcx> {
@@ -386,15 +400,18 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
                     AssocSuggestion::MethodWithSelf if self_is_available => {
                         err.span_suggestion(
                             span,
-                            "try",
+                            "you might have meant to call the method",
                             format!("self.{}", path_str),
                             Applicability::MachineApplicable,
                         );
                     }
-                    AssocSuggestion::MethodWithSelf | AssocSuggestion::AssocItem => {
+                    AssocSuggestion::MethodWithSelf
+                    | AssocSuggestion::AssocFn
+                    | AssocSuggestion::AssocConst
+                    | AssocSuggestion::AssocType => {
                         err.span_suggestion(
                             span,
-                            "try",
+                            &format!("you might have meant to {}", candidate.action()),
                             format!("Self::{}", path_str),
                             Applicability::MachineApplicable,
                         );
@@ -1048,9 +1065,19 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
             }
         }
 
-        for assoc_type_ident in &self.diagnostic_metadata.current_trait_assoc_types {
-            if *assoc_type_ident == ident {
-                return Some(AssocSuggestion::AssocItem);
+        if let Some(items) = self.diagnostic_metadata.current_trait_assoc_items {
+            for assoc_item in &items[..] {
+                if assoc_item.ident == ident {
+                    return Some(match &assoc_item.kind {
+                        ast::AssocItemKind::Const(..) => AssocSuggestion::AssocConst,
+                        ast::AssocItemKind::Fn(_, sig, ..) if sig.decl.has_self() => {
+                            AssocSuggestion::MethodWithSelf
+                        }
+                        ast::AssocItemKind::Fn(..) => AssocSuggestion::AssocFn,
+                        ast::AssocItemKind::TyAlias(..) => AssocSuggestion::AssocType,
+                        ast::AssocItemKind::MacCall(_) => continue,
+                    });
+                }
             }
         }
 
@@ -1066,11 +1093,20 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
             ) {
                 let res = binding.res();
                 if filter_fn(res) {
-                    return Some(if self.r.has_self.contains(&res.def_id()) {
-                        AssocSuggestion::MethodWithSelf
+                    if self.r.has_self.contains(&res.def_id()) {
+                        return Some(AssocSuggestion::MethodWithSelf);
                     } else {
-                        AssocSuggestion::AssocItem
-                    });
+                        match res {
+                            Res::Def(DefKind::AssocFn, _) => return Some(AssocSuggestion::AssocFn),
+                            Res::Def(DefKind::AssocConst, _) => {
+                                return Some(AssocSuggestion::AssocConst);
+                            }
+                            Res::Def(DefKind::AssocTy, _) => {
+                                return Some(AssocSuggestion::AssocType);
+                            }
+                            _ => {}
+                        }
+                    }
                 }
             }
         }
diff --git a/src/test/ui/resolve/associated-fn-called-as-fn.rs b/src/test/ui/resolve/associated-fn-called-as-fn.rs
new file mode 100644
index 00000000000..f31f3d67b5b
--- /dev/null
+++ b/src/test/ui/resolve/associated-fn-called-as-fn.rs
@@ -0,0 +1,32 @@
+struct S;
+impl Foo for S {
+    fn parse(s:&str) {
+        for c in s.chars() {
+            match c {
+                '0'..='9' => collect_primary(&c), //~ ERROR cannot find function `collect_primary`
+                //~^ HELP you might have meant to call the associated function
+                '+' | '-' => println!("We got a sign: {}", c),
+                _ => println!("Not a number!")
+            }
+        }
+    }
+}
+trait Foo {
+    fn collect_primary(ch:&char) { }
+    fn parse(s:&str);
+}
+trait Bar {
+    fn collect_primary(ch:&char) { }
+    fn parse(s:&str) {
+        for c in s.chars() {
+            match c {
+                '0'..='9' => collect_primary(&c), //~ ERROR cannot find function `collect_primary`
+                //~^ HELP you might have meant to call the associated function
+                '+' | '-' => println!("We got a sign: {}", c),
+                _ => println!("Not a number!")
+            }
+        }
+    }
+}
+
+fn main() {}
diff --git a/src/test/ui/resolve/associated-fn-called-as-fn.stderr b/src/test/ui/resolve/associated-fn-called-as-fn.stderr
new file mode 100644
index 00000000000..fbdea30d551
--- /dev/null
+++ b/src/test/ui/resolve/associated-fn-called-as-fn.stderr
@@ -0,0 +1,15 @@
+error[E0425]: cannot find function `collect_primary` in this scope
+  --> $DIR/associated-fn-called-as-fn.rs:6:30
+   |
+LL |                 '0'..='9' => collect_primary(&c),
+   |                              ^^^^^^^^^^^^^^^ help: you might have meant to call the associated function: `Self::collect_primary`
+
+error[E0425]: cannot find function `collect_primary` in this scope
+  --> $DIR/associated-fn-called-as-fn.rs:23:30
+   |
+LL |                 '0'..='9' => collect_primary(&c),
+   |                              ^^^^^^^^^^^^^^^ help: you might have meant to call the associated function: `Self::collect_primary`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0425`.
diff --git a/src/test/ui/resolve/issue-14254.stderr b/src/test/ui/resolve/issue-14254.stderr
index 97d42aa8ef4..b1f45adb8b7 100644
--- a/src/test/ui/resolve/issue-14254.stderr
+++ b/src/test/ui/resolve/issue-14254.stderr
@@ -2,7 +2,7 @@ error[E0425]: cannot find function `baz` in this scope
   --> $DIR/issue-14254.rs:19:9
    |
 LL |         baz();
-   |         ^^^ help: try: `self.baz`
+   |         ^^^ help: you might have meant to call the method: `self.baz`
 
 error[E0425]: cannot find value `a` in this scope
   --> $DIR/issue-14254.rs:21:9
@@ -14,7 +14,7 @@ error[E0425]: cannot find function `baz` in this scope
   --> $DIR/issue-14254.rs:28:9
    |
 LL |         baz();
-   |         ^^^ help: try: `self.baz`
+   |         ^^^ help: you might have meant to call the method: `self.baz`
 
 error[E0425]: cannot find value `x` in this scope
   --> $DIR/issue-14254.rs:30:9
@@ -38,7 +38,7 @@ error[E0425]: cannot find value `bah` in this scope
   --> $DIR/issue-14254.rs:36:9
    |
 LL |         bah;
-   |         ^^^ help: try: `Self::bah`
+   |         ^^^ help: you might have meant to call the associated function: `Self::bah`
 
 error[E0425]: cannot find value `b` in this scope
   --> $DIR/issue-14254.rs:38:9
@@ -50,7 +50,7 @@ error[E0425]: cannot find function `baz` in this scope
   --> $DIR/issue-14254.rs:45:9
    |
 LL |         baz();
-   |         ^^^ help: try: `self.baz`
+   |         ^^^ help: you might have meant to call the method: `self.baz`
 
 error[E0425]: cannot find value `x` in this scope
   --> $DIR/issue-14254.rs:47:9
@@ -74,7 +74,7 @@ error[E0425]: cannot find value `bah` in this scope
   --> $DIR/issue-14254.rs:53:9
    |
 LL |         bah;
-   |         ^^^ help: try: `Self::bah`
+   |         ^^^ help: you might have meant to call the associated function: `Self::bah`
 
 error[E0425]: cannot find value `b` in this scope
   --> $DIR/issue-14254.rs:55:9
@@ -86,61 +86,61 @@ error[E0425]: cannot find function `baz` in this scope
   --> $DIR/issue-14254.rs:62:9
    |
 LL |         baz();
-   |         ^^^ help: try: `self.baz`
+   |         ^^^ help: you might have meant to call the method: `self.baz`
 
 error[E0425]: cannot find value `bah` in this scope
   --> $DIR/issue-14254.rs:64:9
    |
 LL |         bah;
-   |         ^^^ help: try: `Self::bah`
+   |         ^^^ help: you might have meant to call the associated function: `Self::bah`
 
 error[E0425]: cannot find function `baz` in this scope
   --> $DIR/issue-14254.rs:71:9
    |
 LL |         baz();
-   |         ^^^ help: try: `self.baz`
+   |         ^^^ help: you might have meant to call the method: `self.baz`
 
 error[E0425]: cannot find value `bah` in this scope
   --> $DIR/issue-14254.rs:73:9
    |
 LL |         bah;
-   |         ^^^ help: try: `Self::bah`
+   |         ^^^ help: you might have meant to call the associated function: `Self::bah`
 
 error[E0425]: cannot find function `baz` in this scope
   --> $DIR/issue-14254.rs:80:9
    |
 LL |         baz();
-   |         ^^^ help: try: `self.baz`
+   |         ^^^ help: you might have meant to call the method: `self.baz`
 
 error[E0425]: cannot find value `bah` in this scope
   --> $DIR/issue-14254.rs:82:9
    |
 LL |         bah;
-   |         ^^^ help: try: `Self::bah`
+   |         ^^^ help: you might have meant to call the associated function: `Self::bah`
 
 error[E0425]: cannot find function `baz` in this scope
   --> $DIR/issue-14254.rs:89:9
    |
 LL |         baz();
-   |         ^^^ help: try: `self.baz`
+   |         ^^^ help: you might have meant to call the method: `self.baz`
 
 error[E0425]: cannot find value `bah` in this scope
   --> $DIR/issue-14254.rs:91:9
    |
 LL |         bah;
-   |         ^^^ help: try: `Self::bah`
+   |         ^^^ help: you might have meant to call the associated function: `Self::bah`
 
 error[E0425]: cannot find function `baz` in this scope
   --> $DIR/issue-14254.rs:98:9
    |
 LL |         baz();
-   |         ^^^ help: try: `self.baz`
+   |         ^^^ help: you might have meant to call the method: `self.baz`
 
 error[E0425]: cannot find value `bah` in this scope
   --> $DIR/issue-14254.rs:100:9
    |
 LL |         bah;
-   |         ^^^ help: try: `Self::bah`
+   |         ^^^ help: you might have meant to call the associated function: `Self::bah`
 
 error: aborting due to 24 previous errors
 
diff --git a/src/test/ui/resolve/issue-2356.stderr b/src/test/ui/resolve/issue-2356.stderr
index 0339daa0d6a..8083233c01b 100644
--- a/src/test/ui/resolve/issue-2356.stderr
+++ b/src/test/ui/resolve/issue-2356.stderr
@@ -8,7 +8,7 @@ error[E0425]: cannot find function `clone` in this scope
   --> $DIR/issue-2356.rs:24:5
    |
 LL |     clone();
-   |     ^^^^^ help: try: `self.clone`
+   |     ^^^^^ help: you might have meant to call the method: `self.clone`
 
 error[E0425]: cannot find function `default` in this scope
   --> $DIR/issue-2356.rs:31:5
@@ -16,7 +16,7 @@ error[E0425]: cannot find function `default` in this scope
 LL |     default();
    |     ^^^^^^^
    |
-help: try
+help: you might have meant to call the associated function
    |
 LL |     Self::default();
    |     ^^^^^^^^^^^^^
@@ -35,7 +35,7 @@ error[E0425]: cannot find function `shave` in this scope
   --> $DIR/issue-2356.rs:41:5
    |
 LL |     shave(4);
-   |     ^^^^^ help: try: `Self::shave`
+   |     ^^^^^ help: you might have meant to call the associated function: `Self::shave`
 
 error[E0425]: cannot find function `purr` in this scope
   --> $DIR/issue-2356.rs:43:5
diff --git a/src/test/ui/resolve/resolve-assoc-suggestions.stderr b/src/test/ui/resolve/resolve-assoc-suggestions.stderr
index a05ac0f8543..b6acaeb8cc2 100644
--- a/src/test/ui/resolve/resolve-assoc-suggestions.stderr
+++ b/src/test/ui/resolve/resolve-assoc-suggestions.stderr
@@ -20,7 +20,7 @@ error[E0412]: cannot find type `Type` in this scope
   --> $DIR/resolve-assoc-suggestions.rs:23:16
    |
 LL |         let _: Type;
-   |                ^^^^ help: try: `Self::Type`
+   |                ^^^^ help: you might have meant to use the associated type: `Self::Type`
 
 error[E0531]: cannot find tuple struct or tuple variant `Type` in this scope
   --> $DIR/resolve-assoc-suggestions.rs:25:13
@@ -50,7 +50,7 @@ error[E0425]: cannot find value `method` in this scope
   --> $DIR/resolve-assoc-suggestions.rs:34:9
    |
 LL |         method;
-   |         ^^^^^^ help: try: `self.method`
+   |         ^^^^^^ help: you might have meant to call the method: `self.method`
 
 error: aborting due to 9 previous errors
 
diff --git a/src/test/ui/resolve/resolve-speculative-adjustment.stderr b/src/test/ui/resolve/resolve-speculative-adjustment.stderr
index 892b50309a9..1c34af6d0ff 100644
--- a/src/test/ui/resolve/resolve-speculative-adjustment.stderr
+++ b/src/test/ui/resolve/resolve-speculative-adjustment.stderr
@@ -20,7 +20,7 @@ error[E0425]: cannot find function `method` in this scope
   --> $DIR/resolve-speculative-adjustment.rs:25:9
    |
 LL |         method();
-   |         ^^^^^^ help: try: `self.method`
+   |         ^^^^^^ help: you might have meant to call the method: `self.method`
 
 error: aborting due to 4 previous errors
 
diff --git a/src/test/ui/suggestions/assoc-type-in-method-return.stderr b/src/test/ui/suggestions/assoc-type-in-method-return.stderr
index bf908d36d2e..202e4a16ead 100644
--- a/src/test/ui/suggestions/assoc-type-in-method-return.stderr
+++ b/src/test/ui/suggestions/assoc-type-in-method-return.stderr
@@ -2,7 +2,7 @@ error[E0412]: cannot find type `Bla` in this scope
   --> $DIR/assoc-type-in-method-return.rs:3:25
    |
 LL |     fn to_bla(&self) -> Bla;
-   |                         ^^^ help: try: `Self::Bla`
+   |                         ^^^ help: you might have meant to use the associated type: `Self::Bla`
 
 error: aborting due to previous error