Skip to content

Commit 2dd5776

Browse files
Daniel J RollinsManishearth
Daniel J Rollins
authored andcommitted
Add note if method is called on a function object
Fixes issue rust-lang#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(..)`?
1 parent 7c66a89 commit 2dd5776

File tree

1 file changed

+59
-53
lines changed

1 file changed

+59
-53
lines changed

src/librustc_typeck/check/method/suggest.rs

+59-53
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,42 @@ use std::cmp::Ordering;
3737
use super::{MethodError, NoMatchData, CandidateSource, impl_item, trait_item};
3838
use super::probe::Mode;
3939

40+
fn is_fn_ty<'a, 'tcx>(ty: &Ty<'tcx>, fcx: &FnCtxt<'a, 'tcx>, span: Span) -> bool {
41+
let cx = fcx.tcx();
42+
println!("{:?}", ty);
43+
match ty.sty {
44+
// Not all of these (e.g. unsafe fns) implement FnOnce
45+
// so we look for these beforehand
46+
ty::TyClosure(..) | ty::TyFnDef(..) | ty::TyFnPtr(_) => true,
47+
// If it's not a simple function, look for things which implement FnOnce
48+
_ => {
49+
if let Ok(fn_once_trait_did) =
50+
cx.lang_items.require(FnOnceTraitLangItem) {
51+
let infcx = fcx.infcx();
52+
infcx.probe(|_| {
53+
let fn_once_substs =
54+
Substs::new_trait(vec![infcx.next_ty_var()],
55+
Vec::new(),
56+
ty);
57+
let trait_ref =
58+
ty::TraitRef::new(fn_once_trait_did,
59+
cx.mk_substs(fn_once_substs));
60+
let poly_trait_ref = trait_ref.to_poly_trait_ref();
61+
let obligation = Obligation::misc(span,
62+
fcx.body_id,
63+
poly_trait_ref
64+
.to_predicate());
65+
let mut selcx = SelectionContext::new(infcx);
66+
67+
return selcx.evaluate_obligation(&obligation)
68+
})
69+
} else {
70+
false
71+
}
72+
}
73+
}
74+
}
75+
4076
pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
4177
span: Span,
4278
rcvr_ty: Ty<'tcx>,
@@ -79,65 +115,35 @@ pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
79115
// snippet
80116
};
81117

82-
macro_rules! span_stored_function {
83-
() => {
84-
err.span_note(span,
85-
&format!("use `({0}.{1})(...)` if you meant to call \
86-
the function stored in the `{1}` field",
87-
expr_string, item_name));
88-
}
89-
}
90-
91-
macro_rules! span_did_you_mean {
92-
() => {
93-
err.span_note(span, &format!("did you mean to write `{0}.{1}`?",
94-
expr_string, item_name));
95-
}
96-
}
97-
98-
// Determine if the field can be used as a function in some way
99118
let field_ty = field.ty(cx, substs);
100119

101-
match field_ty.sty {
102-
// Not all of these (e.g. unsafe fns) implement FnOnce
103-
// so we look for these beforehand
104-
ty::TyClosure(..) | ty::TyFnDef(..) | ty::TyFnPtr(_) => {
105-
span_stored_function!();
106-
}
107-
// If it's not a simple function, look for things which implement FnOnce
108-
_ => {
109-
if let Ok(fn_once_trait_did) =
110-
cx.lang_items.require(FnOnceTraitLangItem) {
111-
let infcx = fcx.infcx();
112-
infcx.probe(|_| {
113-
let fn_once_substs =
114-
Substs::new_trait(vec![infcx.next_ty_var()],
115-
Vec::new(),
116-
field_ty);
117-
let trait_ref =
118-
ty::TraitRef::new(fn_once_trait_did,
119-
cx.mk_substs(fn_once_substs));
120-
let poly_trait_ref = trait_ref.to_poly_trait_ref();
121-
let obligation = Obligation::misc(span,
122-
fcx.body_id,
123-
poly_trait_ref
124-
.to_predicate());
125-
let mut selcx = SelectionContext::new(infcx);
126-
127-
if selcx.evaluate_obligation(&obligation) {
128-
span_stored_function!();
129-
} else {
130-
span_did_you_mean!();
131-
}
132-
});
133-
} else {
134-
span_did_you_mean!();
135-
}
136-
}
120+
if is_fn_ty(&field_ty, &fcx, span) {
121+
err.span_note(span,
122+
&format!("use `({0}.{1})(...)` if you meant to call \
123+
the function stored in the `{1}` field",
124+
expr_string, item_name));
125+
} else {
126+
err.span_note(span, &format!("did you mean to write `{0}.{1}`?",
127+
expr_string, item_name));
137128
}
138129
}
139130
}
140131

132+
if is_fn_ty(&rcvr_ty, &fcx, span) {
133+
let expr_string = match rcvr_expr {
134+
Some(expr) => match cx.sess.codemap().span_to_snippet(expr.span) {
135+
Ok(expr_string) => expr_string,
136+
_ => "s".into()
137+
},
138+
_ => "s".into()
139+
};
140+
err.fileline_note(
141+
span,
142+
&format!("method invoked on function type. did you \
143+
mean `{}().{}(...)`?",
144+
expr_string, item_name));
145+
}
146+
141147
if !static_sources.is_empty() {
142148
err.fileline_note(
143149
span,

0 commit comments

Comments
 (0)