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.
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.
13 use rustc
::infer
::InferOk
;
14 use rustc
::traits
::ObligationCause
;
17 use syntax_pos
::{self, Span}
;
19 use rustc
::hir
::print
;
20 use rustc
::hir
::def
::Def
;
21 use rustc
::ty
::{self, Ty, AssociatedItem}
;
22 use errors
::{DiagnosticBuilder, CodeMapper}
;
24 use super::method
::probe
;
26 impl<'a
, 'gcx
, 'tcx
> FnCtxt
<'a
, 'gcx
, 'tcx
> {
27 // Requires that the two types unify, and prints an error message if
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());
33 pub fn demand_suptype_diag(&self,
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
);
44 Some(self.report_mismatched_types(&cause
, expected
, actual
, e
))
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
) {
55 pub fn demand_eqtype_diag(&self,
58 actual
: Ty
<'tcx
>) -> Option
<DiagnosticBuilder
<'tcx
>> {
59 self.demand_eqtype_with_origin(&self.misc(sp
), expected
, actual
)
62 pub fn demand_eqtype_with_origin(&self,
63 cause
: &ObligationCause
<'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
);
72 Some(self.report_mismatched_types(cause
, expected
, actual
, e
))
77 pub fn demand_coerce(&self,
82 let (ty
, err
) = self.demand_coerce_diag(expr
, checked_ty
, expected
);
83 if let Some(mut err
) = err
{
89 // Checks that the type of `expr` can be coerced to `expected`.
91 // NB: This code relies on `self.diverges` to be accurate. In
92 // particular, assignments to `!` will be permitted if the
93 // diverges flag is currently "always".
94 pub fn demand_coerce_diag(&self,
98 -> (Ty
<'tcx
>, Option
<DiagnosticBuilder
<'tcx
>>) {
99 let expected
= self.resolve_type_vars_with_obligations(expected
);
101 let e
= match self.try_coerce(expr
, checked_ty
, self.diverges
.get(), expected
) {
102 Ok(ty
) => return (ty
, None
),
106 let cause
= self.misc(expr
.span
);
107 let expr_ty
= self.resolve_type_vars_with_obligations(checked_ty
);
108 let mut err
= self.report_mismatched_types(&cause
, expected
, expr_ty
, e
);
110 // If the expected type is an enum with any variants whose sole
111 // field is of the found type, suggest such variants. See Issue
113 if let ty
::TyAdt(expected_adt
, substs
) = expected
.sty
{
114 let mut compatible_variants
= vec
![];
115 for variant
in &expected_adt
.variants
{
116 if variant
.fields
.len() == 1 {
117 let sole_field
= &variant
.fields
[0];
118 let sole_field_ty
= sole_field
.ty(self.tcx
, substs
);
119 if self.can_coerce(expr_ty
, sole_field_ty
) {
120 let mut variant_path
= self.tcx
.item_path_str(variant
.did
);
121 variant_path
= variant_path
.trim_left_matches("std::prelude::v1::")
123 compatible_variants
.push(variant_path
);
127 if !compatible_variants
.is_empty() {
128 let expr_text
= print
::to_string(print
::NO_ANN
, |s
| s
.print_expr(expr
));
129 let suggestions
= compatible_variants
.iter()
130 .map(|v
| format
!("{}({})", v
, expr_text
)).collect
::<Vec
<_
>>();
131 err
.span_suggestions(expr
.span
,
132 "try using a variant of the expected type",
137 if let Some(suggestion
) = self.check_ref(expr
,
140 err
.help(&suggestion
);
142 let mode
= probe
::Mode
::MethodCall
;
143 let suggestions
= self.probe_for_return_type(syntax_pos
::DUMMY_SP
,
148 if suggestions
.len() > 0 {
149 err
.help(&format
!("here are some functions which \
150 might fulfill your needs:\n{}",
151 self.get_best_match(&suggestions
).join("\n")));
154 (expected
, Some(err
))
157 fn format_method_suggestion(&self, method
: &AssociatedItem
) -> String
{
160 if self.has_no_input_arg(method
) {
167 fn display_suggested_methods(&self, methods
: &[AssociatedItem
]) -> Vec
<String
> {
170 .map(|method
| self.format_method_suggestion(&*method
))
171 .collect
::<Vec
<String
>>()
174 fn get_best_match(&self, methods
: &[AssociatedItem
]) -> Vec
<String
> {
175 let no_argument_methods
: Vec
<_
> =
177 .filter(|ref x
| self.has_no_input_arg(&*x
))
180 if no_argument_methods
.len() > 0 {
181 self.display_suggested_methods(&no_argument_methods
)
183 self.display_suggested_methods(&methods
)
187 // This function checks if the method isn't static and takes other arguments than `self`.
188 fn has_no_input_arg(&self, method
: &AssociatedItem
) -> bool
{
190 Def
::Method(def_id
) => {
191 self.tcx
.fn_sig(def_id
).inputs().skip_binder().len() == 1
197 /// This function is used to determine potential "simple" improvements or users' errors and
198 /// provide them useful help. For example:
201 /// fn some_fn(s: &str) {}
203 /// let x = "hey!".to_owned();
204 /// some_fn(x); // error
207 /// No need to find every potential function which could make a coercion to transform a
208 /// `String` into a `&str` since a `&` would do the trick!
210 /// In addition of this check, it also checks between references mutability state. If the
211 /// expected is mutable but the provided isn't, maybe we could just say "Hey, try with
215 checked_ty
: Ty
<'tcx
>,
218 match (&expected
.sty
, &checked_ty
.sty
) {
219 (&ty
::TyRef(_
, exp
), &ty
::TyRef(_
, check
)) => match (&exp
.ty
.sty
, &check
.ty
.sty
) {
220 (&ty
::TyStr
, &ty
::TyArray(arr
, _
)) |
221 (&ty
::TyStr
, &ty
::TySlice(arr
)) if arr
== self.tcx
.types
.u8 => {
222 if let hir
::ExprLit(_
) = expr
.node
{
223 let sp
= self.sess().codemap().call_span_if_macro(expr
.span
);
224 if let Ok(src
) = self.tcx
.sess
.codemap().span_to_snippet(sp
) {
225 return Some(format
!("try `{}`", &src
[1..]));
230 (&ty
::TyArray(arr
, _
), &ty
::TyStr
) |
231 (&ty
::TySlice(arr
), &ty
::TyStr
) if arr
== self.tcx
.types
.u8 => {
232 if let hir
::ExprLit(_
) = expr
.node
{
233 let sp
= self.sess().codemap().call_span_if_macro(expr
.span
);
234 if let Ok(src
) = self.tcx
.sess
.codemap().span_to_snippet(sp
) {
235 return Some(format
!("try `b{}`", src
));
242 (&ty
::TyRef(_
, mutability
), _
) => {
243 // Check if it can work when put into a ref. For example:
246 // fn bar(x: &mut i32) {}
249 // bar(&x); // error, expected &mut
251 let ref_ty
= match mutability
.mutbl
{
252 hir
::Mutability
::MutMutable
=> self.tcx
.mk_mut_ref(
253 self.tcx
.mk_region(ty
::ReStatic
),
255 hir
::Mutability
::MutImmutable
=> self.tcx
.mk_imm_ref(
256 self.tcx
.mk_region(ty
::ReStatic
),
259 if self.can_coerce(ref_ty
, expected
) {
260 // Use the callsite's span if this is a macro call. #41858
261 let sp
= self.sess().codemap().call_span_if_macro(expr
.span
);
262 if let Ok(src
) = self.tcx
.sess
.codemap().span_to_snippet(sp
) {
263 return Some(format
!("try with `{}{}`",
264 match mutability
.mutbl
{
265 hir
::Mutability
::MutMutable
=> "&mut ",
266 hir
::Mutability
::MutImmutable
=> "&",
273 (_
, &ty
::TyRef(_
, checked
)) => {
274 // We have `&T`, check if what was expected was `T`. If so,
275 // we may want to suggest adding a `*`, or removing
278 // (But, also check check the `expn_info()` to see if this is
279 // a macro; if so, it's hard to extract the text and make a good
280 // suggestion, so don't bother.)
281 if self.infcx
.can_sub(self.param_env
, checked
.ty
, &expected
).is_ok() &&
282 expr
.span
.ctxt().outer().expn_info().is_none() {
285 hir
::ExprAddrOf(_
, ref expr
) => {
286 if let Ok(code
) = self.tcx
.sess
.codemap().span_to_snippet(expr
.span
) {
287 return Some(format
!("try with `{}`", code
));
291 // Maybe add `*`? Only if `T: Copy`.
293 if !self.infcx
.type_moves_by_default(self.param_env
,
296 let sp
= self.sess().codemap().call_span_if_macro(expr
.span
);
297 if let Ok(code
) = self.tcx
.sess
.codemap().span_to_snippet(sp
) {
298 return Some(format
!("try with `*{}`", code
));