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