about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorKevin Butler <haqkrs@gmail.com>2014-04-22 12:20:08 +0100
committerKevin Butler <haqkrs@gmail.com>2014-05-02 22:46:26 +0100
commitcb08cb8aefa38d082b3f30629e15ff2eb4e61aec (patch)
tree918dde117532dd51b84f4d62fc10dcbca2e14823 /src
parentb5d6b07370b665df6b54fa20e971e61041a233b0 (diff)
downloadrust-cb08cb8aefa38d082b3f30629e15ff2eb4e61aec.tar.gz
rust-cb08cb8aefa38d082b3f30629e15ff2eb4e61aec.zip
Provide a note if method lookup fails and there are static definitions with the same name.
Diffstat (limited to 'src')
-rw-r--r--src/librustc/middle/resolve.rs40
-rw-r--r--src/librustc/middle/typeck/check/method.rs69
-rw-r--r--src/librustc/middle/typeck/check/mod.rs22
-rw-r--r--src/test/compile-fail/issue-7575.rs80
-rw-r--r--src/test/run-pass/issue-7575.rs24
5 files changed, 200 insertions, 35 deletions
diff --git a/src/librustc/middle/resolve.rs b/src/librustc/middle/resolve.rs
index cd6cd48508d..8df94ea1428 100644
--- a/src/librustc/middle/resolve.rs
+++ b/src/librustc/middle/resolve.rs
@@ -16,7 +16,7 @@ use metadata::decoder::{DefLike, DlDef, DlField, DlImpl};
 use middle::lang_items::LanguageItems;
 use middle::lint::{UnnecessaryQualification, UnusedImports};
 use middle::pat_util::pat_bindings;
-use util::nodemap::{NodeMap, DefIdSet, FnvHashSet};
+use util::nodemap::{NodeMap, DefIdSet, FnvHashMap};
 
 use syntax::ast::*;
 use syntax::ast;
@@ -821,7 +821,7 @@ fn Resolver<'a>(session: &'a Session,
 
         graph_root: graph_root,
 
-        method_set: RefCell::new(FnvHashSet::new()),
+        method_map: RefCell::new(FnvHashMap::new()),
         structs: HashSet::new(),
 
         unresolved_imports: 0,
@@ -860,7 +860,7 @@ struct Resolver<'a> {
 
     graph_root: NameBindings,
 
-    method_set: RefCell<FnvHashSet<(Name, DefId)>>,
+    method_map: RefCell<FnvHashMap<(Name, DefId), ast::ExplicitSelf_>>,
     structs: HashSet<DefId>,
 
     // The number of imports that are currently unresolved.
@@ -1371,10 +1371,8 @@ impl<'a> Resolver<'a> {
                                        ty_m.span);
                     method_name_bindings.define_value(def, ty_m.span, true);
 
-                    // Add it to the trait info if not static.
-                    if ty_m.explicit_self.node != SelfStatic {
-                        self.method_set.borrow_mut().insert((ident.name, def_id));
-                    }
+                    self.method_map.borrow_mut().insert((ident.name, def_id),
+                                                        ty_m.explicit_self.node);
                 }
 
                 name_bindings.define_type(DefTrait(def_id), sp, is_public);
@@ -1660,10 +1658,8 @@ impl<'a> Resolver<'a> {
                           trait method '{}'",
                          token::get_ident(method_name));
 
-                  // Add it to the trait info if not static.
-                  if explicit_self != SelfStatic {
-                      self.method_set.borrow_mut().insert((method_name.name, def_id));
-                  }
+                  self.method_map.borrow_mut().insert((method_name.name, def_id), explicit_self);
+
                   if is_exported {
                       self.external_exports.insert(method_def_id);
                   }
