]> git.lizzy.rs Git - rust.git/blob - src/librustc_typeck/check/demand.rs
Rollup merge of #42919 - zackmdavis:once_again_we_heard_you_the_first_time, r=eddyb
[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     pub fn demand_coerce(&self, expr: &hir::Expr, checked_ty: Ty<'tcx>, expected: Ty<'tcx>) {
77         if let Some(mut err) = self.demand_coerce_diag(expr, checked_ty, expected) {
78             err.emit();
79         }
80     }
81
82     // Checks that the type of `expr` can be coerced to `expected`.
83     //
84     // NB: This code relies on `self.diverges` to be accurate.  In
85     // particular, assignments to `!` will be permitted if the
86     // diverges flag is currently "always".
87     pub fn demand_coerce_diag(&self,
88                               expr: &hir::Expr,
89                               checked_ty: Ty<'tcx>,
90                               expected: Ty<'tcx>) -> Option<DiagnosticBuilder<'tcx>> {
91         let expected = self.resolve_type_vars_with_obligations(expected);
92
93         if let Err(e) = self.try_coerce(expr, checked_ty, self.diverges.get(), expected) {
94             let cause = self.misc(expr.span);
95             let expr_ty = self.resolve_type_vars_with_obligations(checked_ty);
96             let mut err = self.report_mismatched_types(&cause, expected, expr_ty, e);
97             if let Some(suggestion) = self.check_ref(expr,
98                                                      checked_ty,
99                                                      expected) {
100                 err.help(&suggestion);
101             } else {
102                 let mode = probe::Mode::MethodCall;
103                 let suggestions = self.probe_for_return_type(syntax_pos::DUMMY_SP,
104                                                              mode,
105                                                              expected,
106                                                              checked_ty,
107                                                              ast::DUMMY_NODE_ID);
108                 if suggestions.len() > 0 {
109                     err.help(&format!("here are some functions which \
110                                        might fulfill your needs:\n{}",
111                                       self.get_best_match(&suggestions).join("\n")));
112                 }
113             }
114             return Some(err);
115         }
116         None
117     }
118
119     fn format_method_suggestion(&self, method: &AssociatedItem) -> String {
120         format!("- .{}({})",
121                 method.name,
122                 if self.has_no_input_arg(method) {
123                     ""
124                 } else {
125                     "..."
126                 })
127     }
128
129     fn display_suggested_methods(&self, methods: &[AssociatedItem]) -> Vec<String> {
130         methods.iter()
131                .take(5)
132                .map(|method| self.format_method_suggestion(&*method))
133                .collect::<Vec<String>>()
134     }
135
136     fn get_best_match(&self, methods: &[AssociatedItem]) -> Vec<String> {
137         let no_argument_methods: Vec<_> =
138             methods.iter()
139                    .filter(|ref x| self.has_no_input_arg(&*x))
140                    .map(|x| x.clone())
141                    .collect();
142         if no_argument_methods.len() > 0 {
143             self.display_suggested_methods(&no_argument_methods)
144         } else {
145             self.display_suggested_methods(&methods)
146         }
147     }
148
149     // This function checks if the method isn't static and takes other arguments than `self`.
150     fn has_no_input_arg(&self, method: &AssociatedItem) -> bool {
151         match method.def() {
152             Def::Method(def_id) => {
153                 self.tcx.fn_sig(def_id).inputs().skip_binder().len() == 1
154             }
155             _ => false,
156         }
157     }
158
159     /// This function is used to determine potential "simple" improvements or users' errors and
160     /// provide them useful help. For example:
161     ///
162     /// ```
163     /// fn some_fn(s: &str) {}
164     ///
165     /// let x = "hey!".to_owned();
166     /// some_fn(x); // error
167     /// ```
168     ///
169     /// No need to find every potential function which could make a coercion to transform a
170     /// `String` into a `&str` since a `&` would do the trick!
171     ///
172     /// In addition of this check, it also checks between references mutability state. If the
173     /// expected is mutable but the provided isn't, maybe we could just say "Hey, try with
174     /// `&mut`!".
175     fn check_ref(&self,
176                  expr: &hir::Expr,
177                  checked_ty: Ty<'tcx>,
178                  expected: Ty<'tcx>)
179                  -> Option<String> {
180         match (&expected.sty, &checked_ty.sty) {
181             (&ty::TyRef(_, _), &ty::TyRef(_, _)) => None,
182             (&ty::TyRef(_, mutability), _) => {
183                 // Check if it can work when put into a ref. For example:
184                 //
185                 // ```
186                 // fn bar(x: &mut i32) {}
187                 //
188                 // let x = 0u32;
189                 // bar(&x); // error, expected &mut
190                 // ```
191                 let ref_ty = match mutability.mutbl {
192                     hir::Mutability::MutMutable => self.tcx.mk_mut_ref(
193                                                        self.tcx.mk_region(ty::ReStatic),
194                                                        checked_ty),
195                     hir::Mutability::MutImmutable => self.tcx.mk_imm_ref(
196                                                        self.tcx.mk_region(ty::ReStatic),
197                                                        checked_ty),
198                 };
199                 if self.can_coerce(ref_ty, expected) {
200                     // Use the callsite's span if this is a macro call. #41858
201                     let sp = self.sess().codemap().call_span_if_macro(expr.span);
202                     if let Ok(src) = self.tcx.sess.codemap().span_to_snippet(sp) {
203                         return Some(format!("try with `{}{}`",
204                                             match mutability.mutbl {
205                                                 hir::Mutability::MutMutable => "&mut ",
206                                                 hir::Mutability::MutImmutable => "&",
207                                             },
208                                             &src));
209                     }
210                 }
211                 None
212             }
213             _ => None,
214         }
215     }
216 }