]> git.lizzy.rs Git - rust.git/commitdiff
Prefer implemented traits in suggestions.
authorHuon Wilson <dbau.pp+github@gmail.com>
Fri, 16 Jan 2015 11:27:40 +0000 (22:27 +1100)
committerHuon Wilson <dbau.pp+github@gmail.com>
Fri, 16 Jan 2015 11:54:19 +0000 (22:54 +1100)
If `a.method();` can't be resolved, we first look for implemented traits
globally and suggest those. If there are no such traits found, we only
then fall back to suggesting from the unfiltered list of traits.

src/librustc_typeck/check/method/mod.rs
src/librustc_typeck/check/method/probe.rs
src/librustc_typeck/check/method/suggest.rs
src/test/auxiliary/no_method_suggested_traits.rs
src/test/compile-fail/no-method-suggested-traits.rs

index a28b4ac34753b5317227c19973a9f8b65dbca473..345bc5fd2aa60772cb367f8fce554789034013ab 100644 (file)
@@ -38,8 +38,9 @@
 
 pub enum MethodError {
     // Did not find an applicable method, but we did find various
-    // static methods that may apply.
-    NoMatch(Vec<CandidateSource>),
+    // static methods that may apply, as well as a list of
+    // not-in-scope traits which may work.
+    NoMatch(Vec<CandidateSource>, Vec<ast::DefId>),
 
     // Multiple methods might apply.
     Ambiguity(Vec<CandidateSource>),
@@ -65,7 +66,7 @@ pub fn exists<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
 {
     match probe::probe(fcx, span, method_name, self_ty, call_expr_id) {
         Ok(_) => true,
-        Err(NoMatch(_)) => false,
+        Err(NoMatch(_, _)) => false,
         Err(Ambiguity(_)) => true,
     }
 }
index dc4d6c9a826c9912d0b6fb6994016a6f56233d9a..9df8875152e3b94e0f9bc40434f9005597a0bb49 100644 (file)
@@ -11,6 +11,7 @@
 use super::{MethodError,Ambiguity,NoMatch};
 use super::MethodIndex;
 use super::{CandidateSource,ImplSource,TraitSource};
+use super::suggest;
 
 use check;
 use check::{FnCtxt, NoPreference};
@@ -25,6 +26,7 @@
 use syntax::ast;
 use syntax::codemap::{Span, DUMMY_SP};
 use std::collections::HashSet;
+use std::mem;
 use std::rc::Rc;
 use util::ppaux::Repr;
 
@@ -42,6 +44,7 @@ struct ProbeContext<'a, 'tcx:'a> {
     extension_candidates: Vec<Candidate<'tcx>>,
     impl_dups: HashSet<ast::DefId>,
     static_candidates: Vec<CandidateSource>,
+    all_traits_search: bool,
 }
 
 struct CandidateStep<'tcx> {
@@ -127,7 +130,7 @@ pub fn probe<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
     // take place in the `fcx.infcx().probe` below.
     let steps = match create_steps(fcx, span, self_ty) {
         Some(steps) => steps,
-        None => return Err(NoMatch(Vec::new())),
+        None => return Err(NoMatch(Vec::new(), Vec::new())),
     };
 
     // Create a list of simplified self types, if we can.
@@ -208,9 +211,17 @@ fn new(fcx: &'a FnCtxt<'a,'tcx>,
             steps: Rc::new(steps),
             opt_simplified_steps: opt_simplified_steps,
             static_candidates: Vec::new(),
+            all_traits_search: false,
         }
     }
 
+    fn reset(&mut self) {
+        self.inherent_candidates.clear();
+        self.extension_candidates.clear();
+        self.impl_dups.clear();
+        self.static_candidates.clear();
+    }
+
     fn tcx(&self) -> &'a ty::ctxt<'tcx> {
         self.fcx.tcx()
     }
@@ -446,6 +457,15 @@ fn assemble_extension_candidates_for_traits_in_scope(&mut self,
         }
     }
 
+    fn assemble_extension_candidates_for_all_traits(&mut self) {
+        let mut duplicates = HashSet::new();
+        for trait_info in suggest::all_traits(self.fcx.ccx) {
+            if duplicates.insert(trait_info.def_id) {
+                self.assemble_extension_candidates_for_trait(trait_info.def_id)
+            }
+        }
+    }
+
     fn assemble_extension_candidates_for_trait(&mut self,
                                                trait_def_id: ast::DefId) {
         debug!("assemble_extension_candidates_for_trait(trait_def_id={})",
@@ -715,7 +735,47 @@ fn pick(mut self) -> PickResult<'tcx> {
             }
         }
 
