]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_typeck/src/check/demand.rs
New upstream version 1.61.0+dfsg1
[rustc.git] / compiler / rustc_typeck / src / 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;
5e7ed085 7use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed};
dfeec247 8use rustc_hir as hir;
3dfed10e 9use rustc_hir::lang_items::LangItem;
ba9703b0 10use rustc_hir::{is_range_literal, Node};
17df50a5 11use rustc_middle::lint::in_external_macro;
ba9703b0 12use rustc_middle::ty::adjustment::AllowTwoPhase;
3c0e092e 13use rustc_middle::ty::error::{ExpectedFound, TypeError};
136023e0 14use rustc_middle::ty::print::with_no_trimmed_paths;
f9f354fc 15use rustc_middle::ty::{self, AssocItem, Ty, TypeAndMut};
5099ac24 16use rustc_span::symbol::{sym, Symbol};
94222f64 17use rustc_span::{BytePos, Span};
32a655c1
SL
18
19use super::method::probe;
1a4d82fc 20
17df50a5 21use std::iter;
f035d41b 22
dc9dc135 23impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
60c5eb7d
XL
24 pub fn emit_coerce_suggestions(
25 &self,
5e7ed085 26 err: &mut Diagnostic,
5099ac24 27 expr: &hir::Expr<'tcx>,
60c5eb7d 28 expr_ty: Ty<'tcx>,
dfeec247 29 expected: Ty<'tcx>,
f035d41b 30 expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
3c0e092e 31 error: TypeError<'tcx>,
60c5eb7d 32 ) {
3c0e092e 33 self.annotate_expected_due_to_let_ty(err, expr, error);
f035d41b 34 self.suggest_deref_ref_or_into(err, expr, expected, expr_ty, expected_ty_expr);
5099ac24 35 self.suggest_compatible_variants(err, expr, expected, expr_ty);
74b04a01
XL
36 if self.suggest_calling_boxed_future_when_appropriate(err, expr, expected, expr_ty) {
37 return;
38 }
5869c6ff 39 self.suggest_no_capture_closure(err, expected, expr_ty);
60c5eb7d 40 self.suggest_boxing_when_appropriate(err, expr, expected, expr_ty);
3dfed10e 41 self.suggest_missing_parentheses(err, expr);
5e7ed085 42 self.suggest_block_to_brackets_peeling_refs(err, expr, expr_ty, expected);
f035d41b 43 self.note_need_for_fn_pointer(err, expected, expr_ty);
3dfed10e 44 self.note_internal_mutation_in_method(err, expr, expected, expr_ty);
94222f64 45 self.report_closure_inferred_return_type(err, expected);
60c5eb7d
XL
46 }
47
a7813a04
XL
48 // Requires that the two types unify, and prints an error message if
49 // they don't.
50 pub fn demand_suptype(&self, sp: Span, expected: Ty<'tcx>, actual: Ty<'tcx>) {
f9f354fc
XL
51 if let Some(mut e) = self.demand_suptype_diag(sp, expected, actual) {
52 e.emit();
53 }
041b39d2
XL
54 }
55
dfeec247
XL
56 pub fn demand_suptype_diag(
57 &self,
58 sp: Span,
59 expected: Ty<'tcx>,
60 actual: Ty<'tcx>,
5e7ed085 61 ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> {
74b04a01
XL
62 self.demand_suptype_with_origin(&self.misc(sp), expected, actual)
63 }
64
c295e0f8 65 #[instrument(skip(self), level = "debug")]
74b04a01
XL
66 pub fn demand_suptype_with_origin(
67 &self,
68 cause: &ObligationCause<'tcx>,
69 expected: Ty<'tcx>,
70 actual: Ty<'tcx>,
5e7ed085 71 ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> {
7cac9316 72 match self.at(cause, self.param_env).sup(expected, actual) {
476ff2be
SL
73 Ok(InferOk { obligations, value: () }) => {
74 self.register_predicates(obligations);
041b39d2 75 None
a7813a04 76 }
dfeec247 77 Err(e) => Some(self.report_mismatched_types(&cause, expected, actual, e)),
54a0048b 78 }
1a4d82fc 79 }
1a4d82fc 80
a7813a04 81 pub fn demand_eqtype(&self, sp: Span, expected: Ty<'tcx>, actual: Ty<'tcx>) {
32a655c1
SL
82 if let Some(mut err) = self.demand_eqtype_diag(sp, expected, actual) {
83 err.emit();
84 }
85 }
86
dfeec247
XL
87 pub fn demand_eqtype_diag(
88 &self,
89 sp: Span,
90 expected: Ty<'tcx>,
91 actual: Ty<'tcx>,
5e7ed085 92 ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> {
32a655c1 93 self.demand_eqtype_with_origin(&self.misc(sp), expected, actual)
5bcae85e
SL
94 }
95
dfeec247
XL
96 pub fn demand_eqtype_with_origin(
97 &self,
98 cause: &ObligationCause<'tcx>,
99 expected: Ty<'tcx>,
100 actual: Ty<'tcx>,
5e7ed085 101 ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> {
7cac9316 102 match self.at(cause, self.param_env).eq(expected, actual) {
476ff2be
SL
103 Ok(InferOk { obligations, value: () }) => {
104 self.register_predicates(obligations);
32a655c1 105 None
3b2f2976 106 }
dfeec247 107 Err(e) => Some(self.report_mismatched_types(cause, expected, actual, e)),
54a0048b 108 }
1a4d82fc 109 }
1a4d82fc 110
dfeec247 111 pub fn demand_coerce(
e74abb32 112 &self,
5099ac24 113 expr: &hir::Expr<'tcx>,
dfeec247 114 checked_ty: Ty<'tcx>,
e74abb32 115 expected: Ty<'tcx>,
f035d41b 116 expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
dfeec247
XL
117 allow_two_phase: AllowTwoPhase,
118 ) -> Ty<'tcx> {
f035d41b
XL
119 let (ty, err) =
120 self.demand_coerce_diag(expr, checked_ty, expected, expected_ty_expr, allow_two_phase);
abe05a73 121 if let Some(mut err) = err {
041b39d2
XL
122 err.emit();
123 }
abe05a73 124 ty
041b39d2
XL
125 }
126
1b1a35ee
XL
127 /// Checks that the type of `expr` can be coerced to `expected`.
128 ///
129 /// N.B., this code relies on `self.diverges` to be accurate. In particular, assignments to `!`
130 /// will be permitted if the diverges flag is currently "always".
60c5eb7d
XL
131 pub fn demand_coerce_diag(
132 &self,
5099ac24 133 expr: &hir::Expr<'tcx>,
60c5eb7d
XL
134 checked_ty: Ty<'tcx>,
135 expected: Ty<'tcx>,
f035d41b 136 expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
60c5eb7d 137 allow_two_phase: AllowTwoPhase,
5e7ed085 138 ) -> (Ty<'tcx>, Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>>) {
e74abb32 139 let expected = self.resolve_vars_with_obligations(expected);
cc61c64b 140
c295e0f8 141 let e = match self.try_coerce(expr, checked_ty, expected, allow_two_phase, None) {
abe05a73 142 Ok(ty) => return (ty, None),
dfeec247 143 Err(e) => e,
abe05a73
XL
144 };
145
c295e0f8 146 self.set_tainted_by_errors();
e74abb32 147 let expr = expr.peel_drop_temps();
abe05a73 148 let cause = self.misc(expr.span);
e74abb32 149 let expr_ty = self.resolve_vars_with_obligations(checked_ty);
3c0e092e 150 let mut err = self.report_mismatched_types(&cause, expected, expr_ty, e.clone());
abe05a73 151
3c0e092e 152 self.emit_coerce_suggestions(&mut err, expr, expr_ty, expected, expected_ty_expr, e);
8faf50e0 153
abe05a73 154 (expected, Some(err))
32a655c1
SL
155 }
156
dfeec247
XL
157 fn annotate_expected_due_to_let_ty(
158 &self,
5e7ed085 159 err: &mut Diagnostic,
dfeec247 160 expr: &hir::Expr<'_>,
3c0e092e 161 error: TypeError<'_>,
dfeec247 162 ) {
60c5eb7d 163 let parent = self.tcx.hir().get_parent_node(expr.hir_id);
3c0e092e
XL
164 match (self.tcx.hir().find(parent), error) {
165 (Some(hir::Node::Local(hir::Local { ty: Some(ty), init: Some(init), .. })), _)
166 if init.hir_id == expr.hir_id =>
167 {
60c5eb7d
XL
168 // Point at `let` assignment type.
169 err.span_label(ty.span, "expected due to this");
170 }
3c0e092e
XL
171 (
172 Some(hir::Node::Expr(hir::Expr {
173 kind: hir::ExprKind::Assign(lhs, rhs, _), ..
174 })),
175 TypeError::Sorts(ExpectedFound { expected, .. }),
176 ) if rhs.hir_id == expr.hir_id && !expected.is_closure() => {
177 // We ignore closures explicitly because we already point at them elsewhere.
178 // Point at the assigned-to binding.
179 let mut primary_span = lhs.span;
180 let mut secondary_span = lhs.span;
181 let mut post_message = "";
182 match lhs.kind {
183 hir::ExprKind::Path(hir::QPath::Resolved(
184 None,
185 hir::Path {
186 res:
187 hir::def::Res::Def(
5e7ed085 188 hir::def::DefKind::Static(_) | hir::def::DefKind::Const,
3c0e092e
XL
189 def_id,
190 ),
191 ..
192 },
193 )) => {
194 if let Some(hir::Node::Item(hir::Item {
195 ident,
196 kind: hir::ItemKind::Static(ty, ..) | hir::ItemKind::Const(ty, ..),
197 ..
198 })) = self.tcx.hir().get_if_local(*def_id)
199 {
200 primary_span = ty.span;
201 secondary_span = ident.span;
202 post_message = " type";
203 }
204 }
205 hir::ExprKind::Path(hir::QPath::Resolved(
206 None,
207 hir::Path { res: hir::def::Res::Local(hir_id), .. },
208 )) => {
209 if let Some(hir::Node::Binding(pat)) = self.tcx.hir().find(*hir_id) {
210 let parent = self.tcx.hir().get_parent_node(pat.hir_id);
211 primary_span = pat.span;
212 secondary_span = pat.span;
213 match self.tcx.hir().find(parent) {
214 Some(hir::Node::Local(hir::Local { ty: Some(ty), .. })) => {
215 primary_span = ty.span;
216 post_message = " type";
217 }
218 Some(hir::Node::Local(hir::Local { init: Some(init), .. })) => {
219 primary_span = init.span;
220 post_message = " value";
221 }
222 Some(hir::Node::Param(hir::Param { ty_span, .. })) => {
223 primary_span = *ty_span;
224 post_message = " parameter type";
225 }
226 _ => {}
227 }
228 }
229 }
230 _ => {}
231 }
232
233 if primary_span != secondary_span
234 && self
235 .tcx
236 .sess
237 .source_map()
238 .is_multiline(secondary_span.shrink_to_hi().until(primary_span))
239 {
240 // We are pointing at the binding's type or initializer value, but it's pattern
241 // is in a different line, so we point at both.
242 err.span_label(secondary_span, "expected due to the type of this binding");
243 err.span_label(primary_span, &format!("expected due to this{}", post_message));
244 } else if post_message == "" {
245 // We are pointing at either the assignment lhs or the binding def pattern.
246 err.span_label(primary_span, "expected due to the type of this binding");
247 } else {
248 // We are pointing at the binding's type or initializer value.
249 err.span_label(primary_span, &format!("expected due to this{}", post_message));
250 }
251
252 if !lhs.is_syntactic_place_expr() {
253 // We already emitted E0070 "invalid left-hand side of assignment", so we
254 // silence this.
5e7ed085 255 err.downgrade_to_delayed_bug();
3c0e092e
XL
256 }
257 }
258 _ => {}
259 }
260 }
261
532ac7d7
XL
262 /// If the expected type is an enum (Issue #55250) with any variants whose
263 /// sole field is of the found type, suggest such variants. (Issue #42764)
264 fn suggest_compatible_variants(
265 &self,
5e7ed085 266 err: &mut Diagnostic,
dfeec247 267 expr: &hir::Expr<'_>,
532ac7d7
XL
268 expected: Ty<'tcx>,
269 expr_ty: Ty<'tcx>,
270 ) {
1b1a35ee 271 if let ty::Adt(expected_adt, substs) = expected.kind() {
3c0e092e
XL
272 // If the expression is of type () and it's the return expression of a block,
273 // we suggest adding a separate return expression instead.
274 // (To avoid things like suggesting `Ok(while .. { .. })`.)
275 if expr_ty.is_unit() {
5e7ed085
FG
276 let mut id = expr.hir_id;
277 let mut parent;
278
279 // Unroll desugaring, to make sure this works for `for` loops etc.
280 loop {
281 parent = self.tcx.hir().get_parent_node(id);
282 if let Some(parent_span) = self.tcx.hir().opt_span(parent) {
283 if parent_span.find_ancestor_inside(expr.span).is_some() {
284 // The parent node is part of the same span, so is the result of the
285 // same expansion/desugaring and not the 'real' parent node.
286 id = parent;
287 continue;
288 }
289 }
290 break;
291 }
292
3c0e092e
XL
293 if let Some(hir::Node::Block(&hir::Block {
294 span: block_span, expr: Some(e), ..
5e7ed085 295 })) = self.tcx.hir().find(parent)
3c0e092e 296 {
5e7ed085 297 if e.hir_id == id {
3c0e092e 298 if let Some(span) = expr.span.find_ancestor_inside(block_span) {
5e7ed085
FG
299 let return_suggestions = if self
300 .tcx
301 .is_diagnostic_item(sym::Result, expected_adt.did())
302 {
303 vec!["Ok(())".to_string()]
304 } else if self.tcx.is_diagnostic_item(sym::Option, expected_adt.did()) {
305 vec!["None".to_string(), "Some(())".to_string()]
306 } else {
307 return;
308 };
3c0e092e
XL
309 if let Some(indent) =
310 self.tcx.sess.source_map().indentation_before(span.shrink_to_lo())
311 {
312 // Add a semicolon, except after `}`.
313 let semicolon =
314 match self.tcx.sess.source_map().span_to_snippet(span) {
315 Ok(s) if s.ends_with('}') => "",
316 _ => ";",
317 };
318 err.span_suggestions(
319 span.shrink_to_hi(),
320 "try adding an expression at the end of the block",
321 return_suggestions
322 .into_iter()
323 .map(|r| format!("{}\n{}{}", semicolon, indent, r)),
324 Applicability::MaybeIncorrect,
325 );
326 }
327 return;
328 }
329 }
330 }
331 }
332
333 let compatible_variants: Vec<String> = expected_adt
5e7ed085 334 .variants()
532ac7d7 335 .iter()
5e7ed085
FG
336 .filter(|variant| {
337 variant.fields.len() == 1 && variant.ctor_kind == hir::def::CtorKind::Fn
338 })
532ac7d7
XL
339 .filter_map(|variant| {
340 let sole_field = &variant.fields[0];
341 let sole_field_ty = sole_field.ty(self.tcx, substs);
342 if self.can_coerce(expr_ty, sole_field_ty) {
136023e0 343 let variant_path =
5e7ed085 344 with_no_trimmed_paths!(self.tcx.def_path_str(variant.def_id));
532ac7d7 345 // FIXME #56861: DRYer prelude filtering
6a06907d
XL
346 if let Some(path) = variant_path.strip_prefix("std::prelude::") {
347 if let Some((_, path)) = path.split_once("::") {
348 return Some(path.to_string());
349 }
350 }
351 Some(variant_path)
532ac7d7
XL
352 } else {
353 None
354 }
dfeec247 355 })
3c0e092e
XL
356 .collect();
357
5099ac24
FG
358 let prefix = match self.maybe_get_struct_pattern_shorthand_field(expr) {
359 Some(ident) => format!("{}: ", ident),
360 None => String::new(),
361 };
362
363 match &compatible_variants[..] {
364 [] => { /* No variants to format */ }
365 [variant] => {
366 // Just a single matching variant.
367 err.multipart_suggestion_verbose(
368 &format!("try wrapping the expression in `{}`", variant),
3c0e092e 369 vec![
5099ac24 370 (expr.span.shrink_to_lo(), format!("{}{}(", prefix, variant)),
3c0e092e 371 (expr.span.shrink_to_hi(), ")".to_string()),
5099ac24
FG
372 ],
373 Applicability::MaybeIncorrect,
374 );
375 }
376 _ => {
377 // More than one matching variant.
378 err.multipart_suggestions(
379 &format!(
380 "try wrapping the expression in a variant of `{}`",
5e7ed085 381 self.tcx.def_path_str(expected_adt.did())
5099ac24
FG
382 ),
383 compatible_variants.into_iter().map(|variant| {
384 vec![
385 (expr.span.shrink_to_lo(), format!("{}{}(", prefix, variant)),
386 (expr.span.shrink_to_hi(), ")".to_string()),
387 ]
388 }),
389 Applicability::MaybeIncorrect,
390 );
391 }
532ac7d7
XL
392 }
393 }
394 }
395
dfeec247
XL
396 pub fn get_conversion_methods(
397 &self,
398 span: Span,
399 expected: Ty<'tcx>,
400 checked_ty: Ty<'tcx>,
ba9703b0 401 hir_id: hir::HirId,
dfeec247 402 ) -> Vec<AssocItem> {
ba9703b0
XL
403 let mut methods =
404 self.probe_for_return_type(span, probe::Mode::MethodCall, expected, checked_ty, hir_id);
2c00a5a8 405 methods.retain(|m| {
ba9703b0 406 self.has_only_self_parameter(m)
dfeec247
XL
407 && self
408 .tcx
409 .get_attrs(m.def_id)
410 .iter()
f035d41b 411 // This special internal attribute is used to permit
dfeec247
XL
412 // "identity-like" conversion methods to be suggested here.
413 //
414 // FIXME (#46459 and #46460): ideally
415 // `std::convert::Into::into` and `std::borrow:ToOwned` would
416 // also be `#[rustc_conversion_suggestion]`, if not for
417 // method-probing false-positives and -negatives (respectively).
418 //
419 // FIXME? Other potential candidate methods: `as_ref` and
420 // `as_mut`?
94222f64 421 .any(|a| a.has_name(sym::rustc_conversion_suggestion))
2c00a5a8 422 });
32a655c1 423
2c00a5a8 424 methods
32a655c1
SL
425 }
426
ba9703b0
XL
427 /// This function checks whether the method is not static and does not accept other parameters than `self`.
428 fn has_only_self_parameter(&self, method: &AssocItem) -> bool {
48663c56 429 match method.kind {
ba9703b0
XL
430 ty::AssocKind::Fn => {
431 method.fn_has_self_parameter
432 && self.tcx.fn_sig(method.def_id).inputs().skip_binder().len() == 1
32a655c1
SL
433 }
434 _ => false,
a7813a04
XL
435 }
436 }
cc61c64b 437
94b46f34
XL
438 /// Identify some cases where `as_ref()` would be appropriate and suggest it.
439 ///
440 /// Given the following code:
441 /// ```
442 /// struct Foo;
443 /// fn takes_ref(_: &Foo) {}
444 /// let ref opt = Some(Foo);
445 ///
e1599b0c 446 /// opt.map(|param| takes_ref(param));
94b46f34 447 /// ```
e1599b0c 448 /// Suggest using `opt.as_ref().map(|param| takes_ref(param));` instead.
94b46f34
XL
449 ///
450 /// It only checks for `Option` and `Result` and won't work with
451 /// ```
e1599b0c 452 /// opt.map(|param| { takes_ref(param) });
94b46f34 453 /// ```
dfeec247 454 fn can_use_as_ref(&self, expr: &hir::Expr<'_>) -> Option<(Span, &'static str, String)> {
5e7ed085
FG
455 let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = expr.kind else {
456 return None;
416331ca
XL
457 };
458
5e7ed085
FG
459 let hir::def::Res::Local(local_id) = path.res else {
460 return None;
416331ca
XL
461 };
462
463 let local_parent = self.tcx.hir().get_parent_node(local_id);
5e7ed085
FG
464 let Some(Node::Param(hir::Param { hir_id: param_hir_id, .. })) = self.tcx.hir().find(local_parent) else {
465 return None;
416331ca
XL
466 };
467
e1599b0c 468 let param_parent = self.tcx.hir().get_parent_node(*param_hir_id);
5e7ed085
FG
469 let Some(Node::Expr(hir::Expr {
470 hir_id: expr_hir_id,
471 kind: hir::ExprKind::Closure(_, closure_fn_decl, ..),
472 ..
473 })) = self.tcx.hir().find(param_parent) else {
474 return None;
416331ca
XL
475 };
476
477 let expr_parent = self.tcx.hir().get_parent_node(*expr_hir_id);
478 let hir = self.tcx.hir().find(expr_parent);
479 let closure_params_len = closure_fn_decl.inputs.len();
5e7ed085
FG
480 let (
481 Some(Node::Expr(hir::Expr {
482 kind: hir::ExprKind::MethodCall(method_path, method_expr, _),
483 ..
484 })),
485 1,
486 ) = (hir, closure_params_len) else {
487 return None;
416331ca
XL
488 };
489
5099ac24 490 let self_ty = self.typeck_results.borrow().expr_ty(&method_expr[0]);
416331ca 491 let self_ty = format!("{:?}", self_ty);
3dfed10e 492 let name = method_path.ident.name;
dfeec247
XL
493 let is_as_ref_able = (self_ty.starts_with("&std::option::Option")
494 || self_ty.starts_with("&std::result::Result")
495 || self_ty.starts_with("std::option::Option")
496 || self_ty.starts_with("std::result::Result"))
3dfed10e 497 && (name == sym::map || name == sym::and_then);
5099ac24 498 match (is_as_ref_able, self.sess().source_map().span_to_snippet(method_path.ident.span)) {
416331ca
XL
499 (true, Ok(src)) => {
500 let suggestion = format!("as_ref().{}", src);
5099ac24 501 Some((method_path.ident.span, "consider using `as_ref` instead", suggestion))
dfeec247
XL
502 }
503 _ => None,
94b46f34 504 }
94b46f34
XL
505 }
506
5099ac24 507 crate fn maybe_get_struct_pattern_shorthand_field(
dc9dc135 508 &self,
5099ac24
FG
509 expr: &hir::Expr<'_>,
510 ) -> Option<Symbol> {
511 let hir = self.tcx.hir();
512 let local = match expr {
513 hir::Expr {
514 kind:
515 hir::ExprKind::Path(hir::QPath::Resolved(
516 None,
517 hir::Path {
518 res: hir::def::Res::Local(_),
519 segments: [hir::PathSegment { ident, .. }],
520 ..
521 },
522 )),
523 ..
524 } => Some(ident),
525 _ => None,
526 }?;
527
528 match hir.find(hir.get_parent_node(expr.hir_id))? {
529 Node::Expr(hir::Expr { kind: hir::ExprKind::Struct(_, fields, ..), .. }) => {
530 for field in *fields {
531 if field.ident.name == local.name && field.is_shorthand {
532 return Some(local.name);
532ac7d7
XL
533 }
534 }
535 }
5099ac24 536 _ => {}
532ac7d7 537 }
5099ac24
FG
538
539 None
532ac7d7
XL
540 }
541
cdc7bbd5 542 /// If the given `HirId` corresponds to a block with a trailing expression, return that expression
5099ac24
FG
543 crate fn maybe_get_block_expr(&self, expr: &hir::Expr<'tcx>) -> Option<&'tcx hir::Expr<'tcx>> {
544 match expr {
545 hir::Expr { kind: hir::ExprKind::Block(block, ..), .. } => block.expr,
cdc7bbd5
XL
546 _ => None,
547 }
548 }
549
550 /// Returns whether the given expression is an `else if`.
551 crate fn is_else_if_block(&self, expr: &hir::Expr<'_>) -> bool {
552 if let hir::ExprKind::If(..) = expr.kind {
553 let parent_id = self.tcx.hir().get_parent_node(expr.hir_id);
554 if let Some(Node::Expr(hir::Expr {
555 kind: hir::ExprKind::If(_, _, Some(else_expr)),
556 ..
557 })) = self.tcx.hir().find(parent_id)
558 {
559 return else_expr.hir_id == expr.hir_id;
560 }
561 }
562 false
563 }
564
cc61c64b
XL
565 /// This function is used to determine potential "simple" improvements or users' errors and
566 /// provide them useful help. For example:
567 ///
568 /// ```
569 /// fn some_fn(s: &str) {}
570 ///
571 /// let x = "hey!".to_owned();
572 /// some_fn(x); // error
573 /// ```
574 ///
575 /// No need to find every potential function which could make a coercion to transform a
576 /// `String` into a `&str` since a `&` would do the trick!
577 ///
578 /// In addition of this check, it also checks between references mutability state. If the
579 /// expected is mutable but the provided isn't, maybe we could just say "Hey, try with
580 /// `&mut`!".
dc9dc135
XL
581 pub fn check_ref(
582 &self,
5099ac24 583 expr: &hir::Expr<'tcx>,
dc9dc135
XL
584 checked_ty: Ty<'tcx>,
585 expected: Ty<'tcx>,
5e7ed085 586 ) -> Option<(Span, String, String, Applicability, bool /* verbose */)> {
17df50a5 587 let sess = self.sess();
532ac7d7 588 let sp = expr.span;
17df50a5
XL
589
590 // If the span is from an external macro, there's no suggestion we can make.
591 if in_external_macro(sess, sp) {
8faf50e0
XL
592 return None;
593 }
594
17df50a5
XL
595 let sm = sess.source_map();
596
fc512014
XL
597 let replace_prefix = |s: &str, old: &str, new: &str| {
598 s.strip_prefix(old).map(|stripped| new.to_string() + stripped)
599 };
600
e74abb32
XL
601 // `ExprKind::DropTemps` is semantically irrelevant for these suggestions.
602 let expr = expr.peel_drop_temps();
48663c56 603
1b1a35ee
XL
604 match (&expr.kind, expected.kind(), checked_ty.kind()) {
605 (_, &ty::Ref(_, exp, _), &ty::Ref(_, check, _)) => match (exp.kind(), check.kind()) {
ba9703b0 606 (&ty::Str, &ty::Array(arr, _) | &ty::Slice(arr)) if arr == self.tcx.types.u8 => {
5e7ed085
FG
607 if let hir::ExprKind::Lit(_) = expr.kind
608 && let Ok(src) = sm.span_to_snippet(sp)
609 && replace_prefix(&src, "b\"", "\"").is_some()
610 {
94222f64 611 let pos = sp.lo() + BytePos(1);
dfeec247 612 return Some((
94222f64 613 sp.with_hi(pos),
5e7ed085 614 "consider removing the leading `b`".to_string(),
94222f64 615 String::new(),
f9f354fc 616 Applicability::MachineApplicable,
94222f64 617 true,
dfeec247 618 ));
8faf50e0 619 }
ea8adc8c 620 }
ba9703b0 621 (&ty::Array(arr, _) | &ty::Slice(arr), &ty::Str) if arr == self.tcx.types.u8 => {
5e7ed085
FG
622 if let hir::ExprKind::Lit(_) = expr.kind
623 && let Ok(src) = sm.span_to_snippet(sp)
624 && replace_prefix(&src, "\"", "b\"").is_some()
625 {
dfeec247 626 return Some((
94222f64 627 sp.shrink_to_lo(),
5e7ed085 628 "consider adding a leading `b`".to_string(),
94222f64 629 "b".to_string(),
f9f354fc 630 Applicability::MachineApplicable,
94222f64 631 true,
dfeec247 632 ));
5e7ed085 633
ea8adc8c 634 }
ea8adc8c 635 }
94b46f34 636 _ => {}
ea8adc8c 637 },
48663c56 638 (_, &ty::Ref(_, _, mutability), _) => {
cc61c64b
XL
639 // Check if it can work when put into a ref. For example:
640 //
641 // ```
642 // fn bar(x: &mut i32) {}
643 //
644 // let x = 0u32;
645 // bar(&x); // error, expected &mut
646 // ```
94b46f34 647 let ref_ty = match mutability {
dfeec247 648 hir::Mutability::Mut => {
532ac7d7
XL
649 self.tcx.mk_mut_ref(self.tcx.mk_region(ty::ReStatic), checked_ty)
650 }
dfeec247 651 hir::Mutability::Not => {
532ac7d7
XL
652 self.tcx.mk_imm_ref(self.tcx.mk_region(ty::ReStatic), checked_ty)
653 }
cc61c64b
XL
654 };
655 if self.can_coerce(ref_ty, expected) {
dc9dc135 656 let mut sugg_sp = sp;
5099ac24
FG
657 if let hir::ExprKind::MethodCall(ref segment, ref args, _) = expr.kind {
658 let clone_trait =
659 self.tcx.require_lang_item(LangItem::Clone, Some(segment.ident.span));
60c5eb7d 660 if let ([arg], Some(true), sym::clone) = (
dc9dc135 661 &args[..],
3dfed10e
XL
662 self.typeck_results.borrow().type_dependent_def_id(expr.hir_id).map(
663 |did| {
664 let ai = self.tcx.associated_item(did);
665 ai.container == ty::TraitContainer(clone_trait)
666 },
667 ),
60c5eb7d 668 segment.ident.name,
dc9dc135
XL
669 ) {
670 // If this expression had a clone call when suggesting borrowing
60c5eb7d 671 // we want to suggest removing it because it'd now be unnecessary.
dc9dc135
XL
672 sugg_sp = arg.span;
673 }
674 }
74b04a01 675 if let Ok(src) = sm.span_to_snippet(sugg_sp) {
e74abb32 676 let needs_parens = match expr.kind {
0bf4aa26 677 // parenthesize if needed (Issue #46756)
dfeec247 678 hir::ExprKind::Cast(_, _) | hir::ExprKind::Binary(_, _, _) => true,
0bf4aa26 679 // parenthesize borrows of range literals (Issue #54505)
3dfed10e 680 _ if is_range_literal(expr) => true,
0bf4aa26
XL
681 _ => false,
682 };
dfeec247 683 let sugg_expr = if needs_parens { format!("({})", src) } else { src };
0bf4aa26 684
94b46f34 685 if let Some(sugg) = self.can_use_as_ref(expr) {
f9f354fc
XL
686 return Some((
687 sugg.0,
5e7ed085 688 sugg.1.to_string(),
f9f354fc
XL
689 sugg.2,
690 Applicability::MachineApplicable,
94222f64 691 false,
f9f354fc 692 ));
94b46f34 693 }
5099ac24
FG
694
695 let prefix = match self.maybe_get_struct_pattern_shorthand_field(expr) {
696 Some(ident) => format!("{}: ", ident),
697 None => String::new(),
532ac7d7 698 };
5099ac24 699
dc9dc135 700 if let Some(hir::Node::Expr(hir::Expr {
dfeec247 701 kind: hir::ExprKind::Assign(left_expr, ..),
dc9dc135 702 ..
dfeec247
XL
703 })) = self.tcx.hir().find(self.tcx.hir().get_parent_node(expr.hir_id))
704 {
705 if mutability == hir::Mutability::Mut {
dc9dc135
XL
706 // Found the following case:
707 // fn foo(opt: &mut Option<String>){ opt = None }
708 // --- ^^^^
709 // | |
710 // consider dereferencing here: `*opt` |
711 // expected mutable reference, found enum `Option`
94222f64 712 if sm.span_to_snippet(left_expr.span).is_ok() {
dc9dc135 713 return Some((
94222f64 714 left_expr.span.shrink_to_lo(),
dc9dc135 715 "consider dereferencing here to assign to the mutable \
5e7ed085
FG
716 borrowed piece of memory"
717 .to_string(),
94222f64 718 "*".to_string(),
f9f354fc 719 Applicability::MachineApplicable,
94222f64 720 true,
dc9dc135
XL
721 ));
722 }
723 }
724 }
725
94b46f34 726 return Some(match mutability {
dfeec247 727 hir::Mutability::Mut => (
532ac7d7 728 sp,
5e7ed085 729 "consider mutably borrowing here".to_string(),
5099ac24 730 format!("{}&mut {}", prefix, sugg_expr),
f9f354fc 731 Applicability::MachineApplicable,
94222f64 732 false,
532ac7d7 733 ),
dfeec247 734 hir::Mutability::Not => (
532ac7d7 735 sp,
5e7ed085 736 "consider borrowing here".to_string(),
5099ac24 737 format!("{}&{}", prefix, sugg_expr),
f9f354fc 738 Applicability::MachineApplicable,
94222f64 739 false,
532ac7d7 740 ),
ff7c6d11 741 });
cc61c64b
XL
742 }
743 }
dfeec247 744 }
60c5eb7d
XL
745 (
746 hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, ref expr),
747 _,
dfeec247 748 &ty::Ref(_, checked, _),
5099ac24 749 ) if self.infcx.can_sub(self.param_env, checked, expected).is_ok() => {
ea8adc8c 750 // We have `&T`, check if what was expected was `T`. If so,
48663c56 751 // we may want to suggest removing a `&`.
ba9703b0 752 if sm.is_imported(expr.span) {
17df50a5
XL
753 // Go through the spans from which this span was expanded,
754 // and find the one that's pointing inside `sp`.
755 //
756 // E.g. for `&format!("")`, where we want the span to the
757 // `format!()` invocation instead of its expansion.
758 if let Some(call_span) =
c295e0f8
XL
759 iter::successors(Some(expr.span), |s| s.parent_callsite())
760 .find(|&s| sp.contains(s))
17df50a5 761 {
94222f64 762 if sm.span_to_snippet(call_span).is_ok() {
48663c56 763 return Some((
94222f64 764 sp.with_hi(call_span.lo()),
5e7ed085 765 "consider removing the borrow".to_string(),
94222f64 766 String::new(),
f9f354fc 767 Applicability::MachineApplicable,
94222f64 768 true,
48663c56 769 ));
2c00a5a8 770 }
ea8adc8c 771 }
48663c56 772 return None;
ea8adc8c 773 }
17df50a5 774 if sp.contains(expr.span) {
94222f64 775 if sm.span_to_snippet(expr.span).is_ok() {
17df50a5 776 return Some((
94222f64 777 sp.with_hi(expr.span.lo()),
5e7ed085 778 "consider removing the borrow".to_string(),
94222f64 779 String::new(),
17df50a5 780 Applicability::MachineApplicable,
94222f64 781 true,
17df50a5
XL
782 ));
783 }
f9f354fc
XL
784 }
785 }
786 (
787 _,
788 &ty::RawPtr(TypeAndMut { ty: ty_b, mutbl: mutbl_b }),
789 &ty::Ref(_, ty_a, mutbl_a),
790 ) => {
791 if let Some(steps) = self.deref_steps(ty_a, ty_b) {
792 // Only suggest valid if dereferencing needed.
793 if steps > 0 {
794 // The pointer type implements `Copy` trait so the suggestion is always valid.
795 if let Ok(src) = sm.span_to_snippet(sp) {
94222f64
XL
796 let derefs = "*".repeat(steps);
797 if let Some((span, src, applicability)) = match mutbl_b {
f9f354fc 798 hir::Mutability::Mut => {
94222f64 799 let new_prefix = "&mut ".to_owned() + &derefs;
f9f354fc
XL
800 match mutbl_a {
801 hir::Mutability::Mut => {
94222f64
XL
802 replace_prefix(&src, "&mut ", &new_prefix).map(|_| {
803 let pos = sp.lo() + BytePos(5);
804 let sp = sp.with_lo(pos).with_hi(pos);
805 (sp, derefs, Applicability::MachineApplicable)
806 })
f9f354fc
XL
807 }
808 hir::Mutability::Not => {
94222f64
XL
809 replace_prefix(&src, "&", &new_prefix).map(|_| {
810 let pos = sp.lo() + BytePos(1);
811 let sp = sp.with_lo(pos).with_hi(pos);
812 (
813 sp,
814 format!("mut {}", derefs),
815 Applicability::Unspecified,
816 )
817 })
f9f354fc
XL
818 }
819 }
820 }
821 hir::Mutability::Not => {
94222f64 822 let new_prefix = "&".to_owned() + &derefs;
f9f354fc
XL
823 match mutbl_a {
824 hir::Mutability::Mut => {
94222f64
XL
825 replace_prefix(&src, "&mut ", &new_prefix).map(|_| {
826 let lo = sp.lo() + BytePos(1);
827 let hi = sp.lo() + BytePos(5);
828 let sp = sp.with_lo(lo).with_hi(hi);
829 (sp, derefs, Applicability::MachineApplicable)
830 })
f9f354fc
XL
831 }
832 hir::Mutability::Not => {
94222f64
XL
833 replace_prefix(&src, "&", &new_prefix).map(|_| {
834 let pos = sp.lo() + BytePos(1);
835 let sp = sp.with_lo(pos).with_hi(pos);
836 (sp, derefs, Applicability::MachineApplicable)
837 })
f9f354fc
XL
838 }
839 }
840 }
841 } {
94222f64
XL
842 return Some((
843 span,
5e7ed085 844 "consider dereferencing".to_string(),
94222f64
XL
845 src,
846 applicability,
847 true,
848 ));
f9f354fc
XL
849 }
850 }
851 }
9fa01778 852 }
dfeec247 853 }
17df50a5 854 _ if sp == expr.span => {
5e7ed085
FG
855 if let Some(mut steps) = self.deref_steps(checked_ty, expected) {
856 let mut expr = expr.peel_blocks();
857 let mut prefix_span = expr.span.shrink_to_lo();
858 let mut remove = String::new();
6a06907d 859
5e7ed085
FG
860 // Try peeling off any existing `&` and `&mut` to reach our target type
861 while steps > 0 {
6a06907d
XL
862 if let hir::ExprKind::AddrOf(_, mutbl, inner) = expr.kind {
863 // If the expression has `&`, removing it would fix the error
5e7ed085
FG
864 prefix_span = prefix_span.with_hi(inner.span.lo());
865 expr = inner;
866 remove += match mutbl {
867 hir::Mutability::Not => "&",
868 hir::Mutability::Mut => "&mut ",
6a06907d 869 };
5e7ed085
FG
870 steps -= 1;
871 } else {
872 break;
5099ac24 873 }
5e7ed085
FG
874 }
875 // If we've reached our target type with just removing `&`, then just print now.
876 if steps == 0 {
877 return Some((
878 prefix_span,
879 format!("consider removing the `{}`", remove.trim()),
880 String::new(),
881 // Do not remove `&&` to get to bool, because it might be something like
882 // { a } && b, which we have a separate fixup suggestion that is more
883 // likely correct...
884 if remove.trim() == "&&" && expected == self.tcx.types.bool {
885 Applicability::MaybeIncorrect
5099ac24 886 } else {
5e7ed085
FG
887 Applicability::MachineApplicable
888 },
889 true,
890 ));
891 }
892
893 // For this suggestion to make sense, the type would need to be `Copy`,
894 // or we have to be moving out of a `Box<T>`
895 if self.infcx.type_is_copy_modulo_regions(self.param_env, expected, sp)
896 // FIXME(compiler-errors): We can actually do this if the checked_ty is
897 // `steps` layers of boxes, not just one, but this is easier and most likely.
898 || (checked_ty.is_box() && steps == 1)
899 {
900 let deref_kind = if checked_ty.is_box() {
901 "unboxing the value"
902 } else if checked_ty.is_region_ptr() {
903 "dereferencing the borrow"
904 } else {
905 "dereferencing the type"
906 };
907
908 // Suggest removing `&` if we have removed any, otherwise suggest just
909 // dereferencing the remaining number of steps.
910 let message = if remove.is_empty() {
911 format!("consider {}", deref_kind)
912 } else {
913 format!(
914 "consider removing the `{}` and {} instead",
915 remove.trim(),
916 deref_kind
917 )
918 };
919
920 let prefix = match self.maybe_get_struct_pattern_shorthand_field(expr) {
921 Some(ident) => format!("{}: ", ident),
922 None => String::new(),
923 };
924
925 let (span, suggestion) = if self.is_else_if_block(expr) {
926 // Don't suggest nonsense like `else *if`
927 return None;
928 } else if let Some(expr) = self.maybe_get_block_expr(expr) {
929 // prefix should be empty here..
930 (expr.span.shrink_to_lo(), "*".to_string())
931 } else {
932 (prefix_span, format!("{}{}", prefix, "*".repeat(steps)))
933 };
934
935 return Some((
936 span,
937 message,
938 suggestion,
939 Applicability::MachineApplicable,
940 true,
941 ));
0bf4aa26
XL
942 }
943 }
944 }
0bf4aa26
XL
945 _ => {}
946 }
48663c56 947 None
0bf4aa26
XL
948 }
949
9fa01778
XL
950 pub fn check_for_cast(
951 &self,
5e7ed085 952 err: &mut Diagnostic,
dfeec247 953 expr: &hir::Expr<'_>,
9fa01778
XL
954 checked_ty: Ty<'tcx>,
955 expected_ty: Ty<'tcx>,
f035d41b 956 expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
9fa01778 957 ) -> bool {
ba9703b0 958 if self.tcx.sess.source_map().is_imported(expr.span) {
e1599b0c
XL
959 // Ignore if span is from within a macro.
960 return false;
961 }
2c00a5a8 962
3c0e092e 963 let Ok(src) = self.tcx.sess.source_map().span_to_snippet(expr.span) else {
f035d41b
XL
964 return false;
965 };
966
2c00a5a8
XL
967 // If casting this expression to a given numeric type would be appropriate in case of a type
968 // mismatch.
969 //
970 // We want to minimize the amount of casting operations that are suggested, as it can be a
971 // lossy operation with potentially bad side effects, so we only suggest when encountering
972 // an expression that indicates that the original type couldn't be directly changed.
973 //
974 // For now, don't suggest casting with `as`.
975 let can_cast = false;
976
c295e0f8
XL
977 let mut sugg = vec![];
978
979 if let Some(hir::Node::Expr(hir::Expr {
980 kind: hir::ExprKind::Struct(_, fields, _), ..
dfeec247
XL
981 })) = self.tcx.hir().find(self.tcx.hir().get_parent_node(expr.hir_id))
982 {
9fa01778 983 // `expr` is a literal field for a struct, only suggest if appropriate
f9f354fc
XL
984 match (*fields)
985 .iter()
986 .find(|field| field.expr.hir_id == expr.hir_id && field.is_shorthand)
987 {
988 // This is a field literal
c295e0f8
XL
989 Some(field) => {
990 sugg.push((field.ident.span.shrink_to_lo(), format!("{}: ", field.ident)));
991 }
9fa01778 992 // Likely a field was meant, but this field wasn't found. Do not suggest anything.
f9f354fc 993 None => return false,
9fa01778 994 }
f9f354fc 995 };
f035d41b 996
e74abb32 997 if let hir::ExprKind::Call(path, args) = &expr.kind {
dfeec247
XL
998 if let (hir::ExprKind::Path(hir::QPath::TypeRelative(base_ty, path_segment)), 1) =
999 (&path.kind, args.len())
1000 {
e1599b0c 1001 // `expr` is a conversion like `u32::from(val)`, do not suggest anything (#63697).
dfeec247
XL
1002 if let (hir::TyKind::Path(hir::QPath::Resolved(None, base_ty_path)), sym::from) =
1003 (&base_ty.kind, path_segment.ident.name)
1004 {
e1599b0c
XL
1005 if let Some(ident) = &base_ty_path.segments.iter().map(|s| s.ident).next() {
1006 match ident.name {
dfeec247
XL
1007 sym::i128
1008 | sym::i64
1009 | sym::i32
1010 | sym::i16
1011 | sym::i8
1012 | sym::u128
1013 | sym::u64
1014 | sym::u32
1015 | sym::u16
1016 | sym::u8
1017 | sym::isize
1018 | sym::usize
1019 if base_ty_path.segments.len() == 1 =>
1020 {
e1599b0c
XL
1021 return false;
1022 }
1023 _ => {}
1024 }
1025 }
1026 }
1027 }
1028 }
9fa01778 1029
29967ef6
XL
1030 let msg = format!(
1031 "you can convert {} `{}` to {} `{}`",
1032 checked_ty.kind().article(),
1033 checked_ty,
1034 expected_ty.kind().article(),
1035 expected_ty,
1036 );
1037 let cast_msg = format!(
1038 "you can cast {} `{}` to {} `{}`",
1039 checked_ty.kind().article(),
1040 checked_ty,
1041 expected_ty.kind().article(),
1042 expected_ty,
1043 );
48663c56
XL
1044 let lit_msg = format!(
1045 "change the type of the numeric literal from `{}` to `{}`",
dfeec247 1046 checked_ty, expected_ty,
48663c56
XL
1047 );
1048
c295e0f8
XL
1049 let close_paren = if expr.precedence().order() < PREC_POSTFIX {
1050 sugg.push((expr.span.shrink_to_lo(), "(".to_string()));
1051 ")"
1052 } else {
1053 ""
1054 };
f035d41b 1055
c295e0f8
XL
1056 let mut cast_suggestion = sugg.clone();
1057 cast_suggestion
1058 .push((expr.span.shrink_to_hi(), format!("{} as {}", close_paren, expected_ty)));
1059 let mut into_suggestion = sugg.clone();
1060 into_suggestion.push((expr.span.shrink_to_hi(), format!("{}.into()", close_paren)));
1061 let mut suffix_suggestion = sugg.clone();
1062 suffix_suggestion.push((
f035d41b 1063 if matches!(
1b1a35ee 1064 (&expected_ty.kind(), &checked_ty.kind()),
f035d41b
XL
1065 (ty::Int(_) | ty::Uint(_), ty::Float(_))
1066 ) {
1067 // Remove fractional part from literal, for example `42.0f32` into `42`
1068 let src = src.trim_end_matches(&checked_ty.to_string());
c295e0f8
XL
1069 let len = src.split('.').next().unwrap().len();
1070 expr.span.with_lo(expr.span.lo() + BytePos(len as u32))
f035d41b 1071 } else {
c295e0f8
XL
1072 let len = src.trim_end_matches(&checked_ty.to_string()).len();
1073 expr.span.with_lo(expr.span.lo() + BytePos(len as u32))
1074 },
1075 if expr.precedence().order() < PREC_POSTFIX {
1076 // Readd `)`
1077 format!("{})", expected_ty)
1078 } else {
1079 expected_ty.to_string()
f035d41b 1080 },
f035d41b
XL
1081 ));
1082 let literal_is_ty_suffixed = |expr: &hir::Expr<'_>| {
1083 if let hir::ExprKind::Lit(lit) = &expr.kind { lit.node.is_suffixed() } else { false }
1084 };
1085 let is_negative_int =
6a06907d 1086 |expr: &hir::Expr<'_>| matches!(expr.kind, hir::ExprKind::Unary(hir::UnOp::Neg, ..));
1b1a35ee 1087 let is_uint = |ty: Ty<'_>| matches!(ty.kind(), ty::Uint(..));
f035d41b
XL
1088
1089 let in_const_context = self.tcx.hir().is_inside_const_context(expr.hir_id);
1090
1091 let suggest_fallible_into_or_lhs_from =
5e7ed085 1092 |err: &mut Diagnostic, exp_to_found_is_fallible: bool| {
f035d41b
XL
1093 // If we know the expression the expected type is derived from, we might be able
1094 // to suggest a widening conversion rather than a narrowing one (which may
1095 // panic). For example, given x: u8 and y: u32, if we know the span of "x",
1096 // x > y
1097 // can be given the suggestion "u32::from(x) > y" rather than
1098 // "x > y.try_into().unwrap()".
1099 let lhs_expr_and_src = expected_ty_expr.and_then(|expr| {
fc512014
XL
1100 self.tcx
1101 .sess
1102 .source_map()
1103 .span_to_snippet(expr.span)
1104 .ok()
1105 .map(|src| (expr, src))
f035d41b 1106 });
c295e0f8 1107 let (msg, suggestion) = if let (Some((lhs_expr, lhs_src)), false) =
f035d41b 1108 (lhs_expr_and_src, exp_to_found_is_fallible)
dfeec247 1109 {
f035d41b
XL
1110 let msg = format!(
1111 "you can convert `{}` from `{}` to `{}`, matching the type of `{}`",
1112 lhs_src, expected_ty, checked_ty, src
1113 );
c295e0f8
XL
1114 let suggestion = vec![
1115 (lhs_expr.span.shrink_to_lo(), format!("{}::from(", checked_ty)),
1116 (lhs_expr.span.shrink_to_hi(), ")".to_string()),
1117 ];
1118 (msg, suggestion)
48663c56 1119 } else {
29967ef6 1120 let msg = format!("{} and panic if the converted value doesn't fit", msg);
c295e0f8
XL
1121 let mut suggestion = sugg.clone();
1122 suggestion.push((
1123 expr.span.shrink_to_hi(),
1124 format!("{}.try_into().unwrap()", close_paren),
1125 ));
1126 (msg, suggestion)
f035d41b 1127 };
c295e0f8
XL
1128 err.multipart_suggestion_verbose(
1129 &msg,
1130 suggestion,
1131 Applicability::MachineApplicable,
1132 );
f035d41b
XL
1133 };
1134
1135 let suggest_to_change_suffix_or_into =
5e7ed085 1136 |err: &mut Diagnostic,
f035d41b
XL
1137 found_to_exp_is_fallible: bool,
1138 exp_to_found_is_fallible: bool| {
5869c6ff
XL
1139 let exp_is_lhs =
1140 expected_ty_expr.map(|e| self.tcx.hir().is_lhs(e.hir_id)).unwrap_or(false);
1141
1142 if exp_is_lhs {
1143 return;
1144 }
1145
f035d41b
XL
1146 let always_fallible = found_to_exp_is_fallible
1147 && (exp_to_found_is_fallible || expected_ty_expr.is_none());
1148 let msg = if literal_is_ty_suffixed(expr) {
1149 &lit_msg
1150 } else if always_fallible && (is_negative_int(expr) && is_uint(expected_ty)) {
1151 // We now know that converting either the lhs or rhs is fallible. Before we
1152 // suggest a fallible conversion, check if the value can never fit in the
1153 // expected type.
1154 let msg = format!("`{}` cannot fit into type `{}`", src, expected_ty);
1155 err.note(&msg);
1156 return;
1157 } else if in_const_context {
1158 // Do not recommend `into` or `try_into` in const contexts.
1159 return;
1160 } else if found_to_exp_is_fallible {
1161 return suggest_fallible_into_or_lhs_from(err, exp_to_found_is_fallible);
0bf4aa26 1162 } else {
f035d41b
XL
1163 &msg
1164 };
1165 let suggestion = if literal_is_ty_suffixed(expr) {
1166 suffix_suggestion.clone()
1167 } else {
1168 into_suggestion.clone()
1169 };
c295e0f8 1170 err.multipart_suggestion_verbose(msg, suggestion, Applicability::MachineApplicable);
0bf4aa26
XL
1171 };
1172
1b1a35ee 1173 match (&expected_ty.kind(), &checked_ty.kind()) {
f035d41b
XL
1174 (&ty::Int(ref exp), &ty::Int(ref found)) => {
1175 let (f2e_is_fallible, e2f_is_fallible) = match (exp.bit_width(), found.bit_width())
1176 {
1177 (Some(exp), Some(found)) if exp < found => (true, false),
1178 (Some(exp), Some(found)) if exp > found => (false, true),
1179 (None, Some(8 | 16)) => (false, true),
1180 (Some(8 | 16), None) => (true, false),
1181 (None, _) | (_, None) => (true, true),
1182 _ => (false, false),
1183 };
1184 suggest_to_change_suffix_or_into(err, f2e_is_fallible, e2f_is_fallible);
1185 true
1186 }
1187 (&ty::Uint(ref exp), &ty::Uint(ref found)) => {
1188 let (f2e_is_fallible, e2f_is_fallible) = match (exp.bit_width(), found.bit_width())
1189 {
1190 (Some(exp), Some(found)) if exp < found => (true, false),
1191 (Some(exp), Some(found)) if exp > found => (false, true),
1192 (None, Some(8 | 16)) => (false, true),
1193 (Some(8 | 16), None) => (true, false),
1194 (None, _) | (_, None) => (true, true),
1195 _ => (false, false),
1196 };
1197 suggest_to_change_suffix_or_into(err, f2e_is_fallible, e2f_is_fallible);
1198 true
1199 }
1200 (&ty::Int(exp), &ty::Uint(found)) => {
1201 let (f2e_is_fallible, e2f_is_fallible) = match (exp.bit_width(), found.bit_width())
1202 {
1203 (Some(exp), Some(found)) if found < exp => (false, true),
1204 (None, Some(8)) => (false, true),
1205 _ => (true, true),
1206 };
1207 suggest_to_change_suffix_or_into(err, f2e_is_fallible, e2f_is_fallible);
1208 true
1209 }
1210 (&ty::Uint(exp), &ty::Int(found)) => {
1211 let (f2e_is_fallible, e2f_is_fallible) = match (exp.bit_width(), found.bit_width())
1212 {
1213 (Some(exp), Some(found)) if found > exp => (true, false),
1214 (Some(8), None) => (true, false),
1215 _ => (true, true),
1216 };
1217 suggest_to_change_suffix_or_into(err, f2e_is_fallible, e2f_is_fallible);
1218 true
1219 }
1220 (&ty::Float(ref exp), &ty::Float(ref found)) => {
1221 if found.bit_width() < exp.bit_width() {
1222 suggest_to_change_suffix_or_into(err, false, true);
1223 } else if literal_is_ty_suffixed(expr) {
c295e0f8 1224 err.multipart_suggestion_verbose(
f035d41b
XL
1225 &lit_msg,
1226 suffix_suggestion,
dfeec247
XL
1227 Applicability::MachineApplicable,
1228 );
f035d41b
XL
1229 } else if can_cast {
1230 // Missing try_into implementation for `f64` to `f32`
c295e0f8 1231 err.multipart_suggestion_verbose(
f035d41b
XL
1232 &format!("{}, producing the closest possible value", cast_msg),
1233 cast_suggestion,
1234 Applicability::MaybeIncorrect, // lossy conversion
1235 );
2c00a5a8 1236 }
f035d41b
XL
1237 true
1238 }
1239 (&ty::Uint(_) | &ty::Int(_), &ty::Float(_)) => {
1240 if literal_is_ty_suffixed(expr) {
c295e0f8 1241 err.multipart_suggestion_verbose(
f035d41b
XL
1242 &lit_msg,
1243 suffix_suggestion,
1244 Applicability::MachineApplicable,
1245 );
1246 } else if can_cast {
1247 // Missing try_into implementation for `{float}` to `{integer}`
c295e0f8 1248 err.multipart_suggestion_verbose(
f035d41b
XL
1249 &format!("{}, rounding the float towards zero", msg),
1250 cast_suggestion,
1251 Applicability::MaybeIncorrect, // lossy conversion
1252 );
2c00a5a8 1253 }
f035d41b
XL
1254 true
1255 }
1256 (&ty::Float(ref exp), &ty::Uint(ref found)) => {
1257 // if `found` is `None` (meaning found is `usize`), don't suggest `.into()`
1258 if exp.bit_width() > found.bit_width().unwrap_or(256) {
c295e0f8 1259 err.multipart_suggestion_verbose(
f035d41b
XL
1260 &format!(
1261 "{}, producing the floating point representation of the integer",
1262 msg,
1263 ),
1264 into_suggestion,
1265 Applicability::MachineApplicable,
1266 );
1267 } else if literal_is_ty_suffixed(expr) {
c295e0f8 1268 err.multipart_suggestion_verbose(
f035d41b
XL
1269 &lit_msg,
1270 suffix_suggestion,
1271 Applicability::MachineApplicable,
1272 );
1273 } else {
1274 // Missing try_into implementation for `{integer}` to `{float}`
c295e0f8 1275 err.multipart_suggestion_verbose(
f035d41b 1276 &format!(
a2a8927a 1277 "{}, producing the floating point representation of the integer, \
48663c56 1278 rounded if necessary",
f035d41b
XL
1279 cast_msg,
1280 ),
1281 cast_suggestion,
1282 Applicability::MaybeIncorrect, // lossy conversion
1283 );
2c00a5a8 1284 }
f035d41b
XL
1285 true
1286 }
1287 (&ty::Float(ref exp), &ty::Int(ref found)) => {
1288 // if `found` is `None` (meaning found is `isize`), don't suggest `.into()`
1289 if exp.bit_width() > found.bit_width().unwrap_or(256) {
c295e0f8 1290 err.multipart_suggestion_verbose(
f035d41b
XL
1291 &format!(
1292 "{}, producing the floating point representation of the integer",
1293 &msg,
1294 ),
1295 into_suggestion,
1296 Applicability::MachineApplicable,
1297 );
1298 } else if literal_is_ty_suffixed(expr) {
c295e0f8 1299 err.multipart_suggestion_verbose(
f035d41b
XL
1300 &lit_msg,
1301 suffix_suggestion,
1302 Applicability::MachineApplicable,
1303 );
1304 } else {
1305 // Missing try_into implementation for `{integer}` to `{float}`
c295e0f8 1306 err.multipart_suggestion_verbose(
f035d41b
XL
1307 &format!(
1308 "{}, producing the floating point representation of the integer, \
1309 rounded if necessary",
1310 &msg,
1311 ),
1312 cast_suggestion,
1313 Applicability::MaybeIncorrect, // lossy conversion
1314 );
2c00a5a8 1315 }
f035d41b 1316 true
2c00a5a8 1317 }
a2a8927a
XL
1318 (
1319 &ty::Uint(ty::UintTy::U32 | ty::UintTy::U64 | ty::UintTy::U128)
1320 | &ty::Int(ty::IntTy::I32 | ty::IntTy::I64 | ty::IntTy::I128),
1321 &ty::Char,
1322 ) => {
1323 err.multipart_suggestion_verbose(
1324 &format!("{}, since a `char` always occupies 4 bytes", cast_msg,),
1325 cast_suggestion,
1326 Applicability::MachineApplicable,
1327 );
1328 true
1329 }
f035d41b 1330 _ => false,
2c00a5a8
XL
1331 }
1332 }
cdc7bbd5
XL
1333
1334 // Report the type inferred by the return statement.
5e7ed085 1335 fn report_closure_inferred_return_type(&self, err: &mut Diagnostic, expected: Ty<'tcx>) {
cdc7bbd5 1336 if let Some(sp) = self.ret_coercion_span.get() {
94222f64
XL
1337 // If the closure has an explicit return type annotation, or if
1338 // the closure's return type has been inferred from outside
1339 // requirements (such as an Fn* trait bound), then a type error
1340 // may occur at the first return expression we see in the closure
1341 // (if it conflicts with the declared return type). Skip adding a
1342 // note in this case, since it would be incorrect.
1343 if !self.return_type_pre_known {
1344 err.span_note(
1345 sp,
1346 &format!(
1347 "return type inferred to be `{}` here",
1348 self.resolve_vars_if_possible(expected)
1349 ),
1350 );
cdc7bbd5
XL
1351 }
1352 }
1353 }
1a4d82fc 1354}