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