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