Skip to content

Commit 18b3357

Browse files
splitting methods into smaller ones, add docs, better variable naming
1 parent b568642 commit 18b3357

File tree

3 files changed

+109
-64
lines changed

3 files changed

+109
-64
lines changed

src/librustc_typeck/check/demand.rs

Lines changed: 65 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -61,22 +61,50 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
6161
}
6262
}
6363

64-
fn check_ref(&self, expr: &hir::Expr, checked_ty: Ty<'tcx>,
65-
expected: Ty<'tcx>) -> Option<String> {
66-
match (&checked_ty.sty, &expected.sty) {
67-
(&ty::TyRef(_, x_mutability), &ty::TyRef(_, y_mutability)) => {
64+
/// This function is used to determine potential "simple" improvements or users' errors and
65+
/// provide them useful help. For example:
66+
///
67+
/// ```
68+
/// fn some_fn(s: &str) {}
69+
///
70+
/// let x = "hey!".to_owned();
71+
/// some_fn(x); // error
72+
/// ```
73+
///
74+
/// No need to find every potential function which could make a coercion to transform a
75+
/// `String` into a `&str` since a `&` would do the trick!
76+
///
77+
/// In addition of this check, it also checks between references mutability state. If the
78+
/// expected is mutable but the provided isn't, maybe we could just say "Hey, try with
79+
/// `&mut`!".
80+
fn check_ref(&self,
81+
expr: &hir::Expr,
82+
checked_ty: Ty<'tcx>,
83+
expected: Ty<'tcx>)
84+
-> Option<String> {
85+
match (&expected.sty, &checked_ty.sty) {
86+
(&ty::TyRef(_, expected_mutability),
87+
&ty::TyRef(_, checked_mutability)) => {
6888
// check if there is a mutability difference
69-
if x_mutability.mutbl == hir::Mutability::MutImmutable &&
70-
x_mutability.mutbl != y_mutability.mutbl &&
71-
self.can_sub_types(&x_mutability.ty, y_mutability.ty).is_ok() {
89+
if checked_mutability.mutbl == hir::Mutability::MutImmutable &&
90+
checked_mutability.mutbl != expected_mutability.mutbl &&
91+
self.can_sub_types(&checked_mutability.ty,
92+
expected_mutability.ty).is_ok() {
7293
if let Ok(src) = self.tcx.sess.codemap().span_to_snippet(expr.span) {
7394
return Some(format!("try with `&mut {}`", &src.replace("&", "")));
7495
}
7596
}
7697
None
7798
}
78-
(_, &ty::TyRef(_, mutability)) => {
79-
// check if it can work when put into a ref
99+
(&ty::TyRef(_, mutability), _) => {
100+
// Check if it can work when put into a ref. For example:
101+
//
102+
// ```
103+
// fn bar(x: &mut i32) {}
104+
//
105+
// let x = 0u32;
106+
// bar(&x); // error, expected &mut
107+
// ```
80108
let ref_ty = match mutability.mutbl {
81109
hir::Mutability::MutMutable => self.tcx.mk_mut_ref(
82110
self.tcx.mk_region(ty::ReStatic),
@@ -110,11 +138,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
110138
let mode = probe::Mode::MethodCall;
111139
let suggestions = if let Some(s) = self.check_ref(expr, checked_ty, expected) {
112140
Some(s)
113-
} else if let Ok(methods) = self.probe_return(syntax_pos::DUMMY_SP,
114-
mode,
115-
expected,
116-
checked_ty,
117-
ast::DUMMY_NODE_ID) {
141+
} else if let Ok(methods) = self.probe_for_return_type(syntax_pos::DUMMY_SP,
142+
mode,
143+
expected,
144+
checked_ty,
145+
ast::DUMMY_NODE_ID) {
118146
let suggestions: Vec<_> =
119147
methods.iter()
120148
.map(|ref x| {
@@ -143,29 +171,38 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
143171
}
144172
}
145173

174+
fn format_method_suggestion(&self, method: &ImplOrTraitItem<'tcx>) -> String {
175+
format!(".{}({})",
176+
method.name(),
177+
if self.has_not_input_arg(method) {
178+
""
179+
} else {
180+
"..."
181+
})
182+
}
183+
184+
fn display_suggested_methods(&self, methods: &[Rc<ImplOrTraitItem<'tcx>>]) -> String {
185+
methods.iter()
186+
.take(5)
187+
.map(|method| self.format_method_suggestion(&*method))
188+
.collect::<Vec<String>>()
189+
.join("\n - ")
190+
}
191+
146192
fn get_best_match(&self, methods: &[Rc<ImplOrTraitItem<'tcx>>]) -> String {
147-
if methods.len() == 1 {
148-
return format!(" - {}", methods[0].name());
149-
}
150-
let no_argument_methods: Vec<&Rc<ImplOrTraitItem<'tcx>>> =
193+
let no_argument_methods: Vec<Rc<ImplOrTraitItem<'tcx>>> =
151194
methods.iter()
152195
.filter(|ref x| self.has_not_input_arg(&*x))
196+
.map(|x| x.clone())
153197
.collect();
154198
if no_argument_methods.len() > 0 {
155-
no_argument_methods.iter()
156-
.take(5)
157-
.map(|method| format!(".{}()", method.name()))
158-
.collect::<Vec<String>>()
159-
.join("\n - ")
199+
self.display_suggested_methods(&no_argument_methods)
160200
} else {
161-
methods.iter()
162-
.take(5)
163-
.map(|method| format!(".{}()", method.name()))
164-
.collect::<Vec<String>>()
165-
.join("\n - ")
201+
self.display_suggested_methods(&methods)
166202
}
167203
}
168204

205+
// This function checks if the method isn't static and takes other arguments than `self`.
169206
fn has_not_input_arg(&self, method: &ImplOrTraitItem<'tcx>) -> bool {
170207
match *method {
171208
ImplOrTraitItem::MethodTraitItem(ref x) => {

src/librustc_typeck/check/method/mod.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
9191
allow_private: bool)
9292
-> bool {
9393
let mode = probe::Mode::MethodCall;
94-
match self.probe_method(span, mode, method_name, self_ty, call_expr_id) {
94+
match self.probe_for_name(span, mode, method_name, self_ty, call_expr_id) {
9595
Ok(..) => true,
9696
Err(NoMatch(..)) => false,
9797
Err(Ambiguity(..)) => true,
@@ -130,7 +130,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
130130

131131
let mode = probe::Mode::MethodCall;
132132
let self_ty = self.resolve_type_vars_if_possible(&self_ty);
133-
let pick = self.probe_method(span, mode, method_name, self_ty, call_expr.id)?.remove(0);
133+
let pick = self.probe_for_name(span, mode, method_name, self_ty, call_expr.id)?.remove(0);
134134

135135
if let Some(import_id) = pick.import_id {
136136
self.tcx.used_trait_imports.borrow_mut().insert(import_id);
@@ -328,7 +328,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
328328
expr_id: ast::NodeId)
329329
-> Result<Def, MethodError<'tcx>> {
330330
let mode = probe::Mode::Path;
331-
let picks = self.probe_method(span, mode, method_name, self_ty, expr_id)?;
331+
let picks = self.probe_for_name(span, mode, method_name, self_ty, expr_id)?;
332332
let pick = &picks[0];
333333

334334
if let Some(import_id) = pick.import_id {

src/librustc_typeck/check/method/probe.rs

Lines changed: 41 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -150,41 +150,41 @@ pub enum Mode {
150150
}
151151

152152
impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
153-
pub fn probe_return(&self,
154-
span: Span,
155-
mode: Mode,
156-
return_type: Ty<'tcx>,
157-
self_ty: Ty<'tcx>,
158-
scope_expr_id: ast::NodeId)
159-
-> PickResult<'tcx> {
153+
pub fn probe_for_return_type(&self,
154+
span: Span,
155+
mode: Mode,
156+
return_type: Ty<'tcx>,
157+
self_ty: Ty<'tcx>,
158+
scope_expr_id: ast::NodeId)
159+
-> PickResult<'tcx> {
160160
debug!("probe(self_ty={:?}, return_type={}, scope_expr_id={})",
161161
self_ty,
162162
return_type,
163163
scope_expr_id);
164-
self._probe(span, mode, LookingFor::ReturnType(return_type), self_ty, scope_expr_id)
164+
self.probe_for(span, mode, LookingFor::ReturnType(return_type), self_ty, scope_expr_id)
165165
}
166166

167-
pub fn probe_method(&self,
168-
span: Span,
169-
mode: Mode,
170-
item_name: ast::Name,
171-
self_ty: Ty<'tcx>,
172-
scope_expr_id: ast::NodeId)
173-
-> PickResult<'tcx> {
167+
pub fn probe_for_name(&self,
168+
span: Span,
169+
mode: Mode,
170+
item_name: ast::Name,
171+
self_ty: Ty<'tcx>,
172+
scope_expr_id: ast::NodeId)
173+
-> PickResult<'tcx> {
174174
debug!("probe(self_ty={:?}, item_name={}, scope_expr_id={})",
175175
self_ty,
176176
item_name,
177177
scope_expr_id);
178-
self._probe(span, mode, LookingFor::MethodName(item_name), self_ty, scope_expr_id)
178+
self.probe_for(span, mode, LookingFor::MethodName(item_name), self_ty, scope_expr_id)
179179
}
180180

181-
fn _probe(&self,
182-
span: Span,
183-
mode: Mode,
184-
looking_for: LookingFor<'tcx>,
185-
self_ty: Ty<'tcx>,
186-
scope_expr_id: ast::NodeId)
187-
-> PickResult<'tcx> {
181+
fn probe_for(&self,
182+
span: Span,
183+
mode: Mode,
184+
looking_for: LookingFor<'tcx>,
185+
self_ty: Ty<'tcx>,
186+
scope_expr_id: ast::NodeId)
187+
-> PickResult<'tcx> {
188188

189189
// FIXME(#18741) -- right now, creating the steps involves evaluating the
190190
// `*` operator, which registers obligations that then escape into
@@ -265,6 +265,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
265265

266266
let final_ty = match looking_for {
267267
&LookingFor::MethodName(_) => autoderef.unambiguous_final_ty(),
268+
// Since ReturnType case tries to coerce the returned type to the
269+
// expected one, we need all the information!
268270
&LookingFor::ReturnType(_) => self_ty,
269271
};
270272
match final_ty.sty {
@@ -627,10 +629,10 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
627629
match *method {
628630
ty::ImplOrTraitItem::MethodTraitItem(ref x) => {
629631
self.probe(|_| {
630-
let output = self.replace_late_bound_regions_with_fresh_var(
631-
self.span, infer::FnCall, &x.fty.sig.output());
632632
let substs = self.fresh_substs_for_item(self.span, method.def_id());
633-
let output = output.0.subst(self.tcx, substs);
633+
let output = x.fty.sig.output().subst(self.tcx, substs);
634+
let (output, _) = self.replace_late_bound_regions_with_fresh_var(
635+
self.span, infer::FnCall, &output);
634636
self.can_sub_types(output, expected).is_ok()
635637
})
636638
}
@@ -950,10 +952,17 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
950952
let steps = self.steps.clone();
951953

952954
match self.looking_for {
953-
LookingFor::MethodName(_) => steps.iter()
954-
.filter_map(|step| self.pick_step(step))
955-
.next(),
955+
LookingFor::MethodName(_) => {
956+
// find the first step that works
957+
steps.iter()
958+
.filter_map(|step| self.pick_step(step))
959+
.next()
960+
}
956961
LookingFor::ReturnType(_) => {
962+
// Normally, we stop at the first step where we find a positive match.
963+
// But when we are scanning for methods with a suitable return type,
964+
// these methods have distinct names and hence may not shadow one another
965+
// (also, this is just for hints, so precision is less important).
957966
let mut ret = Vec::new();
958967

959968
for step in steps.iter() {
@@ -1050,10 +1059,9 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
10501059
match self.looking_for {
10511060
LookingFor::MethodName(_) => it.nth(0),
10521061
LookingFor::ReturnType(_) => {
1053-
let mut ret = Vec::new();
1054-
it.filter_map(|entry| entry.ok())
1055-
.map(|mut v| { ret.append(&mut v); })
1056-
.all(|_| true);
1062+
let ret = it.filter_map(|entry| entry.ok())
1063+
.flat_map(|v| v)
1064+
.collect::<Vec<_>>();
10571065

10581066
if ret.len() < 1 {
10591067
None

0 commit comments

Comments
 (0)