about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEsteban Küber <esteban@kuber.com.ar>2019-10-09 10:10:54 -0700
committerEsteban Küber <esteban@kuber.com.ar>2019-10-15 08:02:49 -0700
commitdee53d7c90812d0cbabe7f19b35c125432c1c8c7 (patch)
treef38f0299fec582b440bb4e3c12b750c4f347efbb
parente413dc36a83a5aad3ab6270373000693a917e92b (diff)
downloadrust-dee53d7c90812d0cbabe7f19b35c125432c1c8c7.tar.gz
rust-dee53d7c90812d0cbabe7f19b35c125432c1c8c7.zip
Fix suggestion to constrain trait for method to be found
-rw-r--r--src/librustc_typeck/check/method/suggest.rs113
-rw-r--r--src/test/ui/suggestions/constrain-trait.fixed47
-rw-r--r--src/test/ui/suggestions/constrain-trait.rs47
-rw-r--r--src/test/ui/suggestions/constrain-trait.stderr27
4 files changed, 191 insertions, 43 deletions
diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs
index 96cc5aa1dc2..f2d001eaded 100644
--- a/src/librustc_typeck/check/method/suggest.rs
+++ b/src/librustc_typeck/check/method/suggest.rs
@@ -777,7 +777,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             } else {
                 "items from traits can only be used if the trait is implemented and in scope"
             });
