1use core::ops::ControlFlow;
2
3use hir::def::CtorKind;
4use hir::intravisit::{Visitor, walk_expr, walk_stmt};
5use hir::{LetStmt, QPath};
6use rustc_data_structures::fx::FxIndexSet;
7use rustc_errors::{Applicability, Diag};
8use rustc_hir as hir;
9use rustc_hir::def::Res;
10use rustc_hir::{MatchSource, Node};
11use rustc_middle::traits::{MatchExpressionArmCause, ObligationCause, ObligationCauseCode};
12use rustc_middle::ty::error::TypeError;
13use rustc_middle::ty::print::with_no_trimmed_paths;
14use rustc_middle::ty::{
15 self as ty, GenericArgKind, IsSuggestable, Ty, TypeVisitableExt, Unnormalized,
16};
17use rustc_span::{Span, sym};
18use tracing::debug;
19
20use crate::diagnostics::{
21 ConsiderAddingAwait, FnConsiderCasting, FnConsiderCastingBoth, FnItemsAreDistinct, FnUniqTypes,
22 FunctionPointerSuggestion, SuggestAccessingField, SuggestRemoveSemiOrReturnBinding,
23 SuggestTuplePatternMany, SuggestTuplePatternOne, TypeErrorAdditionalDiags,
24};
25use crate::error_reporting::TypeErrCtxt;
26use crate::error_reporting::infer::hir::Path;
27
28#[derive(#[automatically_derived]
impl ::core::marker::Copy for StatementAsExpression { }Copy, #[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for StatementAsExpression { }
#[automatically_derived]
impl ::core::clone::Clone for StatementAsExpression {
#[inline]
fn clone(&self) -> Self { *self }
}Clone, #[automatically_derived]
impl ::core::fmt::Debug for StatementAsExpression {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::write_str(f,
match self {
StatementAsExpression::CorrectType => "CorrectType",
StatementAsExpression::NeedsBoxing => "NeedsBoxing",
})
}
}Debug, #[automatically_derived]
impl ::core::marker::StructuralPartialEq for StatementAsExpression { }
#[automatically_derived]
impl ::core::cmp::PartialEq for StatementAsExpression {
#[inline]
fn eq(&self, other: &Self) -> bool {
::core::intrinsics::discriminant_value(self) ==
::core::intrinsics::discriminant_value(other)
}
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for StatementAsExpression { }Eq, #[automatically_derived]
impl ::core::hash::Hash for StatementAsExpression {
#[inline]
fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) {
::core::hash::Hash::hash(&::core::intrinsics::discriminant_value(self),
state)
}
}Hash)]
29enum StatementAsExpression {
30 CorrectType,
31 NeedsBoxing,
32}
33
34#[derive(#[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for SuggestAsRefKind { }
#[automatically_derived]
impl ::core::clone::Clone for SuggestAsRefKind {
#[inline]
fn clone(&self) -> Self { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for SuggestAsRefKind { }Copy)]
35enum SuggestAsRefKind {
36 Option,
37 Result,
38}
39
40impl<'tcx> TypeErrCtxt<'_, 'tcx> {
41 pub(super) fn suggest_remove_semi_or_return_binding(
42 &self,
43 first_id: Option<hir::HirId>,
44 first_ty: Ty<'tcx>,
45 first_span: Span,
46 second_id: Option<hir::HirId>,
47 second_ty: Ty<'tcx>,
48 second_span: Span,
49 ) -> Option<SuggestRemoveSemiOrReturnBinding> {
50 let remove_semicolon = [
51 (first_id, self.deeply_resolve_ignoring_regions(second_ty)),
52 (second_id, self.deeply_resolve_ignoring_regions(first_ty)),
53 ]
54 .into_iter()
55 .find_map(|(id, ty)| {
56 let hir::Node::Block(blk) = self.tcx.hir_node(id?) else { return None };
57 self.could_remove_semicolon(blk, ty)
58 });
59 match remove_semicolon {
60 Some((sp, StatementAsExpression::NeedsBoxing)) => {
61 Some(SuggestRemoveSemiOrReturnBinding::RemoveAndBox {
62 first_lo: first_span.shrink_to_lo(),
63 first_hi: first_span.shrink_to_hi(),
64 second_lo: second_span.shrink_to_lo(),
65 second_hi: second_span.shrink_to_hi(),
66 sp,
67 })
68 }
69 Some((sp, StatementAsExpression::CorrectType)) => {
70 Some(SuggestRemoveSemiOrReturnBinding::Remove { sp })
71 }
72 None => {
73 let mut ret = None;
74 for (id, ty) in [(first_id, second_ty), (second_id, first_ty)] {
75 if let Some(id) = id
76 && let hir::Node::Block(blk) = self.tcx.hir_node(id)
77 && let Some(diag) = self.consider_returning_binding_diag(blk, ty)
78 {
79 ret = Some(diag);
80 break;
81 }
82 }
83 ret
84 }
85 }
86 }
87
88 pub(super) fn suggest_tuple_pattern(
89 &self,
90 cause: &ObligationCause<'tcx>,
91 exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
92 diag: &mut Diag<'_>,
93 ) {
94 if let ObligationCauseCode::Pattern { .. } = cause.code()
97 && let ty::Adt(expected_adt, args) = exp_found.expected.kind()
98 {
99 let compatible_variants: Vec<_> = expected_adt
100 .variants()
101 .iter()
102 .filter(|variant| {
103 variant.fields.len() == 1 && variant.ctor_kind() == Some(CtorKind::Fn)
104 })
105 .filter_map(|variant| {
106 let sole_field = &variant.single_field();
107 let sole_field_ty = sole_field.ty(self.tcx, args).skip_norm_wip();
108 if self.same_type_modulo_infer(sole_field_ty, exp_found.found) {
109 let variant_path =
110 { let _guard = NoTrimmedGuard::new(); self.tcx.def_path_str(variant.def_id) }with_no_trimmed_paths!(self.tcx.def_path_str(variant.def_id));
111 if let Some(path) = variant_path.strip_prefix("std::prelude::")
113 && let Some((_, path)) = path.split_once("::")
114 {
115 return Some(path.to_string());
116 }
117 Some(variant_path)
118 } else {
119 None
120 }
121 })
122 .collect();
123 match &compatible_variants[..] {
124 [] => {}
125 [variant] => {
126 let sugg = SuggestTuplePatternOne {
127 variant: variant.to_owned(),
128 span_low: cause.span.shrink_to_lo(),
129 span_high: cause.span.shrink_to_hi(),
130 };
131 diag.subdiagnostic(sugg);
132 }
133 _ => {
134 let sugg = SuggestTuplePatternMany {
136 path: self.tcx.def_path_str(expected_adt.did()),
137 cause_span: cause.span,
138 compatible_variants,
139 };
140 diag.subdiagnostic(sugg);
141 }
142 }
143 }
144 }
145
146 pub(super) fn suggest_await_on_expect_found(
166 &self,
167 cause: &ObligationCause<'tcx>,
168 exp_span: Span,
169 exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
170 diag: &mut Diag<'_>,
171 ) {
172 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event /rustc-dev/d080e7dff1b0fc54541545252818f8cccf995d05/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs:172",
"rustc_trait_selection::error_reporting::infer::suggest",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("/rustc-dev/d080e7dff1b0fc54541545252818f8cccf995d05/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs"),
::tracing_core::__macro_support::Option::Some(172u32),
::tracing_core::__macro_support::Option::Some("rustc_trait_selection::error_reporting::infer::suggest"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("suggest_await_on_expect_found: exp_span={0:?}, expected_ty={1:?}, found_ty={2:?}",
exp_span, exp_found.expected, exp_found.found) as
&dyn ::tracing::field::Value))])
});
} else { ; }
};debug!(
173 "suggest_await_on_expect_found: exp_span={:?}, expected_ty={:?}, found_ty={:?}",
174 exp_span, exp_found.expected, exp_found.found,
175 );
176
177 match self.tcx.coroutine_kind(cause.body_def_id) {
178 Some(hir::CoroutineKind::Desugared(
179 hir::CoroutineDesugaring::Async | hir::CoroutineDesugaring::AsyncGen,
180 _,
181 )) => (),
182 Some(
183 hir::CoroutineKind::Coroutine(_)
184 | hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Gen, _),
185 ) => return,
186 None => {
187 self.suggest_add_async_for_tail_return_expr(cause, exp_span, exp_found, diag);
188 return;
189 }
190 }
191
192 if let ObligationCauseCode::CompareImplItem { .. } = cause.code() {
193 return;
194 }
195
196 let subdiag = match (
197 self.tcx.get_impl_future_output_ty(exp_found.expected),
198 self.tcx.get_impl_future_output_ty(exp_found.found),
199 ) {
200 (Some(exp), Some(found)) if self.same_type_modulo_infer(exp, found) => match cause
201 .code()
202 {
203 ObligationCauseCode::IfExpression { expr_id, .. } => {
204 let hir::Node::Expr(hir::Expr {
205 kind: hir::ExprKind::If(_, then_expr, _), ..
206 }) = self.tcx.hir_node(*expr_id)
207 else {
208 return;
209 };
210 let then_span = self.find_block_span_from_hir_id(then_expr.hir_id);
211 Some(ConsiderAddingAwait::BothFuturesSugg {
212 first: then_span.shrink_to_hi(),
213 second: exp_span.shrink_to_hi(),
214 })
215 }
216 ObligationCauseCode::MatchExpressionArm(MatchExpressionArmCause {
217 prior_non_diverging_arms,
218 ..
219 }) => {
220 if let [.., arm_span] = &prior_non_diverging_arms[..] {
221 Some(ConsiderAddingAwait::BothFuturesSugg {
222 first: arm_span.shrink_to_hi(),
223 second: exp_span.shrink_to_hi(),
224 })
225 } else {
226 Some(ConsiderAddingAwait::BothFuturesHelp)
227 }
228 }
229 _ => Some(ConsiderAddingAwait::BothFuturesHelp),
230 },
231 (_, Some(ty)) if self.same_type_modulo_infer(exp_found.expected, ty) => {
232 diag.subdiagnostic(ConsiderAddingAwait::FutureSugg {
234 span: exp_span.shrink_to_hi(),
235 });
236 Some(ConsiderAddingAwait::FutureSuggNote { span: exp_span })
237 }
238 (Some(ty), _) if self.same_type_modulo_infer(ty, exp_found.found) => match cause.code()
239 {
240 ObligationCauseCode::Pattern { span: Some(then_span), origin_expr, .. } => {
241 origin_expr.is_some().then_some(ConsiderAddingAwait::FutureSugg {
242 span: then_span.shrink_to_hi(),
243 })
244 }
245 ObligationCauseCode::IfExpression { expr_id, .. } => {
246 let hir::Node::Expr(hir::Expr {
247 kind: hir::ExprKind::If(_, then_expr, _), ..
248 }) = self.tcx.hir_node(*expr_id)
249 else {
250 return;
251 };
252 let then_span = self.find_block_span_from_hir_id(then_expr.hir_id);
253 Some(ConsiderAddingAwait::FutureSugg { span: then_span.shrink_to_hi() })
254 }
255 ObligationCauseCode::MatchExpressionArm(MatchExpressionArmCause {
256 prior_non_diverging_arms,
257 ..
258 }) => Some({
259 ConsiderAddingAwait::FutureSuggMultiple {
260 spans: prior_non_diverging_arms
261 .iter()
262 .map(|arm| arm.shrink_to_hi())
263 .collect(),
264 }
265 }),
266 _ => None,
267 },
268 _ => None,
269 };
270 if let Some(subdiag) = subdiag {
271 diag.subdiagnostic(subdiag);
272 }
273 }
274
275 fn suggest_add_async_for_tail_return_expr(
276 &self,
277 cause: &ObligationCause<'tcx>,
278 exp_span: Span,
279 exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
280 diag: &mut Diag<'_>,
281 ) {
282 let (ObligationCauseCode::BlockTailExpression(return_hir_id, ..)
283 | ObligationCauseCode::ReturnValue(return_hir_id)) = cause.code()
284 else {
285 return;
286 };
287
288 let body_def_id = cause.body_def_id;
289 if !self.tcx.sess.at_least_rust_2018() || self.tcx.is_entrypoint(body_def_id.to_def_id()) {
290 return;
291 }
292
293 let node = self.tcx.hir_node_by_def_id(body_def_id);
294 let (item_span, vis_span) = match node {
295 Node::Item(item) if #[allow(non_exhaustive_omitted_patterns)] match item.kind {
hir::ItemKind::Fn { .. } => true,
_ => false,
}matches!(item.kind, hir::ItemKind::Fn { .. }) => {
296 (item.span, item.vis_span)
297 }
298 Node::ImplItem(item) if #[allow(non_exhaustive_omitted_patterns)] match item.kind {
hir::ImplItemKind::Fn(..) => true,
_ => false,
}matches!(item.kind, hir::ImplItemKind::Fn(..)) => {
299 let Some(vis_span) = item.vis_span() else { return };
300 (item.span, vis_span)
301 }
302 _ => return,
303 };
304 let Some(sig) = node.fn_sig() else {
305 return;
306 };
307 if sig.header.asyncness.is_async()
308 || sig.header.constness != hir::Constness::NotConst
309 || item_span.from_expansion()
310 {
311 return;
312 }
313
314 let (async_span, async_prefix) = if vis_span.is_empty() {
315 (item_span.shrink_to_lo(), "async ".to_string())
316 } else {
317 (vis_span.shrink_to_hi(), " async".to_string())
318 };
319 let body_hir_id = self.tcx.local_def_id_to_hir_id(body_def_id);
320 if self.tcx.hir_get_fn_id_for_return_block(*return_hir_id) == Some(body_hir_id)
321 && let Some(found) = self.tcx.get_impl_future_output_ty(exp_found.found)
322 && self.same_type_modulo_infer(exp_found.expected, found)
323 && exp_span.can_be_used_for_suggestions()
324 {
325 diag.subdiagnostic(ConsiderAddingAwait::MakeFunctionAsync {
326 async_span,
327 async_prefix,
328 await_span: exp_span.shrink_to_hi(),
329 });
330 }
331 }
332
333 pub(super) fn suggest_accessing_field_where_appropriate(
334 &self,
335 cause: &ObligationCause<'tcx>,
336 exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
337 diag: &mut Diag<'_>,
338 ) {
339 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event /rustc-dev/d080e7dff1b0fc54541545252818f8cccf995d05/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs:339",
"rustc_trait_selection::error_reporting::infer::suggest",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("/rustc-dev/d080e7dff1b0fc54541545252818f8cccf995d05/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs"),
::tracing_core::__macro_support::Option::Some(339u32),
::tracing_core::__macro_support::Option::Some("rustc_trait_selection::error_reporting::infer::suggest"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("suggest_accessing_field_where_appropriate(cause={0:?}, exp_found={1:?})",
cause, exp_found) as &dyn ::tracing::field::Value))])
});
} else { ; }
};debug!(
340 "suggest_accessing_field_where_appropriate(cause={:?}, exp_found={:?})",
341 cause, exp_found
342 );
343 if let ty::Adt(expected_def, expected_args) = exp_found.expected.kind() {
344 if expected_def.is_enum() {
345 return;
346 }
347
348 if let Some((name, ty)) = expected_def
349 .non_enum_variant()
350 .fields
351 .iter()
352 .filter(|field| field.vis.is_accessible_from(field.did, self.tcx))
353 .map(|field| (field.name, field.ty(self.tcx, expected_args).skip_norm_wip()))
354 .find(|(_, ty)| self.same_type_modulo_infer(*ty, exp_found.found))
355 && let ObligationCauseCode::Pattern { span: Some(span), .. } = *cause.code()
356 && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span)
357 {
358 let suggestion = if expected_def.is_struct() {
359 SuggestAccessingField::Safe { span, snippet, name, ty }
360 } else if expected_def.is_union() {
361 SuggestAccessingField::Unsafe { span, snippet, name, ty }
362 } else {
363 return;
364 };
365 diag.subdiagnostic(suggestion);
366 }
367 }
368 }
369
370 pub(super) fn suggest_turning_stmt_into_expr(
371 &self,
372 cause: &ObligationCause<'tcx>,
373 exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
374 diag: &mut Diag<'_>,
375 ) {
376 let ty::error::ExpectedFound { expected, found } = exp_found;
377 if !found.peel_refs().is_unit() {
378 return;
379 }
380
381 let ObligationCauseCode::BlockTailExpression(hir_id, MatchSource::Normal) = cause.code()
382 else {
383 return;
384 };
385
386 let node = self.tcx.hir_node(*hir_id);
387 let mut blocks = ::alloc::vec::Vec::new()vec![];
388 if let hir::Node::Block(block) = node
389 && let Some(expr) = block.expr
390 && let hir::ExprKind::Path(QPath::Resolved(_, Path { res, .. })) = expr.kind
391 && let Res::Local(local) = res
392 && let Node::LetStmt(LetStmt { init: Some(init), .. }) =
393 self.tcx.parent_hir_node(*local)
394 {
395 fn collect_blocks<'hir>(expr: &hir::Expr<'hir>, blocks: &mut Vec<&hir::Block<'hir>>) {
396 match expr.kind {
397 hir::ExprKind::If(_, blk1, Some(blk2)) => {
399 collect_blocks(blk1, blocks);
400 collect_blocks(blk2, blocks);
401 }
402 hir::ExprKind::Match(_, arms, _) => {
403 for arm in arms.iter() {
405 collect_blocks(arm.body, blocks);
406 }
407 }
408 hir::ExprKind::Block(blk, _) => {
409 blocks.push(blk);
410 }
411 _ => {}
412 }
413 }
414 collect_blocks(init, &mut blocks);
415 }
416
417 let expected_inner: Ty<'_> = expected.peel_refs();
418 for block in blocks.iter() {
419 self.consider_removing_semicolon(block, expected_inner, diag);
420 }
421 }
422
423 pub fn consider_removing_semicolon(
435 &self,
436 blk: &'tcx hir::Block<'tcx>,
437 expected_ty: Ty<'tcx>,
438 diag: &mut Diag<'_>,
439 ) -> bool {
440 if let Some((span_semi, boxed)) = self.could_remove_semicolon(blk, expected_ty) {
441 if let StatementAsExpression::NeedsBoxing = boxed {
442 diag.span_suggestion_verbose(
443 span_semi,
444 "consider removing this semicolon and boxing the expression",
445 "",
446 Applicability::HasPlaceholders,
447 );
448 } else {
449 diag.span_suggestion_short(
450 span_semi,
451 "remove this semicolon to return this value",
452 "",
453 Applicability::MachineApplicable,
454 );
455 }
456 true
457 } else {
458 false
459 }
460 }
461
462 pub(crate) fn suggest_function_pointers_impl(
463 &self,
464 span: Option<Span>,
465 exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
466 diag: &mut Diag<'_>,
467 ) {
468 let ty::error::ExpectedFound { expected, found } = exp_found;
469 let expected_inner = expected.peel_refs();
470 let found_inner = found.peel_refs();
471 if !expected_inner.is_fn() || !found_inner.is_fn() {
472 return;
473 }
474 match (expected_inner.kind(), found_inner.kind()) {
475 (ty::FnPtr(sig_tys, hdr), ty::FnDef(did, args)) => {
476 let args = args.no_bound_vars().unwrap();
477
478 let sig = sig_tys.with(*hdr);
479 let expected_sig = self.normalize_fn_sig(Unnormalized::new_wip(sig));
480 let found_sig =
481 self.normalize_fn_sig(self.tcx.fn_sig(*did).instantiate(self.tcx, args));
482
483 let fn_name = self.tcx.def_path_str_with_args(*did, args);
484
485 if !self.same_type_modulo_infer(found_sig, expected_sig)
486 || !sig.is_suggestable(self.tcx, true)
487 || self.tcx.intrinsic(*did).is_some()
488 {
489 return;
490 }
491
492 let Some(span) = span else {
493 let casting = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0} as {1}", fn_name, sig))
})format!("{fn_name} as {sig}");
494 diag.subdiagnostic(FnItemsAreDistinct);
495 diag.subdiagnostic(FnConsiderCasting { casting });
496 return;
497 };
498
499 let sugg = match (expected.is_ref(), found.is_ref()) {
500 (true, false) => {
501 FunctionPointerSuggestion::UseRef { span: span.shrink_to_lo() }
502 }
503 (false, true) => FunctionPointerSuggestion::RemoveRef { span, fn_name },
504 (true, true) => {
505 diag.subdiagnostic(FnItemsAreDistinct);
506 FunctionPointerSuggestion::CastRef { span, fn_name, sig }
507 }
508 (false, false) => {
509 diag.subdiagnostic(FnItemsAreDistinct);
510 FunctionPointerSuggestion::Cast { span: span.shrink_to_hi(), sig }
511 }
512 };
513 diag.subdiagnostic(sugg);
514 }
515 (ty::FnDef(did1, args1), ty::FnDef(did2, args2)) => {
516 let args1 = args1.no_bound_vars().unwrap();
517 let args2 = args2.no_bound_vars().unwrap();
518
519 let expected_sig =
520 self.normalize_fn_sig(self.tcx.fn_sig(*did1).instantiate(self.tcx, args1));
521 let found_sig =
522 self.normalize_fn_sig(self.tcx.fn_sig(*did2).instantiate(self.tcx, args2));
523
524 let expected_sig_anon = self.tcx.anonymize_bound_vars(expected_sig);
525 let found_sig_anon = self.tcx.anonymize_bound_vars(found_sig);
526 if self.same_type_modulo_infer(expected_sig_anon, found_sig_anon) {
527 diag.subdiagnostic(FnUniqTypes);
528 }
529
530 if !self.same_type_modulo_infer(found_sig, expected_sig)
531 || !found_sig.is_suggestable(self.tcx, true)
532 || !expected_sig.is_suggestable(self.tcx, true)
533 || self.tcx.intrinsic(*did1).is_some()
534 || self.tcx.intrinsic(*did2).is_some()
535 {
536 return;
537 }
538
539 let fn_name = self.tcx.def_path_str_with_args(*did2, args2);
540
541 let Some(span) = span else {
542 diag.subdiagnostic(FnConsiderCastingBoth { sig: expected_sig });
543 return;
544 };
545
546 let sug = if found.is_ref() {
547 FunctionPointerSuggestion::CastBothRef {
548 span,
549 fn_name,
550 found_sig,
551 expected_sig,
552 }
553 } else {
554 FunctionPointerSuggestion::CastBoth {
555 span: span.shrink_to_hi(),
556 found_sig,
557 expected_sig,
558 }
559 };
560
561 diag.subdiagnostic(sug);
562 }
563 (ty::FnDef(did, args), ty::FnPtr(sig_tys, hdr)) => {
564 let args = args.no_bound_vars().unwrap();
565
566 let expected_sig =
567 self.normalize_fn_sig(self.tcx.fn_sig(*did).instantiate(self.tcx, args));
568 let found_sig = self.normalize_fn_sig(Unnormalized::new_wip(sig_tys.with(*hdr)));
569
570 if !self.same_type_modulo_infer(found_sig, expected_sig) {
571 return;
572 }
573
574 let fn_name = self.tcx.def_path_str_with_args(*did, args);
575
576 let casting = if expected.is_ref() {
577 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("&({0} as {1})", fn_name,
found_sig))
})format!("&({fn_name} as {found_sig})")
578 } else {
579 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0} as {1}", fn_name, found_sig))
})format!("{fn_name} as {found_sig}")
580 };
581
582 diag.subdiagnostic(FnConsiderCasting { casting });
583 }
584 _ => {
585 return;
586 }
587 };
588 }
589
590 pub(super) fn suggest_function_pointers(
591 &self,
592 cause: &ObligationCause<'tcx>,
593 span: Span,
594 exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
595 terr: TypeError<'tcx>,
596 diag: &mut Diag<'_>,
597 ) {
598 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event /rustc-dev/d080e7dff1b0fc54541545252818f8cccf995d05/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs:598",
"rustc_trait_selection::error_reporting::infer::suggest",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("/rustc-dev/d080e7dff1b0fc54541545252818f8cccf995d05/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs"),
::tracing_core::__macro_support::Option::Some(598u32),
::tracing_core::__macro_support::Option::Some("rustc_trait_selection::error_reporting::infer::suggest"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("suggest_function_pointers(cause={0:?}, exp_found={1:?})",
cause, exp_found) as &dyn ::tracing::field::Value))])
});
} else { ; }
};debug!("suggest_function_pointers(cause={:?}, exp_found={:?})", cause, exp_found);
599
600 if exp_found.expected.peel_refs().is_fn() && exp_found.found.peel_refs().is_fn() {
601 self.suggest_function_pointers_impl(Some(span), exp_found, diag);
602 } else if let TypeError::Sorts(exp_found) = terr {
603 self.suggest_function_pointers_impl(None, &exp_found, diag);
604 }
605 }
606
607 fn should_suggest_as_ref_kind(
608 &self,
609 expected: Ty<'tcx>,
610 found: Ty<'tcx>,
611 ) -> Option<SuggestAsRefKind> {
612 if let (ty::Adt(exp_def, exp_args), ty::Ref(_, found_ty, _)) =
613 (expected.kind(), found.kind())
614 && let ty::Adt(found_def, found_args) = *found_ty.kind()
615 {
616 if exp_def == &found_def {
617 let have_as_ref = &[
618 (sym::Option, SuggestAsRefKind::Option),
619 (sym::Result, SuggestAsRefKind::Result),
620 ];
621 if let Some(msg) = have_as_ref.iter().find_map(|(name, msg)| {
622 self.tcx.is_diagnostic_item(*name, exp_def.did()).then_some(msg)
623 }) {
624 let mut show_suggestion = true;
625 for (exp_ty, found_ty) in std::iter::zip(exp_args.types(), found_args.types()) {
626 match *exp_ty.kind() {
627 ty::Ref(_, exp_ty, _) => {
628 match (exp_ty.kind(), found_ty.kind()) {
629 (_, ty::Param(_))
630 | (_, ty::Infer(_))
631 | (ty::Param(_), _)
632 | (ty::Infer(_), _) => {}
633 _ if self.same_type_modulo_infer(exp_ty, found_ty) => {}
634 _ => show_suggestion = false,
635 };
636 }
637 ty::Param(_) | ty::Infer(_) => {}
638 _ => show_suggestion = false,
639 }
640 }
641 if show_suggestion {
642 return Some(*msg);
643 }
644 }
645 }
646 }
647 None
648 }
649
650 pub fn should_suggest_as_ref(&self, expected: Ty<'tcx>, found: Ty<'tcx>) -> Option<&str> {
652 match self.should_suggest_as_ref_kind(expected, found) {
653 Some(SuggestAsRefKind::Option) => Some(
654 "you can convert from `&Option<T>` to `Option<&T>` using \
655 `.as_ref()`",
656 ),
657 Some(SuggestAsRefKind::Result) => Some(
658 "you can convert from `&Result<T, E>` to \
659 `Result<&T, &E>` using `.as_ref()`",
660 ),
661 None => None,
662 }
663 }
664 pub(super) fn suggest_let_for_letchains(
668 &self,
669 cause: &ObligationCause<'_>,
670 span: Span,
671 ) -> Option<TypeErrorAdditionalDiags> {
672 struct IfVisitor {
674 found_if: bool,
675 err_span: Span,
676 }
677
678 impl<'v> Visitor<'v> for IfVisitor {
679 type Result = ControlFlow<()>;
680 fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) -> Self::Result {
681 match ex.kind {
682 hir::ExprKind::If(cond, _, _) => {
683 self.found_if = true;
684 walk_expr(self, cond)?;
685 self.found_if = false;
686 ControlFlow::Continue(())
687 }
688 _ => walk_expr(self, ex),
689 }
690 }
691
692 fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) -> Self::Result {
693 if let hir::StmtKind::Let(LetStmt {
694 span,
695 pat: hir::Pat { .. },
696 ty: None,
697 init: Some(_),
698 ..
699 }) = &ex.kind
700 && self.found_if
701 && span.eq(&self.err_span)
702 {
703 ControlFlow::Break(())
704 } else {
705 walk_stmt(self, ex)
706 }
707 }
708 }
709
710 self.tcx.hir_maybe_body_owned_by(cause.body_def_id).and_then(|body| {
711 IfVisitor { err_span: span, found_if: false }
712 .visit_body(&body)
713 .is_break()
714 .then(|| TypeErrorAdditionalDiags::AddLetForLetChains { span: span.shrink_to_lo() })
715 })
716 }
717
718 pub(super) fn suggest_for_all_lifetime_closure(
721 &self,
722 span: Span,
723 hir: hir::Node<'_>,
724 exp_found: &ty::error::ExpectedFound<ty::TraitRef<'tcx>>,
725 diag: &mut Diag<'_>,
726 ) {
727 let hir::Node::Expr(hir::Expr {
729 kind: hir::ExprKind::Closure(hir::Closure { body, fn_decl, .. }),
730 ..
731 }) = hir
732 else {
733 return;
734 };
735 let hir::Body { params, .. } = self.tcx.hir_body(*body);
736
737 let Some(expected) = exp_found.expected.args.get(1) else {
740 return;
741 };
742 let Some(found) = exp_found.found.args.get(1) else {
743 return;
744 };
745 let expected = expected.kind();
746 let found = found.kind();
747 if let GenericArgKind::Type(expected) = expected
749 && let GenericArgKind::Type(found) = found
750 && let ty::Tuple(expected) = expected.kind()
751 && let ty::Tuple(found) = found.kind()
752 && expected.len() == found.len()
753 {
754 let mut suggestion = "|".to_string();
755 let mut is_first = true;
756 let mut has_suggestion = false;
757
758 for (((expected, found), param_hir), arg_hir) in
759 expected.iter().zip(found.iter()).zip(params.iter()).zip(fn_decl.inputs.iter())
760 {
761 if is_first {
762 is_first = false;
763 } else {
764 suggestion += ", ";
765 }
766
767 if let ty::Ref(expected_region, _, _) = expected.kind()
768 && let ty::Ref(found_region, _, _) = found.kind()
769 && expected_region.is_bound()
770 && !found_region.is_bound()
771 && let hir::TyKind::Infer(()) = arg_hir.kind
772 {
773 if param_hir.pat.span == param_hir.ty_span {
776 let Ok(pat) =
778 self.tcx.sess.source_map().span_to_snippet(param_hir.pat.span)
779 else {
780 return;
781 };
782 suggestion += &::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}: &_", pat))
})format!("{pat}: &_");
783 } else {
784 let Ok(pat) =
786 self.tcx.sess.source_map().span_to_snippet(param_hir.pat.span)
787 else {
788 return;
789 };
790 let Ok(ty) = self.tcx.sess.source_map().span_to_snippet(param_hir.ty_span)
791 else {
792 return;
793 };
794 suggestion += &::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}: &{1}", pat, ty))
})format!("{pat}: &{ty}");
795 }
796 has_suggestion = true;
797 } else {
798 let Ok(arg) = self.tcx.sess.source_map().span_to_snippet(param_hir.span) else {
799 return;
800 };
801 suggestion += &arg;
803 }
804 }
805 suggestion += "|";
806
807 if has_suggestion {
808 diag.span_suggestion_verbose(
809 span,
810 "consider specifying the type of the closure parameters",
811 suggestion,
812 Applicability::MaybeIncorrect,
813 );
814 }
815 }
816 }
817}
818
819impl<'tcx> TypeErrCtxt<'_, 'tcx> {
820 fn could_remove_semicolon(
823 &self,
824 blk: &'tcx hir::Block<'tcx>,
825 expected_ty: Ty<'tcx>,
826 ) -> Option<(Span, StatementAsExpression)> {
827 let blk = blk.innermost_block();
828 if blk.expr.is_some() {
830 return None;
831 }
832 let last_stmt = blk.stmts.last()?;
833 let hir::StmtKind::Semi(last_expr) = last_stmt.kind else {
834 return None;
835 };
836 let last_expr_ty = self.typeck_results.as_ref()?.expr_ty_opt(last_expr)?;
837 let needs_box = match (last_expr_ty.kind(), expected_ty.kind()) {
838 _ if last_expr_ty.references_error() => return None,
839 _ if self.same_type_modulo_infer(last_expr_ty, expected_ty) => {
840 StatementAsExpression::CorrectType
841 }
842 (
843 ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id: last_def_id }, .. }),
844 ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id: exp_def_id }, .. }),
845 ) if last_def_id == exp_def_id => StatementAsExpression::CorrectType,
846 (
847 ty::Alias(
848 _,
849 ty::AliasTy {
850 kind: ty::Opaque { def_id: last_def_id }, args: last_bounds, ..
851 },
852 ),
853 ty::Alias(
854 _,
855 ty::AliasTy {
856 kind: ty::Opaque { def_id: exp_def_id }, args: exp_bounds, ..
857 },
858 ),
859 ) => {
860 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event /rustc-dev/d080e7dff1b0fc54541545252818f8cccf995d05/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs:860",
"rustc_trait_selection::error_reporting::infer::suggest",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("/rustc-dev/d080e7dff1b0fc54541545252818f8cccf995d05/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs"),
::tracing_core::__macro_support::Option::Some(860u32),
::tracing_core::__macro_support::Option::Some("rustc_trait_selection::error_reporting::infer::suggest"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("both opaque, likely future {0:?} {1:?} {2:?} {3:?}",
last_def_id, last_bounds, exp_def_id, exp_bounds) as
&dyn ::tracing::field::Value))])
});
} else { ; }
};debug!(
861 "both opaque, likely future {:?} {:?} {:?} {:?}",
862 last_def_id, last_bounds, exp_def_id, exp_bounds
863 );
864
865 let last_local_id = last_def_id.as_local()?;
866 let exp_local_id = exp_def_id.as_local()?;
867
868 match (
869 &self.tcx.hir_expect_opaque_ty(last_local_id),
870 &self.tcx.hir_expect_opaque_ty(exp_local_id),
871 ) {
872 (
873 hir::OpaqueTy { bounds: last_bounds, .. },
874 hir::OpaqueTy { bounds: exp_bounds, .. },
875 ) if std::iter::zip(*last_bounds, *exp_bounds).all(|(left, right)| match (
876 left, right,
877 ) {
878 (hir::GenericBound::Trait(tl), hir::GenericBound::Trait(tr))
880 if tl.trait_ref.trait_def_id() == tr.trait_ref.trait_def_id()
881 && tl.modifiers == tr.modifiers =>
882 {
883 true
884 }
885 _ => false,
886 }) =>
887 {
888 StatementAsExpression::NeedsBoxing
889 }
890 _ => StatementAsExpression::CorrectType,
891 }
892 }
893 _ => return None,
894 };
895 let span = if last_stmt.span.from_expansion() {
896 let mac_call = rustc_span::source_map::original_sp(last_stmt.span, blk.span);
897 self.tcx.sess.source_map().mac_call_stmt_semi_span(mac_call)?
898 } else {
899 self.tcx
900 .sess
901 .source_map()
902 .span_extend_while_whitespace(last_expr.span)
903 .shrink_to_hi()
904 .with_hi(last_stmt.span.hi())
905 };
906
907 Some((span, needs_box))
908 }
909
910 fn consider_returning_binding_diag(
913 &self,
914 blk: &'tcx hir::Block<'tcx>,
915 expected_ty: Ty<'tcx>,
916 ) -> Option<SuggestRemoveSemiOrReturnBinding> {
917 let blk = blk.innermost_block();
918 if blk.expr.is_some() {
920 return None;
921 }
922 let mut shadowed = FxIndexSet::default();
923 let mut candidate_idents = ::alloc::vec::Vec::new()vec![];
924 let mut find_compatible_candidates = |pat: &hir::Pat<'_>| {
925 if let hir::PatKind::Binding(_, hir_id, ident, _) = &pat.kind
926 && let Some(pat_ty) = self
927 .typeck_results
928 .as_ref()
929 .and_then(|typeck_results| typeck_results.node_type_opt(*hir_id))
930 {
931 let pat_ty = self.deeply_resolve_ignoring_regions(pat_ty);
932 if self.same_type_modulo_infer(pat_ty, expected_ty)
933 && !(pat_ty, expected_ty).references_error()
934 && shadowed.insert(ident.name)
935 {
936 candidate_idents.push((*ident, pat_ty));
937 }
938 }
939 true
940 };
941
942 for stmt in blk.stmts.iter().rev() {
943 let hir::StmtKind::Let(local) = &stmt.kind else {
944 continue;
945 };
946 local.pat.walk(&mut find_compatible_candidates);
947 }
948 match self.tcx.parent_hir_node(blk.hir_id) {
949 hir::Node::Expr(hir::Expr { hir_id, .. }) => match self.tcx.parent_hir_node(*hir_id) {
950 hir::Node::Arm(hir::Arm { pat, .. }) => {
951 pat.walk(&mut find_compatible_candidates);
952 }
953
954 hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn { body, .. }, .. })
955 | hir::Node::ImplItem(hir::ImplItem {
956 kind: hir::ImplItemKind::Fn(_, body), ..
957 })
958 | hir::Node::TraitItem(hir::TraitItem {
959 kind: hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(body)),
960 ..
961 })
962 | hir::Node::Expr(hir::Expr {
963 kind: hir::ExprKind::Closure(hir::Closure { body, .. }),
964 ..
965 }) => {
966 for param in self.tcx.hir_body(*body).params {
967 param.pat.walk(&mut find_compatible_candidates);
968 }
969 }
970 hir::Node::Expr(hir::Expr {
971 kind:
972 hir::ExprKind::If(
973 hir::Expr { kind: hir::ExprKind::Let(let_), .. },
974 then_block,
975 _,
976 ),
977 ..
978 }) if then_block.hir_id == *hir_id => {
979 let_.pat.walk(&mut find_compatible_candidates);
980 }
981 _ => {}
982 },
983 _ => {}
984 }
985
986 match &candidate_idents[..] {
987 [(ident, _ty)] => {
988 let sm = self.tcx.sess.source_map();
989 let (span, sugg) = if let Some(stmt) = blk.stmts.last() {
990 let stmt_span = sm.stmt_span(stmt.span, blk.span);
991 let sugg = if sm.is_multiline(blk.span)
992 && let Some(spacing) = sm.indentation_before(stmt_span)
993 {
994 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("\n{0}{1}", spacing, ident))
})format!("\n{spacing}{ident}")
995 } else {
996 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!(" {0}", ident))
})format!(" {ident}")
997 };
998 (stmt_span.shrink_to_hi(), sugg)
999 } else {
1000 let sugg = if sm.is_multiline(blk.span)
1001 && let Some(spacing) = sm.indentation_before(blk.span.shrink_to_lo())
1002 {
1003 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("\n{0} {1}\n{0}", spacing,
ident))
})format!("\n{spacing} {ident}\n{spacing}")
1004 } else {
1005 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!(" {0} ", ident))
})format!(" {ident} ")
1006 };
1007 let left_span = sm.span_through_char(blk.span, '{').shrink_to_hi();
1008 (sm.span_extend_while_whitespace(left_span), sugg)
1009 };
1010 Some(SuggestRemoveSemiOrReturnBinding::Add { sp: span, code: sugg, ident: *ident })
1011 }
1012 values if (1..3).contains(&values.len()) => {
1013 let spans = values.iter().map(|(ident, _)| ident.span).collect::<Vec<_>>();
1014 Some(SuggestRemoveSemiOrReturnBinding::AddOne { spans: spans.into() })
1015 }
1016 _ => None,
1017 }
1018 }
1019
1020 pub fn consider_returning_binding(
1021 &self,
1022 blk: &'tcx hir::Block<'tcx>,
1023 expected_ty: Ty<'tcx>,
1024 err: &mut Diag<'_>,
1025 ) -> bool {
1026 let diag = self.consider_returning_binding_diag(blk, expected_ty);
1027 match diag {
1028 Some(diag) => {
1029 err.subdiagnostic(diag);
1030 true
1031 }
1032 None => false,
1033 }
1034 }
1035}