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