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