From 2dd5776b1110a24d9ceb1d1f89dd684d0e63fbf7 Mon Sep 17 00:00:00 2001 From: Daniel J Rollins Date: Sat, 5 Mar 2016 00:26:45 +0000 Subject: [PATCH] Add note if method is called on a function object Fixes issue #29124. If method is called on a function type a note is generated to suggest that the developer may have forgotten to call it. e.g. fn main() { let mut guess = String::new(); std::io::stdin.read_line(&mut guess); } will generate the note: note: called method on function type. did you mean `std::io::stdin().read_line(..)`? --- src/librustc_typeck/check/method/suggest.rs | 112 +++++++++++--------- 1 file changed, 59 insertions(+), 53 deletions(-) diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs index cdf7e1bd33a..fa55da8b6ca 100644 --- a/src/librustc_typeck/check/method/suggest.rs +++ b/src/librustc_typeck/check/method/suggest.rs @@ -37,6 +37,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,65 +115,35 @@ 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 selcx.evaluate_obligation(&obligation) { - span_stored_function!(); - } else { - span_did_you_mean!(); - } - }); - } else { - span_did_you_mean!(); - } - } + 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 is_fn_ty(&rcvr_ty, &fcx, span) { + let expr_string = match rcvr_expr { + Some(expr) => match cx.sess.codemap().span_to_snippet(expr.span) { + Ok(expr_string) => expr_string, + _ => "s".into() + }, + _ => "s".into() + }; + err.fileline_note( + span, + &format!("method invoked on function type. did you \ + mean `{}().{}(...)`?", + expr_string, item_name)); + } + if !static_sources.is_empty() { err.fileline_note( span,