-            let mut msg = format!(
+            let message = |action| format!(
                 "the following {traits_define} an item `{name}`, perhaps you need to {action} \
                  {one_of_them}:",
                 traits_define = if candidates.len() == 1 {
@@ -785,11 +785,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 } else {
                     "traits define"
                 },
-                action = if let Some(param) = param_type {
-                    format!("restrict type parameter `{}` with", param)
-                } else {
-                    "implement".to_string()
-                },
+                action = action,
                 one_of_them = if candidates.len() == 1 {
                     "it"
                 } else {
@@ -809,50 +805,81 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         // Get the `hir::Param` to verify whether it already has any bounds.
                         // We do this to avoid suggesting code that ends up as `T: FooBar`,
                         // instead we suggest `T: Foo + Bar` in that case.
-                        let mut has_bounds = None;
-                        let mut impl_trait = false;
-                        if let Node::GenericParam(ref param) = hir.get(id) {
-                            let kind = &param.kind;
-                            if let hir::GenericParamKind::Type { synthetic: Some(_), .. } = kind {
-                                // We've found `fn foo(x: impl Trait)` instead of
-                                // `fn foo<T>(x: T)`. We want to suggest the correct
-                                // `fn foo(x: impl Trait + TraitBound)` instead of
-                                // `fn foo<T: TraitBound>(x: T)`. (See #63706.)
-                                impl_trait = true;
-                                has_bounds = param.bounds.get(1);
-                            } else {
-                                has_bounds = param.bounds.get(0);
+                        match hir.get(id) {
+                            Node::GenericParam(ref param) => {
+                                let mut impl_trait = false;
+                                let has_bounds = if let hir::GenericParamKind::Type {
+                                    synthetic: Some(_), ..
+                                } = &param.kind {
+                                    // We've found `fn foo(x: impl Trait)` instead of
+                                    // `fn foo<T>(x: T)`. We want to suggest the correct
+                                    // `fn foo(x: impl Trait + TraitBound)` instead of
+                                    // `fn foo<T: TraitBound>(x: T)`. (#63706)
+                                    impl_trait = true;
+                                    param.bounds.get(1)
+                                } else {
+                                    param.bounds.get(0)
+                                };
+                                let sp = hir.span(id);
+                                let sp = if let Some(first_bound) = has_bounds {
+                                    // `sp` only covers `T`, change it so that it covers
+                                    // `T:` when appropriate
+                                    sp.until(first_bound.span())
+                                } else {
+                                    sp
+                                };
+                                // FIXME: contrast `t.def_id` against `param.bounds` to not suggest
+                                // traits already there. That can happen when the cause is that
+                                // we're in a const scope or associated function used as a method.
+                                err.span_suggestions(
+                                    sp,
+                                    &message(format!(
+                                        "restrict type parameter `{}` with",
+                                        param.name.ident().as_str(),
+                                    )),
+                                    candidates.iter().map(|t| format!(
+                                        "{}{} {}{}",
+                                        param.name.ident().as_str(),
+                                        if impl_trait { " +" } else { ":" },
+                                        self.tcx.def_path_str(t.def_id),
+                                        if has_bounds.is_some() { " + "} else { "" },
+                                    )),
+                                    Applicability::MaybeIncorrect,
+                                );
+                                suggested = true;
+                            }
+                            Node::Item(hir::Item {
+                                kind: hir::ItemKind::Trait(.., bounds, _), ident, ..
+                            }) => {
+                                let (sp, sep, article) = if bounds.is_empty() {
+                                    (ident.span.shrink_to_hi(), ":", "a")
+                                } else {
+                                    (bounds.last().unwrap().span().shrink_to_hi(), " +", "another")
+                                };
+                                err.span_suggestions(
+                                    sp,
+                                    &message(format!("add {} supertrait for", article)),
+                                    candidates.iter().map(|t| format!(
+                                        "{} {}",
+                                        sep,
+                                        self.tcx.def_path_str(t.def_id),
+                                    )),
+                                    Applicability::MaybeIncorrect,
+                                );
+                                suggested = true;
                             }
+                            _ => {}
                         }
-                        let sp = hir.span(id);
-                        // `sp` only covers `T`, change it so that it covers `T:` when appropriate.
-                        let sp = if let Some(first_bound) = has_bounds {
-                            sp.until(first_bound.span())
-                        } else {
-                            sp
-                        };
-
-                        // FIXME: contrast `t.def_id` against `param.bounds` to not suggest traits
-                        // already there. That can happen when the cause is that we're in a const
-                        // scope or associated function used as a method.
-                        err.span_suggestions(
-                            sp,
-                            &msg[..],
-                            candidates.iter().map(|t| format!(
-                                "{}{} {}{}",
-                                param,
-                                if impl_trait { " +" } else { ":" },
-                                self.tcx.def_path_str(t.def_id),
-                                if has_bounds.is_some() { " + " } else { "" },
-                            )),
-                            Applicability::MaybeIncorrect,
-                        );
-                        suggested = true;
                     }
                 };
             }
 
             if !suggested {
+                let mut msg = message(if let Some(param) = param_type {
+                    format!("restrict type parameter `{}` with", param)
+                } else {
+                    "implement".to_string()
+                });
                 for (i, trait_info) in candidates.iter().enumerate() {
                     msg.push_str(&format!(
                         "\ncandidate #{}: `{}`",
diff --git a/src/test/ui/suggestions/constrain-trait.fixed b/src/test/ui/suggestions/constrain-trait.fixed
new file mode 100644
index 00000000000..dda9e931353
--- /dev/null
+++ b/src/test/ui/suggestions/constrain-trait.fixed
@@ -0,0 +1,47 @@
+// run-rustfix
+// check-only
+
+#[derive(Debug)]
+struct Demo {
+    a: String
+}
+
+trait GetString {
+    fn get_a(&self) -> &String;
+}
+
+trait UseString: std::fmt::Debug + GetString {
+    fn use_string(&self) {
+        println!("{:?}", self.get_a()); //~ ERROR no method named `get_a` found for type `&Self`
+    }
+}
+
+trait UseString2: GetString {
+    fn use_string(&self) {
+        println!("{:?}", self.get_a()); //~ ERROR no method named `get_a` found for type `&Self`
+    }
+}
+
+impl GetString for Demo {
+    fn get_a(&self) -> &String {
+        &self.a
+    }
+}
+
+impl UseString for Demo {}
+impl UseString2 for Demo {}
+
+
+#[cfg(test)]
+mod tests {
+    use crate::{Demo, UseString};
+
+    #[test]
+    fn it_works() {
+        let d = Demo { a: "test".to_string() };
+        d.use_string();
+    }
+}
+
+
+fn main() {}
diff --git a/src/test/ui/suggestions/constrain-trait.rs b/src/test/ui/suggestions/constrain-trait.rs
new file mode 100644
index 00000000000..4ef0eff5bd7
--- /dev/null
+++ b/src/test/ui/suggestions/constrain-trait.rs
@@ -0,0 +1,47 @@
+// run-rustfix
+// check-only
+
+#[derive(Debug)]
+struct Demo {
+    a: String
+}
+
+trait GetString {
+    fn get_a(&self) -> &String;
+}
+
+trait UseString: std::fmt::Debug {
+    fn use_string(&self) {
+        println!("{:?}", self.get_a()); //~ ERROR no method named `get_a` found for type `&Self`
+    }
+}
+
+trait UseString2 {
+    fn use_string(&self) {
+        println!("{:?}", self.get_a()); //~ ERROR no method named `get_a` found for type `&Self`
+    }
+}
+
+impl GetString for Demo {
+    fn get_a(&self) -> &String {
+        &self.a
+    }
+}
+
+impl UseString for Demo {}
+impl UseString2 for Demo {}
+
+
+#[cfg(test)]
+mod tests {
+    use crate::{Demo, UseString};
+
+    #[test]
+    fn it_works() {
+        let d = Demo { a: "test".to_string() };
+        d.use_string();
+    }
+}
+
+
+fn main() {}
diff --git a/src/test/ui/suggestions/constrain-trait.stderr b/src/test/ui/suggestions/constrain-trait.stderr
new file mode 100644
index 00000000000..3cc351ac80a
--- /dev/null
+++ b/src/test/ui/suggestions/constrain-trait.stderr
@@ -0,0 +1,27 @@
+error[E0599]: no method named `get_a` found for type `&Self` in the current scope
+  --> $DIR/constrain-trait.rs:15:31
+   |
+LL |         println!("{:?}", self.get_a());
+   |                               ^^^^^ method not found in `&Self`
+   |
+   = help: items from traits can only be used if the type parameter is bounded by the trait
+help: the following trait defines an item `get_a`, perhaps you need to add another supertrait for it:
+   |
+LL | trait UseString: std::fmt::Debug + GetString {
+   |                                  ^^^^^^^^^^^
+
+error[E0599]: no method named `get_a` found for type `&Self` in the current scope
+  --> $DIR/constrain-trait.rs:21:31
+   |
+LL |         println!("{:?}", self.get_a());
+   |                               ^^^^^ method not found in `&Self`
+   |
+   = help: items from traits can only be used if the type parameter is bounded by the trait
+help: the following trait defines an item `get_a`, perhaps you need to add a supertrait for it:
+   |
+LL | trait UseString2: GetString {
+   |                 ^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0599`.