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