Skip to content
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

Closed
wants to merge 6 commits into from
Closed
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 59 additions & 53 deletions src/librustc_typeck/check/method/suggest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hooray, DRY!

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>,
Expand Down Expand Up @@ -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 \
Copy link
Member

Choose a reason for hiding this comment

The 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 span_suggestion which suggests you try {}().{} or whatever. Don't include suggestions within the notes.

mean `{}().{}(...)`?",
expr_string, item_name));
}

if !static_sources.is_empty() {
err.fileline_note(
span,
Expand Down