]> git.lizzy.rs Git - rust.git/blob - src/librustc_typeck/check/demand.rs
Review comments
[rust.git] / src / librustc_typeck / check / demand.rs
1 // Copyright 2012 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
12 use check::FnCtxt;
13 use rustc::infer::InferOk;
14 use rustc::traits::ObligationCause;
15
16 use syntax::ast;
17 use syntax_pos::{self, Span};
18 use rustc::hir;
19 use rustc::hir::def::Def;
20 use rustc::ty::{self, Ty, AssociatedItem};
21 use errors::{DiagnosticBuilder, CodeMapper};
22
23 use super::method::probe;
24
25 impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
26     // Requires that the two types unify, and prints an error message if
27     // they don't.
28     pub fn demand_suptype(&self, sp: Span, expected: Ty<'tcx>, actual: Ty<'tcx>) {
29         self.demand_suptype_diag(sp, expected, actual).map(|mut e| e.emit());
30     }
31
32     pub fn demand_suptype_diag(&self,
33                                sp: Span,
34                                expected: Ty<'tcx>,
35                                actual: Ty<'tcx>) -> Option<DiagnosticBuilder<'tcx>> {
36         let cause = &self.misc(sp);
37         match self.at(cause, self.param_env).sup(expected, actual) {
38             Ok(InferOk { obligations, value: () }) => {
39                 self.register_predicates(obligations);
40                 None
41             },
42             Err(e) => {
43                 Some(self.report_mismatched_types(&cause, expected, actual, e))
44             }
45         }
46     }
47
48     pub fn demand_eqtype(&self, sp: Span, expected: Ty<'tcx>, actual: Ty<'tcx>) {
49         if let Some(mut err) = self.demand_eqtype_diag(sp, expected, actual) {
50             err.emit();
51         }
52     }
53
54     pub fn demand_eqtype_diag(&self,
55                              sp: Span,
56                              expected: Ty<'tcx>,
57                              actual: Ty<'tcx>) -> Option<DiagnosticBuilder<'tcx>> {
58         self.demand_eqtype_with_origin(&self.misc(sp), expected, actual)
59     }
60
61     pub fn demand_eqtype_with_origin(&self,
62                                      cause: &ObligationCause<'tcx>,
63                                      expected: Ty<'tcx>,
64                                      actual: Ty<'tcx>) -> Option<DiagnosticBuilder<'tcx>> {
65         match self.at(cause, self.param_env).eq(expected, actual) {
66             Ok(InferOk { obligations, value: () }) => {
67                 self.register_predicates(obligations);
68                 None
69             },
70             Err(e) => {
71                 Some(self.report_mismatched_types(cause, expected, actual, e))
72             }
73         }
74     }
75
76     // Checks that the type of `expr` can be coerced to `expected`.
77     //
78     // NB: This code relies on `self.diverges` to be accurate.  In
79     // particular, assignments to `!` will be permitted if the
80     // diverges flag is currently "always".
81     pub fn demand_coerce(&self,
82                          expr: &hir::Expr,
83                          checked_ty: Ty<'tcx>,
84                          expected: Ty<'tcx>) {
85         let expected = self.resolve_type_vars_with_obligations(expected);
86
87         if let Err(e) = self.try_coerce(expr, checked_ty, self.diverges.get(), expected) {
88             let cause = self.misc(expr.span);
89             let expr_ty = self.resolve_type_vars_with_obligations(checked_ty);
90             let mut err = self.report_mismatched_types(&cause, expected, expr_ty, e);
91             if let Some(suggestion) = self.check_ref(expr,
92                                                      checked_ty,
93                                                      expected) {
94                 err.help(&suggestion);
95             } else {
96                 let mode = probe::Mode::MethodCall;
97                 let suggestions = self.probe_for_return_type(syntax_pos::DUMMY_SP,
98                                                              mode,
99                                                              expected,
100                                                              checked_ty,
101                                                              ast::DUMMY_NODE_ID);
102                 if suggestions.len() > 0 {
103                     err.help(&format!("here are some functions which \
104                                        might fulfill your needs:\n{}",
105                                       self.get_best_match(&suggestions).join("\n")));
106                 }
107             }
108             err.emit();
109         }
110     }
111
112     fn format_method_suggestion(&self, method: &AssociatedItem) -> String {
113         format!("- .{}({})",
114                 method.name,
115                 if self.has_no_input_arg(method) {
116                     ""
117                 } else {
118                     "..."
119                 })
120     }
121
122     fn display_suggested_methods(&self, methods: &[AssociatedItem]) -> Vec<String> {
123         methods.iter()
124                .take(5)
125                .map(|method| self.format_method_suggestion(&*method))
126                .collect::<Vec<String>>()
127     }
128
129     fn get_best_match(&self, methods: &[AssociatedItem]) -> Vec<String> {
130         let no_argument_methods: Vec<_> =
131             methods.iter()
132                    .filter(|ref x| self.has_no_input_arg(&*x))
133                    .map(|x| x.clone())
134                    .collect();
135         if no_argument_methods.len() > 0 {
136             self.display_suggested_methods(&no_argument_methods)
137         } else {
138             self.display_suggested_methods(&methods)
139         }
140     }
141
142     // This function checks if the method isn't static and takes other arguments than `self`.
143     fn has_no_input_arg(&self, method: &AssociatedItem) -> bool {
144         match method.def() {
145             Def::Method(def_id) => {
146                 match self.tcx.type_of(def_id).sty {
147                     ty::TypeVariants::TyFnDef(_, _, sig) => {
148                         sig.inputs().skip_binder().len() == 1
149                     }
150                     _ => false,
151                 }
152             }
153             _ => false,
154         }
155     }
156
157     /// This function is used to determine potential "simple" improvements or users' errors and
158     /// provide them useful help. For example:
159     ///
160     /// ```
161     /// fn some_fn(s: &str) {}
162     ///
163     /// let x = "hey!".to_owned();
164     /// some_fn(x); // error
165     /// ```
166     ///
167     /// No need to find every potential function which could make a coercion to transform a
168     /// `String` into a `&str` since a `&` would do the trick!
169     ///
170     /// In addition of this check, it also checks between references mutability state. If the
171     /// expected is mutable but the provided isn't, maybe we could just say "Hey, try with
172     /// `&mut`!".
173     fn check_ref(&self,
174                  expr: &hir::Expr,
175                  checked_ty: Ty<'tcx>,
176                  expected: Ty<'tcx>)
177                  -> Option<String> {
178         match (&expected.sty, &checked_ty.sty) {
179             (&ty::TyRef(_, _), &ty::TyRef(_, _)) => None,
180             (&ty::TyRef(_, mutability), _) => {
181                 // Check if it can work when put into a ref. For example:
182                 //
183                 // ```
184                 // fn bar(x: &mut i32) {}
185                 //
186                 // let x = 0u32;
187                 // bar(&x); // error, expected &mut
188                 // ```
189                 let ref_ty = match mutability.mutbl {
190                     hir::Mutability::MutMutable => self.tcx.mk_mut_ref(
191                                                        self.tcx.mk_region(ty::ReStatic),
192                                                        checked_ty),
193                     hir::Mutability::MutImmutable => self.tcx.mk_imm_ref(
194                                                        self.tcx.mk_region(ty::ReStatic),
195                                                        checked_ty),
196                 };
197                 if self.can_coerce(ref_ty, expected) {
198                     // Use the callsite's span if this is a macro call. #41858
199                     let sp = self.sess().codemap().call_span_if_macro(expr.span);
200                     if let Ok(src) = self.tcx.sess.codemap().span_to_snippet(sp) {
201                         return Some(format!("try with `{}{}`",
202                                             match mutability.mutbl {
203                                                 hir::Mutability::MutMutable => "&mut ",
204                                                 hir::Mutability::MutImmutable => "&",
205                                             },
206                                             &src));
207                     }
208                 }
209                 None
210             }
211             _ => None,
212         }
213     }
214 }