]> git.proxmox.com Git - rustc.git/blame - src/librustc_typeck/check/demand.rs
New upstream version 1.46.0+dfsg1
[rustc.git] / src / librustc_typeck / check / demand.rs
CommitLineData
9fa01778 1use crate::check::FnCtxt;
74b04a01 2use rustc_infer::infer::InferOk;
ba9703b0 3use rustc_trait_selection::infer::InferCtxtExt as _;
f9f354fc 4use rustc_trait_selection::traits::ObligationCause;
1a4d82fc 5
74b04a01 6use rustc_ast::util::parser::PREC_POSTFIX;
dfeec247
XL
7use rustc_errors::{Applicability, DiagnosticBuilder};
8use rustc_hir as hir;
f9f354fc 9use rustc_hir::lang_items::CloneTraitLangItem;
ba9703b0
XL
10use rustc_hir::{is_range_literal, Node};
11use rustc_middle::ty::adjustment::AllowTwoPhase;
f9f354fc 12use rustc_middle::ty::{self, AssocItem, Ty, TypeAndMut};
dfeec247
XL
13use rustc_span::symbol::sym;
14use rustc_span::Span;
32a655c1
SL
15
16use super::method::probe;
1a4d82fc 17
f035d41b
XL
18use std::fmt;
19
dc9dc135 20impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
60c5eb7d
XL
21 pub fn emit_coerce_suggestions(
22 &self,
23 err: &mut DiagnosticBuilder<'_>,
dfeec247 24 expr: &hir::Expr<'_>,
60c5eb7d 25 expr_ty: Ty<'tcx>,
dfeec247 26 expected: Ty<'tcx>,
f035d41b 27 expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
60c5eb7d
XL
28 ) {
29 self.annotate_expected_due_to_let_ty(err, expr);
30 self.suggest_compatible_variants(err, expr, expected, expr_ty);
f035d41b 31 self.suggest_deref_ref_or_into(err, expr, expected, expr_ty, expected_ty_expr);
74b04a01
XL
32 if self.suggest_calling_boxed_future_when_appropriate(err, expr, expected, expr_ty) {
33 return;
34 }
60c5eb7d
XL
35 self.suggest_boxing_when_appropriate(err, expr, expected, expr_ty);
36 self.suggest_missing_await(err, expr, expected, expr_ty);
f035d41b 37 self.note_need_for_fn_pointer(err, expected, expr_ty);
60c5eb7d
XL
38 }
39
a7813a04
XL
40 // Requires that the two types unify, and prints an error message if
41 // they don't.
42 pub fn demand_suptype(&self, sp: Span, expected: Ty<'tcx>, actual: Ty<'tcx>) {
f9f354fc
XL
43 if let Some(mut e) = self.demand_suptype_diag(sp, expected, actual) {
44 e.emit();
45 }
041b39d2
XL
46 }
47
dfeec247
XL
48 pub fn demand_suptype_diag(
49 &self,
50 sp: Span,
51 expected: Ty<'tcx>,
52 actual: Ty<'tcx>,
53 ) -> Option<DiagnosticBuilder<'tcx>> {
74b04a01
XL
54 self.demand_suptype_with_origin(&self.misc(sp), expected, actual)
55 }
56
57 pub fn demand_suptype_with_origin(
58 &self,
59 cause: &ObligationCause<'tcx>,
60 expected: Ty<'tcx>,
61 actual: Ty<'tcx>,
62 ) -> Option<DiagnosticBuilder<'tcx>> {
7cac9316 63 match self.at(cause, self.param_env).sup(expected, actual) {
476ff2be
SL
64 Ok(InferOk { obligations, value: () }) => {
65 self.register_predicates(obligations);
041b39d2 66 None
a7813a04 67 }
dfeec247 68 Err(e) => Some(self.report_mismatched_types(&cause, expected, actual, e)),
54a0048b 69 }
1a4d82fc 70 }
1a4d82fc 71
a7813a04 72 pub fn demand_eqtype(&self, sp: Span, expected: Ty<'tcx>, actual: Ty<'tcx>) {
32a655c1
SL
73 if let Some(mut err) = self.demand_eqtype_diag(sp, expected, actual) {
74 err.emit();
75 }
76 }
77
dfeec247
XL
78 pub fn demand_eqtype_diag(
79 &self,
80 sp: Span,
81 expected: Ty<'tcx>,
82 actual: Ty<'tcx>,
83 ) -> Option<DiagnosticBuilder<'tcx>> {
32a655c1 84 self.demand_eqtype_with_origin(&self.misc(sp), expected, actual)
5bcae85e
SL
85 }
86
dfeec247
XL
87 pub fn demand_eqtype_with_origin(
88 &self,
89 cause: &ObligationCause<'tcx>,
90 expected: Ty<'tcx>,
91 actual: Ty<'tcx>,
92 ) -> Option<DiagnosticBuilder<'tcx>> {
7cac9316 93 match self.at(cause, self.param_env).eq(expected, actual) {
476ff2be
SL
94 Ok(InferOk { obligations, value: () }) => {
95 self.register_predicates(obligations);
32a655c1 96 None
3b2f2976 97 }
dfeec247 98 Err(e) => Some(self.report_mismatched_types(cause, expected, actual, e)),
54a0048b 99 }
1a4d82fc 100 }
1a4d82fc 101
dfeec247 102 pub fn demand_coerce(
e74abb32 103 &self,
dfeec247
XL
104 expr: &hir::Expr<'_>,
105 checked_ty: Ty<'tcx>,
e74abb32 106 expected: Ty<'tcx>,
f035d41b 107 expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
dfeec247
XL
108 allow_two_phase: AllowTwoPhase,
109 ) -> Ty<'tcx> {
f035d41b
XL
110 let (ty, err) =
111 self.demand_coerce_diag(expr, checked_ty, expected, expected_ty_expr, allow_two_phase);
abe05a73 112 if let Some(mut err) = err {
041b39d2
XL
113 err.emit();
114 }
abe05a73 115 ty
041b39d2
XL
116 }
117
a7813a04 118 // Checks that the type of `expr` can be coerced to `expected`.
cc61c64b 119 //
0731742a 120 // N.B., this code relies on `self.diverges` to be accurate. In
cc61c64b
XL
121 // particular, assignments to `!` will be permitted if the
122 // diverges flag is currently "always".
60c5eb7d
XL
123 pub fn demand_coerce_diag(
124 &self,
dfeec247 125 expr: &hir::Expr<'_>,
60c5eb7d
XL
126 checked_ty: Ty<'tcx>,
127 expected: Ty<'tcx>,
f035d41b 128 expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
60c5eb7d
XL
129 allow_two_phase: AllowTwoPhase,
130 ) -> (Ty<'tcx>, Option<DiagnosticBuilder<'tcx>>) {
e74abb32 131 let expected = self.resolve_vars_with_obligations(expected);
cc61c64b 132
83c7162d 133 let e = match self.try_coerce(expr, checked_ty, expected, allow_two_phase) {
abe05a73 134 Ok(ty) => return (ty, None),
dfeec247 135 Err(e) => e,
abe05a73
XL
136 };
137
e74abb32 138 let expr = expr.peel_drop_temps();
abe05a73 139 let cause = self.misc(expr.span);
e74abb32 140 let expr_ty = self.resolve_vars_with_obligations(checked_ty);
abe05a73
XL
141 let mut err = self.report_mismatched_types(&cause, expected, expr_ty, e);
142
532ac7d7
XL
143 if self.is_assign_to_bool(expr, expected) {
144 // Error reported in `check_assign` so avoid emitting error again.
145 err.delay_as_bug();
dfeec247 146 return (expected, None);
abe05a73 147 }
3b2f2976 148
f035d41b 149 self.emit_coerce_suggestions(&mut err, expr, expr_ty, expected, expected_ty_expr);
8faf50e0 150
abe05a73 151 (expected, Some(err))
32a655c1
SL
152 }
153
dfeec247
XL
154 fn annotate_expected_due_to_let_ty(
155 &self,
156 err: &mut DiagnosticBuilder<'_>,
157 expr: &hir::Expr<'_>,
158 ) {
60c5eb7d 159 let parent = self.tcx.hir().get_parent_node(expr.hir_id);
dfeec247
XL
160 if let Some(hir::Node::Local(hir::Local { ty: Some(ty), init: Some(init), .. })) =
161 self.tcx.hir().find(parent)
162 {
60c5eb7d
XL
163 if init.hir_id == expr.hir_id {
164 // Point at `let` assignment type.
165 err.span_label(ty.span, "expected due to this");
166 }
167 }
168 }
169
532ac7d7 170 /// Returns whether the expected type is `bool` and the expression is `x = y`.
dfeec247 171 pub fn is_assign_to_bool(&self, expr: &hir::Expr<'_>, expected: Ty<'tcx>) -> bool {
e74abb32 172 if let hir::ExprKind::Assign(..) = expr.kind {
532ac7d7
XL
173 return expected == self.tcx.types.bool;
174 }
175 false
176 }
177
178 /// If the expected type is an enum (Issue #55250) with any variants whose
179 /// sole field is of the found type, suggest such variants. (Issue #42764)
180 fn suggest_compatible_variants(
181 &self,
182 err: &mut DiagnosticBuilder<'_>,
dfeec247 183 expr: &hir::Expr<'_>,
532ac7d7
XL
184 expected: Ty<'tcx>,
185 expr_ty: Ty<'tcx>,
186 ) {
e74abb32 187 if let ty::Adt(expected_adt, substs) = expected.kind {
532ac7d7
XL
188 if !expected_adt.is_enum() {
189 return;
190 }
191
dfeec247
XL
192 let mut compatible_variants = expected_adt
193 .variants
532ac7d7
XL
194 .iter()
195 .filter(|variant| variant.fields.len() == 1)
196 .filter_map(|variant| {
197 let sole_field = &variant.fields[0];
198 let sole_field_ty = sole_field.ty(self.tcx, substs);
199 if self.can_coerce(expr_ty, sole_field_ty) {
200 let variant_path = self.tcx.def_path_str(variant.def_id);
201 // FIXME #56861: DRYer prelude filtering
202 Some(variant_path.trim_start_matches("std::prelude::v1::").to_string())
203 } else {
204 None
205 }
dfeec247
XL
206 })
207 .peekable();
532ac7d7
XL
208
209 if compatible_variants.peek().is_some() {
ba9703b0
XL
210 if let Ok(expr_text) = self.tcx.sess.source_map().span_to_snippet(expr.span) {
211 let suggestions = compatible_variants.map(|v| format!("{}({})", v, expr_text));
212 let msg = "try using a variant of the expected enum";
213 err.span_suggestions(
214 expr.span,
215 msg,
216 suggestions,
217 Applicability::MaybeIncorrect,
218 );
219 }
532ac7d7
XL
220 }
221 }
222 }
223
dfeec247
XL
224 pub fn get_conversion_methods(
225 &self,
226 span: Span,
227 expected: Ty<'tcx>,
228 checked_ty: Ty<'tcx>,
ba9703b0 229 hir_id: hir::HirId,
dfeec247 230 ) -> Vec<AssocItem> {
ba9703b0
XL
231 let mut methods =
232 self.probe_for_return_type(span, probe::Mode::MethodCall, expected, checked_ty, hir_id);
2c00a5a8 233 methods.retain(|m| {
ba9703b0 234 self.has_only_self_parameter(m)
dfeec247
XL
235 && self
236 .tcx
237 .get_attrs(m.def_id)
238 .iter()
f035d41b 239 // This special internal attribute is used to permit
dfeec247
XL
240 // "identity-like" conversion methods to be suggested here.
241 //
242 // FIXME (#46459 and #46460): ideally
243 // `std::convert::Into::into` and `std::borrow:ToOwned` would
244 // also be `#[rustc_conversion_suggestion]`, if not for
245 // method-probing false-positives and -negatives (respectively).
246 //
247 // FIXME? Other potential candidate methods: `as_ref` and
248 // `as_mut`?
74b04a01 249 .any(|a| a.check_name(sym::rustc_conversion_suggestion))
2c00a5a8 250 });
32a655c1 251
2c00a5a8 252 methods
32a655c1
SL
253 }
254
ba9703b0
XL
255 /// This function checks whether the method is not static and does not accept other parameters than `self`.
256 fn has_only_self_parameter(&self, method: &AssocItem) -> bool {
48663c56 257 match method.kind {
ba9703b0
XL
258 ty::AssocKind::Fn => {
259 method.fn_has_self_parameter
260 && self.tcx.fn_sig(method.def_id).inputs().skip_binder().len() == 1
32a655c1
SL
261 }
262 _ => false,
a7813a04
XL
263 }
264 }
cc61c64b 265
94b46f34
XL
266 /// Identify some cases where `as_ref()` would be appropriate and suggest it.
267 ///
268 /// Given the following code:
269 /// ```
270 /// struct Foo;
271 /// fn takes_ref(_: &Foo) {}
272 /// let ref opt = Some(Foo);
273 ///
e1599b0c 274 /// opt.map(|param| takes_ref(param));
94b46f34 275 /// ```
e1599b0c 276 /// Suggest using `opt.as_ref().map(|param| takes_ref(param));` instead.
94b46f34
XL
277 ///
278 /// It only checks for `Option` and `Result` and won't work with
279 /// ```
e1599b0c 280 /// opt.map(|param| { takes_ref(param) });
94b46f34 281 /// ```
dfeec247 282 fn can_use_as_ref(&self, expr: &hir::Expr<'_>) -> Option<(Span, &'static str, String)> {
e74abb32 283 let path = match expr.kind {
416331ca 284 hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) => path,
dfeec247 285 _ => return None,
416331ca
XL
286 };
287
288 let local_id = match path.res {
289 hir::def::Res::Local(id) => id,
dfeec247 290 _ => return None,
416331ca
XL
291 };
292
293 let local_parent = self.tcx.hir().get_parent_node(local_id);
e1599b0c
XL
294 let param_hir_id = match self.tcx.hir().find(local_parent) {
295 Some(Node::Param(hir::Param { hir_id, .. })) => hir_id,
dfeec247 296 _ => return None,
416331ca
XL
297 };
298
e1599b0c
XL
299 let param_parent = self.tcx.hir().get_parent_node(*param_hir_id);
300 let (expr_hir_id, closure_fn_decl) = match self.tcx.hir().find(param_parent) {
dfeec247
XL
301 Some(Node::Expr(hir::Expr {
302 hir_id,
303 kind: hir::ExprKind::Closure(_, decl, ..),
304 ..
305 })) => (hir_id, decl),
306 _ => return None,
416331ca
XL
307 };
308
309 let expr_parent = self.tcx.hir().get_parent_node(*expr_hir_id);
310 let hir = self.tcx.hir().find(expr_parent);
311 let closure_params_len = closure_fn_decl.inputs.len();
312 let (method_path, method_span, method_expr) = match (hir, closure_params_len) {
dfeec247
XL
313 (
314 Some(Node::Expr(hir::Expr {
f035d41b 315 kind: hir::ExprKind::MethodCall(path, span, expr, _),
dfeec247
XL
316 ..
317 })),
318 1,
319 ) => (path, span, expr),
320 _ => return None,
416331ca
XL
321 };
322
323 let self_ty = self.tables.borrow().node_type(method_expr[0].hir_id);
324 let self_ty = format!("{:?}", self_ty);
325 let name = method_path.ident.as_str();
dfeec247
XL
326 let is_as_ref_able = (self_ty.starts_with("&std::option::Option")
327 || self_ty.starts_with("&std::result::Result")
328 || self_ty.starts_with("std::option::Option")
329 || self_ty.starts_with("std::result::Result"))
330 && (name == "map" || name == "and_then");
416331ca
XL
331 match (is_as_ref_able, self.sess().source_map().span_to_snippet(*method_span)) {
332 (true, Ok(src)) => {
333 let suggestion = format!("as_ref().{}", src);
334 Some((*method_span, "consider using `as_ref` instead", suggestion))
dfeec247
XL
335 }
336 _ => None,
94b46f34 337 }
94b46f34
XL
338 }
339
dc9dc135
XL
340 crate fn is_hir_id_from_struct_pattern_shorthand_field(
341 &self,
342 hir_id: hir::HirId,
343 sp: Span,
344 ) -> bool {
74b04a01 345 let sm = self.sess().source_map();
dc9dc135
XL
346 let parent_id = self.tcx.hir().get_parent_node(hir_id);
347 if let Some(parent) = self.tcx.hir().find(parent_id) {
532ac7d7 348 // Account for fields
dfeec247
XL
349 if let Node::Expr(hir::Expr { kind: hir::ExprKind::Struct(_, fields, ..), .. }) = parent
350 {
74b04a01 351 if let Ok(src) = sm.span_to_snippet(sp) {
dfeec247 352 for field in *fields {
60c5eb7d 353 if field.ident.as_str() == src && field.is_shorthand {
532ac7d7
XL
354 return true;
355 }
356 }
357 }
358 }
359 }
360 false
361 }
362
f9f354fc
XL
363 fn replace_prefix<A, B, C>(&self, s: A, old: B, new: C) -> Option<String>
364 where
365 A: AsRef<str>,
366 B: AsRef<str>,
367 C: AsRef<str>,
368 {
369 let s = s.as_ref();
370 let old = old.as_ref();
371 if s.starts_with(old) { Some(new.as_ref().to_owned() + &s[old.len()..]) } else { None }
372 }
373
cc61c64b
XL
374 /// This function is used to determine potential "simple" improvements or users' errors and
375 /// provide them useful help. For example:
376 ///
377 /// ```
378 /// fn some_fn(s: &str) {}
379 ///
380 /// let x = "hey!".to_owned();
381 /// some_fn(x); // error
382 /// ```
383 ///
384 /// No need to find every potential function which could make a coercion to transform a
385 /// `String` into a `&str` since a `&` would do the trick!
386 ///
387 /// In addition of this check, it also checks between references mutability state. If the
388 /// expected is mutable but the provided isn't, maybe we could just say "Hey, try with
389 /// `&mut`!".
dc9dc135
XL
390 pub fn check_ref(
391 &self,
dfeec247 392 expr: &hir::Expr<'_>,
dc9dc135
XL
393 checked_ty: Ty<'tcx>,
394 expected: Ty<'tcx>,
f9f354fc 395 ) -> Option<(Span, &'static str, String, Applicability)> {
74b04a01 396 let sm = self.sess().source_map();
532ac7d7 397 let sp = expr.span;
ba9703b0 398 if sm.is_imported(sp) {
532ac7d7
XL
399 // Ignore if span is from within a macro #41858, #58298. We previously used the macro
400 // call span, but that breaks down when the type error comes from multiple calls down.
8faf50e0
XL
401 return None;
402 }
403
dfeec247
XL
404 let is_struct_pat_shorthand_field =
405 self.is_hir_id_from_struct_pattern_shorthand_field(expr.hir_id, sp);
532ac7d7 406
e1599b0c
XL
407 // If the span is from a macro, then it's hard to extract the text
408 // and make a good suggestion, so don't bother.
e74abb32
XL
409 let is_macro = sp.from_expansion() && sp.desugaring_kind().is_none();
410
411 // `ExprKind::DropTemps` is semantically irrelevant for these suggestions.
412 let expr = expr.peel_drop_temps();
48663c56 413
e74abb32
XL
414 match (&expr.kind, &expected.kind, &checked_ty.kind) {
415 (_, &ty::Ref(_, exp, _), &ty::Ref(_, check, _)) => match (&exp.kind, &check.kind) {
ba9703b0 416 (&ty::Str, &ty::Array(arr, _) | &ty::Slice(arr)) if arr == self.tcx.types.u8 => {
e74abb32 417 if let hir::ExprKind::Lit(_) = expr.kind {
74b04a01 418 if let Ok(src) = sm.span_to_snippet(sp) {
f9f354fc 419 if let Some(src) = self.replace_prefix(src, "b\"", "\"") {
dfeec247
XL
420 return Some((
421 sp,
422 "consider removing the leading `b`",
f9f354fc
XL
423 src,
424 Applicability::MachineApplicable,
dfeec247 425 ));
8faf50e0 426 }
ea8adc8c
XL
427 }
428 }
dfeec247 429 }
ba9703b0 430 (&ty::Array(arr, _) | &ty::Slice(arr), &ty::Str) if arr == self.tcx.types.u8 => {
e74abb32 431 if let hir::ExprKind::Lit(_) = expr.kind {
74b04a01 432 if let Ok(src) = sm.span_to_snippet(sp) {
f9f354fc 433 if let Some(src) = self.replace_prefix(src, "\"", "b\"") {
dfeec247
XL
434 return Some((
435 sp,
436 "consider adding a leading `b`",
f9f354fc
XL
437 src,
438 Applicability::MachineApplicable,
dfeec247 439 ));
8faf50e0 440 }
ea8adc8c
XL
441 }
442 }
ea8adc8c 443 }
94b46f34 444 _ => {}
ea8adc8c 445 },
48663c56 446 (_, &ty::Ref(_, _, mutability), _) => {
cc61c64b
XL
447 // Check if it can work when put into a ref. For example:
448 //
449 // ```
450 // fn bar(x: &mut i32) {}
451 //
452 // let x = 0u32;
453 // bar(&x); // error, expected &mut
454 // ```
94b46f34 455 let ref_ty = match mutability {
dfeec247 456 hir::Mutability::Mut => {
532ac7d7
XL
457 self.tcx.mk_mut_ref(self.tcx.mk_region(ty::ReStatic), checked_ty)
458 }
dfeec247 459 hir::Mutability::Not => {
532ac7d7
XL
460 self.tcx.mk_imm_ref(self.tcx.mk_region(ty::ReStatic), checked_ty)
461 }
cc61c64b
XL
462 };
463 if self.can_coerce(ref_ty, expected) {
dc9dc135 464 let mut sugg_sp = sp;
f035d41b 465 if let hir::ExprKind::MethodCall(ref segment, sp, ref args, _) = expr.kind {
f9f354fc 466 let clone_trait = self.tcx.require_lang_item(CloneTraitLangItem, Some(sp));
60c5eb7d 467 if let ([arg], Some(true), sym::clone) = (
dc9dc135
XL
468 &args[..],
469 self.tables.borrow().type_dependent_def_id(expr.hir_id).map(|did| {
470 let ai = self.tcx.associated_item(did);
471 ai.container == ty::TraitContainer(clone_trait)
472 }),
60c5eb7d 473 segment.ident.name,
dc9dc135
XL
474 ) {
475 // If this expression had a clone call when suggesting borrowing
60c5eb7d 476 // we want to suggest removing it because it'd now be unnecessary.
dc9dc135
XL
477 sugg_sp = arg.span;
478 }
479 }
74b04a01 480 if let Ok(src) = sm.span_to_snippet(sugg_sp) {
e74abb32 481 let needs_parens = match expr.kind {
0bf4aa26 482 // parenthesize if needed (Issue #46756)
dfeec247 483 hir::ExprKind::Cast(_, _) | hir::ExprKind::Binary(_, _, _) => true,
0bf4aa26 484 // parenthesize borrows of range literals (Issue #54505)
dfeec247 485 _ if is_range_literal(self.tcx.sess.source_map(), expr) => true,
0bf4aa26
XL
486 _ => false,
487 };
dfeec247 488 let sugg_expr = if needs_parens { format!("({})", src) } else { src };
0bf4aa26 489
94b46f34 490 if let Some(sugg) = self.can_use_as_ref(expr) {
f9f354fc
XL
491 return Some((
492 sugg.0,
493 sugg.1,
494 sugg.2,
495 Applicability::MachineApplicable,
496 ));
94b46f34 497 }
532ac7d7
XL
498 let field_name = if is_struct_pat_shorthand_field {
499 format!("{}: ", sugg_expr)
500 } else {
501 String::new()
502 };
dc9dc135 503 if let Some(hir::Node::Expr(hir::Expr {
dfeec247 504 kind: hir::ExprKind::Assign(left_expr, ..),
dc9dc135 505 ..
dfeec247
XL
506 })) = self.tcx.hir().find(self.tcx.hir().get_parent_node(expr.hir_id))
507 {
508 if mutability == hir::Mutability::Mut {
dc9dc135
XL
509 // Found the following case:
510 // fn foo(opt: &mut Option<String>){ opt = None }
511 // --- ^^^^
512 // | |
513 // consider dereferencing here: `*opt` |
514 // expected mutable reference, found enum `Option`
74b04a01 515 if let Ok(src) = sm.span_to_snippet(left_expr.span) {
dc9dc135
XL
516 return Some((
517 left_expr.span,
518 "consider dereferencing here to assign to the mutable \
519 borrowed piece of memory",
520 format!("*{}", src),
f9f354fc 521 Applicability::MachineApplicable,
dc9dc135
XL
522 ));
523 }
524 }
525 }
526
94b46f34 527 return Some(match mutability {
dfeec247 528 hir::Mutability::Mut => (
532ac7d7
XL
529 sp,
530 "consider mutably borrowing here",
531 format!("{}&mut {}", field_name, sugg_expr),
f9f354fc 532 Applicability::MachineApplicable,
532ac7d7 533 ),
dfeec247 534 hir::Mutability::Not => (
532ac7d7
XL
535 sp,
536 "consider borrowing here",
537 format!("{}&{}", field_name, sugg_expr),
f9f354fc 538 Applicability::MachineApplicable,
532ac7d7 539 ),
ff7c6d11 540 });
cc61c64b
XL
541 }
542 }
dfeec247 543 }
60c5eb7d
XL
544 (
545 hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, ref expr),
546 _,
dfeec247 547 &ty::Ref(_, checked, _),
60c5eb7d 548 ) if {
48663c56 549 self.infcx.can_sub(self.param_env, checked, &expected).is_ok() && !is_macro
dfeec247
XL
550 } =>
551 {
ea8adc8c 552 // We have `&T`, check if what was expected was `T`. If so,
48663c56 553 // we may want to suggest removing a `&`.
ba9703b0 554 if sm.is_imported(expr.span) {
f9f354fc
XL
555 if let Ok(src) = sm.span_to_snippet(sp) {
556 if let Some(src) = self.replace_prefix(src, "&", "") {
48663c56
XL
557 return Some((
558 sp,
559 "consider removing the borrow",
f9f354fc
XL
560 src,
561 Applicability::MachineApplicable,
48663c56 562 ));
2c00a5a8 563 }
ea8adc8c 564 }
48663c56 565 return None;
ea8adc8c 566 }
74b04a01 567 if let Ok(code) = sm.span_to_snippet(expr.span) {
f9f354fc
XL
568 return Some((
569 sp,
570 "consider removing the borrow",
571 code,
572 Applicability::MachineApplicable,
573 ));
574 }
575 }
576 (
577 _,
578 &ty::RawPtr(TypeAndMut { ty: ty_b, mutbl: mutbl_b }),
579 &ty::Ref(_, ty_a, mutbl_a),
580 ) => {
581 if let Some(steps) = self.deref_steps(ty_a, ty_b) {
582 // Only suggest valid if dereferencing needed.
583 if steps > 0 {
584 // The pointer type implements `Copy` trait so the suggestion is always valid.
585 if let Ok(src) = sm.span_to_snippet(sp) {
586 let derefs = &"*".repeat(steps);
587 if let Some((src, applicability)) = match mutbl_b {
588 hir::Mutability::Mut => {
589 let new_prefix = "&mut ".to_owned() + derefs;
590 match mutbl_a {
591 hir::Mutability::Mut => {
592 if let Some(s) =
593 self.replace_prefix(src, "&mut ", new_prefix)
594 {
595 Some((s, Applicability::MachineApplicable))
596 } else {
597 None
598 }
599 }
600 hir::Mutability::Not => {
601 if let Some(s) =
602 self.replace_prefix(src, "&", new_prefix)
603 {
604 Some((s, Applicability::Unspecified))
605 } else {
606 None
607 }
608 }
609 }
610 }
611 hir::Mutability::Not => {
612 let new_prefix = "&".to_owned() + derefs;
613 match mutbl_a {
614 hir::Mutability::Mut => {
615 if let Some(s) =
616 self.replace_prefix(src, "&mut ", new_prefix)
617 {
618 Some((s, Applicability::MachineApplicable))
619 } else {
620 None
621 }
622 }
623 hir::Mutability::Not => {
624 if let Some(s) =
625 self.replace_prefix(src, "&", new_prefix)
626 {
627 Some((s, Applicability::MachineApplicable))
628 } else {
629 None
630 }
631 }
632 }
633 }
634 } {
635 return Some((sp, "consider dereferencing", src, applicability));
636 }
637 }
638 }
9fa01778 639 }
dfeec247 640 }
48663c56 641 _ if sp == expr.span && !is_macro => {
f9f354fc
XL
642 if let Some(steps) = self.deref_steps(checked_ty, expected) {
643 if steps == 1 {
644 // For a suggestion to make sense, the type would need to be `Copy`.
645 if self.infcx.type_is_copy_modulo_regions(self.param_env, expected, sp) {
646 if let Ok(code) = sm.span_to_snippet(sp) {
647 let message = if checked_ty.is_region_ptr() {
648 "consider dereferencing the borrow"
649 } else {
650 "consider dereferencing the type"
651 };
652 let suggestion = if is_struct_pat_shorthand_field {
653 format!("{}: *{}", code, code)
654 } else {
655 format!("*{}", code)
656 };
657 return Some((
658 sp,
659 message,
660 suggestion,
661 Applicability::MachineApplicable,
662 ));
663 }
664 }
0bf4aa26
XL
665 }
666 }
667 }
0bf4aa26
XL
668 _ => {}
669 }
48663c56 670 None
0bf4aa26
XL
671 }
672
9fa01778
XL
673 pub fn check_for_cast(
674 &self,
60c5eb7d 675 err: &mut DiagnosticBuilder<'_>,
dfeec247 676 expr: &hir::Expr<'_>,
9fa01778
XL
677 checked_ty: Ty<'tcx>,
678 expected_ty: Ty<'tcx>,
f035d41b 679 expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
9fa01778 680 ) -> bool {
ba9703b0 681 if self.tcx.sess.source_map().is_imported(expr.span) {
e1599b0c
XL
682 // Ignore if span is from within a macro.
683 return false;
684 }
2c00a5a8 685
f035d41b
XL
686 let src = if let Ok(src) = self.tcx.sess.source_map().span_to_snippet(expr.span) {
687 src
688 } else {
689 return false;
690 };
691
2c00a5a8
XL
692 // If casting this expression to a given numeric type would be appropriate in case of a type
693 // mismatch.
694 //
695 // We want to minimize the amount of casting operations that are suggested, as it can be a
696 // lossy operation with potentially bad side effects, so we only suggest when encountering
697 // an expression that indicates that the original type couldn't be directly changed.
698 //
699 // For now, don't suggest casting with `as`.
700 let can_cast = false;
701
f9f354fc
XL
702 let prefix = if let Some(hir::Node::Expr(hir::Expr {
703 kind: hir::ExprKind::Struct(_, fields, _),
704 ..
dfeec247
XL
705 })) = self.tcx.hir().find(self.tcx.hir().get_parent_node(expr.hir_id))
706 {
9fa01778 707 // `expr` is a literal field for a struct, only suggest if appropriate
f9f354fc
XL
708 match (*fields)
709 .iter()
710 .find(|field| field.expr.hir_id == expr.hir_id && field.is_shorthand)
711 {
712 // This is a field literal
713 Some(field) => format!("{}: ", field.ident),
9fa01778 714 // Likely a field was meant, but this field wasn't found. Do not suggest anything.
f9f354fc 715 None => return false,
9fa01778 716 }
f9f354fc
XL
717 } else {
718 String::new()
719 };
f035d41b 720
e74abb32 721 if let hir::ExprKind::Call(path, args) = &expr.kind {
dfeec247
XL
722 if let (hir::ExprKind::Path(hir::QPath::TypeRelative(base_ty, path_segment)), 1) =
723 (&path.kind, args.len())
724 {
e1599b0c 725 // `expr` is a conversion like `u32::from(val)`, do not suggest anything (#63697).
dfeec247
XL
726 if let (hir::TyKind::Path(hir::QPath::Resolved(None, base_ty_path)), sym::from) =
727 (&base_ty.kind, path_segment.ident.name)
728 {
e1599b0c
XL
729 if let Some(ident) = &base_ty_path.segments.iter().map(|s| s.ident).next() {
730 match ident.name {
dfeec247
XL
731 sym::i128
732 | sym::i64
733 | sym::i32
734 | sym::i16
735 | sym::i8
736 | sym::u128
737 | sym::u64
738 | sym::u32
739 | sym::u16
740 | sym::u8
741 | sym::isize
742 | sym::usize
743 if base_ty_path.segments.len() == 1 =>
744 {
e1599b0c
XL
745 return false;
746 }
747 _ => {}
748 }
749 }
750 }
751 }
752 }
9fa01778 753
48663c56
XL
754 let msg = format!("you can convert an `{}` to `{}`", checked_ty, expected_ty);
755 let cast_msg = format!("you can cast an `{} to `{}`", checked_ty, expected_ty);
48663c56
XL
756 let lit_msg = format!(
757 "change the type of the numeric literal from `{}` to `{}`",
dfeec247 758 checked_ty, expected_ty,
48663c56
XL
759 );
760
f035d41b
XL
761 let with_opt_paren: fn(&dyn fmt::Display) -> String =
762 if expr.precedence().order() < PREC_POSTFIX {
763 |s| format!("({})", s)
764 } else {
765 |s| s.to_string()
766 };
767
768 let cast_suggestion = format!("{}{} as {}", prefix, with_opt_paren(&src), expected_ty);
769 let into_suggestion = format!("{}{}.into()", prefix, with_opt_paren(&src));
770 let suffix_suggestion = with_opt_paren(&format_args!(
771 "{}{}",
772 if matches!(
773 (&expected_ty.kind, &checked_ty.kind),
774 (ty::Int(_) | ty::Uint(_), ty::Float(_))
775 ) {
776 // Remove fractional part from literal, for example `42.0f32` into `42`
777 let src = src.trim_end_matches(&checked_ty.to_string());
778 src.split('.').next().unwrap()
779 } else {
780 src.trim_end_matches(&checked_ty.to_string())
781 },
782 expected_ty,
783 ));
784 let literal_is_ty_suffixed = |expr: &hir::Expr<'_>| {
785 if let hir::ExprKind::Lit(lit) = &expr.kind { lit.node.is_suffixed() } else { false }
786 };
787 let is_negative_int =
788 |expr: &hir::Expr<'_>| matches!(expr.kind, hir::ExprKind::Unary(hir::UnOp::UnNeg, ..));
789 let is_uint = |ty: Ty<'_>| matches!(ty.kind, ty::Uint(..));
790
791 let in_const_context = self.tcx.hir().is_inside_const_context(expr.hir_id);
792
793 let suggest_fallible_into_or_lhs_from =
794 |err: &mut DiagnosticBuilder<'_>, exp_to_found_is_fallible: bool| {
795 // If we know the expression the expected type is derived from, we might be able
796 // to suggest a widening conversion rather than a narrowing one (which may
797 // panic). For example, given x: u8 and y: u32, if we know the span of "x",
798 // x > y
799 // can be given the suggestion "u32::from(x) > y" rather than
800 // "x > y.try_into().unwrap()".
801 let lhs_expr_and_src = expected_ty_expr.and_then(|expr| {
802 match self.tcx.sess.source_map().span_to_snippet(expr.span).ok() {
803 Some(src) => Some((expr, src)),
804 None => None,
805 }
806 });
807 let (span, msg, suggestion) = if let (Some((lhs_expr, lhs_src)), false) =
808 (lhs_expr_and_src, exp_to_found_is_fallible)
dfeec247 809 {
f035d41b
XL
810 let msg = format!(
811 "you can convert `{}` from `{}` to `{}`, matching the type of `{}`",
812 lhs_src, expected_ty, checked_ty, src
813 );
814 let suggestion = format!("{}::from({})", checked_ty, lhs_src);
815 (lhs_expr.span, msg, suggestion)
48663c56 816 } else {
f035d41b
XL
817 let msg = format!("{} and panic if the converted value wouldn't fit", msg);
818 let suggestion =
819 format!("{}{}.try_into().unwrap()", prefix, with_opt_paren(&src));
820 (expr.span, msg, suggestion)
821 };
822 err.span_suggestion(span, &msg, suggestion, Applicability::MachineApplicable);
823 };
824
825 let suggest_to_change_suffix_or_into =
826 |err: &mut DiagnosticBuilder<'_>,
827 found_to_exp_is_fallible: bool,
828 exp_to_found_is_fallible: bool| {
829 let always_fallible = found_to_exp_is_fallible
830 && (exp_to_found_is_fallible || expected_ty_expr.is_none());
831 let msg = if literal_is_ty_suffixed(expr) {
832 &lit_msg
833 } else if always_fallible && (is_negative_int(expr) && is_uint(expected_ty)) {
834 // We now know that converting either the lhs or rhs is fallible. Before we
835 // suggest a fallible conversion, check if the value can never fit in the
836 // expected type.
837 let msg = format!("`{}` cannot fit into type `{}`", src, expected_ty);
838 err.note(&msg);
839 return;
840 } else if in_const_context {
841 // Do not recommend `into` or `try_into` in const contexts.
842 return;
843 } else if found_to_exp_is_fallible {
844 return suggest_fallible_into_or_lhs_from(err, exp_to_found_is_fallible);
0bf4aa26 845 } else {
f035d41b
XL
846 &msg
847 };
848 let suggestion = if literal_is_ty_suffixed(expr) {
849 suffix_suggestion.clone()
850 } else {
851 into_suggestion.clone()
852 };
853 err.span_suggestion(expr.span, msg, suggestion, Applicability::MachineApplicable);
0bf4aa26
XL
854 };
855
f035d41b
XL
856 match (&expected_ty.kind, &checked_ty.kind) {
857 (&ty::Int(ref exp), &ty::Int(ref found)) => {
858 let (f2e_is_fallible, e2f_is_fallible) = match (exp.bit_width(), found.bit_width())
859 {
860 (Some(exp), Some(found)) if exp < found => (true, false),
861 (Some(exp), Some(found)) if exp > found => (false, true),
862 (None, Some(8 | 16)) => (false, true),
863 (Some(8 | 16), None) => (true, false),
864 (None, _) | (_, None) => (true, true),
865 _ => (false, false),
866 };
867 suggest_to_change_suffix_or_into(err, f2e_is_fallible, e2f_is_fallible);
868 true
869 }
870 (&ty::Uint(ref exp), &ty::Uint(ref found)) => {
871 let (f2e_is_fallible, e2f_is_fallible) = match (exp.bit_width(), found.bit_width())
872 {
873 (Some(exp), Some(found)) if exp < found => (true, false),
874 (Some(exp), Some(found)) if exp > found => (false, true),
875 (None, Some(8 | 16)) => (false, true),
876 (Some(8 | 16), None) => (true, false),
877 (None, _) | (_, None) => (true, true),
878 _ => (false, false),
879 };
880 suggest_to_change_suffix_or_into(err, f2e_is_fallible, e2f_is_fallible);
881 true
882 }
883 (&ty::Int(exp), &ty::Uint(found)) => {
884 let (f2e_is_fallible, e2f_is_fallible) = match (exp.bit_width(), found.bit_width())
885 {
886 (Some(exp), Some(found)) if found < exp => (false, true),
887 (None, Some(8)) => (false, true),
888 _ => (true, true),
889 };
890 suggest_to_change_suffix_or_into(err, f2e_is_fallible, e2f_is_fallible);
891 true
892 }
893 (&ty::Uint(exp), &ty::Int(found)) => {
894 let (f2e_is_fallible, e2f_is_fallible) = match (exp.bit_width(), found.bit_width())
895 {
896 (Some(exp), Some(found)) if found > exp => (true, false),
897 (Some(8), None) => (true, false),
898 _ => (true, true),
899 };
900 suggest_to_change_suffix_or_into(err, f2e_is_fallible, e2f_is_fallible);
901 true
902 }
903 (&ty::Float(ref exp), &ty::Float(ref found)) => {
904 if found.bit_width() < exp.bit_width() {
905 suggest_to_change_suffix_or_into(err, false, true);
906 } else if literal_is_ty_suffixed(expr) {
dfeec247
XL
907 err.span_suggestion(
908 expr.span,
f035d41b
XL
909 &lit_msg,
910 suffix_suggestion,
dfeec247
XL
911 Applicability::MachineApplicable,
912 );
f035d41b
XL
913 } else if can_cast {
914 // Missing try_into implementation for `f64` to `f32`
915 err.span_suggestion(
916 expr.span,
917 &format!("{}, producing the closest possible value", cast_msg),
918 cast_suggestion,
919 Applicability::MaybeIncorrect, // lossy conversion
920 );
2c00a5a8 921 }
f035d41b
XL
922 true
923 }
924 (&ty::Uint(_) | &ty::Int(_), &ty::Float(_)) => {
925 if literal_is_ty_suffixed(expr) {
926 err.span_suggestion(
927 expr.span,
928 &lit_msg,
929 suffix_suggestion,
930 Applicability::MachineApplicable,
931 );
932 } else if can_cast {
933 // Missing try_into implementation for `{float}` to `{integer}`
934 err.span_suggestion(
935 expr.span,
936 &format!("{}, rounding the float towards zero", msg),
937 cast_suggestion,
938 Applicability::MaybeIncorrect, // lossy conversion
939 );
2c00a5a8 940 }
f035d41b
XL
941 true
942 }
943 (&ty::Float(ref exp), &ty::Uint(ref found)) => {
944 // if `found` is `None` (meaning found is `usize`), don't suggest `.into()`
945 if exp.bit_width() > found.bit_width().unwrap_or(256) {
946 err.span_suggestion(
947 expr.span,
948 &format!(
949 "{}, producing the floating point representation of the integer",
950 msg,
951 ),
952 into_suggestion,
953 Applicability::MachineApplicable,
954 );
955 } else if literal_is_ty_suffixed(expr) {
956 err.span_suggestion(
957 expr.span,
958 &lit_msg,
959 suffix_suggestion,
960 Applicability::MachineApplicable,
961 );
962 } else {
963 // Missing try_into implementation for `{integer}` to `{float}`
964 err.span_suggestion(
965 expr.span,
966 &format!(
967 "{}, producing the floating point representation of the integer,
48663c56 968 rounded if necessary",
f035d41b
XL
969 cast_msg,
970 ),
971 cast_suggestion,
972 Applicability::MaybeIncorrect, // lossy conversion
973 );
2c00a5a8 974 }
f035d41b
XL
975 true
976 }
977 (&ty::Float(ref exp), &ty::Int(ref found)) => {
978 // if `found` is `None` (meaning found is `isize`), don't suggest `.into()`
979 if exp.bit_width() > found.bit_width().unwrap_or(256) {
980 err.span_suggestion(
981 expr.span,
982 &format!(
983 "{}, producing the floating point representation of the integer",
984 &msg,
985 ),
986 into_suggestion,
987 Applicability::MachineApplicable,
988 );
989 } else if literal_is_ty_suffixed(expr) {
990 err.span_suggestion(
991 expr.span,
992 &lit_msg,
993 suffix_suggestion,
994 Applicability::MachineApplicable,
995 );
996 } else {
997 // Missing try_into implementation for `{integer}` to `{float}`
998 err.span_suggestion(
999 expr.span,
1000 &format!(
1001 "{}, producing the floating point representation of the integer, \
1002 rounded if necessary",
1003 &msg,
1004 ),
1005 cast_suggestion,
1006 Applicability::MaybeIncorrect, // lossy conversion
1007 );
2c00a5a8 1008 }
f035d41b 1009 true
2c00a5a8 1010 }
f035d41b 1011 _ => false,
2c00a5a8
XL
1012 }
1013 }
1a4d82fc 1014}