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