Skip to content

Commit 7d4d77f

Browse files
committed
Auto merge of #26305 - Nashenas88:field-method-message-2392, r=eddyb
This fixes #2392 I'd like to thank @eddyb for helping me on this one! I wouldn't have gotten the complicated FnOnce check done without his help.
2 parents dd2151c + bae1df6 commit 7d4d77f

File tree

3 files changed

+124
-7
lines changed

3 files changed

+124
-7
lines changed

src/librustc_typeck/check/method/suggest.rs

Lines changed: 55 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,11 @@ use CrateCtxt;
1515

1616
use astconv::AstConv;
1717
use check::{self, FnCtxt};
18-
use middle::ty::{self, Ty};
18+
use middle::ty::{self, Ty, ToPolyTraitRef, AsPredicate};
1919
use middle::def;
20+
use middle::lang_items::FnOnceTraitLangItem;
21+
use middle::subst::Substs;
22+
use middle::traits::{Obligation, SelectionContext};
2023
use metadata::{csearch, cstore, decoder};
2124

2225
use syntax::{ast, ast_util};
@@ -59,12 +62,58 @@ pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
5962
None);
6063

6164
// If the item has the name of a field, give a help note
62-
if let (&ty::TyStruct(did, _), Some(_)) = (&rcvr_ty.sty, rcvr_expr) {
65+
if let (&ty::TyStruct(did, substs), Some(expr)) = (&rcvr_ty.sty, rcvr_expr) {
6366
let fields = ty::lookup_struct_fields(cx, did);
64-
if fields.iter().any(|f| f.name == item_name) {
65-
cx.sess.span_note(span,
66-
&format!("use `(s.{0})(...)` if you meant to call the \
67-
function stored in the `{0}` field", item_name));
67+
68+
if let Some(field) = fields.iter().find(|f| f.name == item_name) {
69+
let expr_string = match cx.sess.codemap().span_to_snippet(expr.span) {
70+
Ok(expr_string) => expr_string,
71+
_ => "s".into() // Default to a generic placeholder for the
72+
// expression when we can't generate a string
73+
// snippet
74+
};
75+
76+
let span_stored_function = || {
77+
cx.sess.span_note(span,
78+
&format!("use `({0}.{1})(...)` if you meant to call \
79+
the function stored in the `{1}` field",
80+
expr_string, item_name));
81+
};
82+
83+
let span_did_you_mean = || {
84+
cx.sess.span_note(span, &format!("did you mean to write `{0}.{1}`?",
85+
expr_string, item_name));
86+
};
87+
88+
// Determine if the field can be used as a function in some way
89+
let field_ty = ty::lookup_field_type(cx, did, field.id, substs);
90+
if let Ok(fn_once_trait_did) = cx.lang_items.require(FnOnceTraitLangItem) {
91+
let infcx = fcx.infcx();
92+
infcx.probe(|_| {
93+
let fn_once_substs = Substs::new_trait(vec![infcx.next_ty_var()],
94+
Vec::new(),
95+
field_ty);
96+
let trait_ref = ty::TraitRef::new(fn_once_trait_did,
97+
cx.mk_substs(fn_once_substs));
98+
let poly_trait_ref = trait_ref.to_poly_trait_ref();
99+
let obligation = Obligation::misc(span,
100+
fcx.body_id,
101+
poly_trait_ref.as_predicate());
102+
let mut selcx = SelectionContext::new(infcx, fcx);
103+
104+
if selcx.evaluate_obligation(&obligation) {
105+
span_stored_function();
106+
} else {
107+
span_did_you_mean();
108+
}
109+
});
110+
} else {
111+
match field_ty.sty {
112+
// fallback to matching a closure or function pointer
113+
ty::TyClosure(..) | ty::TyBareFn(..) => span_stored_function(),
114+
_ => span_did_you_mean(),
115+
}
116+
}
68117
}
69118
}
70119

src/test/compile-fail/issue-18343.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,5 @@ struct Obj<F> where F: FnMut() -> u32 {
1515
fn main() {
1616
let o = Obj { closure: || 42 };
1717
o.closure(); //~ ERROR no method named `closure` found
18-
//~^ NOTE use `(s.closure)(...)` if you meant to call the function stored in the `closure` field
18+
//~^ NOTE use `(o.closure)(...)` if you meant to call the function stored in the `closure` field
1919
}

src/test/compile-fail/issue-2392.rs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![feature(core)]
12+
use std::boxed::FnBox;
13+
14+
struct Obj<F> where F: FnOnce() -> u32 {
15+
closure: F,
16+
not_closure: usize,
17+
}
18+
19+
struct BoxedObj {
20+
boxed_closure: Box<FnBox() -> u32>,
21+
}
22+
23+
struct Wrapper<F> where F: FnMut() -> u32 {
24+
wrap: Obj<F>,
25+
}
26+
27+
fn func() -> u32 {
28+
0
29+
}
30+
31+
fn check_expression() -> Obj<Box<FnBox() -> u32>> {
32+
Obj { closure: Box::new(|| 42_u32) as Box<FnBox() -> u32>, not_closure: 42 }
33+
}
34+
35+
fn main() {
36+
// test variations of function
37+
38+
let o_closure = Obj { closure: || 42, not_closure: 42 };
39+
o_closure.closure(); //~ ERROR no method named `closure` found
40+
//~^ NOTE use `(o_closure.closure)(...)` if you meant to call the function stored
41+
42+
o_closure.not_closure(); //~ ERROR no method named `not_closure` found
43+
//~^ NOTE did you mean to write `o_closure.not_closure`?
44+
45+
let o_func = Obj { closure: func, not_closure: 5 };
46+
o_func.closure(); //~ ERROR no method named `closure` found
47+
//~^ NOTE use `(o_func.closure)(...)` if you meant to call the function stored
48+
49+
let boxed_fn = BoxedObj { boxed_closure: Box::new(func) };
50+
boxed_fn.boxed_closure();//~ ERROR no method named `boxed_closure` found
51+
//~^ NOTE use `(boxed_fn.boxed_closure)(...)` if you meant to call the function stored
52+
53+
let boxed_closure = BoxedObj { boxed_closure: Box::new(|| 42_u32) as Box<FnBox() -> u32> };
54+
boxed_closure.boxed_closure();//~ ERROR no method named `boxed_closure` found
55+
//~^ NOTE use `(boxed_closure.boxed_closure)(...)` if you meant to call the function stored
56+
57+
// test expression writing in the notes
58+
59+
let w = Wrapper { wrap: o_func };
60+
w.wrap.closure();//~ ERROR no method named `closure` found
61+
//~^ NOTE use `(w.wrap.closure)(...)` if you meant to call the function stored
62+
63+
w.wrap.not_closure();//~ ERROR no method named `not_closure` found
64+
//~^ NOTE did you mean to write `w.wrap.not_closure`?
65+
66+
check_expression().closure();//~ ERROR no method named `closure` found
67+
//~^ NOTE use `(check_expression().closure)(...)` if you meant to call the function stored
68+
}

0 commit comments

Comments
 (0)