1use core::cmp::min;
3use core::iter;
4
5use hir::def_id::LocalDefId;
6use rustc_ast::util::parser::ExprPrecedence;
7use rustc_data_structures::packed::Pu128;
8use rustc_errors::{Applicability, Diag, MultiSpan, listify, msg};
9use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
10use rustc_hir::intravisit::Visitor;
11use rustc_hir::lang_items::LangItem;
12use rustc_hir::{
13 self as hir, Arm, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind,
14 GenericBound, HirId, LoopSource, Node, PatExpr, PatExprKind, Path, QPath, Stmt, StmtKind,
15 TyKind, WherePredicateKind, expr_needs_parens, is_range_literal,
16};
17use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer;
18use rustc_hir_analysis::suggest_impl_trait;
19use rustc_middle::middle::stability::EvalResult;
20use rustc_middle::span_bug;
21use rustc_middle::ty::print::with_no_trimmed_paths;
22use rustc_middle::ty::{
23 self, Article, Binder, IsSuggestable, Ty, TyCtxt, TypeVisitableExt, Unnormalized, Upcast,
24 suggest_constraining_type_params,
25};
26use rustc_session::errors::ExprParenthesesNeeded;
27use rustc_span::{ExpnKind, Ident, MacroKind, Span, Spanned, Symbol, sym};
28use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
29use rustc_trait_selection::error_reporting::traits::DefIdOrName;
30use rustc_trait_selection::error_reporting::traits::suggestions::ReturnsVisitor;
31use rustc_trait_selection::infer::InferCtxtExt;
32use rustc_trait_selection::traits;
33use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
34use tracing::{debug, instrument};
35
36use super::FnCtxt;
37use crate::errors::{self, SuggestBoxingForReturnImplTrait};
38use crate::fn_ctxt::rustc_span::BytePos;
39use crate::method::probe;
40use crate::method::probe::{IsSuggestion, Mode, ProbeScope};
41
42impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
43 pub(crate) fn body_fn_sig(&self) -> Option<ty::FnSig<'tcx>> {
44 self.typeck_results
45 .borrow()
46 .liberated_fn_sigs()
47 .get(self.tcx.local_def_id_to_hir_id(self.body_id))
48 .copied()
49 }
50
51 pub(in super::super) fn suggest_semicolon_at_end(&self, span: Span, err: &mut Diag<'_>) {
52 err.span_suggestion_short(
55 span.shrink_to_hi(),
56 "consider using a semicolon here",
57 ";",
58 Applicability::MaybeIncorrect,
59 );
60 }
61
62 pub(crate) fn suggest_mismatched_types_on_tail(
68 &self,
69 err: &mut Diag<'_>,
70 expr: &'tcx hir::Expr<'tcx>,
71 expected: Ty<'tcx>,
72 found: Ty<'tcx>,
73 blk_id: HirId,
74 ) -> bool {
75 let expr = expr.peel_drop_temps();
76 let mut pointing_at_return_type = false;
77 if let hir::ExprKind::Break(..) = expr.kind {
78 return false;
80 }
81 if let Some((fn_id, fn_decl)) = self.get_fn_decl(blk_id) {
82 pointing_at_return_type =
83 self.suggest_missing_return_type(err, fn_decl, expected, found, fn_id);
84 self.suggest_missing_break_or_return_expr(
85 err, expr, fn_decl, expected, found, blk_id, fn_id,
86 );
87 }
88 pointing_at_return_type
89 }
90
91 pub(crate) fn suggest_fn_call(
99 &self,
100 err: &mut Diag<'_>,
101 expr: &hir::Expr<'_>,
102 found: Ty<'tcx>,
103 can_satisfy: impl FnOnce(Ty<'tcx>) -> bool,
104 ) -> bool {
105 let Some((def_id_or_name, output, inputs)) = self.extract_callable_info(found) else {
106 return false;
107 };
108 if can_satisfy(output) {
109 let (sugg_call, mut applicability) = match inputs.len() {
110 0 => ("".to_string(), Applicability::MachineApplicable),
111 1..=4 => (
112 inputs
113 .iter()
114 .map(|ty| {
115 if ty.is_suggestable(self.tcx, false) {
116 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("/* {0} */", ty))
})format!("/* {ty} */")
117 } else {
118 "/* value */".to_string()
119 }
120 })
121 .collect::<Vec<_>>()
122 .join(", "),
123 Applicability::HasPlaceholders,
124 ),
125 _ => ("/* ... */".to_string(), Applicability::HasPlaceholders),
126 };
127
128 let msg = match def_id_or_name {
129 DefIdOrName::DefId(def_id) => match self.tcx.def_kind(def_id) {
130 DefKind::Ctor(CtorOf::Struct, _) => "construct this tuple struct".to_string(),
131 DefKind::Ctor(CtorOf::Variant, _) => "construct this tuple variant".to_string(),
132 kind => ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("call this {0}",
self.tcx.def_kind_descr(kind, def_id)))
})format!("call this {}", self.tcx.def_kind_descr(kind, def_id)),
133 },
134 DefIdOrName::Name(name) => ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("call this {0}", name))
})format!("call this {name}"),
135 };
136
137 let sugg = match expr.kind {
138 hir::ExprKind::Call(..)
139 | hir::ExprKind::Path(..)
140 | hir::ExprKind::Index(..)
141 | hir::ExprKind::Lit(..) => {
142 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[(expr.span.shrink_to_hi(),
::alloc::__export::must_use({
::alloc::fmt::format(format_args!("({0})", sugg_call))
}))]))vec![(expr.span.shrink_to_hi(), format!("({sugg_call})"))]
143 }
144 hir::ExprKind::Closure { .. } => {
145 applicability = Applicability::MaybeIncorrect;
147 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[(expr.span.shrink_to_lo(), "(".to_string()),
(expr.span.shrink_to_hi(),
::alloc::__export::must_use({
::alloc::fmt::format(format_args!(")({0})", sugg_call))
}))]))vec![
148 (expr.span.shrink_to_lo(), "(".to_string()),
149 (expr.span.shrink_to_hi(), format!(")({sugg_call})")),
150 ]
151 }
152 _ => {
153 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[(expr.span.shrink_to_lo(), "(".to_string()),
(expr.span.shrink_to_hi(),
::alloc::__export::must_use({
::alloc::fmt::format(format_args!(")({0})", sugg_call))
}))]))vec![
154 (expr.span.shrink_to_lo(), "(".to_string()),
155 (expr.span.shrink_to_hi(), format!(")({sugg_call})")),
156 ]
157 }
158 };
159
160 err.multipart_suggestion(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("use parentheses to {0}", msg))
})format!("use parentheses to {msg}"), sugg, applicability);
161 return true;
162 }
163 false
164 }
165
166 pub(in super::super) fn extract_callable_info(
170 &self,
171 ty: Ty<'tcx>,
172 ) -> Option<(DefIdOrName, Ty<'tcx>, Vec<Ty<'tcx>>)> {
173 self.err_ctxt().extract_callable_info(self.body_id, self.param_env, ty)
174 }
175
176 pub(crate) fn suggest_two_fn_call(
177 &self,
178 err: &mut Diag<'_>,
179 lhs_expr: &'tcx hir::Expr<'tcx>,
180 lhs_ty: Ty<'tcx>,
181 rhs_expr: &'tcx hir::Expr<'tcx>,
182 rhs_ty: Ty<'tcx>,
183 can_satisfy: impl FnOnce(Ty<'tcx>, Ty<'tcx>) -> bool,
184 ) -> bool {
185 if lhs_expr.span.in_derive_expansion() || rhs_expr.span.in_derive_expansion() {
186 return false;
187 }
188 let Some((_, lhs_output_ty, lhs_inputs)) = self.extract_callable_info(lhs_ty) else {
189 return false;
190 };
191 let Some((_, rhs_output_ty, rhs_inputs)) = self.extract_callable_info(rhs_ty) else {
192 return false;
193 };
194
195 if can_satisfy(lhs_output_ty, rhs_output_ty) {
196 let mut sugg = ::alloc::vec::Vec::new()vec![];
197 let mut applicability = Applicability::MachineApplicable;
198
199 for (expr, inputs) in [(lhs_expr, lhs_inputs), (rhs_expr, rhs_inputs)] {
200 let (sugg_call, this_applicability) = match inputs.len() {
201 0 => ("".to_string(), Applicability::MachineApplicable),
202 1..=4 => (
203 inputs
204 .iter()
205 .map(|ty| {
206 if ty.is_suggestable(self.tcx, false) {
207 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("/* {0} */", ty))
})format!("/* {ty} */")
208 } else {
209 "/* value */".to_string()
210 }
211 })
212 .collect::<Vec<_>>()
213 .join(", "),
214 Applicability::HasPlaceholders,
215 ),
216 _ => ("/* ... */".to_string(), Applicability::HasPlaceholders),
217 };
218
219 applicability = applicability.max(this_applicability);
220
221 match expr.kind {
222 hir::ExprKind::Call(..)
223 | hir::ExprKind::Path(..)
224 | hir::ExprKind::Index(..)
225 | hir::ExprKind::Lit(..) => {
226 sugg.extend([(expr.span.shrink_to_hi(), ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("({0})", sugg_call))
})format!("({sugg_call})"))]);
227 }
228 hir::ExprKind::Closure { .. } => {
229 applicability = Applicability::MaybeIncorrect;
231 sugg.extend([
232 (expr.span.shrink_to_lo(), "(".to_string()),
233 (expr.span.shrink_to_hi(), ::alloc::__export::must_use({
::alloc::fmt::format(format_args!(")({0})", sugg_call))
})format!(")({sugg_call})")),
234 ]);
235 }
236 _ => {
237 sugg.extend([
238 (expr.span.shrink_to_lo(), "(".to_string()),
239 (expr.span.shrink_to_hi(), ::alloc::__export::must_use({
::alloc::fmt::format(format_args!(")({0})", sugg_call))
})format!(")({sugg_call})")),
240 ]);
241 }
242 }
243 }
244
245 err.multipart_suggestion("use parentheses to call these", sugg, applicability);
246
247 true
248 } else {
249 false
250 }
251 }
252
253 pub(crate) fn suggest_remove_last_method_call(
254 &self,
255 err: &mut Diag<'_>,
256 expr: &hir::Expr<'tcx>,
257 expected: Ty<'tcx>,
258 ) -> bool {
259 if let hir::ExprKind::MethodCall(hir::PathSegment { ident: method, .. }, recv_expr, &[], _) =
260 expr.kind
261 && let Some(recv_ty) = self.typeck_results.borrow().expr_ty_opt(recv_expr)
262 && self.may_coerce(recv_ty, expected)
263 && let name = method.name.as_str()
264 && (name.starts_with("to_") || name.starts_with("as_") || name == "into")
265 {
266 let span = if let Some(recv_span) = recv_expr.span.find_ancestor_inside(expr.span) {
267 expr.span.with_lo(recv_span.hi())
268 } else {
269 expr.span.with_lo(method.span.lo() - rustc_span::BytePos(1))
270 };
271 err.span_suggestion_verbose(
272 span,
273 "try removing the method call",
274 "",
275 Applicability::MachineApplicable,
276 );
277 return true;
278 }
279 false
280 }
281
282 pub(crate) fn suggest_deref_ref_or_into(
283 &self,
284 err: &mut Diag<'_>,
285 expr: &hir::Expr<'tcx>,
286 expected: Ty<'tcx>,
287 found: Ty<'tcx>,
288 expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
289 ) -> bool {
290 let expr = expr.peel_blocks();
291 let methods =
292 self.get_conversion_methods_for_diagnostic(expr.span, expected, found, expr.hir_id);
293
294 if let Some((suggestion, msg, applicability, verbose, annotation)) =
295 self.suggest_deref_or_ref(expr, found, expected)
296 {
297 if verbose {
298 err.multipart_suggestion(msg, suggestion, applicability);
299 } else {
300 err.multipart_suggestion(msg, suggestion, applicability);
301 }
302 if annotation {
303 let suggest_annotation = match expr.peel_drop_temps().kind {
304 hir::ExprKind::AddrOf(hir::BorrowKind::Ref, mutbl, _) => mutbl.ref_prefix_str(),
305 _ => return true,
306 };
307 let mut tuple_indexes = Vec::new();
308 let mut expr_id = expr.hir_id;
309 for (parent_id, node) in self.tcx.hir_parent_iter(expr.hir_id) {
310 match node {
311 Node::Expr(&Expr { kind: ExprKind::Tup(subs), .. }) => {
312 tuple_indexes.push(
313 subs.iter()
314 .enumerate()
315 .find(|(_, sub_expr)| sub_expr.hir_id == expr_id)
316 .unwrap()
317 .0,
318 );
319 expr_id = parent_id;
320 }
321 Node::LetStmt(local) => {
322 if let Some(mut ty) = local.ty {
323 while let Some(index) = tuple_indexes.pop() {
324 match ty.kind {
325 TyKind::Tup(tys) => ty = &tys[index],
326 _ => return true,
327 }
328 }
329 let annotation_span = ty.span;
330 err.span_suggestion(
331 annotation_span.with_hi(annotation_span.lo()),
332 "alternatively, consider changing the type annotation",
333 suggest_annotation,
334 Applicability::MaybeIncorrect,
335 );
336 }
337 break;
338 }
339 _ => break,
340 }
341 }
342 }
343 return true;
344 }
345
346 if self.suggest_else_fn_with_closure(err, expr, found, expected) {
347 return true;
348 }
349
350 if self.suggest_fn_call(err, expr, found, |output| self.may_coerce(output, expected))
351 && let ty::FnDef(def_id, ..) = *found.kind()
352 && let Some(sp) = self.tcx.hir_span_if_local(def_id)
353 {
354 let name = self.tcx.item_name(def_id);
355 let kind = self.tcx.def_kind(def_id);
356 if let DefKind::Ctor(of, CtorKind::Fn) = kind {
357 err.span_label(
358 sp,
359 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("`{1}` defines {0} constructor here, which should be called",
match of {
CtorOf::Struct => "a struct",
CtorOf::Variant => "an enum variant",
}, name))
})format!(
360 "`{name}` defines {} constructor here, which should be called",
361 match of {
362 CtorOf::Struct => "a struct",
363 CtorOf::Variant => "an enum variant",
364 }
365 ),
366 );
367 } else {
368 let descr = self.tcx.def_kind_descr(kind, def_id);
369 err.span_label(sp, ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0} `{1}` defined here", descr,
name))
})format!("{descr} `{name}` defined here"));
370 }
371 return true;
372 }
373
374 if self.suggest_cast(err, expr, found, expected, expected_ty_expr) {
375 return true;
376 }
377
378 if !methods.is_empty() {
379 let mut suggestions = methods
380 .iter()
381 .filter_map(|conversion_method| {
382 let conversion_method_name = conversion_method.name();
383 let receiver_method_ident = expr.method_ident();
384 if let Some(method_ident) = receiver_method_ident
385 && method_ident.name == conversion_method_name
386 {
387 return None; }
389
390 let method_call_list = [sym::to_vec, sym::to_string];
391 let mut sugg = if let ExprKind::MethodCall(receiver_method, ..) = expr.kind
392 && receiver_method.ident.name == sym::clone
393 && method_call_list.contains(&conversion_method_name)
394 {
399 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[(receiver_method.ident.span, conversion_method_name.to_string())]))vec![(receiver_method.ident.span, conversion_method_name.to_string())]
400 } else if self.precedence(expr) < ExprPrecedence::Unambiguous {
401 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[(expr.span.shrink_to_lo(), "(".to_string()),
(expr.span.shrink_to_hi(),
::alloc::__export::must_use({
::alloc::fmt::format(format_args!(").{0}()",
conversion_method_name))
}))]))vec![
402 (expr.span.shrink_to_lo(), "(".to_string()),
403 (expr.span.shrink_to_hi(), format!(").{}()", conversion_method_name)),
404 ]
405 } else {
406 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[(expr.span.shrink_to_hi(),
::alloc::__export::must_use({
::alloc::fmt::format(format_args!(".{0}()",
conversion_method_name))
}))]))vec![(expr.span.shrink_to_hi(), format!(".{}()", conversion_method_name))]
407 };
408 let struct_pat_shorthand_field =
409 self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr);
410 if let Some(name) = struct_pat_shorthand_field {
411 sugg.insert(0, (expr.span.shrink_to_lo(), ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}: ", name))
})format!("{name}: ")));
412 }
413 Some(sugg)
414 })
415 .peekable();
416 if suggestions.peek().is_some() {
417 err.multipart_suggestions(
418 "try using a conversion method",
419 suggestions,
420 Applicability::MaybeIncorrect,
421 );
422 return true;
423 }
424 }
425
426 if let Some((found_ty_inner, expected_ty_inner, error_tys)) =
427 self.deconstruct_option_or_result(found, expected)
428 && let ty::Ref(_, peeled, hir::Mutability::Not) = *expected_ty_inner.kind()
429 {
430 let inner_expr = expr.peel_borrows();
432 if !inner_expr.span.eq_ctxt(expr.span) {
433 return false;
434 }
435 let borrow_removal_span = if inner_expr.hir_id == expr.hir_id {
436 None
437 } else {
438 Some(expr.span.shrink_to_lo().until(inner_expr.span))
439 };
440 let error_tys_equate_as_ref = error_tys.is_none_or(|(found, expected)| {
443 self.can_eq(
444 self.param_env,
445 Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_erased, found),
446 expected,
447 )
448 });
449
450 let prefix_wrap = |sugg: &str| {
451 if let Some(name) = self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr) {
452 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!(": {0}{1}", name, sugg))
})format!(": {}{}", name, sugg)
453 } else {
454 sugg.to_string()
455 }
456 };
457
458 if self.can_eq(self.param_env, found_ty_inner, peeled) && error_tys_equate_as_ref {
461 let sugg = prefix_wrap(".as_ref()");
462 err.subdiagnostic(errors::SuggestConvertViaMethod {
463 span: expr.span.shrink_to_hi(),
464 sugg,
465 expected,
466 found,
467 borrow_removal_span,
468 });
469 return true;
470 } else if let ty::Ref(_, peeled_found_ty, _) = found_ty_inner.kind()
471 && let ty::Adt(adt, _) = peeled_found_ty.peel_refs().kind()
472 && self.tcx.is_lang_item(adt.did(), LangItem::String)
473 && peeled.is_str()
474 && error_tys.is_none_or(|(found, expected)| {
476 self.can_eq(self.param_env, found, expected)
477 })
478 {
479 let sugg = prefix_wrap(".map(|x| x.as_str())");
480 err.span_suggestion_verbose(
481 expr.span.shrink_to_hi(),
482 rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("try converting the passed type into a `&str`"))msg!("try converting the passed type into a `&str`"),
483 sugg,
484 Applicability::MachineApplicable,
485 );
486 return true;
487 } else {
488 if !error_tys_equate_as_ref {
489 return false;
490 }
491 let mut steps = self.autoderef(expr.span, found_ty_inner).silence_errors();
492 if let Some((deref_ty, _)) = steps.nth(1)
493 && self.can_eq(self.param_env, deref_ty, peeled)
494 {
495 let sugg = prefix_wrap(".as_deref()");
496 err.subdiagnostic(errors::SuggestConvertViaMethod {
497 span: expr.span.shrink_to_hi(),
498 sugg,
499 expected,
500 found,
501 borrow_removal_span,
502 });
503 return true;
504 }
505 for (deref_ty, n_step) in steps {
506 if self.can_eq(self.param_env, deref_ty, peeled) {
507 let explicit_deref = "*".repeat(n_step);
508 let sugg = prefix_wrap(&::alloc::__export::must_use({
::alloc::fmt::format(format_args!(".map(|v| &{0}v)", explicit_deref))
})format!(".map(|v| &{explicit_deref}v)"));
509 err.subdiagnostic(errors::SuggestConvertViaMethod {
510 span: expr.span.shrink_to_hi(),
511 sugg,
512 expected,
513 found,
514 borrow_removal_span,
515 });
516 return true;
517 }
518 }
519 }
520 }
521
522 false
523 }
524
525 fn deconstruct_option_or_result(
529 &self,
530 found_ty: Ty<'tcx>,
531 expected_ty: Ty<'tcx>,
532 ) -> Option<(Ty<'tcx>, Ty<'tcx>, Option<(Ty<'tcx>, Ty<'tcx>)>)> {
533 let ty::Adt(found_adt, found_args) = found_ty.peel_refs().kind() else {
534 return None;
535 };
536 let ty::Adt(expected_adt, expected_args) = expected_ty.kind() else {
537 return None;
538 };
539 if self.tcx.is_diagnostic_item(sym::Option, found_adt.did())
540 && self.tcx.is_diagnostic_item(sym::Option, expected_adt.did())
541 {
542 Some((found_args.type_at(0), expected_args.type_at(0), None))
543 } else if self.tcx.is_diagnostic_item(sym::Result, found_adt.did())
544 && self.tcx.is_diagnostic_item(sym::Result, expected_adt.did())
545 {
546 Some((
547 found_args.type_at(0),
548 expected_args.type_at(0),
549 Some((found_args.type_at(1), expected_args.type_at(1))),
550 ))
551 } else {
552 None
553 }
554 }
555
556 pub(in super::super) fn suggest_boxing_when_appropriate(
559 &self,
560 err: &mut Diag<'_>,
561 span: Span,
562 hir_id: HirId,
563 expected: Ty<'tcx>,
564 found: Ty<'tcx>,
565 ) -> bool {
566 if self.tcx.hir_is_inside_const_context(hir_id) || !expected.is_box() || found.is_box() {
568 return false;
569 }
570 if self.may_coerce(Ty::new_box(self.tcx, found), expected) {
571 let suggest_boxing = match *found.kind() {
572 ty::Tuple(tuple) if tuple.is_empty() => {
573 errors::SuggestBoxing::Unit { start: span.shrink_to_lo(), end: span }
574 }
575 ty::Coroutine(def_id, ..)
576 if #[allow(non_exhaustive_omitted_patterns)] match self.tcx.coroutine_kind(def_id)
{
Some(CoroutineKind::Desugared(CoroutineDesugaring::Async,
CoroutineSource::Closure)) => true,
_ => false,
}matches!(
577 self.tcx.coroutine_kind(def_id),
578 Some(CoroutineKind::Desugared(
579 CoroutineDesugaring::Async,
580 CoroutineSource::Closure
581 ))
582 ) =>
583 {
584 errors::SuggestBoxing::AsyncBody
585 }
586 _ if let Node::ExprField(expr_field) = self.tcx.parent_hir_node(hir_id)
587 && expr_field.is_shorthand =>
588 {
589 errors::SuggestBoxing::ExprFieldShorthand {
590 start: span.shrink_to_lo(),
591 end: span.shrink_to_hi(),
592 ident: expr_field.ident,
593 }
594 }
595 _ => errors::SuggestBoxing::Other {
596 start: span.shrink_to_lo(),
597 end: span.shrink_to_hi(),
598 },
599 };
600 err.subdiagnostic(suggest_boxing);
601
602 true
603 } else {
604 false
605 }
606 }
607
608 pub(in super::super) fn suggest_no_capture_closure(
611 &self,
612 err: &mut Diag<'_>,
613 expected: Ty<'tcx>,
614 found: Ty<'tcx>,
615 ) -> bool {
616 if let (ty::FnPtr(..), ty::Closure(def_id, _)) = (expected.kind(), found.kind())
617 && let Some(upvars) = self.tcx.upvars_mentioned(*def_id)
618 {
619 let spans_and_labels = upvars
622 .iter()
623 .take(4)
624 .map(|(var_hir_id, upvar)| {
625 let var_name = self.tcx.hir_name(*var_hir_id).to_string();
626 let msg = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("`{0}` captured here", var_name))
})format!("`{var_name}` captured here");
627 (upvar.span, msg)
628 })
629 .collect::<Vec<_>>();
630
631 let mut multi_span: MultiSpan =
632 spans_and_labels.iter().map(|(sp, _)| *sp).collect::<Vec<_>>().into();
633 for (sp, label) in spans_and_labels {
634 multi_span.push_span_label(sp, label);
635 }
636 err.span_note(
637 multi_span,
638 "closures can only be coerced to `fn` types if they do not capture any variables",
639 );
640 return true;
641 }
642 false
643 }
644
645 #[allow(clippy :: suspicious_else_formatting)]
{
let __tracing_attr_span;
let __tracing_attr_guard;
if ::tracing::Level::INFO <= ::tracing::level_filters::STATIC_MAX_LEVEL &&
::tracing::Level::INFO <=
::tracing::level_filters::LevelFilter::current() ||
{ false } {
__tracing_attr_span =
{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("suggest_calling_boxed_future_when_appropriate",
"rustc_hir_typeck::fn_ctxt::suggestions",
::tracing::Level::INFO,
::tracing_core::__macro_support::Option::Some("compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs"),
::tracing_core::__macro_support::Option::Some(646u32),
::tracing_core::__macro_support::Option::Some("rustc_hir_typeck::fn_ctxt::suggestions"),
::tracing_core::field::FieldSet::new(&["expr", "expected",
"found"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::SPAN)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let mut interest = ::tracing::subscriber::Interest::never();
if ::tracing::Level::INFO <=
::tracing::level_filters::STATIC_MAX_LEVEL &&
::tracing::Level::INFO <=
::tracing::level_filters::LevelFilter::current() &&
{ interest = __CALLSITE.interest(); !interest.is_never() }
&&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest) {
let meta = __CALLSITE.metadata();
::tracing::Span::new(meta,
&{
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = meta.fields().iter();
meta.fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&::tracing::field::debug(&expr)
as &dyn Value)),
(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&::tracing::field::debug(&expected)
as &dyn Value)),
(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&::tracing::field::debug(&found)
as &dyn Value))])
})
} else {
let span =
::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
{};
span
}
};
__tracing_attr_guard = __tracing_attr_span.enter();
}
#[warn(clippy :: suspicious_else_formatting)]
{
#[allow(unknown_lints, unreachable_code, clippy ::
diverging_sub_expression, clippy :: empty_loop, clippy ::
let_unit_value, clippy :: let_with_type_underscore, clippy ::
needless_return, clippy :: unreachable)]
if false {
let __tracing_attr_fake_return: bool = loop {};
return __tracing_attr_fake_return;
}
{
if self.tcx.hir_is_inside_const_context(expr.hir_id) {
return false;
}
let pin_did = self.tcx.lang_items().pin_type();
if pin_did.is_none() ||
self.tcx.lang_items().owned_box().is_none() {
return false;
}
let box_found = Ty::new_box(self.tcx, found);
let Some(pin_box_found) =
Ty::new_lang_item(self.tcx, box_found,
LangItem::Pin) else { return false; };
let Some(pin_found) =
Ty::new_lang_item(self.tcx, found,
LangItem::Pin) else { return false; };
match expected.kind() {
ty::Adt(def, _) if Some(def.did()) == pin_did => {
if self.may_coerce(pin_box_found, expected) {
{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs:675",
"rustc_hir_typeck::fn_ctxt::suggestions",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs"),
::tracing_core::__macro_support::Option::Some(675u32),
::tracing_core::__macro_support::Option::Some("rustc_hir_typeck::fn_ctxt::suggestions"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <=
::tracing::level_filters::STATIC_MAX_LEVEL &&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("can coerce {0:?} to {1:?}, suggesting Box::pin",
pin_box_found, expected) as &dyn Value))])
});
} else { ; }
};
match found.kind() {
ty::Adt(def, _) if def.is_box() => {
err.help("use `Box::pin`");
}
_ => {
let prefix =
if let Some(name) =
self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr)
{
::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}: ", name))
})
} else { String::new() };
let suggestion =
::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[(expr.span.shrink_to_lo(),
::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}Box::pin(", prefix))
})), (expr.span.shrink_to_hi(), ")".to_string())]));
err.multipart_suggestion("you need to pin and box this expression",
suggestion, Applicability::MaybeIncorrect);
}
}
true
} else if self.may_coerce(pin_found, expected) {
match found.kind() {
ty::Adt(def, _) if def.is_box() => {
err.help("use `Box::pin`");
true
}
_ => false,
}
} else { false }
}
ty::Adt(def, _) if
def.is_box() && self.may_coerce(box_found, expected) => {
let Node::Expr(Expr { kind: ExprKind::Call(fn_name, _), ..
}) =
self.tcx.parent_hir_node(expr.hir_id) else {
return false;
};
match fn_name.kind {
ExprKind::Path(QPath::TypeRelative(hir::Ty {
kind: TyKind::Path(QPath::Resolved(_, Path { res: recv_ty,
.. })), .. }, method)) if
recv_ty.opt_def_id() == pin_did &&
method.ident.name == sym::new => {
err.span_suggestion(fn_name.span,
"use `Box::pin` to pin and box this expression", "Box::pin",
Applicability::MachineApplicable);
true
}
_ => false,
}
}
_ => false,
}
}
}
}#[instrument(skip(self, err))]
647 pub(in super::super) fn suggest_calling_boxed_future_when_appropriate(
648 &self,
649 err: &mut Diag<'_>,
650 expr: &hir::Expr<'_>,
651 expected: Ty<'tcx>,
652 found: Ty<'tcx>,
653 ) -> bool {
654 if self.tcx.hir_is_inside_const_context(expr.hir_id) {
657 return false;
659 }
660 let pin_did = self.tcx.lang_items().pin_type();
661 if pin_did.is_none() || self.tcx.lang_items().owned_box().is_none() {
663 return false;
664 }
665 let box_found = Ty::new_box(self.tcx, found);
666 let Some(pin_box_found) = Ty::new_lang_item(self.tcx, box_found, LangItem::Pin) else {
667 return false;
668 };
669 let Some(pin_found) = Ty::new_lang_item(self.tcx, found, LangItem::Pin) else {
670 return false;
671 };
672 match expected.kind() {
673 ty::Adt(def, _) if Some(def.did()) == pin_did => {
674 if self.may_coerce(pin_box_found, expected) {
675 debug!("can coerce {:?} to {:?}, suggesting Box::pin", pin_box_found, expected);
676 match found.kind() {
677 ty::Adt(def, _) if def.is_box() => {
678 err.help("use `Box::pin`");
679 }
680 _ => {
681 let prefix = if let Some(name) =
682 self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr)
683 {
684 format!("{}: ", name)
685 } else {
686 String::new()
687 };
688 let suggestion = vec![
689 (expr.span.shrink_to_lo(), format!("{prefix}Box::pin(")),
690 (expr.span.shrink_to_hi(), ")".to_string()),
691 ];
692 err.multipart_suggestion(
693 "you need to pin and box this expression",
694 suggestion,
695 Applicability::MaybeIncorrect,
696 );
697 }
698 }
699 true
700 } else if self.may_coerce(pin_found, expected) {
701 match found.kind() {
702 ty::Adt(def, _) if def.is_box() => {
703 err.help("use `Box::pin`");
704 true
705 }
706 _ => false,
707 }
708 } else {
709 false
710 }
711 }
712 ty::Adt(def, _) if def.is_box() && self.may_coerce(box_found, expected) => {
713 let Node::Expr(Expr { kind: ExprKind::Call(fn_name, _), .. }) =
717 self.tcx.parent_hir_node(expr.hir_id)
718 else {
719 return false;
720 };
721 match fn_name.kind {
722 ExprKind::Path(QPath::TypeRelative(
723 hir::Ty {
724 kind: TyKind::Path(QPath::Resolved(_, Path { res: recv_ty, .. })),
725 ..
726 },
727 method,
728 )) if recv_ty.opt_def_id() == pin_did && method.ident.name == sym::new => {
729 err.span_suggestion(
730 fn_name.span,
731 "use `Box::pin` to pin and box this expression",
732 "Box::pin",
733 Applicability::MachineApplicable,
734 );
735 true
736 }
737 _ => false,
738 }
739 }
740 _ => false,
741 }
742 }
743
744 pub(crate) fn suggest_missing_semicolon(
760 &self,
761 err: &mut Diag<'_>,
762 expression: &'tcx hir::Expr<'tcx>,
763 expected: Ty<'tcx>,
764 needs_block: bool,
765 parent_is_closure: bool,
766 ) {
767 if !expected.is_unit() {
768 return;
769 }
770 match expression.kind {
773 ExprKind::Call(..)
774 | ExprKind::MethodCall(..)
775 | ExprKind::Loop(..)
776 | ExprKind::If(..)
777 | ExprKind::Match(..)
778 | ExprKind::Block(..)
779 if expression.can_have_side_effects()
780 && !expression.span.in_external_macro(self.tcx.sess.source_map()) =>
784 {
785 if needs_block {
786 err.multipart_suggestion(
787 "consider using a semicolon here",
788 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[(expression.span.shrink_to_lo(), "{ ".to_owned()),
(expression.span.shrink_to_hi(), "; }".to_owned())]))vec![
789 (expression.span.shrink_to_lo(), "{ ".to_owned()),
790 (expression.span.shrink_to_hi(), "; }".to_owned()),
791 ],
792 Applicability::MachineApplicable,
793 );
794 } else if let hir::Node::Block(block) = self.tcx.parent_hir_node(expression.hir_id)
795 && let hir::Node::Expr(expr) = self.tcx.parent_hir_node(block.hir_id)
796 && let hir::Node::Expr(if_expr) = self.tcx.parent_hir_node(expr.hir_id)
797 && let hir::ExprKind::If(_cond, _then, Some(_else)) = if_expr.kind
798 && let hir::Node::Stmt(stmt) = self.tcx.parent_hir_node(if_expr.hir_id)
799 && let hir::StmtKind::Expr(_) = stmt.kind
800 && self.is_next_stmt_expr_continuation(stmt.hir_id)
801 {
802 err.multipart_suggestion(
803 "parentheses are required to parse this as an expression",
804 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[(stmt.span.shrink_to_lo(), "(".to_string()),
(stmt.span.shrink_to_hi(), ")".to_string())]))vec![
805 (stmt.span.shrink_to_lo(), "(".to_string()),
806 (stmt.span.shrink_to_hi(), ")".to_string()),
807 ],
808 Applicability::MachineApplicable,
809 );
810 } else {
811 err.span_suggestion(
812 expression.span.shrink_to_hi(),
813 "consider using a semicolon here",
814 ";",
815 Applicability::MachineApplicable,
816 );
817 }
818 }
819 ExprKind::Path(..) | ExprKind::Lit(_)
820 if parent_is_closure
821 && !expression.span.in_external_macro(self.tcx.sess.source_map()) =>
822 {
823 err.span_suggestion_verbose(
824 expression.span.shrink_to_lo(),
825 "consider ignoring the value",
826 "_ = ",
827 Applicability::MachineApplicable,
828 );
829 }
830 _ => {
831 if let hir::Node::Block(block) = self.tcx.parent_hir_node(expression.hir_id)
832 && let hir::Node::Expr(expr) = self.tcx.parent_hir_node(block.hir_id)
833 && let hir::Node::Expr(if_expr) = self.tcx.parent_hir_node(expr.hir_id)
834 && let hir::ExprKind::If(_cond, _then, Some(_else)) = if_expr.kind
835 && let hir::Node::Stmt(stmt) = self.tcx.parent_hir_node(if_expr.hir_id)
836 && let hir::StmtKind::Expr(_) = stmt.kind
837 && self.is_next_stmt_expr_continuation(stmt.hir_id)
838 {
839 err.multipart_suggestion(
847 "parentheses are required to parse this as an expression",
848 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[(stmt.span.shrink_to_lo(), "(".to_string()),
(stmt.span.shrink_to_hi(), ")".to_string())]))vec![
849 (stmt.span.shrink_to_lo(), "(".to_string()),
850 (stmt.span.shrink_to_hi(), ")".to_string()),
851 ],
852 Applicability::MachineApplicable,
853 );
854 }
855 }
856 }
857 }
858
859 pub(crate) fn is_next_stmt_expr_continuation(&self, hir_id: HirId) -> bool {
860 if let hir::Node::Block(b) = self.tcx.parent_hir_node(hir_id)
861 && let mut stmts = b.stmts.iter().skip_while(|s| s.hir_id != hir_id)
862 && let Some(_) = stmts.next() && let Some(next) = match (stmts.next(), b.expr) { (Some(next), _) => match next.kind {
865 hir::StmtKind::Expr(next) | hir::StmtKind::Semi(next) => Some(next),
866 _ => None,
867 },
868 (None, Some(next)) => Some(next),
869 _ => None,
870 }
871 && let hir::ExprKind::AddrOf(..) | hir::ExprKind::Unary(..) | hir::ExprKind::Err(_) = next.kind
874 {
876 true
877 } else {
878 false
879 }
880 }
881
882 #[allow(clippy :: suspicious_else_formatting)]
{
let __tracing_attr_span;
let __tracing_attr_guard;
if ::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::TRACE <=
::tracing::level_filters::LevelFilter::current() ||
{ false } {
__tracing_attr_span =
{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("suggest_missing_return_type",
"rustc_hir_typeck::fn_ctxt::suggestions",
::tracing::Level::TRACE,
::tracing_core::__macro_support::Option::Some("compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs"),
::tracing_core::__macro_support::Option::Some(894u32),
::tracing_core::__macro_support::Option::Some("rustc_hir_typeck::fn_ctxt::suggestions"),
::tracing_core::field::FieldSet::new(&["fn_decl",
"expected", "found", "fn_id"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::SPAN)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let mut interest = ::tracing::subscriber::Interest::never();
if ::tracing::Level::TRACE <=
::tracing::level_filters::STATIC_MAX_LEVEL &&
::tracing::Level::TRACE <=
::tracing::level_filters::LevelFilter::current() &&
{ interest = __CALLSITE.interest(); !interest.is_never() }
&&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest) {
let meta = __CALLSITE.metadata();
::tracing::Span::new(meta,
&{
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = meta.fields().iter();
meta.fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&::tracing::field::debug(&fn_decl)
as &dyn Value)),
(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&::tracing::field::debug(&expected)
as &dyn Value)),
(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&::tracing::field::debug(&found)
as &dyn Value)),
(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&::tracing::field::debug(&fn_id)
as &dyn Value))])
})
} else {
let span =
::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
{};
span
}
};
__tracing_attr_guard = __tracing_attr_span.enter();
}
#[warn(clippy :: suspicious_else_formatting)]
{
#[allow(unknown_lints, unreachable_code, clippy ::
diverging_sub_expression, clippy :: empty_loop, clippy ::
let_unit_value, clippy :: let_with_type_underscore, clippy ::
needless_return, clippy :: unreachable)]
if false {
let __tracing_attr_fake_return: bool = loop {};
return __tracing_attr_fake_return;
}
{
if let Some(hir::CoroutineKind::Desugared(_,
hir::CoroutineSource::Block)) =
self.tcx.coroutine_kind(fn_id) {
return false;
}
let found =
self.resolve_numeric_literals_with_default(self.resolve_vars_if_possible(found));
match &fn_decl.output {
&hir::FnRetTy::DefaultReturn(_) if
self.tcx.is_closure_like(fn_id.to_def_id()) => {}
&hir::FnRetTy::DefaultReturn(span) if expected.is_unit() => {
if !self.can_add_return_type(fn_id) {
err.subdiagnostic(errors::ExpectedReturnTypeLabel::Unit {
span,
});
} else if let Some(found) =
found.make_suggestable(self.tcx, false, None) {
err.subdiagnostic(errors::AddReturnTypeSuggestion::Add {
span,
found: found.to_string(),
});
} else if let Some(sugg) =
suggest_impl_trait(self, self.param_env, found) {
err.subdiagnostic(errors::AddReturnTypeSuggestion::Add {
span,
found: sugg,
});
} else {
err.subdiagnostic(errors::AddReturnTypeSuggestion::MissingHere {
span,
});
}
return true;
}
hir::FnRetTy::Return(hir_ty) => {
if let hir::TyKind::OpaqueDef(op_ty, ..) = hir_ty.kind &&
let [hir::GenericBound::Trait(trait_ref)] = op_ty.bounds &&
!trait_ref.trait_ref.path.segments.last().and_then(|seg|
seg.args).map_or(false, |args| !args.constraints.is_empty())
{
let trait_name =
trait_ref.trait_ref.path.segments.iter().map(|seg|
seg.ident.as_str()).collect::<Vec<_>>().join("::");
err.subdiagnostic(errors::ExpectedReturnTypeLabel::ImplTrait {
span: hir_ty.span,
trait_name,
});
if let Some(ret_coercion_span) =
self.ret_coercion_span.get() {
let expected_name = expected.to_string();
err.span_label(ret_coercion_span,
::alloc::__export::must_use({
::alloc::fmt::format(format_args!("return type resolved to be `{0}`",
expected_name))
}));
}
let trait_def_id = trait_ref.trait_ref.path.res.def_id();
if self.tcx.is_dyn_compatible(trait_def_id) {
err.subdiagnostic(SuggestBoxingForReturnImplTrait::ChangeReturnType {
start_sp: hir_ty.span.with_hi(hir_ty.span.lo() +
BytePos(4)),
end_sp: hir_ty.span.shrink_to_hi(),
});
let body = self.tcx.hir_body_owned_by(fn_id);
let mut visitor = ReturnsVisitor::default();
visitor.visit_body(&body);
if !visitor.returns.is_empty() {
let starts: Vec<Span> =
visitor.returns.iter().filter(|expr|
expr.span.can_be_used_for_suggestions()).map(|expr|
expr.span.shrink_to_lo()).collect();
let ends: Vec<Span> =
visitor.returns.iter().filter(|expr|
expr.span.can_be_used_for_suggestions()).map(|expr|
expr.span.shrink_to_hi()).collect();
if !starts.is_empty() {
err.subdiagnostic(SuggestBoxingForReturnImplTrait::BoxReturnExpr {
starts,
ends,
});
}
}
}
self.try_suggest_return_impl_trait(err, expected, found,
fn_id);
self.try_note_caller_chooses_ty_for_ty_param(err, expected,
found);
return true;
} else if let hir::TyKind::OpaqueDef(op_ty, ..) =
hir_ty.kind &&
let [hir::GenericBound::Trait(trait_ref)] = op_ty.bounds &&
let Some(hir::PathSegment { args: Some(generic_args), .. })
= trait_ref.trait_ref.path.segments.last() &&
let [constraint] = generic_args.constraints &&
let Some(ty) = constraint.ty() {
{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs:1015",
"rustc_hir_typeck::fn_ctxt::suggestions",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs"),
::tracing_core::__macro_support::Option::Some(1015u32),
::tracing_core::__macro_support::Option::Some("rustc_hir_typeck::fn_ctxt::suggestions"),
::tracing_core::field::FieldSet::new(&["found"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <=
::tracing::level_filters::STATIC_MAX_LEVEL &&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&debug(&found) as
&dyn Value))])
});
} else { ; }
};
if found.is_suggestable(self.tcx, false) {
if ty.span.is_empty() {
err.subdiagnostic(errors::AddReturnTypeSuggestion::Add {
span: ty.span,
found: found.to_string(),
});
return true;
} else {
err.subdiagnostic(errors::ExpectedReturnTypeLabel::Other {
span: ty.span,
expected,
});
}
}
} else {
{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs:1033",
"rustc_hir_typeck::fn_ctxt::suggestions",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs"),
::tracing_core::__macro_support::Option::Some(1033u32),
::tracing_core::__macro_support::Option::Some("rustc_hir_typeck::fn_ctxt::suggestions"),
::tracing_core::field::FieldSet::new(&["message", "hir_ty"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <=
::tracing::level_filters::STATIC_MAX_LEVEL &&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("return type")
as &dyn Value)),
(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&debug(&hir_ty) as
&dyn Value))])
});
} else { ; }
};
let ty = self.lowerer().lower_ty(hir_ty);
{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs:1035",
"rustc_hir_typeck::fn_ctxt::suggestions",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs"),
::tracing_core::__macro_support::Option::Some(1035u32),
::tracing_core::__macro_support::Option::Some("rustc_hir_typeck::fn_ctxt::suggestions"),
::tracing_core::field::FieldSet::new(&["message", "ty"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <=
::tracing::level_filters::STATIC_MAX_LEVEL &&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("return type (lowered)")
as &dyn Value)),
(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&debug(&ty) as
&dyn Value))])
});
} else { ; }
};
{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs:1036",
"rustc_hir_typeck::fn_ctxt::suggestions",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs"),
::tracing_core::__macro_support::Option::Some(1036u32),
::tracing_core::__macro_support::Option::Some("rustc_hir_typeck::fn_ctxt::suggestions"),
::tracing_core::field::FieldSet::new(&["message",
"expected"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <=
::tracing::level_filters::STATIC_MAX_LEVEL &&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("expected type")
as &dyn Value)),
(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&debug(&expected)
as &dyn Value))])
});
} else { ; }
};
let bound_vars =
self.tcx.late_bound_vars(self.tcx.local_def_id_to_hir_id(fn_id));
let ty = Binder::bind_with_vars(ty, bound_vars);
let ty =
self.normalize(hir_ty.span, Unnormalized::new_wip(ty));
let ty = self.tcx.instantiate_bound_regions_with_erased(ty);
if self.may_coerce(expected, ty) {
err.subdiagnostic(errors::ExpectedReturnTypeLabel::Other {
span: hir_ty.span,
expected,
});
self.try_suggest_return_impl_trait(err, expected, found,
fn_id);
self.try_note_caller_chooses_ty_for_ty_param(err, expected,
found);
return true;
}
}
}
_ => {}
}
false
}
}
}#[instrument(level = "trace", skip(self, err))]
895 pub(in super::super) fn suggest_missing_return_type(
896 &self,
897 err: &mut Diag<'_>,
898 fn_decl: &hir::FnDecl<'tcx>,
899 expected: Ty<'tcx>,
900 found: Ty<'tcx>,
901 fn_id: LocalDefId,
902 ) -> bool {
903 if let Some(hir::CoroutineKind::Desugared(_, hir::CoroutineSource::Block)) =
905 self.tcx.coroutine_kind(fn_id)
906 {
907 return false;
908 }
909
910 let found =
911 self.resolve_numeric_literals_with_default(self.resolve_vars_if_possible(found));
912 match &fn_decl.output {
915 &hir::FnRetTy::DefaultReturn(_) if self.tcx.is_closure_like(fn_id.to_def_id()) => {}
917 &hir::FnRetTy::DefaultReturn(span) if expected.is_unit() => {
918 if !self.can_add_return_type(fn_id) {
919 err.subdiagnostic(errors::ExpectedReturnTypeLabel::Unit { span });
920 } else if let Some(found) = found.make_suggestable(self.tcx, false, None) {
921 err.subdiagnostic(errors::AddReturnTypeSuggestion::Add {
922 span,
923 found: found.to_string(),
924 });
925 } else if let Some(sugg) = suggest_impl_trait(self, self.param_env, found) {
926 err.subdiagnostic(errors::AddReturnTypeSuggestion::Add { span, found: sugg });
927 } else {
928 err.subdiagnostic(errors::AddReturnTypeSuggestion::MissingHere { span });
930 }
931
932 return true;
933 }
934 hir::FnRetTy::Return(hir_ty) => {
935 if let hir::TyKind::OpaqueDef(op_ty, ..) = hir_ty.kind
936 && let [hir::GenericBound::Trait(trait_ref)] = op_ty.bounds
937 && !trait_ref
938 .trait_ref
939 .path
940 .segments
941 .last()
942 .and_then(|seg| seg.args)
943 .map_or(false, |args| !args.constraints.is_empty())
944 {
945 let trait_name = trait_ref
947 .trait_ref
948 .path
949 .segments
950 .iter()
951 .map(|seg| seg.ident.as_str())
952 .collect::<Vec<_>>()
953 .join("::");
954
955 err.subdiagnostic(errors::ExpectedReturnTypeLabel::ImplTrait {
956 span: hir_ty.span,
957 trait_name,
958 });
959
960 if let Some(ret_coercion_span) = self.ret_coercion_span.get() {
961 let expected_name = expected.to_string();
962 err.span_label(
963 ret_coercion_span,
964 format!("return type resolved to be `{expected_name}`"),
965 );
966 }
967
968 let trait_def_id = trait_ref.trait_ref.path.res.def_id();
969 if self.tcx.is_dyn_compatible(trait_def_id) {
970 err.subdiagnostic(SuggestBoxingForReturnImplTrait::ChangeReturnType {
971 start_sp: hir_ty.span.with_hi(hir_ty.span.lo() + BytePos(4)),
972 end_sp: hir_ty.span.shrink_to_hi(),
973 });
974
975 let body = self.tcx.hir_body_owned_by(fn_id);
976 let mut visitor = ReturnsVisitor::default();
977 visitor.visit_body(&body);
978
979 if !visitor.returns.is_empty() {
980 let starts: Vec<Span> = visitor
981 .returns
982 .iter()
983 .filter(|expr| expr.span.can_be_used_for_suggestions())
984 .map(|expr| expr.span.shrink_to_lo())
985 .collect();
986 let ends: Vec<Span> = visitor
987 .returns
988 .iter()
989 .filter(|expr| expr.span.can_be_used_for_suggestions())
990 .map(|expr| expr.span.shrink_to_hi())
991 .collect();
992
993 if !starts.is_empty() {
994 err.subdiagnostic(SuggestBoxingForReturnImplTrait::BoxReturnExpr {
995 starts,
996 ends,
997 });
998 }
999 }
1000 }
1001
1002 self.try_suggest_return_impl_trait(err, expected, found, fn_id);
1003 self.try_note_caller_chooses_ty_for_ty_param(err, expected, found);
1004 return true;
1005 } else if let hir::TyKind::OpaqueDef(op_ty, ..) = hir_ty.kind
1006 && let [hir::GenericBound::Trait(trait_ref)] = op_ty.bounds
1008 && let Some(hir::PathSegment { args: Some(generic_args), .. }) =
1009 trait_ref.trait_ref.path.segments.last()
1010 && let [constraint] = generic_args.constraints
1011 && let Some(ty) = constraint.ty()
1012 {
1013 debug!(?found);
1016 if found.is_suggestable(self.tcx, false) {
1017 if ty.span.is_empty() {
1018 err.subdiagnostic(errors::AddReturnTypeSuggestion::Add {
1019 span: ty.span,
1020 found: found.to_string(),
1021 });
1022 return true;
1023 } else {
1024 err.subdiagnostic(errors::ExpectedReturnTypeLabel::Other {
1025 span: ty.span,
1026 expected,
1027 });
1028 }
1029 }
1030 } else {
1031 debug!(?hir_ty, "return type");
1034 let ty = self.lowerer().lower_ty(hir_ty);
1035 debug!(?ty, "return type (lowered)");
1036 debug!(?expected, "expected type");
1037 let bound_vars =
1038 self.tcx.late_bound_vars(self.tcx.local_def_id_to_hir_id(fn_id));
1039 let ty = Binder::bind_with_vars(ty, bound_vars);
1040 let ty = self.normalize(hir_ty.span, Unnormalized::new_wip(ty));
1041 let ty = self.tcx.instantiate_bound_regions_with_erased(ty);
1042 if self.may_coerce(expected, ty) {
1043 err.subdiagnostic(errors::ExpectedReturnTypeLabel::Other {
1044 span: hir_ty.span,
1045 expected,
1046 });
1047 self.try_suggest_return_impl_trait(err, expected, found, fn_id);
1048 self.try_note_caller_chooses_ty_for_ty_param(err, expected, found);
1049 return true;
1050 }
1051 }
1052 }
1053 _ => {}
1054 }
1055 false
1056 }
1057
1058 fn can_add_return_type(&self, fn_id: LocalDefId) -> bool {
1061 match self.tcx.hir_node_by_def_id(fn_id) {
1062 Node::Item(item) => {
1063 let (ident, _, _, _) = item.expect_fn();
1064 ident.name != sym::main
1068 }
1069 Node::ImplItem(item) => {
1070 let Node::Item(&hir::Item {
1072 kind: hir::ItemKind::Impl(hir::Impl { of_trait, .. }),
1073 ..
1074 }) = self.tcx.parent_hir_node(item.hir_id())
1075 else {
1076 ::core::panicking::panic("internal error: entered unreachable code");unreachable!();
1077 };
1078
1079 of_trait.is_none()
1080 }
1081 _ => true,
1082 }
1083 }
1084
1085 fn try_note_caller_chooses_ty_for_ty_param(
1086 &self,
1087 diag: &mut Diag<'_>,
1088 expected: Ty<'tcx>,
1089 found: Ty<'tcx>,
1090 ) {
1091 let ty::Param(expected_ty_as_param) = expected.kind() else {
1098 return;
1099 };
1100
1101 if found.contains(expected) {
1102 return;
1103 }
1104
1105 diag.subdiagnostic(errors::NoteCallerChoosesTyForTyParam {
1106 ty_param_name: expected_ty_as_param.name,
1107 found_ty: found,
1108 });
1109 }
1110
1111 fn try_suggest_return_impl_trait(
1120 &self,
1121 err: &mut Diag<'_>,
1122 expected: Ty<'tcx>,
1123 found: Ty<'tcx>,
1124 fn_id: LocalDefId,
1125 ) {
1126 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs:1134",
"rustc_hir_typeck::fn_ctxt::suggestions",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs"),
::tracing_core::__macro_support::Option::Some(1134u32),
::tracing_core::__macro_support::Option::Some("rustc_hir_typeck::fn_ctxt::suggestions"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("try_suggest_return_impl_trait, expected = {0:?}, found = {1:?}",
expected, found) as &dyn Value))])
});
} else { ; }
};debug!("try_suggest_return_impl_trait, expected = {:?}, found = {:?}", expected, found);
1135
1136 let ty::Param(expected_ty_as_param) = expected.kind() else { return };
1137
1138 let fn_node = self.tcx.hir_node_by_def_id(fn_id);
1139
1140 let hir::Node::Item(hir::Item {
1141 kind:
1142 hir::ItemKind::Fn {
1143 sig:
1144 hir::FnSig {
1145 decl: hir::FnDecl { inputs: fn_parameters, output: fn_return, .. },
1146 ..
1147 },
1148 generics: hir::Generics { params, predicates, .. },
1149 ..
1150 },
1151 ..
1152 }) = fn_node
1153 else {
1154 return;
1155 };
1156
1157 if params.get(expected_ty_as_param.index as usize).is_none() {
1158 return;
1159 };
1160
1161 let where_predicates = predicates
1163 .iter()
1164 .filter_map(|p| match p.kind {
1165 WherePredicateKind::BoundPredicate(hir::WhereBoundPredicate {
1166 bounds,
1167 bounded_ty,
1168 ..
1169 }) => {
1170 let ty = self.lowerer().lower_ty(bounded_ty);
1172 Some((ty, bounds))
1173 }
1174 _ => None,
1175 })
1176 .map(|(ty, bounds)| match ty.kind() {
1177 ty::Param(param_ty) if param_ty == expected_ty_as_param => Ok(Some(bounds)),
1178 _ => match ty.contains(expected) {
1180 true => Err(()),
1181 false => Ok(None),
1182 },
1183 })
1184 .collect::<Result<Vec<_>, _>>();
1185
1186 let Ok(where_predicates) = where_predicates else { return };
1187
1188 let predicates_from_where =
1190 where_predicates.iter().flatten().flat_map(|bounds| bounds.iter());
1191
1192 let all_matching_bounds_strs = predicates_from_where
1194 .filter_map(|bound| match bound {
1195 GenericBound::Trait(_) => {
1196 self.tcx.sess.source_map().span_to_snippet(bound.span()).ok()
1197 }
1198 _ => None,
1199 })
1200 .collect::<Vec<String>>();
1201
1202 if all_matching_bounds_strs.is_empty() {
1203 return;
1204 }
1205
1206 let all_bounds_str = all_matching_bounds_strs.join(" + ");
1207
1208 let ty_param_used_in_fn_params = fn_parameters.iter().any(|param| {
1209 let ty = self.lowerer().lower_ty( param);
1210 #[allow(non_exhaustive_omitted_patterns)] match ty.kind() {
ty::Param(fn_param_ty_param) if expected_ty_as_param == fn_param_ty_param
=> true,
_ => false,
}matches!(ty.kind(), ty::Param(fn_param_ty_param) if expected_ty_as_param == fn_param_ty_param)
1211 });
1212
1213 if ty_param_used_in_fn_params {
1214 return;
1215 }
1216
1217 err.span_suggestion(
1218 fn_return.span(),
1219 "consider using an impl return type",
1220 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("impl {0}", all_bounds_str))
})format!("impl {all_bounds_str}"),
1221 Applicability::MaybeIncorrect,
1222 );
1223 }
1224
1225 pub(in super::super) fn suggest_missing_break_or_return_expr(
1226 &self,
1227 err: &mut Diag<'_>,
1228 expr: &'tcx hir::Expr<'tcx>,
1229 fn_decl: &hir::FnDecl<'tcx>,
1230 expected: Ty<'tcx>,
1231 found: Ty<'tcx>,
1232 id: HirId,
1233 fn_id: LocalDefId,
1234 ) {
1235 if !expected.is_unit() {
1236 return;
1237 }
1238 let found = self.resolve_vars_if_possible(found);
1239
1240 let innermost_loop = if self.is_loop(id) {
1241 Some(self.tcx.hir_node(id))
1242 } else {
1243 self.tcx
1244 .hir_parent_iter(id)
1245 .take_while(|(_, node)| {
1246 node.body_id().is_none()
1248 })
1249 .find_map(|(parent_id, node)| self.is_loop(parent_id).then_some(node))
1250 };
1251 let can_break_with_value = innermost_loop.is_some_and(|node| {
1252 #[allow(non_exhaustive_omitted_patterns)] match node {
Node::Expr(Expr { kind: ExprKind::Loop(_, _, LoopSource::Loop, ..), .. })
=> true,
_ => false,
}matches!(
1253 node,
1254 Node::Expr(Expr { kind: ExprKind::Loop(_, _, LoopSource::Loop, ..), .. })
1255 )
1256 });
1257
1258 let in_local_statement = self.is_local_statement(id)
1259 || self
1260 .tcx
1261 .hir_parent_iter(id)
1262 .any(|(parent_id, _)| self.is_local_statement(parent_id));
1263
1264 if can_break_with_value && in_local_statement {
1265 err.multipart_suggestion(
1266 "you might have meant to break the loop with this value",
1267 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[(expr.span.shrink_to_lo(), "break ".to_string()),
(expr.span.shrink_to_hi(), ";".to_string())]))vec![
1268 (expr.span.shrink_to_lo(), "break ".to_string()),
1269 (expr.span.shrink_to_hi(), ";".to_string()),
1270 ],
1271 Applicability::MaybeIncorrect,
1272 );
1273 return;
1274 }
1275
1276 let scope = self.tcx.hir_parent_iter(id).find(|(_, node)| {
1277 #[allow(non_exhaustive_omitted_patterns)] match node {
Node::Expr(Expr { kind: ExprKind::Closure(..), .. }) | Node::Item(_) |
Node::TraitItem(_) | Node::ImplItem(_) => true,
_ => false,
}matches!(
1278 node,
1279 Node::Expr(Expr { kind: ExprKind::Closure(..), .. })
1280 | Node::Item(_)
1281 | Node::TraitItem(_)
1282 | Node::ImplItem(_)
1283 )
1284 });
1285 let in_closure =
1286 #[allow(non_exhaustive_omitted_patterns)] match scope {
Some((_, Node::Expr(Expr { kind: ExprKind::Closure(..), .. }))) => true,
_ => false,
}matches!(scope, Some((_, Node::Expr(Expr { kind: ExprKind::Closure(..), .. }))));
1287
1288 let can_return = match fn_decl.output {
1289 hir::FnRetTy::Return(ty) => {
1290 let ty = self.lowerer().lower_ty(ty);
1291 let bound_vars = self.tcx.late_bound_vars(self.tcx.local_def_id_to_hir_id(fn_id));
1292 let ty = self
1293 .tcx
1294 .instantiate_bound_regions_with_erased(Binder::bind_with_vars(ty, bound_vars));
1295 let ty = match self.tcx.asyncness(fn_id) {
1296 ty::Asyncness::Yes => {
1297 self.err_ctxt().get_impl_future_output_ty(ty).unwrap_or_else(|| {
1298 ::rustc_middle::util::bug::span_bug_fmt(fn_decl.output.span(),
format_args!("failed to get output type of async function"))span_bug!(
1299 fn_decl.output.span(),
1300 "failed to get output type of async function"
1301 )
1302 })
1303 }
1304 ty::Asyncness::No => ty,
1305 };
1306 let ty = self.normalize(expr.span, Unnormalized::new_wip(ty));
1307 self.may_coerce(found, ty)
1308 }
1309 hir::FnRetTy::DefaultReturn(_) if in_closure => {
1310 self.ret_coercion.as_ref().is_some_and(|ret| {
1311 let ret_ty = ret.borrow().expected_ty();
1312 self.may_coerce(found, ret_ty)
1313 })
1314 }
1315 _ => false,
1316 };
1317 if can_return
1318 && let Some(span) = expr.span.find_ancestor_inside(
1319 self.tcx.hir_span_with_body(self.tcx.local_def_id_to_hir_id(fn_id)),
1320 )
1321 {
1322 fn is_in_arm<'tcx>(expr: &'tcx hir::Expr<'tcx>, tcx: TyCtxt<'tcx>) -> bool {
1333 for (_, node) in tcx.hir_parent_iter(expr.hir_id) {
1334 match node {
1335 hir::Node::Block(block) => {
1336 if let Some(ret) = block.expr
1337 && ret.hir_id == expr.hir_id
1338 {
1339 continue;
1340 }
1341 }
1342 hir::Node::Arm(arm) => {
1343 if let hir::ExprKind::Block(block, _) = arm.body.kind
1344 && let Some(ret) = block.expr
1345 && ret.hir_id == expr.hir_id
1346 {
1347 return true;
1348 }
1349 }
1350 hir::Node::Expr(e) if let hir::ExprKind::Block(block, _) = e.kind => {
1351 if let Some(ret) = block.expr
1352 && ret.hir_id == expr.hir_id
1353 {
1354 continue;
1355 }
1356 }
1357 _ => {
1358 return false;
1359 }
1360 }
1361 }
1362
1363 false
1364 }
1365 let mut suggs = ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[(span.shrink_to_lo(), "return ".to_string())]))vec![(span.shrink_to_lo(), "return ".to_string())];
1366 if !is_in_arm(expr, self.tcx) {
1367 suggs.push((span.shrink_to_hi(), ";".to_string()));
1368 }
1369 err.multipart_suggestion(
1370 "you might have meant to return this value",
1371 suggs,
1372 Applicability::MaybeIncorrect,
1373 );
1374 }
1375 }
1376
1377 pub(in super::super) fn suggest_missing_parentheses(
1378 &self,
1379 err: &mut Diag<'_>,
1380 expr: &hir::Expr<'_>,
1381 ) -> bool {
1382 let sp = self.tcx.sess.source_map().start_point(expr.span).with_parent(None);
1383 if let Some(sp) = self.tcx.sess.psess.ambiguous_block_expr_parse.borrow().get(&sp) {
1384 err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp));
1386 true
1387 } else {
1388 false
1389 }
1390 }
1391
1392 pub(crate) fn suggest_block_to_brackets_peeling_refs(
1396 &self,
1397 diag: &mut Diag<'_>,
1398 mut expr: &hir::Expr<'_>,
1399 mut expr_ty: Ty<'tcx>,
1400 mut expected_ty: Ty<'tcx>,
1401 ) -> bool {
1402 loop {
1403 match (&expr.kind, expr_ty.kind(), expected_ty.kind()) {
1404 (
1405 hir::ExprKind::AddrOf(_, _, inner_expr),
1406 ty::Ref(_, inner_expr_ty, _),
1407 ty::Ref(_, inner_expected_ty, _),
1408 ) => {
1409 expr = *inner_expr;
1410 expr_ty = *inner_expr_ty;
1411 expected_ty = *inner_expected_ty;
1412 }
1413 (hir::ExprKind::Block(blk, _), _, _) => {
1414 self.suggest_block_to_brackets(diag, blk, expr_ty, expected_ty);
1415 break true;
1416 }
1417 _ => break false,
1418 }
1419 }
1420 }
1421
1422 pub(crate) fn suggest_clone_for_ref(
1423 &self,
1424 diag: &mut Diag<'_>,
1425 expr: &hir::Expr<'_>,
1426 expr_ty: Ty<'tcx>,
1427 expected_ty: Ty<'tcx>,
1428 ) -> bool {
1429 if let ty::Ref(_, inner_ty, hir::Mutability::Not) = expr_ty.kind()
1430 && let Some(clone_trait_def) = self.tcx.lang_items().clone_trait()
1431 && expected_ty == *inner_ty
1432 && self
1433 .infcx
1434 .type_implements_trait(
1435 clone_trait_def,
1436 [self.tcx.erase_and_anonymize_regions(expected_ty)],
1437 self.param_env,
1438 )
1439 .must_apply_modulo_regions()
1440 {
1441 let suggestion = match self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr) {
1442 Some(ident) => ::alloc::__export::must_use({
::alloc::fmt::format(format_args!(": {0}.clone()", ident))
})format!(": {ident}.clone()"),
1443 None => ".clone()".to_string(),
1444 };
1445
1446 let span = expr.span.find_ancestor_not_from_macro().unwrap_or(expr.span).shrink_to_hi();
1447
1448 diag.span_suggestion_verbose(
1449 span,
1450 "consider using clone here",
1451 suggestion,
1452 Applicability::MachineApplicable,
1453 );
1454 return true;
1455 }
1456 false
1457 }
1458
1459 pub(crate) fn suggest_copied_cloned_or_as_ref(
1460 &self,
1461 diag: &mut Diag<'_>,
1462 expr: &hir::Expr<'_>,
1463 expr_ty: Ty<'tcx>,
1464 expected_ty: Ty<'tcx>,
1465 ) -> bool {
1466 let ty::Adt(adt_def, args) = expr_ty.kind() else {
1467 return false;
1468 };
1469 let ty::Adt(expected_adt_def, expected_args) = expected_ty.kind() else {
1470 return false;
1471 };
1472 if adt_def != expected_adt_def {
1473 return false;
1474 }
1475
1476 if Some(adt_def.did()) == self.tcx.get_diagnostic_item(sym::Result)
1477 && self.can_eq(self.param_env, args.type_at(1), expected_args.type_at(1))
1478 || Some(adt_def.did()) == self.tcx.get_diagnostic_item(sym::Option)
1479 {
1480 let expr_inner_ty = args.type_at(0);
1481 let expected_inner_ty = expected_args.type_at(0);
1482 if let &ty::Ref(_, ty, _mutability) = expr_inner_ty.kind()
1483 && self.can_eq(self.param_env, ty, expected_inner_ty)
1484 {
1485 let def_path = self.tcx.def_path_str(adt_def.did());
1486 let span = expr.span.shrink_to_hi();
1487 let subdiag = if self.type_is_copy_modulo_regions(self.param_env, ty) {
1488 errors::OptionResultRefMismatch::Copied { span, def_path }
1489 } else if self.type_is_clone_modulo_regions(self.param_env, ty) {
1490 errors::OptionResultRefMismatch::Cloned { span, def_path }
1491 } else {
1492 return false;
1493 };
1494 diag.subdiagnostic(subdiag);
1495 return true;
1496 }
1497 }
1498
1499 false
1500 }
1501
1502 pub(crate) fn suggest_into(
1503 &self,
1504 diag: &mut Diag<'_>,
1505 expr: &hir::Expr<'_>,
1506 expr_ty: Ty<'tcx>,
1507 expected_ty: Ty<'tcx>,
1508 ) -> bool {
1509 let expr = expr.peel_blocks();
1510
1511 if expr_ty.is_scalar() && expected_ty.is_scalar() {
1513 return false;
1514 }
1515
1516 if #[allow(non_exhaustive_omitted_patterns)] match expr.kind {
hir::ExprKind::Block(..) => true,
_ => false,
}matches!(expr.kind, hir::ExprKind::Block(..)) {
1518 return false;
1519 }
1520
1521 if self.err_ctxt().should_suggest_as_ref(expected_ty, expr_ty).is_some() {
1524 return false;
1525 }
1526
1527 if let Some(into_def_id) = self.tcx.get_diagnostic_item(sym::Into)
1528 && self.predicate_must_hold_modulo_regions(&traits::Obligation::new(
1529 self.tcx,
1530 self.misc(expr.span),
1531 self.param_env,
1532 ty::TraitRef::new(self.tcx, into_def_id, [expr_ty, expected_ty]),
1533 ))
1534 && !expr
1535 .span
1536 .macro_backtrace()
1537 .any(|x| #[allow(non_exhaustive_omitted_patterns)] match x.kind {
ExpnKind::Macro(MacroKind::Attr | MacroKind::Derive, ..) => true,
_ => false,
}matches!(x.kind, ExpnKind::Macro(MacroKind::Attr | MacroKind::Derive, ..)))
1538 {
1539 let span = expr
1540 .span
1541 .find_ancestor_not_from_extern_macro(self.tcx.sess.source_map())
1542 .unwrap_or(expr.span);
1543
1544 let mut sugg = if self.precedence(expr) >= ExprPrecedence::Unambiguous {
1545 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[(span.shrink_to_hi(), ".into()".to_owned())]))vec![(span.shrink_to_hi(), ".into()".to_owned())]
1546 } else {
1547 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[(span.shrink_to_lo(), "(".to_owned()),
(span.shrink_to_hi(), ").into()".to_owned())]))vec![
1548 (span.shrink_to_lo(), "(".to_owned()),
1549 (span.shrink_to_hi(), ").into()".to_owned()),
1550 ]
1551 };
1552 if let Some(name) = self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr) {
1553 sugg.insert(0, (expr.span.shrink_to_lo(), ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}: ", name))
})format!("{}: ", name)));
1554 }
1555 diag.multipart_suggestion(
1556 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("call `Into::into` on this expression to convert `{0}` into `{1}`",
expr_ty, expected_ty))
})format!("call `Into::into` on this expression to convert `{expr_ty}` into `{expected_ty}`"),
1557 sugg,
1558 Applicability::MaybeIncorrect
1559 );
1560 return true;
1561 }
1562
1563 false
1564 }
1565
1566 pub(crate) fn suggest_option_to_bool(
1568 &self,
1569 diag: &mut Diag<'_>,
1570 expr: &hir::Expr<'_>,
1571 expr_ty: Ty<'tcx>,
1572 expected_ty: Ty<'tcx>,
1573 ) -> bool {
1574 if !expected_ty.is_bool() {
1575 return false;
1576 }
1577
1578 let ty::Adt(def, _) = expr_ty.peel_refs().kind() else {
1579 return false;
1580 };
1581 if !self.tcx.is_diagnostic_item(sym::Option, def.did()) {
1582 return false;
1583 }
1584
1585 let cond_parent = self.tcx.hir_parent_iter(expr.hir_id).find(|(_, node)| {
1586 !#[allow(non_exhaustive_omitted_patterns)] match node {
hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Binary(op, _, _), .. })
if op.node == hir::BinOpKind::And => true,
_ => false,
}matches!(node, hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Binary(op, _, _), .. }) if op.node == hir::BinOpKind::And)
1587 });
1588 if let Some((_, hir::Node::LetStmt(local))) = cond_parent
1594 && let hir::PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), .. })
1595 | hir::PatKind::TupleStruct(qpath, _, _) = &local.pat.kind
1596 && let hir::QPath::Resolved(None, path) = qpath
1597 && let Some(did) = path
1598 .res
1599 .opt_def_id()
1600 .and_then(|did| self.tcx.opt_parent(did))
1601 .and_then(|did| self.tcx.opt_parent(did))
1602 && self.tcx.is_diagnostic_item(sym::Option, did)
1603 {
1604 return false;
1605 }
1606
1607 let suggestion = match self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr) {
1608 Some(ident) => ::alloc::__export::must_use({
::alloc::fmt::format(format_args!(": {0}.is_some()", ident))
})format!(": {ident}.is_some()"),
1609 None => ".is_some()".to_string(),
1610 };
1611
1612 diag.span_suggestion_verbose(
1613 expr.span.shrink_to_hi(),
1614 "use `Option::is_some` to test if the `Option` has a value",
1615 suggestion,
1616 Applicability::MachineApplicable,
1617 );
1618 true
1619 }
1620
1621 #[allow(clippy :: suspicious_else_formatting)]
{
let __tracing_attr_span;
let __tracing_attr_guard;
if ::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::TRACE <=
::tracing::level_filters::LevelFilter::current() ||
{ false } {
__tracing_attr_span =
{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("suggest_deref_unwrap_or",
"rustc_hir_typeck::fn_ctxt::suggestions",
::tracing::Level::TRACE,
::tracing_core::__macro_support::Option::Some("compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs"),
::tracing_core::__macro_support::Option::Some(1622u32),
::tracing_core::__macro_support::Option::Some("rustc_hir_typeck::fn_ctxt::suggestions"),
::tracing_core::field::FieldSet::new(&["callee_ty",
"call_ident", "expected_ty", "provided_ty", "is_method"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::SPAN)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let mut interest = ::tracing::subscriber::Interest::never();
if ::tracing::Level::TRACE <=
::tracing::level_filters::STATIC_MAX_LEVEL &&
::tracing::Level::TRACE <=
::tracing::level_filters::LevelFilter::current() &&
{ interest = __CALLSITE.interest(); !interest.is_never() }
&&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest) {
let meta = __CALLSITE.metadata();
::tracing::Span::new(meta,
&{
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = meta.fields().iter();
meta.fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&::tracing::field::debug(&callee_ty)
as &dyn Value)),
(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&::tracing::field::debug(&call_ident)
as &dyn Value)),
(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&::tracing::field::debug(&expected_ty)
as &dyn Value)),
(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&::tracing::field::debug(&provided_ty)
as &dyn Value)),
(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&is_method as
&dyn Value))])
})
} else {
let span =
::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
{};
span
}
};
__tracing_attr_guard = __tracing_attr_span.enter();
}
#[warn(clippy :: suspicious_else_formatting)]
{
#[allow(unknown_lints, unreachable_code, clippy ::
diverging_sub_expression, clippy :: empty_loop, clippy ::
let_unit_value, clippy :: let_with_type_underscore, clippy ::
needless_return, clippy :: unreachable)]
if false {
let __tracing_attr_fake_return: () = loop {};
return __tracing_attr_fake_return;
}
{
if !is_method { return; }
let Some(callee_ty) = callee_ty else { return; };
let ty::Adt(callee_adt, _) =
callee_ty.peel_refs().kind() else { return; };
let adt_name =
if self.tcx.is_diagnostic_item(sym::Option, callee_adt.did())
{
"Option"
} else if self.tcx.is_diagnostic_item(sym::Result,
callee_adt.did()) {
"Result"
} else { return; };
let Some(call_ident) = call_ident else { return; };
if call_ident.name != sym::unwrap_or { return; }
let ty::Ref(_, peeled, _mutability) =
provided_ty.kind() else { return; };
let dummy_ty =
if let ty::Array(elem_ty, size) = peeled.kind() &&
let ty::Infer(_) = elem_ty.kind() &&
self.try_structurally_resolve_const(provided_expr.span,
*size).try_to_target_usize(self.tcx) == Some(0) {
let slice = Ty::new_slice(self.tcx, *elem_ty);
Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_static,
slice)
} else { provided_ty };
if !self.may_coerce(expected_ty, dummy_ty) { return; }
let msg =
::alloc::__export::must_use({
::alloc::fmt::format(format_args!("use `{0}::map_or` to deref inner value of `{0}`",
adt_name))
});
err.multipart_suggestion(msg,
::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[(call_ident.span, "map_or".to_owned()),
(provided_expr.span.shrink_to_hi(),
", |v| v".to_owned())])), Applicability::MachineApplicable);
}
}
}#[instrument(level = "trace", skip(self, err, provided_expr))]
1623 pub(crate) fn suggest_deref_unwrap_or(
1624 &self,
1625 err: &mut Diag<'_>,
1626 callee_ty: Option<Ty<'tcx>>,
1627 call_ident: Option<Ident>,
1628 expected_ty: Ty<'tcx>,
1629 provided_ty: Ty<'tcx>,
1630 provided_expr: &Expr<'tcx>,
1631 is_method: bool,
1632 ) {
1633 if !is_method {
1634 return;
1635 }
1636 let Some(callee_ty) = callee_ty else {
1637 return;
1638 };
1639 let ty::Adt(callee_adt, _) = callee_ty.peel_refs().kind() else {
1640 return;
1641 };
1642 let adt_name = if self.tcx.is_diagnostic_item(sym::Option, callee_adt.did()) {
1643 "Option"
1644 } else if self.tcx.is_diagnostic_item(sym::Result, callee_adt.did()) {
1645 "Result"
1646 } else {
1647 return;
1648 };
1649
1650 let Some(call_ident) = call_ident else {
1651 return;
1652 };
1653 if call_ident.name != sym::unwrap_or {
1654 return;
1655 }
1656
1657 let ty::Ref(_, peeled, _mutability) = provided_ty.kind() else {
1658 return;
1659 };
1660
1661 let dummy_ty = if let ty::Array(elem_ty, size) = peeled.kind()
1665 && let ty::Infer(_) = elem_ty.kind()
1666 && self
1667 .try_structurally_resolve_const(provided_expr.span, *size)
1668 .try_to_target_usize(self.tcx)
1669 == Some(0)
1670 {
1671 let slice = Ty::new_slice(self.tcx, *elem_ty);
1672 Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_static, slice)
1673 } else {
1674 provided_ty
1675 };
1676
1677 if !self.may_coerce(expected_ty, dummy_ty) {
1678 return;
1679 }
1680 let msg = format!("use `{adt_name}::map_or` to deref inner value of `{adt_name}`");
1681 err.multipart_suggestion(
1682 msg,
1683 vec![
1684 (call_ident.span, "map_or".to_owned()),
1685 (provided_expr.span.shrink_to_hi(), ", |v| v".to_owned()),
1686 ],
1687 Applicability::MachineApplicable,
1688 );
1689 }
1690
1691 pub(crate) fn suggest_block_to_brackets(
1694 &self,
1695 diag: &mut Diag<'_>,
1696 blk: &hir::Block<'_>,
1697 blk_ty: Ty<'tcx>,
1698 expected_ty: Ty<'tcx>,
1699 ) {
1700 if let ty::Slice(elem_ty) | ty::Array(elem_ty, _) = expected_ty.kind() {
1701 if self.may_coerce(blk_ty, *elem_ty)
1702 && blk.stmts.is_empty()
1703 && blk.rules == hir::BlockCheckMode::DefaultBlock
1704 && let source_map = self.tcx.sess.source_map()
1705 && let Ok(snippet) = source_map.span_to_snippet(blk.span)
1706 && snippet.starts_with('{')
1707 && snippet.ends_with('}')
1708 {
1709 diag.multipart_suggestion(
1710 "to create an array, use square brackets instead of curly braces",
1711 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[(blk.span.shrink_to_lo().with_hi(rustc_span::BytePos(blk.span.lo().0
+ 1)), "[".to_string()),
(blk.span.shrink_to_hi().with_lo(rustc_span::BytePos(blk.span.hi().0
- 1)), "]".to_string())]))vec![
1712 (
1713 blk.span
1714 .shrink_to_lo()
1715 .with_hi(rustc_span::BytePos(blk.span.lo().0 + 1)),
1716 "[".to_string(),
1717 ),
1718 (
1719 blk.span
1720 .shrink_to_hi()
1721 .with_lo(rustc_span::BytePos(blk.span.hi().0 - 1)),
1722 "]".to_string(),
1723 ),
1724 ],
1725 Applicability::MachineApplicable,
1726 );
1727 }
1728 }
1729 }
1730
1731 #[allow(clippy :: suspicious_else_formatting)]
{
let __tracing_attr_span;
let __tracing_attr_guard;
if ::tracing::Level::INFO <= ::tracing::level_filters::STATIC_MAX_LEVEL &&
::tracing::Level::INFO <=
::tracing::level_filters::LevelFilter::current() ||
{ false } {
__tracing_attr_span =
{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("suggest_floating_point_literal",
"rustc_hir_typeck::fn_ctxt::suggestions",
::tracing::Level::INFO,
::tracing_core::__macro_support::Option::Some("compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs"),
::tracing_core::__macro_support::Option::Some(1731u32),
::tracing_core::__macro_support::Option::Some("rustc_hir_typeck::fn_ctxt::suggestions"),
::tracing_core::field::FieldSet::new(&["expr",
"expected_ty"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::SPAN)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let mut interest = ::tracing::subscriber::Interest::never();
if ::tracing::Level::INFO <=
::tracing::level_filters::STATIC_MAX_LEVEL &&
::tracing::Level::INFO <=
::tracing::level_filters::LevelFilter::current() &&
{ interest = __CALLSITE.interest(); !interest.is_never() }
&&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest) {
let meta = __CALLSITE.metadata();
::tracing::Span::new(meta,
&{
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = meta.fields().iter();
meta.fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&::tracing::field::debug(&expr)
as &dyn Value)),
(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&::tracing::field::debug(&expected_ty)
as &dyn Value))])
})
} else {
let span =
::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
{};
span
}
};
__tracing_attr_guard = __tracing_attr_span.enter();
}
#[warn(clippy :: suspicious_else_formatting)]
{
#[allow(unknown_lints, unreachable_code, clippy ::
diverging_sub_expression, clippy :: empty_loop, clippy ::
let_unit_value, clippy :: let_with_type_underscore, clippy ::
needless_return, clippy :: unreachable)]
if false {
let __tracing_attr_fake_return: bool = loop {};
return __tracing_attr_fake_return;
}
{
if !expected_ty.is_floating_point() { return false; }
match expr.kind {
ExprKind::Struct(&qpath, [start, end], _) if
is_range_literal(expr) &&
self.tcx.qpath_is_lang_item(qpath, LangItem::Range) => {
err.span_suggestion_verbose(start.expr.span.shrink_to_hi().with_hi(end.expr.span.lo()),
"remove the unnecessary `.` operator for a floating point literal",
'.', Applicability::MaybeIncorrect);
true
}
ExprKind::Struct(&qpath, [arg], _) if
is_range_literal(expr) &&
let Some(qpath @ (LangItem::RangeFrom | LangItem::RangeTo))
= self.tcx.qpath_lang_item(qpath) => {
let range_span = expr.span.parent_callsite().unwrap();
match qpath {
LangItem::RangeFrom => {
err.span_suggestion_verbose(range_span.with_lo(arg.expr.span.hi()),
"remove the unnecessary `.` operator for a floating point literal",
'.', Applicability::MaybeIncorrect);
}
_ => {
err.span_suggestion_verbose(range_span.until(arg.expr.span),
"remove the unnecessary `.` operator and add an integer part for a floating point literal",
"0.", Applicability::MaybeIncorrect);
}
}
true
}
ExprKind::Lit(Spanned {
node: rustc_ast::LitKind::Int(lit,
rustc_ast::LitIntType::Unsuffixed),
span }) => {
let Ok(snippet) =
self.tcx.sess.source_map().span_to_snippet(span) else {
return false;
};
if !(snippet.starts_with("0x") || snippet.starts_with("0X"))
{
return false;
}
if snippet.len() <= 5 ||
!snippet.is_char_boundary(snippet.len() - 3) {
return false;
}
let (_, suffix) = snippet.split_at(snippet.len() - 3);
let value =
match suffix {
"f32" => (lit.get() - 0xf32) / (16 * 16 * 16),
"f64" => (lit.get() - 0xf64) / (16 * 16 * 16),
_ => return false,
};
err.span_suggestions(expr.span,
"rewrite this as a decimal floating point literal, or use `as` to turn a hex literal into a float",
[::alloc::__export::must_use({
::alloc::fmt::format(format_args!("0x{0:X} as {1}", value,
suffix))
}),
::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}_{1}", value, suffix))
})], Applicability::MaybeIncorrect);
true
}
_ => false,
}
}
}
}#[instrument(skip(self, err))]
1732 pub(crate) fn suggest_floating_point_literal(
1733 &self,
1734 err: &mut Diag<'_>,
1735 expr: &hir::Expr<'_>,
1736 expected_ty: Ty<'tcx>,
1737 ) -> bool {
1738 if !expected_ty.is_floating_point() {
1739 return false;
1740 }
1741 match expr.kind {
1742 ExprKind::Struct(&qpath, [start, end], _)
1743 if is_range_literal(expr)
1744 && self.tcx.qpath_is_lang_item(qpath, LangItem::Range) =>
1745 {
1746 err.span_suggestion_verbose(
1747 start.expr.span.shrink_to_hi().with_hi(end.expr.span.lo()),
1748 "remove the unnecessary `.` operator for a floating point literal",
1749 '.',
1750 Applicability::MaybeIncorrect,
1751 );
1752 true
1753 }
1754 ExprKind::Struct(&qpath, [arg], _)
1755 if is_range_literal(expr)
1756 && let Some(qpath @ (LangItem::RangeFrom | LangItem::RangeTo)) =
1757 self.tcx.qpath_lang_item(qpath) =>
1758 {
1759 let range_span = expr.span.parent_callsite().unwrap();
1760 match qpath {
1761 LangItem::RangeFrom => {
1762 err.span_suggestion_verbose(
1763 range_span.with_lo(arg.expr.span.hi()),
1764 "remove the unnecessary `.` operator for a floating point literal",
1765 '.',
1766 Applicability::MaybeIncorrect,
1767 );
1768 }
1769 _ => {
1770 err.span_suggestion_verbose(
1771 range_span.until(arg.expr.span),
1772 "remove the unnecessary `.` operator and add an integer part for a floating point literal",
1773 "0.",
1774 Applicability::MaybeIncorrect,
1775 );
1776 }
1777 }
1778 true
1779 }
1780 ExprKind::Lit(Spanned {
1781 node: rustc_ast::LitKind::Int(lit, rustc_ast::LitIntType::Unsuffixed),
1782 span,
1783 }) => {
1784 let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) else {
1785 return false;
1786 };
1787 if !(snippet.starts_with("0x") || snippet.starts_with("0X")) {
1788 return false;
1789 }
1790 if snippet.len() <= 5 || !snippet.is_char_boundary(snippet.len() - 3) {
1791 return false;
1792 }
1793 let (_, suffix) = snippet.split_at(snippet.len() - 3);
1794 let value = match suffix {
1795 "f32" => (lit.get() - 0xf32) / (16 * 16 * 16),
1796 "f64" => (lit.get() - 0xf64) / (16 * 16 * 16),
1797 _ => return false,
1798 };
1799 err.span_suggestions(
1800 expr.span,
1801 "rewrite this as a decimal floating point literal, or use `as` to turn a hex literal into a float",
1802 [format!("0x{value:X} as {suffix}"), format!("{value}_{suffix}")],
1803 Applicability::MaybeIncorrect,
1804 );
1805 true
1806 }
1807 _ => false,
1808 }
1809 }
1810
1811 #[allow(clippy :: suspicious_else_formatting)]
{
let __tracing_attr_span;
let __tracing_attr_guard;
if ::tracing::Level::INFO <= ::tracing::level_filters::STATIC_MAX_LEVEL &&
::tracing::Level::INFO <=
::tracing::level_filters::LevelFilter::current() ||
{ false } {
__tracing_attr_span =
{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("suggest_null_ptr_for_literal_zero_given_to_ptr_arg",
"rustc_hir_typeck::fn_ctxt::suggestions",
::tracing::Level::INFO,
::tracing_core::__macro_support::Option::Some("compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs"),
::tracing_core::__macro_support::Option::Some(1813u32),
::tracing_core::__macro_support::Option::Some("rustc_hir_typeck::fn_ctxt::suggestions"),
::tracing_core::field::FieldSet::new(&["expr",
"expected_ty"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::SPAN)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let mut interest = ::tracing::subscriber::Interest::never();
if ::tracing::Level::INFO <=
::tracing::level_filters::STATIC_MAX_LEVEL &&
::tracing::Level::INFO <=
::tracing::level_filters::LevelFilter::current() &&
{ interest = __CALLSITE.interest(); !interest.is_never() }
&&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest) {
let meta = __CALLSITE.metadata();
::tracing::Span::new(meta,
&{
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = meta.fields().iter();
meta.fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&::tracing::field::debug(&expr)
as &dyn Value)),
(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&::tracing::field::debug(&expected_ty)
as &dyn Value))])
})
} else {
let span =
::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
{};
span
}
};
__tracing_attr_guard = __tracing_attr_span.enter();
}
#[warn(clippy :: suspicious_else_formatting)]
{
#[allow(unknown_lints, unreachable_code, clippy ::
diverging_sub_expression, clippy :: empty_loop, clippy ::
let_unit_value, clippy :: let_with_type_underscore, clippy ::
needless_return, clippy :: unreachable)]
if false {
let __tracing_attr_fake_return: bool = loop {};
return __tracing_attr_fake_return;
}
{
let ty::RawPtr(_, mutbl) =
expected_ty.kind() else { return false; };
let ExprKind::Lit(Spanned {
node: rustc_ast::LitKind::Int(Pu128(0), _), span }) =
expr.kind else { return false; };
let null_sym =
match mutbl {
hir::Mutability::Not => sym::ptr_null,
hir::Mutability::Mut => sym::ptr_null_mut,
};
let Some(null_did) =
self.tcx.get_diagnostic_item(null_sym) else { return false; };
let null_path_str =
{
let _guard = NoTrimmedGuard::new();
self.tcx.def_path_str(null_did)
};
err.span_suggestion(span,
::alloc::__export::must_use({
::alloc::fmt::format(format_args!("if you meant to create a null pointer, use `{0}()`",
null_path_str))
}), null_path_str + "()", Applicability::MachineApplicable);
true
}
}
}#[instrument(skip(self, err))]
1814 pub(crate) fn suggest_null_ptr_for_literal_zero_given_to_ptr_arg(
1815 &self,
1816 err: &mut Diag<'_>,
1817 expr: &hir::Expr<'_>,
1818 expected_ty: Ty<'tcx>,
1819 ) -> bool {
1820 let ty::RawPtr(_, mutbl) = expected_ty.kind() else {
1822 return false;
1823 };
1824
1825 let ExprKind::Lit(Spanned { node: rustc_ast::LitKind::Int(Pu128(0), _), span }) = expr.kind
1827 else {
1828 return false;
1829 };
1830
1831 let null_sym = match mutbl {
1833 hir::Mutability::Not => sym::ptr_null,
1834 hir::Mutability::Mut => sym::ptr_null_mut,
1835 };
1836 let Some(null_did) = self.tcx.get_diagnostic_item(null_sym) else {
1837 return false;
1838 };
1839 let null_path_str = with_no_trimmed_paths!(self.tcx.def_path_str(null_did));
1840
1841 err.span_suggestion(
1843 span,
1844 format!("if you meant to create a null pointer, use `{null_path_str}()`"),
1845 null_path_str + "()",
1846 Applicability::MachineApplicable,
1847 );
1848
1849 true
1850 }
1851
1852 pub(crate) fn suggest_associated_const(
1853 &self,
1854 err: &mut Diag<'_>,
1855 expr: &hir::Expr<'tcx>,
1856 expected_ty: Ty<'tcx>,
1857 ) -> bool {
1858 let Some((DefKind::AssocFn, old_def_id)) =
1859 self.typeck_results.borrow().type_dependent_def(expr.hir_id)
1860 else {
1861 return false;
1862 };
1863 let old_item_name = self.tcx.item_name(old_def_id);
1864 let capitalized_name = Symbol::intern(&old_item_name.as_str().to_uppercase());
1865 if old_item_name == capitalized_name {
1866 return false;
1867 }
1868 let (item, segment) = match expr.kind {
1869 hir::ExprKind::Path(QPath::Resolved(
1870 Some(ty),
1871 hir::Path { segments: [segment], .. },
1872 ))
1873 | hir::ExprKind::Path(QPath::TypeRelative(ty, segment))
1874 if let Some(self_ty) = self.typeck_results.borrow().node_type_opt(ty.hir_id)
1875 && let Ok(pick) = self.probe_for_name(
1876 Mode::Path,
1877 Ident::new(capitalized_name, segment.ident.span),
1878 Some(expected_ty),
1879 IsSuggestion(true),
1880 self_ty,
1881 expr.hir_id,
1882 ProbeScope::TraitsInScope,
1883 ) =>
1884 {
1885 (pick.item, segment)
1886 }
1887 hir::ExprKind::Path(QPath::Resolved(
1888 None,
1889 hir::Path { segments: [.., segment], .. },
1890 )) => {
1891 if old_item_name != segment.ident.name {
1894 return false;
1895 }
1896 let Some(item) = self
1897 .tcx
1898 .associated_items(self.tcx.parent(old_def_id))
1899 .filter_by_name_unhygienic(capitalized_name)
1900 .next()
1901 else {
1902 return false;
1903 };
1904 (*item, segment)
1905 }
1906 _ => return false,
1907 };
1908 if item.def_id == old_def_id
1909 || !#[allow(non_exhaustive_omitted_patterns)] match self.tcx.def_kind(item.def_id)
{
DefKind::AssocConst { .. } => true,
_ => false,
}matches!(self.tcx.def_kind(item.def_id), DefKind::AssocConst { .. })
1910 {
1911 return false;
1913 }
1914 let item_ty = self.tcx.type_of(item.def_id).instantiate_identity().skip_norm_wip();
1915 if item_ty.has_param() {
1917 return false;
1918 }
1919 if self.may_coerce(item_ty, expected_ty) {
1920 err.span_suggestion_verbose(
1921 segment.ident.span,
1922 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("try referring to the associated const `{0}` instead",
capitalized_name))
})format!("try referring to the associated const `{capitalized_name}` instead",),
1923 capitalized_name,
1924 Applicability::MachineApplicable,
1925 );
1926 true
1927 } else {
1928 false
1929 }
1930 }
1931
1932 fn is_loop(&self, id: HirId) -> bool {
1933 let node = self.tcx.hir_node(id);
1934 #[allow(non_exhaustive_omitted_patterns)] match node {
Node::Expr(Expr { kind: ExprKind::Loop(..), .. }) => true,
_ => false,
}matches!(node, Node::Expr(Expr { kind: ExprKind::Loop(..), .. }))
1935 }
1936
1937 fn is_local_statement(&self, id: HirId) -> bool {
1938 let node = self.tcx.hir_node(id);
1939 #[allow(non_exhaustive_omitted_patterns)] match node {
Node::Stmt(Stmt { kind: StmtKind::Let(..), .. }) => true,
_ => false,
}matches!(node, Node::Stmt(Stmt { kind: StmtKind::Let(..), .. }))
1940 }
1941
1942 pub(crate) fn note_type_is_not_clone(
1945 &self,
1946 diag: &mut Diag<'_>,
1947 expected_ty: Ty<'tcx>,
1948 found_ty: Ty<'tcx>,
1949 expr: &hir::Expr<'_>,
1950 ) {
1951 let expr = self.note_type_is_not_clone_inner_expr(expr);
1954
1955 let hir::ExprKind::MethodCall(segment, callee_expr, &[], _) = expr.kind else {
1957 return;
1958 };
1959
1960 let Some(clone_trait_did) = self.tcx.lang_items().clone_trait() else {
1961 return;
1962 };
1963 let ty::Ref(_, pointee_ty, _) = found_ty.kind() else { return };
1964 let results = self.typeck_results.borrow();
1965 if segment.ident.name == sym::clone
1967 && results.type_dependent_def_id(expr.hir_id).is_some_and(|did| {
1968 let assoc_item = self.tcx.associated_item(did);
1969 assoc_item.container == ty::AssocContainer::Trait
1970 && assoc_item.container_id(self.tcx) == clone_trait_did
1971 })
1972 && !results.expr_adjustments(callee_expr).iter().any(|adj| #[allow(non_exhaustive_omitted_patterns)] match adj.kind {
ty::adjustment::Adjust::Deref(..) => true,
_ => false,
}matches!(adj.kind, ty::adjustment::Adjust::Deref(..)))
1975 && self.may_coerce(*pointee_ty, expected_ty)
1977 && let trait_ref = ty::TraitRef::new(self.tcx, clone_trait_did, [expected_ty])
1978 && !self.predicate_must_hold_considering_regions(&traits::Obligation::new(
1980 self.tcx,
1981 traits::ObligationCause::dummy(),
1982 self.param_env,
1983 trait_ref,
1984 ))
1985 {
1986 diag.span_note(
1987 callee_expr.span,
1988 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("`{0}` does not implement `Clone`, so `{1}` was cloned instead",
expected_ty, found_ty))
})format!(
1989 "`{expected_ty}` does not implement `Clone`, so `{found_ty}` was cloned instead"
1990 ),
1991 );
1992 let owner = self.tcx.hir_enclosing_body_owner(expr.hir_id);
1993 if let ty::Param(param) = expected_ty.kind()
1994 && let Some(generics) = self.tcx.hir_get_generics(owner)
1995 {
1996 suggest_constraining_type_params(
1997 self.tcx,
1998 generics,
1999 diag,
2000 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[(param.name.as_str(), "Clone", Some(clone_trait_did))]))vec![(param.name.as_str(), "Clone", Some(clone_trait_did))].into_iter(),
2001 None,
2002 );
2003 } else {
2004 let mut suggest_derive = true;
2005 if let Some(errors) =
2006 self.type_implements_trait_shallow(clone_trait_did, expected_ty, self.param_env)
2007 {
2008 let manually_impl = "consider manually implementing `Clone` to avoid the \
2009 implicit type parameter bounds";
2010 match &errors[..] {
2011 [] => {}
2012 [error] => {
2013 let msg = "`Clone` is not implemented because a trait bound is not \
2014 satisfied";
2015 if let traits::ObligationCauseCode::ImplDerived(data) =
2016 error.obligation.cause.code()
2017 {
2018 let mut span: MultiSpan = data.span.into();
2019 if self.tcx.is_automatically_derived(data.impl_or_alias_def_id) {
2020 span.push_span_label(
2021 data.span,
2022 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("derive introduces an implicit `{0}` bound",
error.obligation.predicate))
})format!(
2023 "derive introduces an implicit `{}` bound",
2024 error.obligation.predicate
2025 ),
2026 );
2027 }
2028 diag.span_help(span, msg);
2029 if self.tcx.is_automatically_derived(data.impl_or_alias_def_id)
2030 && data.impl_or_alias_def_id.is_local()
2031 {
2032 diag.help(manually_impl);
2033 suggest_derive = false;
2034 }
2035 } else {
2036 diag.help(msg);
2037 }
2038 }
2039 _ => {
2040 let unsatisfied_bounds: Vec<_> = errors
2041 .iter()
2042 .filter_map(|error| match error.obligation.cause.code() {
2043 traits::ObligationCauseCode::ImplDerived(data) => {
2044 let pre = if self
2045 .tcx
2046 .is_automatically_derived(data.impl_or_alias_def_id)
2047 {
2048 "derive introduces an implicit "
2049 } else {
2050 ""
2051 };
2052 Some((
2053 data.span,
2054 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{1}unsatisfied trait bound `{0}`",
error.obligation.predicate, pre))
})format!(
2055 "{pre}unsatisfied trait bound `{}`",
2056 error.obligation.predicate
2057 ),
2058 ))
2059 }
2060 _ => None,
2061 })
2062 .collect();
2063 let msg = "`Clone` is not implemented because the some trait bounds \
2064 could not be satisfied";
2065 if errors.len() == unsatisfied_bounds.len() {
2066 let mut unsatisfied_bounds_spans: MultiSpan = unsatisfied_bounds
2067 .iter()
2068 .map(|(span, _)| *span)
2069 .collect::<Vec<Span>>()
2070 .into();
2071 for (span, label) in unsatisfied_bounds {
2072 unsatisfied_bounds_spans.push_span_label(span, label);
2073 }
2074 diag.span_help(unsatisfied_bounds_spans, msg);
2075 if errors.iter().all(|error| match error.obligation.cause.code() {
2076 traits::ObligationCauseCode::ImplDerived(data) => {
2077 self.tcx.is_automatically_derived(data.impl_or_alias_def_id)
2078 && data.impl_or_alias_def_id.is_local()
2079 }
2080 _ => false,
2081 }) {
2082 diag.help(manually_impl);
2083 suggest_derive = false;
2084 }
2085 } else {
2086 diag.help(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{1}: {0}",
listify(&errors,
|e|
::alloc::__export::must_use({
::alloc::fmt::format(format_args!("`{0}`",
e.obligation.predicate))
})).unwrap(), msg))
})format!(
2087 "{msg}: {}",
2088 listify(&errors, |e| format!("`{}`", e.obligation.predicate))
2089 .unwrap(),
2090 ));
2091 }
2092 }
2093 }
2094 for error in errors {
2095 if let traits::FulfillmentErrorCode::Select(
2096 traits::SelectionError::Unimplemented,
2097 ) = error.code
2098 && let ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) =
2099 error.obligation.predicate.kind().skip_binder()
2100 {
2101 self.infcx.err_ctxt().suggest_derive(
2102 &error.obligation,
2103 diag,
2104 error.obligation.predicate.kind().rebind(pred),
2105 );
2106 }
2107 }
2108 }
2109 if suggest_derive {
2110 self.suggest_derive(diag, &::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[(trait_ref.upcast(self.tcx), None, None)]))vec![(trait_ref.upcast(self.tcx), None, None)]);
2111 }
2112 }
2113 }
2114 }
2115
2116 fn note_type_is_not_clone_inner_expr<'b>(
2121 &'b self,
2122 expr: &'b hir::Expr<'b>,
2123 ) -> &'b hir::Expr<'b> {
2124 match expr.peel_blocks().kind {
2125 hir::ExprKind::Path(hir::QPath::Resolved(
2126 None,
2127 hir::Path { segments: [_], res: crate::Res::Local(binding), .. },
2128 )) => {
2129 let hir::Node::Pat(hir::Pat { hir_id, .. }) = self.tcx.hir_node(*binding) else {
2130 return expr;
2131 };
2132
2133 match self.tcx.parent_hir_node(*hir_id) {
2134 hir::Node::LetStmt(hir::LetStmt { init: Some(init), .. }) => {
2136 self.note_type_is_not_clone_inner_expr(init)
2137 }
2138 hir::Node::Pat(hir::Pat {
2140 hir_id: pat_hir_id,
2141 kind: hir::PatKind::Tuple(pats, ..),
2142 ..
2143 }) => {
2144 let hir::Node::LetStmt(hir::LetStmt { init: Some(init), .. }) =
2145 self.tcx.parent_hir_node(*pat_hir_id)
2146 else {
2147 return expr;
2148 };
2149
2150 match init.peel_blocks().kind {
2151 ExprKind::Tup(init_tup) => {
2152 if let Some(init) = pats
2153 .iter()
2154 .enumerate()
2155 .filter(|x| x.1.hir_id == *hir_id)
2156 .find_map(|(i, _)| init_tup.get(i))
2157 {
2158 self.note_type_is_not_clone_inner_expr(init)
2159 } else {
2160 expr
2161 }
2162 }
2163 _ => expr,
2164 }
2165 }
2166 _ => expr,
2167 }
2168 }
2169 hir::ExprKind::Call(Expr { kind: call_expr_kind, .. }, _) => {
2173 if let hir::ExprKind::Path(hir::QPath::Resolved(None, call_expr_path)) =
2174 call_expr_kind
2175 && let hir::Path { segments: [_], res: crate::Res::Local(binding), .. } =
2176 call_expr_path
2177 && let hir::Node::Pat(hir::Pat { hir_id, .. }) = self.tcx.hir_node(*binding)
2178 && let hir::Node::LetStmt(hir::LetStmt { init: Some(init), .. }) =
2179 self.tcx.parent_hir_node(*hir_id)
2180 && let Expr {
2181 kind: hir::ExprKind::Closure(hir::Closure { body: body_id, .. }),
2182 ..
2183 } = init
2184 {
2185 let hir::Body { value: body_expr, .. } = self.tcx.hir_body(*body_id);
2186 self.note_type_is_not_clone_inner_expr(body_expr)
2187 } else {
2188 expr
2189 }
2190 }
2191 _ => expr,
2192 }
2193 }
2194
2195 pub(crate) fn is_field_suggestable(
2196 &self,
2197 field: &ty::FieldDef,
2198 hir_id: HirId,
2199 span: Span,
2200 ) -> bool {
2201 field.vis.is_accessible_from(self.tcx.parent_module(hir_id), self.tcx)
2203 && !#[allow(non_exhaustive_omitted_patterns)] match self.tcx.eval_stability(field.did,
None, rustc_span::DUMMY_SP, None) {
rustc_middle::middle::stability::EvalResult::Deny { .. } => true,
_ => false,
}matches!(
2205 self.tcx.eval_stability(field.did, None, rustc_span::DUMMY_SP, None),
2206 rustc_middle::middle::stability::EvalResult::Deny { .. }
2207 )
2208 && (field.did.is_local() || !self.tcx.is_doc_hidden(field.did))
2210 && self.tcx.def_ident_span(field.did).unwrap().normalize_to_macros_2_0().eq_ctxt(span)
2212 }
2213
2214 pub(crate) fn suggest_missing_unwrap_expect(
2215 &self,
2216 err: &mut Diag<'_>,
2217 expr: &hir::Expr<'tcx>,
2218 expected: Ty<'tcx>,
2219 found: Ty<'tcx>,
2220 ) -> bool {
2221 let ty::Adt(adt, args) = found.kind() else {
2222 return false;
2223 };
2224 let ret_ty_matches = |diagnostic_item| {
2225 let Some(sig) = self.body_fn_sig() else {
2226 return false;
2227 };
2228 let ty::Adt(kind, _) = sig.output().kind() else {
2229 return false;
2230 };
2231 self.tcx.is_diagnostic_item(diagnostic_item, kind.did())
2232 };
2233
2234 let is_ctor = #[allow(non_exhaustive_omitted_patterns)] match expr.kind {
hir::ExprKind::Call(hir::Expr {
kind: hir::ExprKind::Path(hir::QPath::Resolved(None, hir::Path {
res: Res::Def(hir::def::DefKind::Ctor(_, _), _), .. })), .. }, ..)
|
hir::ExprKind::Path(hir::QPath::Resolved(None, hir::Path {
res: Res::Def(hir::def::DefKind::Ctor(_, _), _), .. })) => true,
_ => false,
}matches!(
2237 expr.kind,
2238 hir::ExprKind::Call(
2239 hir::Expr {
2240 kind: hir::ExprKind::Path(hir::QPath::Resolved(
2241 None,
2242 hir::Path { res: Res::Def(hir::def::DefKind::Ctor(_, _), _), .. },
2243 )),
2244 ..
2245 },
2246 ..,
2247 ) | hir::ExprKind::Path(hir::QPath::Resolved(
2248 None,
2249 hir::Path { res: Res::Def(hir::def::DefKind::Ctor(_, _), _), .. },
2250 )),
2251 );
2252
2253 let (article, kind, variant, sugg_operator) = if self.tcx.is_diagnostic_item(sym::Result, adt.did())
2254 && !self.tcx.hir_is_inside_const_context(expr.hir_id)
2256 {
2257 ("a", "Result", "Err", ret_ty_matches(sym::Result))
2258 } else if self.tcx.is_diagnostic_item(sym::Option, adt.did()) {
2259 ("an", "Option", "None", ret_ty_matches(sym::Option))
2260 } else {
2261 return false;
2262 };
2263 if is_ctor || !self.may_coerce(args.type_at(0), expected) {
2264 return false;
2265 }
2266
2267 let (msg, sugg) = if sugg_operator {
2268 (
2269 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("use the `?` operator to extract the `{0}` value, propagating {1} `{2}::{3}` value to the caller",
found, article, kind, variant))
})format!(
2270 "use the `?` operator to extract the `{found}` value, propagating \
2271 {article} `{kind}::{variant}` value to the caller"
2272 ),
2273 "?",
2274 )
2275 } else {
2276 (
2277 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("consider using `{0}::expect` to unwrap the `{1}` value, panicking if the value is {2} `{0}::{3}`",
kind, found, article, variant))
})format!(
2278 "consider using `{kind}::expect` to unwrap the `{found}` value, \
2279 panicking if the value is {article} `{kind}::{variant}`"
2280 ),
2281 ".expect(\"REASON\")",
2282 )
2283 };
2284
2285 let sugg = match self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr) {
2286 Some(_) if expr.span.from_expansion() => return false,
2287 Some(ident) => ::alloc::__export::must_use({
::alloc::fmt::format(format_args!(": {0}{1}", ident, sugg))
})format!(": {ident}{sugg}"),
2288 None => sugg.to_string(),
2289 };
2290
2291 let span = expr
2292 .span
2293 .find_ancestor_not_from_extern_macro(self.tcx.sess.source_map())
2294 .unwrap_or(expr.span);
2295 err.span_suggestion_verbose(span.shrink_to_hi(), msg, sugg, Applicability::HasPlaceholders);
2296 true
2297 }
2298
2299 pub(crate) fn suggest_coercing_result_via_try_operator(
2300 &self,
2301 err: &mut Diag<'_>,
2302 expr: &hir::Expr<'tcx>,
2303 expected: Ty<'tcx>,
2304 found: Ty<'tcx>,
2305 ) -> bool {
2306 let returned = #[allow(non_exhaustive_omitted_patterns)] match self.tcx.parent_hir_node(expr.hir_id)
{
hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Ret(_), .. }) => true,
_ => false,
}matches!(
2307 self.tcx.parent_hir_node(expr.hir_id),
2308 hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Ret(_), .. })
2309 ) || self.tcx.hir_get_fn_id_for_return_block(expr.hir_id).is_some();
2310 if returned
2311 && let ty::Adt(e, args_e) = expected.kind()
2312 && let ty::Adt(f, args_f) = found.kind()
2313 && e.did() == f.did()
2314 && Some(e.did()) == self.tcx.get_diagnostic_item(sym::Result)
2315 && let e_ok = args_e.type_at(0)
2316 && let f_ok = args_f.type_at(0)
2317 && self.infcx.can_eq(self.param_env, f_ok, e_ok)
2318 && let e_err = args_e.type_at(1)
2319 && let f_err = args_f.type_at(1)
2320 && self
2321 .infcx
2322 .type_implements_trait(
2323 self.tcx.get_diagnostic_item(sym::Into).unwrap(),
2324 [f_err, e_err],
2325 self.param_env,
2326 )
2327 .must_apply_modulo_regions()
2328 {
2329 err.multipart_suggestion(
2330 "use `?` to coerce and return an appropriate `Err`, and wrap the resulting value \
2331 in `Ok` so the expression remains of type `Result`",
2332 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[(expr.span.shrink_to_lo(), "Ok(".to_string()),
(expr.span.shrink_to_hi(), "?)".to_string())]))vec![
2333 (expr.span.shrink_to_lo(), "Ok(".to_string()),
2334 (expr.span.shrink_to_hi(), "?)".to_string()),
2335 ],
2336 Applicability::MaybeIncorrect,
2337 );
2338 return true;
2339 }
2340 false
2341 }
2342
2343 pub(crate) fn suggest_returning_value_after_loop(
2346 &self,
2347 err: &mut Diag<'_>,
2348 expr: &hir::Expr<'tcx>,
2349 expected: Ty<'tcx>,
2350 ) -> bool {
2351 let tcx = self.tcx;
2352 let enclosing_scope =
2353 tcx.hir_get_enclosing_scope(expr.hir_id).map(|hir_id| tcx.hir_node(hir_id));
2354
2355 let tail_expr = if let Some(Node::Block(hir::Block { expr, .. })) = enclosing_scope
2357 && expr.is_some()
2358 {
2359 *expr
2360 } else {
2361 let body_def_id = tcx.hir_enclosing_body_owner(expr.hir_id);
2362 let body = tcx.hir_body_owned_by(body_def_id);
2363
2364 match body.value.kind {
2366 hir::ExprKind::Block(block, _) => block.expr,
2368 hir::ExprKind::DropTemps(expr) => Some(expr),
2370 _ => None,
2371 }
2372 };
2373
2374 let Some(tail_expr) = tail_expr else {
2375 return false; };
2377
2378 let loop_expr_in_tail = match expr.kind {
2380 hir::ExprKind::Loop(_, _, hir::LoopSource::While, _) => tail_expr,
2381 hir::ExprKind::Loop(_, _, hir::LoopSource::ForLoop, _) => {
2382 match tail_expr.peel_drop_temps() {
2383 Expr { kind: ExprKind::Match(_, [Arm { body, .. }], _), .. } => body,
2384 _ => return false, }
2386 }
2387 _ => return false, };
2389
2390 if expr.hir_id == loop_expr_in_tail.hir_id {
2393 let span = expr.span;
2394
2395 let (msg, suggestion) = if expected.is_never() {
2396 (
2397 "consider adding a diverging expression here",
2398 "`loop {}` or `panic!(\"...\")`".to_string(),
2399 )
2400 } else {
2401 ("consider returning a value here", ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("`{0}` value", expected))
})format!("`{expected}` value"))
2402 };
2403
2404 let src_map = tcx.sess.source_map();
2405 let suggestion = if src_map.is_multiline(expr.span) {
2406 let indentation = src_map.indentation_before(span).unwrap_or_default();
2407 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("\n{0}/* {1} */", indentation,
suggestion))
})format!("\n{indentation}/* {suggestion} */")
2408 } else {
2409 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!(" /* {0} */", suggestion))
})format!(" /* {suggestion} */")
2412 };
2413
2414 err.span_suggestion_verbose(
2415 span.shrink_to_hi(),
2416 msg,
2417 suggestion,
2418 Applicability::MaybeIncorrect,
2419 );
2420
2421 true
2422 } else {
2423 false
2424 }
2425 }
2426
2427 pub(crate) fn suggest_semicolon_in_repeat_expr(
2430 &self,
2431 err: &mut Diag<'_>,
2432 expr: &hir::Expr<'_>,
2433 expr_ty: Ty<'tcx>,
2434 ) -> bool {
2435 if let hir::Node::Expr(array_expr) = self.tcx.parent_hir_node(expr.hir_id)
2437 && let hir::ExprKind::Array(elements) = array_expr.kind
2438 && let [first, second] = elements
2439 && second.hir_id == expr.hir_id
2440 {
2441 let comma_span = first.span.between(second.span);
2443
2444 let expr_is_const_usize = expr_ty.is_usize()
2452 && match expr.kind {
2453 ExprKind::Path(QPath::Resolved(
2454 None,
2455 Path { res: Res::Def(DefKind::Const { .. }, _), .. },
2456 )) => true,
2457 ExprKind::Call(
2458 Expr {
2459 kind:
2460 ExprKind::Path(QPath::Resolved(
2461 None,
2462 Path { res: Res::Def(DefKind::Fn, fn_def_id), .. },
2463 )),
2464 ..
2465 },
2466 _,
2467 ) => self.tcx.is_const_fn(*fn_def_id),
2468 _ => false,
2469 };
2470
2471 let first_ty = self.typeck_results.borrow().expr_ty(first);
2476
2477 if self.tcx.sess.source_map().is_imported(array_expr.span)
2482 && self.type_is_clone_modulo_regions(self.param_env, first_ty)
2483 && (expr.is_size_lit() || expr_ty.is_usize_like())
2484 {
2485 err.subdiagnostic(errors::ReplaceCommaWithSemicolon {
2486 comma_span,
2487 descr: "a vector",
2488 });
2489 return true;
2490 }
2491
2492 if self.type_is_copy_modulo_regions(self.param_env, first_ty)
2496 && (expr.is_size_lit() || expr_is_const_usize)
2497 {
2498 err.subdiagnostic(errors::ReplaceCommaWithSemicolon {
2499 comma_span,
2500 descr: "an array",
2501 });
2502 return true;
2503 }
2504 }
2505 false
2506 }
2507
2508 pub(crate) fn suggest_compatible_variants(
2511 &self,
2512 err: &mut Diag<'_>,
2513 expr: &hir::Expr<'_>,
2514 expected: Ty<'tcx>,
2515 expr_ty: Ty<'tcx>,
2516 ) -> bool {
2517 if expr.span.in_external_macro(self.tcx.sess.source_map()) {
2518 return false;
2519 }
2520 if let ty::Adt(expected_adt, args) = expected.kind() {
2521 if let hir::ExprKind::Field(base, ident) = expr.kind {
2522 let base_ty = self.typeck_results.borrow().expr_ty(base);
2523 if self.can_eq(self.param_env, base_ty, expected)
2524 && let Some(base_span) = base.span.find_ancestor_inside(expr.span)
2525 {
2526 err.span_suggestion_verbose(
2527 expr.span.with_lo(base_span.hi()),
2528 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("consider removing the tuple struct field `{0}`",
ident))
})format!("consider removing the tuple struct field `{ident}`"),
2529 "",
2530 Applicability::MaybeIncorrect,
2531 );
2532 return true;
2533 }
2534 }
2535
2536 if expr_ty.is_unit() {
2540 let mut id = expr.hir_id;
2541 let mut parent;
2542
2543 loop {
2545 parent = self.tcx.parent_hir_id(id);
2546 let parent_span = self.tcx.hir_span(parent);
2547 if parent_span.find_ancestor_inside(expr.span).is_some() {
2548 id = parent;
2551 continue;
2552 }
2553 break;
2554 }
2555
2556 if let hir::Node::Block(&hir::Block { span: block_span, expr: Some(e), .. }) =
2557 self.tcx.hir_node(parent)
2558 {
2559 if e.hir_id == id {
2560 if let Some(span) = expr.span.find_ancestor_inside(block_span) {
2561 let return_suggestions = if self
2562 .tcx
2563 .is_diagnostic_item(sym::Result, expected_adt.did())
2564 {
2565 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
["Ok(())"]))vec!["Ok(())"]
2566 } else if self.tcx.is_diagnostic_item(sym::Option, expected_adt.did()) {
2567 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
["None", "Some(())"]))vec!["None", "Some(())"]
2568 } else {
2569 return false;
2570 };
2571 if let Some(indent) =
2572 self.tcx.sess.source_map().indentation_before(span.shrink_to_lo())
2573 {
2574 let semicolon =
2576 match self.tcx.sess.source_map().span_to_snippet(span) {
2577 Ok(s) if s.ends_with('}') => "",
2578 _ => ";",
2579 };
2580 err.span_suggestions(
2581 span.shrink_to_hi(),
2582 "try adding an expression at the end of the block",
2583 return_suggestions
2584 .into_iter()
2585 .map(|r| ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}\n{1}{2}", semicolon, indent,
r))
})format!("{semicolon}\n{indent}{r}")),
2586 Applicability::MaybeIncorrect,
2587 );
2588 }
2589 return true;
2590 }
2591 }
2592 }
2593 }
2594
2595 let compatible_variants: Vec<(String, _, _, Option<String>)> = expected_adt
2596 .variants()
2597 .iter()
2598 .filter(|variant| {
2599 variant.fields.len() == 1
2600 })
2601 .filter_map(|variant| {
2602 let sole_field = &variant.single_field();
2603
2604 if let (ty::Adt(exp_adt, _), ty::Adt(act_adt, _)) = (expected.kind(), expr_ty.kind())
2608 && exp_adt.did() == act_adt.did()
2609 && sole_field.ty(self.tcx, args).is_ty_var() {
2610 return None;
2611 }
2612
2613 let field_is_local = sole_field.did.is_local();
2614 let field_is_accessible =
2615 sole_field.vis.is_accessible_from(expr.hir_id.owner.def_id, self.tcx)
2616 && #[allow(non_exhaustive_omitted_patterns)] match self.tcx.eval_stability(sole_field.did,
None, expr.span, None) {
EvalResult::Allow | EvalResult::Unmarked => true,
_ => false,
}matches!(self.tcx.eval_stability(sole_field.did, None, expr.span, None), EvalResult::Allow | EvalResult::Unmarked);
2618
2619 if !field_is_local && !field_is_accessible {
2620 return None;
2621 }
2622
2623 let note_about_variant_field_privacy = (field_is_local && !field_is_accessible)
2624 .then(|| " (its field is private, but it's local to this crate and its privacy can be changed)".to_string());
2625
2626 let sole_field_ty = sole_field.ty(self.tcx, args);
2627 if self.may_coerce(expr_ty, sole_field_ty) {
2628 let variant_path =
2629 { let _guard = NoTrimmedGuard::new(); self.tcx.def_path_str(variant.def_id) }with_no_trimmed_paths!(self.tcx.def_path_str(variant.def_id));
2630 if let Some(path) = variant_path.strip_prefix("std::prelude::")
2632 && let Some((_, path)) = path.split_once("::")
2633 {
2634 return Some((path.to_string(), variant.ctor_kind(), sole_field.name, note_about_variant_field_privacy));
2635 }
2636 Some((variant_path, variant.ctor_kind(), sole_field.name, note_about_variant_field_privacy))
2637 } else {
2638 None
2639 }
2640 })
2641 .collect();
2642
2643 let suggestions_for = |variant: &_, ctor_kind, field_name| {
2644 let prefix = match self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr) {
2645 Some(ident) => ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}: ", ident))
})format!("{ident}: "),
2646 None => String::new(),
2647 };
2648
2649 let (open, close) = match ctor_kind {
2650 Some(CtorKind::Fn) => ("(".to_owned(), ")"),
2651 None => (::alloc::__export::must_use({
::alloc::fmt::format(format_args!(" {{ {0}: ", field_name))
})format!(" {{ {field_name}: "), " }"),
2652
2653 Some(CtorKind::Const) => {
::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
format_args!("unit variants don\'t have fields")));
}unreachable!("unit variants don't have fields"),
2654 };
2655
2656 let mut expr = expr;
2660 while let hir::ExprKind::Block(block, _) = &expr.kind
2661 && let Some(expr_) = &block.expr
2662 {
2663 expr = expr_
2664 }
2665
2666 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[(expr.span.shrink_to_lo(),
::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}{1}{2}", prefix,
variant, open))
})), (expr.span.shrink_to_hi(), close.to_owned())]))vec![
2667 (expr.span.shrink_to_lo(), format!("{prefix}{variant}{open}")),
2668 (expr.span.shrink_to_hi(), close.to_owned()),
2669 ]
2670 };
2671
2672 match &compatible_variants[..] {
2673 [] => { }
2674 [(variant, ctor_kind, field_name, note)] => {
2675 err.multipart_suggestion(
2677 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("try wrapping the expression in `{1}`{0}",
note.as_deref().unwrap_or(""), variant))
})format!(
2678 "try wrapping the expression in `{variant}`{note}",
2679 note = note.as_deref().unwrap_or("")
2680 ),
2681 suggestions_for(&**variant, *ctor_kind, *field_name),
2682 Applicability::MaybeIncorrect,
2683 );
2684 return true;
2685 }
2686 _ => {
2687 err.multipart_suggestions(
2689 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("try wrapping the expression in a variant of `{0}`",
self.tcx.def_path_str(expected_adt.did())))
})format!(
2690 "try wrapping the expression in a variant of `{}`",
2691 self.tcx.def_path_str(expected_adt.did())
2692 ),
2693 compatible_variants.into_iter().map(
2694 |(variant, ctor_kind, field_name, _)| {
2695 suggestions_for(&variant, ctor_kind, field_name)
2696 },
2697 ),
2698 Applicability::MaybeIncorrect,
2699 );
2700 return true;
2701 }
2702 }
2703 }
2704
2705 false
2706 }
2707
2708 pub(crate) fn suggest_non_zero_new_unwrap(
2709 &self,
2710 err: &mut Diag<'_>,
2711 expr: &hir::Expr<'_>,
2712 expected: Ty<'tcx>,
2713 expr_ty: Ty<'tcx>,
2714 ) -> bool {
2715 let tcx = self.tcx;
2716 let (adt, args, unwrap) = match expected.kind() {
2717 ty::Adt(adt, args) if tcx.is_diagnostic_item(sym::Option, adt.did()) => {
2719 let nonzero_type = args.type_at(0); let ty::Adt(adt, args) = nonzero_type.kind() else {
2721 return false;
2722 };
2723 (adt, args, "")
2724 }
2725 ty::Adt(adt, args) => (adt, args, ".unwrap()"),
2727 _ => return false,
2728 };
2729
2730 if !self.tcx.is_diagnostic_item(sym::NonZero, adt.did()) {
2731 return false;
2732 }
2733
2734 let int_type = args.type_at(0);
2735 if !self.may_coerce(expr_ty, int_type) {
2736 return false;
2737 }
2738
2739 err.multipart_suggestion(
2740 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("consider calling `{0}::new`",
sym::NonZero))
})format!("consider calling `{}::new`", sym::NonZero),
2741 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[(expr.span.shrink_to_lo(),
::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}::new(",
sym::NonZero))
})),
(expr.span.shrink_to_hi(),
::alloc::__export::must_use({
::alloc::fmt::format(format_args!("){0}", unwrap))
}))]))vec![
2742 (expr.span.shrink_to_lo(), format!("{}::new(", sym::NonZero)),
2743 (expr.span.shrink_to_hi(), format!("){unwrap}")),
2744 ],
2745 Applicability::MaybeIncorrect,
2746 );
2747
2748 true
2749 }
2750
2751 fn can_use_as_ref(&self, expr: &hir::Expr<'_>) -> Option<(Vec<(Span, String)>, &'static str)> {
2768 let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = expr.kind else {
2769 return None;
2770 };
2771
2772 let hir::def::Res::Local(local_id) = path.res else {
2773 return None;
2774 };
2775
2776 let Node::Param(hir::Param { hir_id: param_hir_id, .. }) =
2777 self.tcx.parent_hir_node(local_id)
2778 else {
2779 return None;
2780 };
2781
2782 let Node::Expr(hir::Expr {
2783 hir_id: expr_hir_id,
2784 kind: hir::ExprKind::Closure(hir::Closure { fn_decl: closure_fn_decl, .. }),
2785 ..
2786 }) = self.tcx.parent_hir_node(*param_hir_id)
2787 else {
2788 return None;
2789 };
2790
2791 let hir = self.tcx.parent_hir_node(*expr_hir_id);
2792 let closure_params_len = closure_fn_decl.inputs.len();
2793 let (
2794 Node::Expr(hir::Expr {
2795 kind: hir::ExprKind::MethodCall(method_path, receiver, ..),
2796 ..
2797 }),
2798 1,
2799 ) = (hir, closure_params_len)
2800 else {
2801 return None;
2802 };
2803
2804 let self_ty = self.typeck_results.borrow().expr_ty(receiver);
2805 let name = method_path.ident.name;
2806 let is_as_ref_able = match self_ty.peel_refs().kind() {
2807 ty::Adt(def, _) => {
2808 (self.tcx.is_diagnostic_item(sym::Option, def.did())
2809 || self.tcx.is_diagnostic_item(sym::Result, def.did()))
2810 && (name == sym::map || name == sym::and_then)
2811 }
2812 _ => false,
2813 };
2814 if is_as_ref_able {
2815 Some((
2816 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[(method_path.ident.span.shrink_to_lo(), "as_ref().".to_string())]))vec![(method_path.ident.span.shrink_to_lo(), "as_ref().".to_string())],
2817 "consider using `as_ref` instead",
2818 ))
2819 } else {
2820 None
2821 }
2822 }
2823
2824 pub(crate) fn suggest_deref_or_ref(
2841 &self,
2842 expr: &hir::Expr<'tcx>,
2843 checked_ty: Ty<'tcx>,
2844 expected: Ty<'tcx>,
2845 ) -> Option<(
2846 Vec<(Span, String)>,
2847 String,
2848 Applicability,
2849 bool, bool, )> {
2852 let sess = self.sess();
2853 let sp = expr.range_span().unwrap_or(expr.span);
2854 let sm = sess.source_map();
2855
2856 if sp.in_external_macro(sm) {
2858 return None;
2859 }
2860
2861 let replace_prefix = |s: &str, old: &str, new: &str| {
2862 s.strip_prefix(old).map(|stripped| new.to_string() + stripped)
2863 };
2864
2865 let expr = expr.peel_drop_temps();
2867
2868 match (&expr.kind, expected.kind(), checked_ty.kind()) {
2869 (_, &ty::Ref(_, exp, _), &ty::Ref(_, check, _)) => match (exp.kind(), check.kind()) {
2870 (&ty::Str, &ty::Array(arr, _) | &ty::Slice(arr)) if arr == self.tcx.types.u8 => {
2871 if let hir::ExprKind::Lit(_) = expr.kind
2872 && let Ok(src) = sm.span_to_snippet(sp)
2873 && replace_prefix(&src, "b\"", "\"").is_some()
2874 {
2875 let pos = sp.lo() + BytePos(1);
2876 return Some((
2877 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[(sp.with_hi(pos), String::new())]))vec![(sp.with_hi(pos), String::new())],
2878 "consider removing the leading `b`".to_string(),
2879 Applicability::MachineApplicable,
2880 true,
2881 false,
2882 ));
2883 }
2884 }
2885 (&ty::Array(arr, _) | &ty::Slice(arr), &ty::Str) if arr == self.tcx.types.u8 => {
2886 if let hir::ExprKind::Lit(_) = expr.kind
2887 && let Ok(src) = sm.span_to_snippet(sp)
2888 && replace_prefix(&src, "\"", "b\"").is_some()
2889 {
2890 return Some((
2891 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[(sp.shrink_to_lo(), "b".to_string())]))vec![(sp.shrink_to_lo(), "b".to_string())],
2892 "consider adding a leading `b`".to_string(),
2893 Applicability::MachineApplicable,
2894 true,
2895 false,
2896 ));
2897 }
2898 }
2899 _ => {}
2900 },
2901 (_, &ty::Ref(_, _, mutability), _) => {
2902 let ref_ty = match mutability {
2911 hir::Mutability::Mut => {
2912 Ty::new_mut_ref(self.tcx, self.tcx.lifetimes.re_static, checked_ty)
2913 }
2914 hir::Mutability::Not => {
2915 Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_static, checked_ty)
2916 }
2917 };
2918 if self.may_coerce(ref_ty, expected) {
2919 let mut sugg_sp = sp;
2920 if let hir::ExprKind::MethodCall(segment, receiver, args, _) = expr.kind {
2921 let clone_trait =
2922 self.tcx.require_lang_item(LangItem::Clone, segment.ident.span);
2923 if args.is_empty()
2924 && self
2925 .typeck_results
2926 .borrow()
2927 .type_dependent_def_id(expr.hir_id)
2928 .is_some_and(|did| {
2929 let ai = self.tcx.associated_item(did);
2930 ai.trait_container(self.tcx) == Some(clone_trait)
2931 })
2932 && segment.ident.name == sym::clone
2933 {
2934 sugg_sp = receiver.span;
2937 }
2938 }
2939
2940 if let hir::ExprKind::Unary(hir::UnOp::Deref, inner) = expr.kind
2941 && let Some(1) = self.deref_steps_for_suggestion(expected, checked_ty)
2942 && self.typeck_results.borrow().expr_ty(inner).is_ref()
2943 {
2944 sugg_sp = sugg_sp.with_hi(inner.span.lo());
2947 return Some((
2948 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[(sugg_sp, String::new())]))vec![(sugg_sp, String::new())],
2949 "consider removing deref here".to_string(),
2950 Applicability::MachineApplicable,
2951 true,
2952 false,
2953 ));
2954 }
2955
2956 if let hir::ExprKind::If(_c, then, els) = expr.kind {
2962 let ExprKind::Block(then, _) = then.kind else { return None };
2965 let Some(then) = then.expr else { return None };
2966 let (mut suggs, help, app, verbose, mutref) =
2967 self.suggest_deref_or_ref(then, checked_ty, expected)?;
2968
2969 let els_expr = match els?.kind {
2971 ExprKind::Block(block, _) => block.expr?,
2972 _ => els?,
2973 };
2974 let (else_suggs, ..) =
2975 self.suggest_deref_or_ref(els_expr, checked_ty, expected)?;
2976 suggs.extend(else_suggs);
2977
2978 return Some((suggs, help, app, verbose, mutref));
2979 }
2980
2981 if let Some((sugg, msg)) = self.can_use_as_ref(expr) {
2982 return Some((
2983 sugg,
2984 msg.to_string(),
2985 Applicability::MachineApplicable,
2986 true,
2987 false,
2988 ));
2989 }
2990
2991 let prefix = match self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr) {
2992 Some(ident) => ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}: ", ident))
})format!("{ident}: "),
2993 None => String::new(),
2994 };
2995
2996 if let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Assign(..), .. }) =
2997 self.tcx.parent_hir_node(expr.hir_id)
2998 {
2999 if mutability.is_mut() {
3000 return None;
3002 }
3003 }
3004
3005 let make_sugg = |expr: &Expr<'_>, span: Span, sugg: &str| {
3006 if expr_needs_parens(expr) {
3007 (
3008 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[(span.shrink_to_lo(),
::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}{1}(", prefix, sugg))
})), (span.shrink_to_hi(), ")".to_string())]))vec![
3009 (span.shrink_to_lo(), format!("{prefix}{sugg}(")),
3010 (span.shrink_to_hi(), ")".to_string()),
3011 ],
3012 false,
3013 )
3014 } else {
3015 (::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[(span.shrink_to_lo(),
::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}{1}", prefix, sugg))
}))]))vec![(span.shrink_to_lo(), format!("{prefix}{sugg}"))], true)
3016 }
3017 };
3018
3019 if let hir::Node::Expr(hir::Expr {
3021 kind: hir::ExprKind::Binary(_, lhs, ..),
3022 ..
3023 }) = self.tcx.parent_hir_node(expr.hir_id)
3024 && let &ty::Ref(..) = self.check_expr(lhs).kind()
3025 {
3026 let (sugg, verbose) = make_sugg(lhs, lhs.span, "*");
3027
3028 return Some((
3029 sugg,
3030 "consider dereferencing the borrow".to_string(),
3031 Applicability::MachineApplicable,
3032 verbose,
3033 false,
3034 ));
3035 }
3036
3037 let sugg = mutability.ref_prefix_str();
3038 let (sugg, verbose) = make_sugg(expr, sp, sugg);
3039 return Some((
3040 sugg,
3041 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("consider {0}borrowing here",
mutability.mutably_str()))
})format!("consider {}borrowing here", mutability.mutably_str()),
3042 Applicability::MachineApplicable,
3043 verbose,
3044 false,
3045 ));
3046 }
3047 }
3048 (hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, expr), _, &ty::Ref(_, checked, _))
3049 if self.can_eq(self.param_env, checked, expected) =>
3050 {
3051 let make_sugg = |start: Span, end: BytePos| {
3052 let sp = sm
3055 .span_extend_while(start.shrink_to_lo(), |c| c == '(' || c.is_whitespace())
3056 .map_or(start, |s| s.shrink_to_hi());
3057 Some((
3058 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[(sp.with_hi(end), String::new())]))vec![(sp.with_hi(end), String::new())],
3059 "consider removing the borrow".to_string(),
3060 Applicability::MachineApplicable,
3061 true,
3062 true,
3063 ))
3064 };
3065
3066 if sm.is_imported(expr.span) {
3069 if let Some(call_span) =
3075 iter::successors(Some(expr.span), |s| s.parent_callsite())
3076 .find(|&s| sp.contains(s))
3077 && sm.is_span_accessible(call_span)
3078 {
3079 return make_sugg(sp, call_span.lo());
3080 }
3081 return None;
3082 }
3083 if sp.contains(expr.span) && sm.is_span_accessible(expr.span) {
3084 return make_sugg(sp, expr.span.lo());
3085 }
3086 }
3087 (_, &ty::RawPtr(ty_b, mutbl_b), &ty::Ref(_, ty_a, mutbl_a)) => {
3088 if let Some(steps) = self.deref_steps_for_suggestion(ty_a, ty_b)
3089 && steps > 0
3091 && let Ok(src) = sm.span_to_snippet(sp)
3093 {
3094 let derefs = "*".repeat(steps);
3095 let old_prefix = mutbl_a.ref_prefix_str();
3096 let new_prefix = mutbl_b.ref_prefix_str().to_owned() + &derefs;
3097
3098 let suggestion = replace_prefix(&src, old_prefix, &new_prefix).map(|_| {
3099 let lo = sp.lo()
3101 + BytePos(min(old_prefix.len(), mutbl_b.ref_prefix_str().len()) as _);
3102 let hi = sp.lo() + BytePos(old_prefix.len() as _);
3104 let sp = sp.with_lo(lo).with_hi(hi);
3105
3106 (
3107 sp,
3108 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}{1}",
if mutbl_a != mutbl_b { mutbl_b.prefix_str() } else { "" },
derefs))
})format!(
3109 "{}{derefs}",
3110 if mutbl_a != mutbl_b { mutbl_b.prefix_str() } else { "" }
3111 ),
3112 if mutbl_b <= mutbl_a {
3113 Applicability::MachineApplicable
3114 } else {
3115 Applicability::MaybeIncorrect
3116 },
3117 )
3118 });
3119
3120 if let Some((span, src, applicability)) = suggestion {
3121 return Some((
3122 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[(span, src)]))vec![(span, src)],
3123 "consider dereferencing".to_string(),
3124 applicability,
3125 true,
3126 false,
3127 ));
3128 }
3129 }
3130 }
3131 _ if sp == expr.span => {
3132 if let Some(mut steps) = self.deref_steps_for_suggestion(checked_ty, expected) {
3133 let mut expr = expr.peel_blocks();
3134 let mut prefix_span = expr.span.shrink_to_lo();
3135 let mut remove = String::new();
3136
3137 while steps > 0 {
3139 if let hir::ExprKind::AddrOf(_, mutbl, inner) = expr.kind {
3140 prefix_span = prefix_span.with_hi(inner.span.lo());
3142 expr = inner;
3143 remove.push_str(mutbl.ref_prefix_str());
3144 steps -= 1;
3145 } else {
3146 break;
3147 }
3148 }
3149 if steps == 0 && !remove.trim().is_empty() {
3151 return Some((
3152 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[(prefix_span, String::new())]))vec![(prefix_span, String::new())],
3153 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("consider removing the `{0}`",
remove.trim()))
})format!("consider removing the `{}`", remove.trim()),
3154 if remove.trim() == "&&" && expected == self.tcx.types.bool {
3158 Applicability::MaybeIncorrect
3159 } else {
3160 Applicability::MachineApplicable
3161 },
3162 true,
3163 false,
3164 ));
3165 }
3166
3167 if self.type_is_copy_modulo_regions(self.param_env, expected)
3170 || (checked_ty.is_box() && steps == 1)
3173 || #[allow(non_exhaustive_omitted_patterns)] match self.tcx.parent_hir_node(expr.hir_id)
{
hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Binary(op, ..), .. }) if
!op.node.is_by_value() => true,
_ => false,
}matches!(
3175 self.tcx.parent_hir_node(expr.hir_id),
3176 hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Binary(op, ..), .. })
3177 if !op.node.is_by_value()
3178 )
3179 {
3180 let deref_kind = if checked_ty.is_box() {
3181 if let ExprKind::Call(box_new, [_]) = expr.kind
3183 && let ExprKind::Path(qpath) = &box_new.kind
3184 && let Res::Def(DefKind::AssocFn, fn_id) =
3185 self.typeck_results.borrow().qpath_res(qpath, box_new.hir_id)
3186 && self.tcx.is_diagnostic_item(sym::box_new, fn_id)
3187 {
3188 let l_paren = self.tcx.sess.source_map().next_point(box_new.span);
3189 let r_paren = self.tcx.sess.source_map().end_point(expr.span);
3190 return Some((
3191 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[(box_new.span.to(l_paren), String::new()),
(r_paren, String::new())]))vec![
3192 (box_new.span.to(l_paren), String::new()),
3193 (r_paren, String::new()),
3194 ],
3195 "consider removing the Box".to_string(),
3196 Applicability::MachineApplicable,
3197 false,
3198 false,
3199 ));
3200 }
3201 "unboxing the value"
3202 } else if checked_ty.is_ref() {
3203 "dereferencing the borrow"
3204 } else {
3205 "dereferencing the type"
3206 };
3207
3208 let message = if remove.is_empty() {
3211 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("consider {0}", deref_kind))
})format!("consider {deref_kind}")
3212 } else {
3213 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("consider removing the `{0}` and {1} instead",
remove.trim(), deref_kind))
})format!(
3214 "consider removing the `{}` and {} instead",
3215 remove.trim(),
3216 deref_kind
3217 )
3218 };
3219
3220 let prefix =
3221 match self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr) {
3222 Some(ident) => ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}: ", ident))
})format!("{ident}: "),
3223 None => String::new(),
3224 };
3225
3226 let (span, suggestion) = if self.is_else_if_block(expr) {
3227 return None;
3229 } else if let Some(expr) = self.maybe_get_block_expr(expr) {
3230 (expr.span.shrink_to_lo(), "*".to_string())
3232 } else {
3233 (prefix_span, ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}{1}", prefix,
"*".repeat(steps)))
})format!("{}{}", prefix, "*".repeat(steps)))
3234 };
3235 if suggestion.trim().is_empty() {
3236 return None;
3237 }
3238
3239 if expr_needs_parens(expr) {
3240 return Some((
3241 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[(span,
::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}(", suggestion))
})), (expr.span.shrink_to_hi(), ")".to_string())]))vec![
3242 (span, format!("{suggestion}(")),
3243 (expr.span.shrink_to_hi(), ")".to_string()),
3244 ],
3245 message,
3246 Applicability::MachineApplicable,
3247 true,
3248 false,
3249 ));
3250 }
3251
3252 return Some((
3253 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[(span, suggestion)]))vec![(span, suggestion)],
3254 message,
3255 Applicability::MachineApplicable,
3256 true,
3257 false,
3258 ));
3259 }
3260 }
3261 }
3262 _ => {}
3263 }
3264 None
3265 }
3266
3267 fn is_else_if_block(&self, expr: &hir::Expr<'_>) -> bool {
3269 if let hir::ExprKind::If(..) = expr.kind
3270 && let Node::Expr(hir::Expr { kind: hir::ExprKind::If(_, _, Some(else_expr)), .. }) =
3271 self.tcx.parent_hir_node(expr.hir_id)
3272 {
3273 return else_expr.hir_id == expr.hir_id;
3274 }
3275 false
3276 }
3277
3278 pub(crate) fn suggest_cast(
3279 &self,
3280 err: &mut Diag<'_>,
3281 expr: &hir::Expr<'_>,
3282 checked_ty: Ty<'tcx>,
3283 expected_ty: Ty<'tcx>,
3284 expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
3285 ) -> bool {
3286 if self.tcx.sess.source_map().is_imported(expr.span) {
3287 return false;
3289 }
3290
3291 let span = if let hir::ExprKind::Lit(lit) = &expr.kind { lit.span } else { expr.span };
3292 let Ok(src) = self.tcx.sess.source_map().span_to_snippet(span) else {
3293 return false;
3294 };
3295
3296 let can_cast = false;
3305
3306 let mut sugg = ::alloc::vec::Vec::new()vec![];
3307
3308 if let hir::Node::ExprField(field) = self.tcx.parent_hir_node(expr.hir_id) {
3309 if field.is_shorthand {
3311 sugg.push((field.ident.span.shrink_to_lo(), ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}: ", field.ident))
})format!("{}: ", field.ident)));
3313 } else {
3314 return false;
3316 }
3317 };
3318
3319 if let hir::ExprKind::Call(path, args) = &expr.kind
3320 && let (hir::ExprKind::Path(hir::QPath::TypeRelative(base_ty, path_segment)), 1) =
3321 (&path.kind, args.len())
3322 && let (hir::TyKind::Path(hir::QPath::Resolved(None, base_ty_path)), sym::from) =
3324 (&base_ty.kind, path_segment.ident.name)
3325 {
3326 if let Some(ident) = &base_ty_path.segments.iter().map(|s| s.ident).next() {
3327 match ident.name {
3328 sym::i128
3329 | sym::i64
3330 | sym::i32
3331 | sym::i16
3332 | sym::i8
3333 | sym::u128
3334 | sym::u64
3335 | sym::u32
3336 | sym::u16
3337 | sym::u8
3338 | sym::isize
3339 | sym::usize
3340 if base_ty_path.segments.len() == 1 =>
3341 {
3342 return false;
3343 }
3344 _ => {}
3345 }
3346 }
3347 }
3348
3349 let msg = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("you can convert {0} `{1}` to {2} `{3}`",
checked_ty.kind().article(), checked_ty,
expected_ty.kind().article(), expected_ty))
})format!(
3350 "you can convert {} `{}` to {} `{}`",
3351 checked_ty.kind().article(),
3352 checked_ty,
3353 expected_ty.kind().article(),
3354 expected_ty,
3355 );
3356 let cast_msg = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("you can cast {0} `{1}` to {2} `{3}`",
checked_ty.kind().article(), checked_ty,
expected_ty.kind().article(), expected_ty))
})format!(
3357 "you can cast {} `{}` to {} `{}`",
3358 checked_ty.kind().article(),
3359 checked_ty,
3360 expected_ty.kind().article(),
3361 expected_ty,
3362 );
3363 let lit_msg = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("change the type of the numeric literal from `{0}` to `{1}`",
checked_ty, expected_ty))
})format!(
3364 "change the type of the numeric literal from `{checked_ty}` to `{expected_ty}`",
3365 );
3366
3367 let close_paren = if self.precedence(expr) < ExprPrecedence::Unambiguous {
3368 sugg.push((expr.span.shrink_to_lo(), "(".to_string()));
3369 ")"
3370 } else {
3371 ""
3372 };
3373
3374 let mut cast_suggestion = sugg.clone();
3375 cast_suggestion.push((expr.span.shrink_to_hi(), ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0} as {1}", close_paren,
expected_ty))
})format!("{close_paren} as {expected_ty}")));
3376 let mut into_suggestion = sugg.clone();
3377 into_suggestion.push((expr.span.shrink_to_hi(), ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}.into()", close_paren))
})format!("{close_paren}.into()")));
3378 let mut suffix_suggestion = sugg.clone();
3379 suffix_suggestion.push((
3380 if #[allow(non_exhaustive_omitted_patterns)] match (expected_ty.kind(),
checked_ty.kind()) {
(ty::Int(_) | ty::Uint(_), ty::Float(_)) => true,
_ => false,
}matches!(
3381 (expected_ty.kind(), checked_ty.kind()),
3382 (ty::Int(_) | ty::Uint(_), ty::Float(_))
3383 ) {
3384 let src = src.trim_end_matches(&checked_ty.to_string());
3386 let len = src.split('.').next().unwrap().len();
3387 span.with_lo(span.lo() + BytePos(len as u32))
3388 } else {
3389 let len = src.trim_end_matches(&checked_ty.to_string()).len();
3390 span.with_lo(span.lo() + BytePos(len as u32))
3391 },
3392 if self.precedence(expr) < ExprPrecedence::Unambiguous {
3393 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0})", expected_ty))
})format!("{expected_ty})")
3395 } else {
3396 expected_ty.to_string()
3397 },
3398 ));
3399 let literal_is_ty_suffixed = |expr: &hir::Expr<'_>| {
3400 if let hir::ExprKind::Lit(lit) = &expr.kind { lit.node.is_suffixed() } else { false }
3401 };
3402 let is_negative_int =
3403 |expr: &hir::Expr<'_>| #[allow(non_exhaustive_omitted_patterns)] match expr.kind {
hir::ExprKind::Unary(hir::UnOp::Neg, ..) => true,
_ => false,
}matches!(expr.kind, hir::ExprKind::Unary(hir::UnOp::Neg, ..));
3404 let is_uint = |ty: Ty<'_>| #[allow(non_exhaustive_omitted_patterns)] match ty.kind() {
ty::Uint(..) => true,
_ => false,
}matches!(ty.kind(), ty::Uint(..));
3405
3406 let in_const_context = self.tcx.hir_is_inside_const_context(expr.hir_id);
3407
3408 let suggest_fallible_into_or_lhs_from =
3409 |err: &mut Diag<'_>, exp_to_found_is_fallible: bool| {
3410 let lhs_expr_and_src = expected_ty_expr.and_then(|expr| {
3417 self.tcx
3418 .sess
3419 .source_map()
3420 .span_to_snippet(expr.span)
3421 .ok()
3422 .map(|src| (expr, src))
3423 });
3424 let (msg, suggestion) = if let (Some((lhs_expr, lhs_src)), false) =
3425 (lhs_expr_and_src, exp_to_found_is_fallible)
3426 {
3427 let msg = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("you can convert `{0}` from `{1}` to `{2}`, matching the type of `{3}`",
lhs_src, expected_ty, checked_ty, src))
})format!(
3428 "you can convert `{lhs_src}` from `{expected_ty}` to `{checked_ty}`, matching the type of `{src}`",
3429 );
3430 let suggestion = ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[(lhs_expr.span.shrink_to_lo(),
::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}::from(", checked_ty))
})), (lhs_expr.span.shrink_to_hi(), ")".to_string())]))vec![
3431 (lhs_expr.span.shrink_to_lo(), format!("{checked_ty}::from(")),
3432 (lhs_expr.span.shrink_to_hi(), ")".to_string()),
3433 ];
3434 (msg, suggestion)
3435 } else {
3436 let msg =
3437 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0} and panic if the converted value doesn\'t fit",
msg.clone()))
})format!("{} and panic if the converted value doesn't fit", msg.clone());
3438 let mut suggestion = sugg.clone();
3439 suggestion.push((
3440 expr.span.shrink_to_hi(),
3441 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}.try_into().unwrap()",
close_paren))
})format!("{close_paren}.try_into().unwrap()"),
3442 ));
3443 (msg, suggestion)
3444 };
3445 err.multipart_suggestion(msg, suggestion, Applicability::MachineApplicable);
3446 };
3447
3448 let suggest_to_change_suffix_or_into =
3449 |err: &mut Diag<'_>, found_to_exp_is_fallible: bool, exp_to_found_is_fallible: bool| {
3450 let exp_is_lhs = expected_ty_expr.is_some_and(|e| self.tcx.hir_is_lhs(e.hir_id));
3451
3452 if exp_is_lhs {
3453 return;
3454 }
3455
3456 let always_fallible = found_to_exp_is_fallible
3457 && (exp_to_found_is_fallible || expected_ty_expr.is_none());
3458 let msg = if literal_is_ty_suffixed(expr) {
3459 lit_msg.clone()
3460 } else if always_fallible && (is_negative_int(expr) && is_uint(expected_ty)) {
3461 let msg = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("`{0}` cannot fit into type `{1}`",
src, expected_ty))
})format!("`{src}` cannot fit into type `{expected_ty}`");
3465 err.note(msg);
3466 return;
3467 } else if in_const_context {
3468 return;
3470 } else if found_to_exp_is_fallible {
3471 return suggest_fallible_into_or_lhs_from(err, exp_to_found_is_fallible);
3472 } else {
3473 msg.clone()
3474 };
3475 let suggestion = if literal_is_ty_suffixed(expr) {
3476 suffix_suggestion.clone()
3477 } else {
3478 into_suggestion.clone()
3479 };
3480 err.multipart_suggestion(msg, suggestion, Applicability::MachineApplicable);
3481 };
3482
3483 match (expected_ty.kind(), checked_ty.kind()) {
3484 (ty::Int(exp), ty::Int(found)) => {
3485 let (f2e_is_fallible, e2f_is_fallible) = match (exp.bit_width(), found.bit_width())
3486 {
3487 (Some(exp), Some(found)) if exp < found => (true, false),
3488 (Some(exp), Some(found)) if exp > found => (false, true),
3489 (None, Some(8 | 16)) => (false, true),
3490 (Some(8 | 16), None) => (true, false),
3491 (None, _) | (_, None) => (true, true),
3492 _ => (false, false),
3493 };
3494 suggest_to_change_suffix_or_into(err, f2e_is_fallible, e2f_is_fallible);
3495 true
3496 }
3497 (ty::Uint(exp), ty::Uint(found)) => {
3498 let (f2e_is_fallible, e2f_is_fallible) = match (exp.bit_width(), found.bit_width())
3499 {
3500 (Some(exp), Some(found)) if exp < found => (true, false),
3501 (Some(exp), Some(found)) if exp > found => (false, true),
3502 (None, Some(8 | 16)) => (false, true),
3503 (Some(8 | 16), None) => (true, false),
3504 (None, _) | (_, None) => (true, true),
3505 _ => (false, false),
3506 };
3507 suggest_to_change_suffix_or_into(err, f2e_is_fallible, e2f_is_fallible);
3508 true
3509 }
3510 (&ty::Int(exp), &ty::Uint(found)) => {
3511 let (f2e_is_fallible, e2f_is_fallible) = match (exp.bit_width(), found.bit_width())
3512 {
3513 (Some(exp), Some(found)) if found < exp => (false, true),
3514 (None, Some(8)) => (false, true),
3515 _ => (true, true),
3516 };
3517 suggest_to_change_suffix_or_into(err, f2e_is_fallible, e2f_is_fallible);
3518 true
3519 }
3520 (&ty::Uint(exp), &ty::Int(found)) => {
3521 let (f2e_is_fallible, e2f_is_fallible) = match (exp.bit_width(), found.bit_width())
3522 {
3523 (Some(exp), Some(found)) if found > exp => (true, false),
3524 (Some(8), None) => (true, false),
3525 _ => (true, true),
3526 };
3527 suggest_to_change_suffix_or_into(err, f2e_is_fallible, e2f_is_fallible);
3528 true
3529 }
3530 (ty::Float(exp), ty::Float(found)) => {
3531 if found.bit_width() < exp.bit_width() {
3532 suggest_to_change_suffix_or_into(err, false, true);
3533 } else if literal_is_ty_suffixed(expr) {
3534 err.multipart_suggestion(
3535 lit_msg,
3536 suffix_suggestion,
3537 Applicability::MachineApplicable,
3538 );
3539 } else if can_cast {
3540 err.multipart_suggestion(
3542 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}, producing the closest possible value",
cast_msg))
})format!("{cast_msg}, producing the closest possible value"),
3543 cast_suggestion,
3544 Applicability::MaybeIncorrect, );
3546 }
3547 true
3548 }
3549 (&ty::Uint(_) | &ty::Int(_), &ty::Float(_)) => {
3550 if literal_is_ty_suffixed(expr) {
3551 err.multipart_suggestion(
3552 lit_msg,
3553 suffix_suggestion,
3554 Applicability::MachineApplicable,
3555 );
3556 } else if can_cast {
3557 err.multipart_suggestion(
3559 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}, rounding the float towards zero",
msg))
})format!("{msg}, rounding the float towards zero"),
3560 cast_suggestion,
3561 Applicability::MaybeIncorrect, );
3563 }
3564 true
3565 }
3566 (ty::Float(exp), ty::Uint(found)) => {
3567 if exp.bit_width() > found.bit_width().unwrap_or(256) {
3569 err.multipart_suggestion(
3570 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}, producing the floating point representation of the integer",
msg))
})format!(
3571 "{msg}, producing the floating point representation of the integer",
3572 ),
3573 into_suggestion,
3574 Applicability::MachineApplicable,
3575 );
3576 } else if literal_is_ty_suffixed(expr) {
3577 err.multipart_suggestion(
3578 lit_msg,
3579 suffix_suggestion,
3580 Applicability::MachineApplicable,
3581 );
3582 } else {
3583 err.multipart_suggestion(
3585 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}, producing the floating point representation of the integer, rounded if necessary",
cast_msg))
})format!(
3586 "{cast_msg}, producing the floating point representation of the integer, \
3587 rounded if necessary",
3588 ),
3589 cast_suggestion,
3590 Applicability::MaybeIncorrect, );
3592 }
3593 true
3594 }
3595 (ty::Float(exp), ty::Int(found)) => {
3596 if exp.bit_width() > found.bit_width().unwrap_or(256) {
3598 err.multipart_suggestion(
3599 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}, producing the floating point representation of the integer",
msg.clone()))
})format!(
3600 "{}, producing the floating point representation of the integer",
3601 msg.clone(),
3602 ),
3603 into_suggestion,
3604 Applicability::MachineApplicable,
3605 );
3606 } else if literal_is_ty_suffixed(expr) {
3607 err.multipart_suggestion(
3608 lit_msg,
3609 suffix_suggestion,
3610 Applicability::MachineApplicable,
3611 );
3612 } else {
3613 err.multipart_suggestion(
3615 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}, producing the floating point representation of the integer, rounded if necessary",
&msg))
})format!(
3616 "{}, producing the floating point representation of the integer, \
3617 rounded if necessary",
3618 &msg,
3619 ),
3620 cast_suggestion,
3621 Applicability::MaybeIncorrect, );
3623 }
3624 true
3625 }
3626 (
3627 &ty::Uint(ty::UintTy::U32 | ty::UintTy::U64 | ty::UintTy::U128)
3628 | &ty::Int(ty::IntTy::I32 | ty::IntTy::I64 | ty::IntTy::I128),
3629 &ty::Char,
3630 ) => {
3631 err.multipart_suggestion(
3632 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}, since a `char` always occupies 4 bytes",
cast_msg))
})format!("{cast_msg}, since a `char` always occupies 4 bytes"),
3633 cast_suggestion,
3634 Applicability::MachineApplicable,
3635 );
3636 true
3637 }
3638 _ => false,
3639 }
3640 }
3641
3642 pub(crate) fn suggest_method_call_on_range_literal(
3644 &self,
3645 err: &mut Diag<'_>,
3646 expr: &hir::Expr<'tcx>,
3647 checked_ty: Ty<'tcx>,
3648 expected_ty: Ty<'tcx>,
3649 ) {
3650 if !hir::is_range_literal(expr) {
3651 return;
3652 }
3653 let hir::ExprKind::Struct(&qpath, [start, end], _) = expr.kind else {
3654 return;
3655 };
3656 if !self.tcx.qpath_is_lang_item(qpath, LangItem::Range) {
3657 return;
3658 }
3659 if let hir::Node::ExprField(_) = self.tcx.parent_hir_node(expr.hir_id) {
3660 return;
3662 }
3663 let mut expr = end.expr;
3664 let mut expectation = Some(expected_ty);
3665 while let hir::ExprKind::MethodCall(_, rcvr, ..) = expr.kind {
3666 expr = rcvr;
3669 expectation = None;
3672 }
3673 let hir::ExprKind::Call(method_name, _) = expr.kind else {
3674 return;
3675 };
3676 let ty::Adt(adt, _) = checked_ty.kind() else {
3677 return;
3678 };
3679 if self.tcx.lang_items().range_struct() != Some(adt.did()) {
3680 return;
3681 }
3682 if let ty::Adt(adt, _) = expected_ty.kind()
3683 && self.tcx.is_lang_item(adt.did(), LangItem::Range)
3684 {
3685 return;
3686 }
3687 let hir::ExprKind::Path(hir::QPath::Resolved(None, p)) = method_name.kind else {
3689 return;
3690 };
3691 let [hir::PathSegment { ident, .. }] = p.segments else {
3692 return;
3693 };
3694 let self_ty = self.typeck_results.borrow().expr_ty(start.expr);
3695 let Ok(_pick) = self.lookup_probe_for_diagnostic(
3696 *ident,
3697 self_ty,
3698 expr,
3699 probe::ProbeScope::AllTraits,
3700 expectation,
3701 ) else {
3702 return;
3703 };
3704 let mut sugg = ".";
3705 let mut span = start.expr.span.between(end.expr.span);
3706 if span.lo() + BytePos(2) == span.hi() {
3707 span = span.with_lo(span.lo() + BytePos(1));
3710 sugg = "";
3711 }
3712 err.span_suggestion_verbose(
3713 span,
3714 "you likely meant to write a method call instead of a range",
3715 sugg,
3716 Applicability::MachineApplicable,
3717 );
3718 }
3719
3720 pub(crate) fn suggest_return_binding_for_missing_tail_expr(
3723 &self,
3724 err: &mut Diag<'_>,
3725 expr: &hir::Expr<'_>,
3726 checked_ty: Ty<'tcx>,
3727 expected_ty: Ty<'tcx>,
3728 ) {
3729 if !checked_ty.is_unit() {
3730 return;
3731 }
3732 let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind else {
3733 return;
3734 };
3735 let hir::def::Res::Local(hir_id) = path.res else {
3736 return;
3737 };
3738 let hir::Node::Pat(pat) = self.tcx.hir_node(hir_id) else {
3739 return;
3740 };
3741 let hir::Node::LetStmt(hir::LetStmt { ty: None, init: Some(init), .. }) =
3742 self.tcx.parent_hir_node(pat.hir_id)
3743 else {
3744 return;
3745 };
3746 let hir::ExprKind::Block(block, None) = init.kind else {
3747 return;
3748 };
3749 if block.expr.is_some() {
3750 return;
3751 }
3752 let [.., stmt] = block.stmts else {
3753 err.span_label(block.span, "this empty block is missing a tail expression");
3754 return;
3755 };
3756 let hir::StmtKind::Semi(tail_expr) = stmt.kind else {
3757 return;
3758 };
3759 let Some(ty) = self.node_ty_opt(tail_expr.hir_id) else {
3760 return;
3761 };
3762 if self.can_eq(self.param_env, expected_ty, ty)
3763 && stmt.span.hi() != tail_expr.span.hi()
3768 {
3769 err.span_suggestion_short(
3770 stmt.span.with_lo(tail_expr.span.hi()),
3771 "remove this semicolon",
3772 "",
3773 Applicability::MachineApplicable,
3774 );
3775 } else {
3776 err.span_label(block.span, "this block is missing a tail expression");
3777 }
3778 }
3779
3780 pub(crate) fn suggest_swapping_lhs_and_rhs(
3781 &self,
3782 err: &mut Diag<'_>,
3783 rhs_ty: Ty<'tcx>,
3784 lhs_ty: Ty<'tcx>,
3785 rhs_expr: &'tcx hir::Expr<'tcx>,
3786 lhs_expr: &'tcx hir::Expr<'tcx>,
3787 ) {
3788 if let Some(partial_eq_def_id) = self.infcx.tcx.lang_items().eq_trait()
3789 && self
3790 .infcx
3791 .type_implements_trait(partial_eq_def_id, [rhs_ty, lhs_ty], self.param_env)
3792 .must_apply_modulo_regions()
3793 {
3794 let sm = self.tcx.sess.source_map();
3795 if !rhs_expr.span.in_external_macro(sm)
3798 && !lhs_expr.span.in_external_macro(sm)
3799 && let Ok(rhs_snippet) = sm.span_to_snippet(rhs_expr.span)
3800 && let Ok(lhs_snippet) = sm.span_to_snippet(lhs_expr.span)
3801 {
3802 err.note(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("`{0}` implements `PartialEq<{1}>`",
rhs_ty, lhs_ty))
})format!("`{rhs_ty}` implements `PartialEq<{lhs_ty}>`"));
3803 err.multipart_suggestion(
3804 "consider swapping the equality",
3805 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[(lhs_expr.span, rhs_snippet), (rhs_expr.span, lhs_snippet)]))vec![(lhs_expr.span, rhs_snippet), (rhs_expr.span, lhs_snippet)],
3806 Applicability::MaybeIncorrect,
3807 );
3808 }
3809 }
3810 }
3811}