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