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]
impl ::core::clone::Clone for StatementAsExpression {
#[inline]
fn clone(&self) -> StatementAsExpression { *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::cmp::PartialEq for StatementAsExpression {
#[inline]
fn eq(&self, other: &StatementAsExpression) -> bool {
let __self_discr = ::core::intrinsics::discriminant_value(self);
let __arg1_discr = ::core::intrinsics::discriminant_value(other);
__self_discr == __arg1_discr
}
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for StatementAsExpression {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_fields_are_eq(&self) {}
}Eq, #[automatically_derived]
impl ::core::hash::Hash for StatementAsExpression {
#[inline]
fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) {
let __self_discr = ::core::intrinsics::discriminant_value(self);
::core::hash::Hash::hash(&__self_discr, state)
}
}Hash)]
29enum StatementAsExpression {
30 CorrectType,
31 NeedsBoxing,
32}
33
34#[derive(#[automatically_derived]
impl ::core::clone::Clone for SuggestAsRefKind {
#[inline]
fn clone(&self) -> SuggestAsRefKind { *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.resolve_vars_if_possible(second_ty)),
52 (second_id, self.resolve_vars_if_possible(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 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("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 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("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 if self.same_type_modulo_infer(expected_sig, found_sig) {
525 diag.subdiagnostic(FnUniqTypes);
526 }
527
528 if !self.same_type_modulo_infer(found_sig, expected_sig)
529 || !found_sig.is_suggestable(self.tcx, true)
530 || !expected_sig.is_suggestable(self.tcx, true)
531 || self.tcx.intrinsic(*did1).is_some()
532 || self.tcx.intrinsic(*did2).is_some()
533 {
534 return;
535 }
536
537 let fn_name = self.tcx.def_path_str_with_args(*did2, args2);
538
539 let Some(span) = span else {
540 diag.subdiagnostic(FnConsiderCastingBoth { sig: expected_sig });
541 return;
542 };
543
544 let sug = if found.is_ref() {
545 FunctionPointerSuggestion::CastBothRef {
546 span,
547 fn_name,
548 found_sig,
549 expected_sig,
550 }
551 } else {
552 FunctionPointerSuggestion::CastBoth {
553 span: span.shrink_to_hi(),
554 found_sig,
555 expected_sig,
556 }
557 };
558
559 diag.subdiagnostic(sug);
560 }
561 (ty::FnDef(did, args), ty::FnPtr(sig_tys, hdr)) => {
562 let args = args.no_bound_vars().unwrap();
563
564 let expected_sig =
565 self.normalize_fn_sig(self.tcx.fn_sig(*did).instantiate(self.tcx, args));
566 let found_sig = self.normalize_fn_sig(Unnormalized::new_wip(sig_tys.with(*hdr)));
567
568 if !self.same_type_modulo_infer(found_sig, expected_sig) {
569 return;
570 }
571
572 let fn_name = self.tcx.def_path_str_with_args(*did, args);
573
574 let casting = if expected.is_ref() {
575 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("&({0} as {1})", fn_name,
found_sig))
})format!("&({fn_name} as {found_sig})")
576 } else {
577 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0} as {1}", fn_name, found_sig))
})format!("{fn_name} as {found_sig}")
578 };
579
580 diag.subdiagnostic(FnConsiderCasting { casting });
581 }
582 _ => {
583 return;
584 }
585 };
586 }
587
588 pub(super) fn suggest_function_pointers(
589 &self,
590 cause: &ObligationCause<'tcx>,
591 span: Span,
592 exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
593 terr: TypeError<'tcx>,
594 diag: &mut Diag<'_>,
595 ) {
596 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs:596",
"rustc_trait_selection::error_reporting::infer::suggest",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs"),
::tracing_core::__macro_support::Option::Some(596u32),
::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);
597
598 if exp_found.expected.peel_refs().is_fn() && exp_found.found.peel_refs().is_fn() {
599 self.suggest_function_pointers_impl(Some(span), exp_found, diag);
600 } else if let TypeError::Sorts(exp_found) = terr {
601 self.suggest_function_pointers_impl(None, &exp_found, diag);
602 }
603 }
604
605 fn should_suggest_as_ref_kind(
606 &self,
607 expected: Ty<'tcx>,
608 found: Ty<'tcx>,
609 ) -> Option<SuggestAsRefKind> {
610 if let (ty::Adt(exp_def, exp_args), ty::Ref(_, found_ty, _)) =
611 (expected.kind(), found.kind())
612 && let ty::Adt(found_def, found_args) = *found_ty.kind()
613 {
614 if exp_def == &found_def {
615 let have_as_ref = &[
616 (sym::Option, SuggestAsRefKind::Option),
617 (sym::Result, SuggestAsRefKind::Result),
618 ];
619 if let Some(msg) = have_as_ref.iter().find_map(|(name, msg)| {
620 self.tcx.is_diagnostic_item(*name, exp_def.did()).then_some(msg)
621 }) {
622 let mut show_suggestion = true;
623 for (exp_ty, found_ty) in std::iter::zip(exp_args.types(), found_args.types()) {
624 match *exp_ty.kind() {
625 ty::Ref(_, exp_ty, _) => {
626 match (exp_ty.kind(), found_ty.kind()) {
627 (_, ty::Param(_))
628 | (_, ty::Infer(_))
629 | (ty::Param(_), _)
630 | (ty::Infer(_), _) => {}
631 _ if self.same_type_modulo_infer(exp_ty, found_ty) => {}
632 _ => show_suggestion = false,
633 };
634 }
635 ty::Param(_) | ty::Infer(_) => {}
636 _ => show_suggestion = false,
637 }
638 }
639 if show_suggestion {
640 return Some(*msg);
641 }
642 }
643 }
644 }
645 None
646 }
647
648 pub fn should_suggest_as_ref(&self, expected: Ty<'tcx>, found: Ty<'tcx>) -> Option<&str> {
650 match self.should_suggest_as_ref_kind(expected, found) {
651 Some(SuggestAsRefKind::Option) => Some(
652 "you can convert from `&Option<T>` to `Option<&T>` using \
653 `.as_ref()`",
654 ),
655 Some(SuggestAsRefKind::Result) => Some(
656 "you can convert from `&Result<T, E>` to \
657 `Result<&T, &E>` using `.as_ref()`",
658 ),
659 None => None,
660 }
661 }
662 pub(super) fn suggest_let_for_letchains(
666 &self,
667 cause: &ObligationCause<'_>,
668 span: Span,
669 ) -> Option<TypeErrorAdditionalDiags> {
670 struct IfVisitor {
672 found_if: bool,
673 err_span: Span,
674 }
675
676 impl<'v> Visitor<'v> for IfVisitor {
677 type Result = ControlFlow<()>;
678 fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) -> Self::Result {
679 match ex.kind {
680 hir::ExprKind::If(cond, _, _) => {
681 self.found_if = true;
682 walk_expr(self, cond)?;
683 self.found_if = false;
684 ControlFlow::Continue(())
685 }
686 _ => walk_expr(self, ex),
687 }
688 }
689
690 fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) -> Self::Result {
691 if let hir::StmtKind::Let(LetStmt {
692 span,
693 pat: hir::Pat { .. },
694 ty: None,
695 init: Some(_),
696 ..
697 }) = &ex.kind
698 && self.found_if
699 && span.eq(&self.err_span)
700 {
701 ControlFlow::Break(())
702 } else {
703 walk_stmt(self, ex)
704 }
705 }
706 }
707
708 self.tcx.hir_maybe_body_owned_by(cause.body_def_id).and_then(|body| {
709 IfVisitor { err_span: span, found_if: false }
710 .visit_body(&body)
711 .is_break()
712 .then(|| TypeErrorAdditionalDiags::AddLetForLetChains { span: span.shrink_to_lo() })
713 })
714 }
715
716 pub(super) fn suggest_for_all_lifetime_closure(
719 &self,
720 span: Span,
721 hir: hir::Node<'_>,
722 exp_found: &ty::error::ExpectedFound<ty::TraitRef<'tcx>>,
723 diag: &mut Diag<'_>,
724 ) {
725 let hir::Node::Expr(hir::Expr {
727 kind: hir::ExprKind::Closure(hir::Closure { body, fn_decl, .. }),
728 ..
729 }) = hir
730 else {
731 return;
732 };
733 let hir::Body { params, .. } = self.tcx.hir_body(*body);
734
735 let Some(expected) = exp_found.expected.args.get(1) else {
738 return;
739 };
740 let Some(found) = exp_found.found.args.get(1) else {
741 return;
742 };
743 let expected = expected.kind();
744 let found = found.kind();
745 if let GenericArgKind::Type(expected) = expected
747 && let GenericArgKind::Type(found) = found
748 && let ty::Tuple(expected) = expected.kind()
749 && let ty::Tuple(found) = found.kind()
750 && expected.len() == found.len()
751 {
752 let mut suggestion = "|".to_string();
753 let mut is_first = true;
754 let mut has_suggestion = false;
755
756 for (((expected, found), param_hir), arg_hir) in
757 expected.iter().zip(found.iter()).zip(params.iter()).zip(fn_decl.inputs.iter())
758 {
759 if is_first {
760 is_first = false;
761 } else {
762 suggestion += ", ";
763 }
764
765 if let ty::Ref(expected_region, _, _) = expected.kind()
766 && let ty::Ref(found_region, _, _) = found.kind()
767 && expected_region.is_bound()
768 && !found_region.is_bound()
769 && let hir::TyKind::Infer(()) = arg_hir.kind
770 {
771 if param_hir.pat.span == param_hir.ty_span {
774 let Ok(pat) =
776 self.tcx.sess.source_map().span_to_snippet(param_hir.pat.span)
777 else {
778 return;
779 };
780 suggestion += &::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}: &_", pat))
})format!("{pat}: &_");
781 } else {
782 let Ok(pat) =
784 self.tcx.sess.source_map().span_to_snippet(param_hir.pat.span)
785 else {
786 return;
787 };
788 let Ok(ty) = self.tcx.sess.source_map().span_to_snippet(param_hir.ty_span)
789 else {
790 return;
791 };
792 suggestion += &::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}: &{1}", pat, ty))
})format!("{pat}: &{ty}");
793 }
794 has_suggestion = true;
795 } else {
796 let Ok(arg) = self.tcx.sess.source_map().span_to_snippet(param_hir.span) else {
797 return;
798 };
799 suggestion += &arg;
801 }
802 }
803 suggestion += "|";
804
805 if has_suggestion {
806 diag.span_suggestion_verbose(
807 span,
808 "consider specifying the type of the closure parameters",
809 suggestion,
810 Applicability::MaybeIncorrect,
811 );
812 }
813 }
814 }
815}
816
817impl<'tcx> TypeErrCtxt<'_, 'tcx> {
818 fn could_remove_semicolon(
821 &self,
822 blk: &'tcx hir::Block<'tcx>,
823 expected_ty: Ty<'tcx>,
824 ) -> Option<(Span, StatementAsExpression)> {
825 let blk = blk.innermost_block();
826 if blk.expr.is_some() {
828 return None;
829 }
830 let last_stmt = blk.stmts.last()?;
831 let hir::StmtKind::Semi(last_expr) = last_stmt.kind else {
832 return None;
833 };
834 let last_expr_ty = self.typeck_results.as_ref()?.expr_ty_opt(last_expr)?;
835 let needs_box = match (last_expr_ty.kind(), expected_ty.kind()) {
836 _ if last_expr_ty.references_error() => return None,
837 _ if self.same_type_modulo_infer(last_expr_ty, expected_ty) => {
838 StatementAsExpression::CorrectType
839 }
840 (
841 ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id: last_def_id }, .. }),
842 ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id: exp_def_id }, .. }),
843 ) if last_def_id == exp_def_id => StatementAsExpression::CorrectType,
844 (
845 ty::Alias(
846 _,
847 ty::AliasTy {
848 kind: ty::Opaque { def_id: last_def_id }, args: last_bounds, ..
849 },
850 ),
851 ty::Alias(
852 _,
853 ty::AliasTy {
854 kind: ty::Opaque { def_id: exp_def_id }, args: exp_bounds, ..
855 },
856 ),
857 ) => {
858 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs:858",
"rustc_trait_selection::error_reporting::infer::suggest",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs"),
::tracing_core::__macro_support::Option::Some(858u32),
::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!(
859 "both opaque, likely future {:?} {:?} {:?} {:?}",
860 last_def_id, last_bounds, exp_def_id, exp_bounds
861 );
862
863 let last_local_id = last_def_id.as_local()?;
864 let exp_local_id = exp_def_id.as_local()?;
865
866 match (
867 &self.tcx.hir_expect_opaque_ty(last_local_id),
868 &self.tcx.hir_expect_opaque_ty(exp_local_id),
869 ) {
870 (
871 hir::OpaqueTy { bounds: last_bounds, .. },
872 hir::OpaqueTy { bounds: exp_bounds, .. },
873 ) if std::iter::zip(*last_bounds, *exp_bounds).all(|(left, right)| match (
874 left, right,
875 ) {
876 (hir::GenericBound::Trait(tl), hir::GenericBound::Trait(tr))
878 if tl.trait_ref.trait_def_id() == tr.trait_ref.trait_def_id()
879 && tl.modifiers == tr.modifiers =>
880 {
881 true
882 }
883 _ => false,
884 }) =>
885 {
886 StatementAsExpression::NeedsBoxing
887 }
888 _ => StatementAsExpression::CorrectType,
889 }
890 }
891 _ => return None,
892 };
893 let span = if last_stmt.span.from_expansion() {
894 let mac_call = rustc_span::source_map::original_sp(last_stmt.span, blk.span);
895 self.tcx.sess.source_map().mac_call_stmt_semi_span(mac_call)?
896 } else {
897 self.tcx
898 .sess
899 .source_map()
900 .span_extend_while_whitespace(last_expr.span)
901 .shrink_to_hi()
902 .with_hi(last_stmt.span.hi())
903 };
904
905 Some((span, needs_box))
906 }
907
908 fn consider_returning_binding_diag(
911 &self,
912 blk: &'tcx hir::Block<'tcx>,
913 expected_ty: Ty<'tcx>,
914 ) -> Option<SuggestRemoveSemiOrReturnBinding> {
915 let blk = blk.innermost_block();
916 if blk.expr.is_some() {
918 return None;
919 }
920 let mut shadowed = FxIndexSet::default();
921 let mut candidate_idents = ::alloc::vec::Vec::new()vec![];
922 let mut find_compatible_candidates = |pat: &hir::Pat<'_>| {
923 if let hir::PatKind::Binding(_, hir_id, ident, _) = &pat.kind
924 && let Some(pat_ty) = self
925 .typeck_results
926 .as_ref()
927 .and_then(|typeck_results| typeck_results.node_type_opt(*hir_id))
928 {
929 let pat_ty = self.resolve_vars_if_possible(pat_ty);
930 if self.same_type_modulo_infer(pat_ty, expected_ty)
931 && !(pat_ty, expected_ty).references_error()
932 && shadowed.insert(ident.name)
933 {
934 candidate_idents.push((*ident, pat_ty));
935 }
936 }
937 true
938 };
939
940 for stmt in blk.stmts.iter().rev() {
941 let hir::StmtKind::Let(local) = &stmt.kind else {
942 continue;
943 };
944 local.pat.walk(&mut find_compatible_candidates);
945 }
946 match self.tcx.parent_hir_node(blk.hir_id) {
947 hir::Node::Expr(hir::Expr { hir_id, .. }) => match self.tcx.parent_hir_node(*hir_id) {
948 hir::Node::Arm(hir::Arm { pat, .. }) => {
949 pat.walk(&mut find_compatible_candidates);
950 }
951
952 hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn { body, .. }, .. })
953 | hir::Node::ImplItem(hir::ImplItem {
954 kind: hir::ImplItemKind::Fn(_, body), ..
955 })
956 | hir::Node::TraitItem(hir::TraitItem {
957 kind: hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(body)),
958 ..
959 })
960 | hir::Node::Expr(hir::Expr {
961 kind: hir::ExprKind::Closure(hir::Closure { body, .. }),
962 ..
963 }) => {
964 for param in self.tcx.hir_body(*body).params {
965 param.pat.walk(&mut find_compatible_candidates);
966 }
967 }
968 hir::Node::Expr(hir::Expr {
969 kind:
970 hir::ExprKind::If(
971 hir::Expr { kind: hir::ExprKind::Let(let_), .. },
972 then_block,
973 _,
974 ),
975 ..
976 }) if then_block.hir_id == *hir_id => {
977 let_.pat.walk(&mut find_compatible_candidates);
978 }
979 _ => {}
980 },
981 _ => {}
982 }
983
984 match &candidate_idents[..] {
985 [(ident, _ty)] => {
986 let sm = self.tcx.sess.source_map();
987 let (span, sugg) = if let Some(stmt) = blk.stmts.last() {
988 let stmt_span = sm.stmt_span(stmt.span, blk.span);
989 let sugg = if sm.is_multiline(blk.span)
990 && let Some(spacing) = sm.indentation_before(stmt_span)
991 {
992 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("\n{0}{1}", spacing, ident))
})format!("\n{spacing}{ident}")
993 } else {
994 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!(" {0}", ident))
})format!(" {ident}")
995 };
996 (stmt_span.shrink_to_hi(), sugg)
997 } else {
998 let sugg = if sm.is_multiline(blk.span)
999 && let Some(spacing) = sm.indentation_before(blk.span.shrink_to_lo())
1000 {
1001 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("\n{0} {1}\n{0}", spacing,
ident))
})format!("\n{spacing} {ident}\n{spacing}")
1002 } else {
1003 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!(" {0} ", ident))
})format!(" {ident} ")
1004 };
1005 let left_span = sm.span_through_char(blk.span, '{').shrink_to_hi();
1006 (sm.span_extend_while_whitespace(left_span), sugg)
1007 };
1008 Some(SuggestRemoveSemiOrReturnBinding::Add { sp: span, code: sugg, ident: *ident })
1009 }
1010 values if (1..3).contains(&values.len()) => {
1011 let spans = values.iter().map(|(ident, _)| ident.span).collect::<Vec<_>>();
1012 Some(SuggestRemoveSemiOrReturnBinding::AddOne { spans: spans.into() })
1013 }
1014 _ => None,
1015 }
1016 }
1017
1018 pub fn consider_returning_binding(
1019 &self,
1020 blk: &'tcx hir::Block<'tcx>,
1021 expected_ty: Ty<'tcx>,
1022 err: &mut Diag<'_>,
1023 ) -> bool {
1024 let diag = self.consider_returning_binding_diag(blk, expected_ty);
1025 match diag {
1026 Some(diag) => {
1027 err.subdiagnostic(diag);
1028 true
1029 }
1030 None => false,
1031 }
1032 }
1033}