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