diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs index cdf7e1bd33a..44724ca26b1 100644 --- a/src/librustc_typeck/check/method/suggest.rs +++ b/src/librustc_typeck/check/method/suggest.rs @@ -25,11 +25,13 @@ use middle::subst::Substs; use middle::traits::{Obligation, SelectionContext}; use util::nodemap::{FnvHashSet}; + use syntax::ast; use syntax::codemap::Span; use syntax::errors::DiagnosticBuilder; use rustc_front::print::pprust; use rustc_front::hir; +use rustc_front::hir::Expr_; use std::cell; use std::cmp::Ordering; @@ -37,6 +39,42 @@ use std::cmp::Ordering; use super::{MethodError, NoMatchData, CandidateSource, impl_item, trait_item}; use super::probe::Mode; +fn is_fn_ty<'a, 'tcx>(ty: &Ty<'tcx>, fcx: &FnCtxt<'a, 'tcx>, span: Span) -> bool { + let cx = fcx.tcx(); + println!("{:?}", ty); + match ty.sty { + // Not all of these (e.g. unsafe fns) implement FnOnce + // so we look for these beforehand + ty::TyClosure(..) | ty::TyFnDef(..) | ty::TyFnPtr(_) => true, + // If it's not a simple function, look for things which implement FnOnce + _ => { + if let Ok(fn_once_trait_did) = + cx.lang_items.require(FnOnceTraitLangItem) { + let infcx = fcx.infcx(); + infcx.probe(|_| { + let fn_once_substs = + Substs::new_trait(vec![infcx.next_ty_var()], + Vec::new(), + ty); + let trait_ref = + ty::TraitRef::new(fn_once_trait_did, + cx.mk_substs(fn_once_substs)); + let poly_trait_ref = trait_ref.to_poly_trait_ref(); + let obligation = Obligation::misc(span, + fcx.body_id, + poly_trait_ref + .to_predicate()); + let mut selcx = SelectionContext::new(infcx); + + return selcx.evaluate_obligation(&obligation) + }) + } else { + false + } + } + } +} + pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, span: Span, rcvr_ty: Ty<'tcx>, @@ -79,60 +117,41 @@ pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, // snippet }; - macro_rules! span_stored_function { - () => { - err.span_note(span, - &format!("use `({0}.{1})(...)` if you meant to call \ - the function stored in the `{1}` field", - expr_string, item_name)); - } - } - - macro_rules! span_did_you_mean { - () => { - err.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 field_ty = field.ty(cx, substs); - match field_ty.sty { - // Not all of these (e.g. unsafe fns) implement FnOnce - // so we look for these beforehand - ty::TyClosure(..) | ty::TyFnDef(..) | ty::TyFnPtr(_) => { - span_stored_function!(); - } - // If it's not a simple function, look for things which implement FnOnce - _ => { - if let Ok(fn_once_trait_did) = - cx.lang_items.require(FnOnceTraitLangItem) { - let infcx = fcx.infcx(); - infcx.probe(|_| { - let fn_once_substs = - Substs::new_trait(vec![infcx.next_ty_var()], - Vec::new(), - field_ty); - let trait_ref = - ty::TraitRef::new(fn_once_trait_did, - cx.mk_substs(fn_once_substs)); - let poly_trait_ref = trait_ref.to_poly_trait_ref(); - let obligation = Obligation::misc(span, - fcx.body_id, - poly_trait_ref - .to_predicate()); - let mut selcx = SelectionContext::new(infcx); + if is_fn_ty(&field_ty, &fcx, span) { + err.span_note(span, + &format!("use `({0}.{1})(...)` if you meant to call \ + the function stored in the `{1}` field", + expr_string, item_name)); + } else { + err.span_note(span, &format!("did you mean to write `{0}.{1}`?", + expr_string, item_name)); + } + } + } - if selcx.evaluate_obligation(&obligation) { - span_stored_function!(); - } else { - span_did_you_mean!(); - } - }); - } else { - span_did_you_mean!(); - } + if is_fn_ty(&rcvr_ty, &fcx, span) { + macro_rules! report_function { + ($span:expr, $name:expr) => { + err.fileline_note( + $span, + &format!("{} is a function, perhaps you wish to call it", + $name)); + } + } + + if let Some(expr) = rcvr_expr { + if let Ok (expr_string) = cx.sess.codemap().span_to_snippet(expr.span) { + report_function!(expr.span, expr_string); + err.span_suggestion(expr.span, + "try calling the base function:", + format!("{}()", + expr_string)); + } + else if let Expr_::ExprPath(_, path) = expr.node.clone() { + if let Some(segment) = path.segments.last() { + report_function!(expr.span, segment.identifier.name); } } } diff --git a/src/test/compile-fail/issue-29124.rs b/src/test/compile-fail/issue-29124.rs new file mode 100644 index 00000000000..b3dc043f502 --- /dev/null +++ b/src/test/compile-fail/issue-29124.rs @@ -0,0 +1,35 @@ +// Copyright 2015 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. + +struct ret; +struct obj; + +impl obj { + fn func() -> ret { + ret + } +} + +fn func() -> ret { + ret +} + +fn main() { + obj::func.x(); + //~^ ERROR no method named `x` found for type `fn() -> ret {obj::func}` in the current scope + //~^^ NOTE obj::func is a function, perhaps you wish to call it + //~^^^ HELP try calling the base function: + //~| SUGGESTION obj::func().x(); + func.x(); + //~^ ERROR no method named `x` found for type `fn() -> ret {func}` in the current scope + //~^^ NOTE func is a function, perhaps you wish to call it + //~^^^ HELP try calling the base function: + //~| SUGGESTION func().x(); +}