@@ -4718,10 +4714,16 @@ impl<'a> Resolver<'a> {
         match containing_module.kind.get() {
             TraitModuleKind | ImplModuleKind => {
                 match containing_module.def_id.get() {
-                    Some(def_id) if self.method_set.borrow().contains(&(ident.name, def_id)) => {
-                        debug!("containing module was a trait or impl \
+                    Some(def_id) => {
+                        match self.method_map.borrow().find(&(ident.name, def_id)) {
+                            Some(x) if *x == SelfStatic => (),
+                            None => (),
+                            _ => {
+                                debug!("containing module was a trait or impl \
                                 and name was a method -> not resolved");
-                        return None;
+                                return None;
+                            }
+                        }
                     },
                     _ => (),
                 }
@@ -5110,9 +5112,9 @@ impl<'a> Resolver<'a> {
             // Look for the current trait.
             match self.current_trait_refs {
                 Some(ref trait_def_ids) => {
-                    let method_set = self.method_set.borrow();
+                    let method_map = self.method_map.borrow();
                     for &trait_def_id in trait_def_ids.iter() {
-                        if method_set.contains(&(name, trait_def_id)) {
+                        if method_map.contains_key(&(name, trait_def_id)) {
                             add_trait_info(&mut found_traits, trait_def_id, name);
                         }
                     }
@@ -5126,7 +5128,7 @@ impl<'a> Resolver<'a> {
             self.populate_module_if_necessary(&search_module);
 
             {
-                let method_set = self.method_set.borrow();
+                let method_map = self.method_map.borrow();
                 for (_, child_names) in search_module.children.borrow().iter() {
                     let def = match child_names.def_for_namespace(TypeNS) {
                         Some(def) => def,
@@ -5136,7 +5138,7 @@ impl<'a> Resolver<'a> {
                         DefTrait(trait_def_id) => trait_def_id,
                         _ => continue,
                     };
-                    if method_set.contains(&(name, trait_def_id)) {
+                    if method_map.contains_key(&(name, trait_def_id)) {
                         add_trait_info(&mut found_traits, trait_def_id, name);
                     }
                 }
@@ -5152,7 +5154,7 @@ impl<'a> Resolver<'a> {
                     Some(DefTrait(trait_def_id)) => trait_def_id,
                     Some(..) | None => continue,
                 };
-                if self.method_set.borrow().contains(&(name, did)) {
+                if self.method_map.borrow().contains_key(&(name, did)) {
                     add_trait_info(&mut found_traits, did, name);
                     self.used_imports.insert((import.type_id, TypeNS));
                 }
diff --git a/src/librustc/middle/typeck/check/method.rs b/src/librustc/middle/typeck/check/method.rs
index 52e0deddf3c..9234270cf7e 100644
--- a/src/librustc/middle/typeck/check/method.rs
+++ b/src/librustc/middle/typeck/check/method.rs
@@ -118,6 +118,12 @@ pub enum AutoderefReceiverFlag {
     DontAutoderefReceiver,
 }
 
+#[deriving(Eq)]
+pub enum StaticMethodsFlag {
+    ReportStaticMethods,
+    IgnoreStaticMethods,
+}
+
 pub fn lookup<'a>(
         fcx: &'a FnCtxt<'a>,
 
@@ -129,7 +135,8 @@ pub fn lookup<'a>(
         supplied_tps: &'a [ty::t],          // The list of types X, Y, ... .
         deref_args: check::DerefArgs,       // Whether we autopointer first.
         check_traits: CheckTraitsFlag,      // Whether we check traits only.
-        autoderef_receiver: AutoderefReceiverFlag)
+        autoderef_receiver: AutoderefReceiverFlag,
+        report_statics: StaticMethodsFlag)
      -> Option<MethodCallee> {
     let mut lcx = LookupContext {
         fcx: fcx,
@@ -143,6 +150,7 @@ pub fn lookup<'a>(
         deref_args: deref_args,
         check_traits: check_traits,
         autoderef_receiver: autoderef_receiver,
+        report_statics: report_statics,
     };
 
     debug!("method lookup(self_ty={}, expr={}, self_expr={})",
@@ -173,7 +181,8 @@ pub fn lookup_in_trait<'a>(
         trait_did: DefId,                   // The trait to limit the lookup to.
         self_ty: ty::t,                     // The type of `a`.
         supplied_tps: &'a [ty::t],          // The list of types X, Y, ... .
-        autoderef_receiver: AutoderefReceiverFlag)
+        autoderef_receiver: AutoderefReceiverFlag,
+        report_statics: StaticMethodsFlag)
      -> Option<MethodCallee> {
     let mut lcx = LookupContext {
         fcx: fcx,
@@ -187,6 +196,7 @@ pub fn lookup_in_trait<'a>(
         deref_args: check::DoDerefArgs,
         check_traits: CheckTraitsOnly,
         autoderef_receiver: autoderef_receiver,
+        report_statics: report_statics,
     };
 
     debug!("method lookup_in_trait(self_ty={}, self_expr={})",
@@ -197,8 +207,6 @@ pub fn lookup_in_trait<'a>(
     lcx.search(self_ty)
 }
 
-
-
 // Determine the index of a method in the list of all methods belonging
 // to a trait and its supertraits.
 fn get_method_index(tcx: &ty::ctxt,
@@ -301,6 +309,7 @@ struct LookupContext<'a> {
     deref_args: check::DerefArgs,
     check_traits: CheckTraitsFlag,
     autoderef_receiver: AutoderefReceiverFlag,
+    report_statics: StaticMethodsFlag,
 }
 
 /**
@@ -661,7 +670,17 @@ impl<'a> LookupContext<'a> {
                                  impl_did: DefId,
                                  impl_methods: &[DefId],
                                  is_extension: bool) {
-        if !self.impl_dups.insert(impl_did) {
+        let did = if self.report_statics == ReportStaticMethods {
+            // we only want to report each base trait once
+            match ty::impl_trait_ref(self.tcx(), impl_did) {
+                Some(trait_ref) => trait_ref.def_id,
+                None => impl_did
+            }
+        } else {
+            impl_did
+        };
+
+        if !self.impl_dups.insert(did) {
             return; // already visited
         }
 
@@ -1018,6 +1037,25 @@ impl<'a> LookupContext<'a> {
             return None;
         }
 
+        if self.report_statics == ReportStaticMethods {
+            // lookup should only be called with ReportStaticMethods if a regular lookup failed
+            assert!(relevant_candidates.iter().all(|c| c.method_ty.explicit_self == SelfStatic));
+
+            self.tcx().sess.fileline_note(self.span,
+                                "found defined static methods, maybe a `self` is missing?");
+
+            for (idx, candidate) in relevant_candidates.iter().enumerate() {
+                self.report_candidate(idx, &candidate.origin);
+            }
+
+            // return something so we don't get errors for every mutability
+            return Some(MethodCallee {
+                origin: relevant_candidates.get(0).origin,
+                ty: ty::mk_err(),
+                substs: substs::empty()
+            });
+        }
+
         if relevant_candidates.len() > 1 {
             self.tcx().sess.span_err(
                 self.span,
@@ -1305,7 +1343,7 @@ impl<'a> LookupContext<'a> {
         return match candidate.method_ty.explicit_self {
             SelfStatic => {
                 debug!("(is relevant?) explicit self is static");
-                false
+                self.report_statics == ReportStaticMethods
             }
 
             SelfValue => {
@@ -1391,11 +1429,20 @@ impl<'a> LookupContext<'a> {
     fn report_candidate(&self, idx: uint, origin: &MethodOrigin) {
         match *origin {
             MethodStatic(impl_did) => {
-                // If it is an instantiated default method, use the original
-                // default method for error reporting.
-                let did = match provided_source(self.tcx(), impl_did) {
-                    None => impl_did,
-                    Some(did) => did
+                let did = if self.report_statics == ReportStaticMethods {
+                    // If we're reporting statics, we want to report the trait
+                    // definition if possible, rather than an impl
+                    match ty::trait_method_of_method(self.tcx(), impl_did) {
+                        None => {debug!("(report candidate) No trait method found"); impl_did},
+                        Some(trait_did) => {debug!("(report candidate) Found trait ref"); trait_did}
+                    }
+                } else {
+                    // If it is an instantiated default method, use the original
+                    // default method for error reporting.
+                    match provided_source(self.tcx(), impl_did) {
+                        None => impl_did,
+                        Some(did) => did
+                    }
                 };
                 self.report_static_candidate(idx, did)
             }
diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs
index 7b62f9f030a..d18e50291fb 100644
--- a/src/librustc/middle/typeck/check/mod.rs
+++ b/src/librustc/middle/typeck/check/mod.rs
@@ -97,6 +97,7 @@ use middle::typeck::check::method::{AutoderefReceiver};
 use middle::typeck::check::method::{AutoderefReceiverFlag};
 use middle::typeck::check::method::{CheckTraitsAndInherentMethods};
 use middle::typeck::check::method::{DontAutoderefReceiver};
+use middle::typeck::check::method::{IgnoreStaticMethods, ReportStaticMethods};
 use middle::typeck::check::regionmanip::replace_late_bound_regions_in_fn_sig;
 use middle::typeck::check::regionmanip::relate_free_regions;
 use middle::typeck::check::vtable::VtableContext;
@@ -1360,7 +1361,7 @@ fn try_overloaded_deref(fcx: &FnCtxt,
         (PreferMutLvalue, Some(trait_did)) => {
             method::lookup_in_trait(fcx, span, base_expr.map(|x| &*x),
                                     token::intern("deref_mut"), trait_did,
-                                    base_ty, [], DontAutoderefReceiver)
+                                    base_ty, [], DontAutoderefReceiver, IgnoreStaticMethods)
         }
         _ => None
     };
@@ -1370,7 +1371,7 @@ fn try_overloaded_deref(fcx: &FnCtxt,
         (None, Some(trait_did)) => {
             method::lookup_in_trait(fcx, span, base_expr.map(|x| &*x),
                                     token::intern("deref"), trait_did,
-                                    base_ty, [], DontAutoderefReceiver)
+                                    base_ty, [], DontAutoderefReceiver, IgnoreStaticMethods)
         }
         (method, _) => method
     };
@@ -1956,7 +1957,7 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
                                          expr_t, tps.as_slice(),
                                          DontDerefArgs,
                                          CheckTraitsAndInherentMethods,
-                                         AutoderefReceiver) {
+                                         AutoderefReceiver, IgnoreStaticMethods) {
             Some(method) => {
                 let method_ty = method.ty;
                 let method_call = MethodCall::expr(expr.id);
@@ -1976,6 +1977,15 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
 
                 // Add error type for the result
                 fcx.write_error(expr.id);
+
+                // Check for potential static matches (missing self parameters)
+                method::lookup(fcx, expr, rcvr,
+                                    method_name.node.name,
+                                    expr_t, tps.as_slice(),
+                                    DontDerefArgs,
+                                    CheckTraitsAndInherentMethods,
+                                    DontAutoderefReceiver, ReportStaticMethods);
+
                 ty::mk_err()
             }
         };
@@ -2040,7 +2050,8 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
         let method = match trait_did {
             Some(trait_did) => {
                 method::lookup_in_trait(fcx, op_ex.span, Some(&*args[0]), opname,
-                                        trait_did, self_t, [], autoderef_receiver)
+                                        trait_did, self_t, [], autoderef_receiver,
+                                        IgnoreStaticMethods)
             }
             None => None
         };
@@ -2348,7 +2359,8 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
                              tps.as_slice(),
                              DontDerefArgs,
                              CheckTraitsAndInherentMethods,
-                             AutoderefReceiver) {
+                             AutoderefReceiver,
+                             IgnoreStaticMethods) {
             Some(_) => {
                 fcx.type_error_message(
                     expr.span,
diff --git a/src/test/compile-fail/issue-7575.rs b/src/test/compile-fail/issue-7575.rs
new file mode 100644
index 00000000000..5b03e6fc226
--- /dev/null
+++ b/src/test/compile-fail/issue-7575.rs
@@ -0,0 +1,80 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+trait CtxtFn {
+    fn f8(self, uint) -> uint;
+    fn f9(uint) -> uint; //~ NOTE candidate #
+}
+
+trait OtherTrait {
+    fn f9(uint) -> uint; //~ NOTE candidate #
+}
+
+trait UnusedTrait { // This should never show up as a candidate
+    fn f9(uint) -> uint;
+}
+
+impl CtxtFn for uint {
+    fn f8(self, i: uint) -> uint {
+        i * 4u
+    }
+
+    fn f9(i: uint) -> uint {
+        i * 4u
+    }
+}
+
+impl OtherTrait for uint {
+    fn f9(i: uint) -> uint {
+        i * 8u
+    }
+}
+
+struct MyInt(int);
+
+impl MyInt {
+    fn fff(i: int) -> int { //~ NOTE candidate #1 is `MyInt::fff`
+        i
+    }
+}
+
+trait ManyImplTrait {
+    fn is_str() -> bool { //~ NOTE candidate #1 is
+        false
+    }
+}
+
+impl ManyImplTrait for ~str {
+    fn is_str() -> bool {
+        true
+    }
+}
+
+impl ManyImplTrait for uint {}
+impl ManyImplTrait for int {}
+impl ManyImplTrait for char {}
+impl ManyImplTrait for MyInt {}
+
+fn no_param_bound(u: uint, m: MyInt) -> uint {
+    u.f8(42) + u.f9(342) + m.fff(42)
+            //~^ ERROR type `uint` does not implement any method in scope named `f9`
+            //~^^ NOTE found defined static methods, maybe a `self` is missing?
+                        //~^^^ ERROR type `MyInt` does not implement any method in scope named `fff`
+                        //~^^^^ NOTE found defined static methods, maybe a `self` is missing?
+}
+
+fn param_bound<T: ManyImplTrait>(t: T) -> bool {
+    t.is_str()
+    //~^ ERROR type `T` does not implement any method in scope named `is_str`
+    //~^^ NOTE found defined static methods, maybe a `self` is missing?
+}
+
+fn main() {
+}
\ No newline at end of file
diff --git a/src/test/run-pass/issue-7575.rs b/src/test/run-pass/issue-7575.rs
new file mode 100644
index 00000000000..f31fbc0a12e
--- /dev/null
+++ b/src/test/run-pass/issue-7575.rs
@@ -0,0 +1,24 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+trait Foo {
+    fn new() -> bool { false }
+}
+
+trait Bar {
+    fn new(&self) -> bool { true }
+}
+
+impl Bar for int {}
+impl Foo for int {}
+
+fn main() {
+    assert!(1i.new());
+}
\ No newline at end of file