-        Err(NoMatch(self.static_candidates))
+        let static_candidates = mem::replace(&mut self.static_candidates, vec![]);
+
+        let out_of_scope_traits = if !self.all_traits_search {
+            // things failed, and we haven't yet looked through all
+            // traits, so lets do that now:
+            self.reset();
+            self.all_traits_search = true;
+
+            let span = self.span;
+            let tcx = self.tcx();
+
+            self.assemble_extension_candidates_for_all_traits();
+
+            match self.pick() {
+                Ok(p) => vec![p.method_ty.container.id()],
+                Err(Ambiguity(v)) => v.into_iter().map(|source| {
+                    match source {
+                        TraitSource(id) => id,
+                        ImplSource(impl_id) => {
+                            match ty::trait_id_of_impl(tcx, impl_id) {
+                                Some(id) => id,
+                                None => tcx.sess.span_bug(span,
+                                                          "found inherent method when looking \
+                                                           at traits")
+                            }
+                        }
+                    }
+                }).collect(),
+                // it'd be really weird for this assertion to trigger,
+                // given the `vec![]` in the else branch below
+                Err(NoMatch(_, others)) => {
+                    assert!(others.is_empty());
+                    vec![]
+                }
+            }
+        } else {
+            // we've just looked through all traits and didn't find
+            // anything at all.
+            vec![]
+        };
+        Err(NoMatch(static_candidates, out_of_scope_traits))
     }
 
     fn pick_step(&mut self, step: &CandidateStep<'tcx>) -> Option<PickResult<'tcx>> {
index aab1fa2a958171bf214977e75ac6a0b27e6b15af..013c6e2f953a07f4d931106b3fbd87aa0e54afcd 100644 (file)
@@ -35,7 +35,7 @@ pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                               error: MethodError)
 {
     match error {
-        MethodError::NoMatch(static_sources) => {
+        MethodError::NoMatch(static_sources, out_of_scope_traits) => {
             let cx = fcx.tcx();
             let method_ustring = method_name.user_string(cx);
 
@@ -75,7 +75,7 @@ pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                 report_candidates(fcx, span, method_name, static_sources);
             }
 
-            suggest_traits_to_import(fcx, span, rcvr_ty, method_name)
+            suggest_traits_to_import(fcx, span, rcvr_ty, method_name, out_of_scope_traits)
         }
 
         MethodError::Ambiguity(sources) => {
@@ -136,10 +136,35 @@ fn report_candidates(fcx: &FnCtxt,
 fn suggest_traits_to_import<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                                       span: Span,
                                       _rcvr_ty: Ty<'tcx>,
-                                      method_name: ast::Name)
+                                      method_name: ast::Name,
+                                      valid_out_of_scope_traits: Vec<ast::DefId>)
 {
     let tcx = fcx.tcx();
+    let method_ustring = method_name.user_string(tcx);
+
+    if !valid_out_of_scope_traits.is_empty() {
+        let mut candidates = valid_out_of_scope_traits;
+        candidates.sort();
+        let msg = format!(
+            "methods from traits can only be called if the trait is in scope; \
+             the following {traits_are} implemented and {define} a method `{name}`:",
+            traits_are = if candidates.len() == 1 {"trait is"} else {"traits are"},
+            define = if candidates.len() == 1 {"defines"} else {"define"},
+            name = method_ustring);
+
+        fcx.sess().fileline_help(span, &msg[]);
+
+        for (i, trait_did) in candidates.iter().enumerate() {
+            fcx.sess().fileline_help(span,
+                                     &*format!("candidate #{}: `{}`",
+                                               i + 1,
+                                               ty::item_path_str(fcx.tcx(), *trait_did)))
+
+        }
+        return
+    }
 
+    // there's no implemented traits, so lets suggest some traits to implement
     let mut candidates = all_traits(fcx.ccx)
         .filter(|info| trait_method(tcx, info.def_id, method_name).is_some())
         .collect::<Vec<_>>();
@@ -148,22 +173,16 @@ fn suggest_traits_to_import<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
         // sort from most relevant to least relevant
         candidates.sort_by(|a, b| a.cmp(b).reverse());
 
-        let method_ustring = method_name.user_string(tcx);
+        let msg = format!(
+            "methods from traits can only be called if the trait is implemented and \
+             in scope; no such traits are but the following {traits_define} a method `{name}`:",
+            traits_define = if candidates.len() == 1 {"trait defines"} else {"traits define"},
+            name = method_ustring);
 
-        span_help!(fcx.sess(), span,
-                   "methods from traits can only be called if the trait is implemented \
-                    and in scope; the following trait{s} define{inv_s} a method `{name}`:",
-                   s = if candidates.len() == 1 {""} else {"s"},
-                   inv_s = if candidates.len() == 1 {"s"} else {""},
-                   name = method_ustring);
+        fcx.sess().fileline_help(span, &msg[]);
 
         for (i, trait_info) in candidates.iter().enumerate() {
-            // provide a good-as-possible span; the span of
-            // the trait if it is local, or the span of the
-            // method call itself if not
-            let trait_span = fcx.tcx().map.def_id_span(trait_info.def_id, span);
-
-            fcx.sess().fileline_help(trait_span,
+            fcx.sess().fileline_help(span,
                                      &*format!("candidate #{}: `{}`",
                                                i + 1,
                                                ty::item_path_str(fcx.tcx(), trait_info.def_id)))
@@ -173,7 +192,7 @@ fn suggest_traits_to_import<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
 
 #[derive(Copy)]
 pub struct TraitInfo {
-    def_id: ast::DefId,
+    pub def_id: ast::DefId,
 }
 
 impl TraitInfo {
@@ -206,7 +225,7 @@ fn cmp(&self, other: &TraitInfo) -> Ordering {
 }
 
 /// Retrieve all traits in this crate and any dependent crates.
-fn all_traits<'a>(ccx: &'a CrateCtxt) -> AllTraits<'a> {
+pub fn all_traits<'a>(ccx: &'a CrateCtxt) -> AllTraits<'a> {
     if ccx.all_traits.borrow().is_none() {
         use syntax::visit;
 
@@ -268,7 +287,7 @@ fn handle_external_def(traits: &mut AllTraitsVec,
     }
 }
 
-struct AllTraits<'a> {
+pub struct AllTraits<'a> {
     borrow: cell::Ref<'a Option<AllTraitsVec>>,
     idx: usize
 }
index fdeace00d4ca2277da72e02cdd50cd627ec81183..328561495eef0a2efafd011611c14e1b7120b1a3 100644 (file)
 
 pub mod foo {
     pub trait PubPub {
-        fn method(&self);
+        fn method(&self) {}
+
+        fn method3(&self) {}
     }
+
+    impl PubPub for u32 {}
+    impl PubPub for i32 {}
 }
 pub mod bar {
     trait PubPriv {
index 2565d2b7302672b9b67ee536374465eab2d7eefa..277800778a87ebc67c493c2e15f13c7e545e1d3c 100644 (file)
 extern crate no_method_suggested_traits;
 
 mod foo {
-    trait Bar { //~ HELP `foo::Bar`
-        fn method(&self);
+    trait Bar {
+        fn method(&self) {}
+
+        fn method2(&self) {}
     }
+
+    impl Bar for u32 {}
+
+    impl Bar for char {}
 }
 
 fn main() {
     1u32.method();
     //~^ ERROR does not implement
+    //~^^ HELP the following traits are implemented and define a method `method`
+    //~^^^ HELP `foo::Bar`
+    //~^^^^ HELP `no_method_suggested_traits::foo::PubPub`
+
+    'a'.method();
+    //~^ ERROR does not implement
+    //~^^ HELP the following trait is implemented and defines a method `method`
+    //~^^^ HELP `foo::Bar`
+
+    1i32.method();
+    //~^ ERROR does not implement
+    //~^^ HELP the following trait is implemented and defines a method `method`
+    //~^^^ HELP `no_method_suggested_traits::foo::PubPub`
+
+    1u64.method();
+    //~^ ERROR does not implement
     //~^^ HELP the following traits define a method `method`
+    //~^^^ HELP `foo::Bar`
+    //~^^^^ HELP `no_method_suggested_traits::foo::PubPub`
+    //~^^^^^ HELP `no_method_suggested_traits::reexport::Reexported`
+    //~^^^^^^ HELP `no_method_suggested_traits::bar::PubPriv`
+    //~^^^^^^^ HELP `no_method_suggested_traits::qux::PrivPub`
+    //~^^^^^^^^ HELP `no_method_suggested_traits::quz::PrivPriv`
+
+    1u64.method2();
+    //~^ ERROR does not implement
+    //~^^ HELP the following trait defines a method `method2`
+    //~^^^ HELP `foo::Bar`
+    1u64.method3();
+    //~^ ERROR does not implement
+    //~^^ HELP the following trait defines a method `method3`
     //~^^^ HELP `no_method_suggested_traits::foo::PubPub`
-    //~^^^^ HELP `no_method_suggested_traits::reexport::Reexported`
-    //~^^^^^ HELP `no_method_suggested_traits::bar::PubPriv`
-    //~^^^^^^ HELP `no_method_suggested_traits::qux::PrivPub`
-    //~^^^^^^^ HELP `no_method_suggested_traits::quz::PrivPriv`
 }