diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs index 27679408717..b6bb9d67a31 100644 --- a/src/librustc_typeck/check/method/suggest.rs +++ b/src/librustc_typeck/check/method/suggest.rs @@ -15,8 +15,11 @@ use CrateCtxt; use astconv::AstConv; use check::{self, FnCtxt}; -use middle::ty::{self, Ty}; +use middle::ty::{self, Ty, ToPolyTraitRef, AsPredicate}; use middle::def; +use middle::lang_items::FnOnceTraitLangItem; +use middle::subst::Substs; +use middle::traits::{Obligation, SelectionContext}; use metadata::{csearch, cstore, decoder}; use syntax::{ast, ast_util}; @@ -65,31 +68,38 @@ pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, if let Some(field) = fields.iter().find(|f| f.name == item_name) { let expr_string = match cx.sess.codemap().span_to_snippet(expr.span) { Ok(expr_string) => expr_string, - _ => "s".into() // default to generic placeholder for expression + _ => "s".into() // Default to a generic placeholder for the + // expression when we can't generate a string + // snippet }; - // TODO Fix when closure note is displayed - // below commented code from eddyb on irc - // let substs = subst::Substs::new_trait(vec![fcx.inh.infcx.next_ty_var()], Vec::new(), field_ty); - // let trait_ref = ty::TraitRef::new(trait_def_id, fcx.tcx().mk_substs(substs)); - // let poly_trait_ref = trait_ref.to_poly_trait_ref(); - // let obligation = traits::Obligation::misc(span, fcx.body_id, poly_trait_ref.as_predicate()); - // let mut selcx = traits::SelectionContext::new(fcx.infcx(), fcx); - // if selcx.evaluate_obligation(&obligation) { /* suggest */ } - - match ty::lookup_field_type(cx, did, field.id, substs).sty { - ty::TyClosure(_, _) | ty::TyBareFn(_,_) => { - cx.sess.span_note(span, - &format!("use `({0}.{1})(...)` if you meant to call the \ - function stored in the `{1}` field", - expr_string, item_name)); - }, - _ => { - cx.sess.span_note(span, - &format!("did you mean to write `{0}.{1}`?", - expr_string, item_name)); - }, + // Determine if the field can be used as a function in some way + let fn_once_trait_did = match cx.lang_items.require(FnOnceTraitLangItem) { + Ok(trait_did) => trait_did, + Err(err) => cx.sess.fatal(&err[..]) }; + + let field_ty = ty::lookup_field_type(cx, did, field.id, substs); + let field_ty_substs = Substs::new_trait(vec![fcx.inh.infcx.next_ty_var()], + Vec::new(), + field_ty); + let trait_ref = ty::TraitRef::new(fn_once_trait_did, + cx.mk_substs(field_ty_substs)); + let poly_trait_ref = trait_ref.to_poly_trait_ref(); + let obligation = Obligation::misc(span, + fcx.body_id, + poly_trait_ref.as_predicate()); + let mut selcx = SelectionContext::new(fcx.infcx(), fcx); + + if selcx.evaluate_obligation(&obligation) { + cx.sess.span_note(span, + &format!("use `({0}.{1})(...)` if you meant to call the \ + function stored in the `{1}` field", + expr_string, item_name)); + } else { + cx.sess.span_note(span, &format!("did you mean to write `{0}.{1}`?", + expr_string, item_name)); + } } } diff --git a/src/test/compile-fail/issue-18343.rs b/src/test/compile-fail/issue-18343.rs index 802b21d211c..4601db9dba0 100644 --- a/src/test/compile-fail/issue-18343.rs +++ b/src/test/compile-fail/issue-18343.rs @@ -10,33 +10,10 @@ struct Obj where F: FnMut() -> u32 { closure: F, - nfn: usize, -} - -struct S where F: FnMut() -> u32 { - v: Obj, -} - -fn func() -> u32 { - 0 } fn main() { - let o = Obj { closure: || 42, nfn: 42 }; + let o = Obj { closure: || 42 }; o.closure(); //~ ERROR no method named `closure` found //~^ NOTE use `(o.closure)(...)` if you meant to call the function stored in the `closure` field - - // TODO move these to a new test for #2392 - let x = o.nfn(); //~ ERROR no method named `nfn` found - //~^ NOTE did you mean to write `o.nfn`? - - let b = Obj { closure: func, nfn: 5 }; - b.closure(); //~ ERROR no method named `closure` found - //~^ NOTE use `(b.closure)(...)` if you meant to call the function stored in the `closure` field - - let s = S { v: b }; - s.v.closure();//~ ERROR no method named `closure` found - //~^ NOTE use `(s.v.closure)(...)` if you meant to call the function stored in the `closure` field - s.v.nfn();//~ ERROR no method named `nfn` found - //~^ NOTE did you mean to write `s.v.nfn`? } diff --git a/src/test/compile-fail/issue-2392.rs b/src/test/compile-fail/issue-2392.rs new file mode 100644 index 00000000000..2e1fbdf6394 --- /dev/null +++ b/src/test/compile-fail/issue-2392.rs @@ -0,0 +1,65 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(core)] +use std::boxed::FnBox; + +struct Obj where F: FnOnce() -> u32 { + closure: F, + not_closure: usize, +} + +struct BoxedObj { + boxed_closure: Box u32>, +} + +struct Wrapper where F: FnMut() -> u32 { + wrap: Obj, +} + +fn func() -> u32 { + 0 +} + +fn check_expression() -> Obj u32>> { + Obj { closure: Box::new(|| 42_u32) as Box u32>, not_closure: 42 } +} + +fn main() { + // test variations of function + let o_closure = Obj { closure: || 42, not_closure: 42 }; + o_closure.closure(); //~ ERROR no method named `closure` found + //~^ NOTE use `(o_closure.closure)(...)` if you meant to call the function stored in the `closure` field + + o_closure.not_closure(); //~ ERROR no method named `not_closure` found + //~^ NOTE did you mean to write `o_closure.not_closure`? + + let o_func = Obj { closure: func, not_closure: 5 }; + o_func.closure(); //~ ERROR no method named `closure` found + //~^ NOTE use `(o_func.closure)(...)` if you meant to call the function stored in the `closure` field + + let boxed_fn = BoxedObj { boxed_closure: Box::new(func) }; + boxed_fn.boxed_closure();//~ ERROR no method named `boxed_closure` found + //~^ NOTE use `(boxed_fn.boxed_closure)(...)` if you meant to call the function stored in the `boxed_closure` field + + let boxed_closure = BoxedObj { boxed_closure: Box::new(|| 42_u32) as Box u32> }; + boxed_closure.boxed_closure();//~ ERROR no method named `boxed_closure` found + //~^ NOTE use `(boxed_closure.boxed_closure)(...)` if you meant to call the function stored in the `boxed_closure` field + + // test expression writing in the notes + let w = Wrapper { wrap: o_func }; + w.wrap.closure();//~ ERROR no method named `closure` found + //~^ NOTE use `(w.wrap.closure)(...)` if you meant to call the function stored in the `closure` field + w.wrap.not_closure();//~ ERROR no method named `not_closure` found + //~^ NOTE did you mean to write `w.wrap.not_closure`? + + check_expression().closure();//~ ERROR no method named `closure` found + //~^ NOTE use `(check_expression().closure)(...)` if you meant to call the function stored in the `closure` field +}