-
Notifications
You must be signed in to change notification settings - Fork 13.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add note if method is called on a function object #32053
Changes from 1 commit
904281f
87b8197
d4cc783
091d143
086feeb
4f1ec4b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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::TyBareFn(..) => 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::TyBareFn(..) => { | ||
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 \ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps the error should be something like "stdin is a function, perhaps you wish to call it"? And, if the span_to_snippet call works (don't use "s" as a placeholder snippet), add a |
||
mean `{}().{}(...)`?", | ||
expr_string, item_name)); | ||
} | ||
|
||
if !static_sources.is_empty() { | ||
err.fileline_note( | ||
span, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hooray, DRY!