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::error_reporting::TypeErrCtxt;
21use crate::error_reporting::infer::hir::Path;
22use crate::errors::{
23 ConsiderAddingAwait, FnConsiderCasting, FnConsiderCastingBoth, FnItemsAreDistinct, FnUniqTypes,
24 FunctionPointerSuggestion, SuggestAccessingField, SuggestRemoveSemiOrReturnBinding,
25 SuggestTuplePatternMany, SuggestTuplePatternOne, TypeErrorAdditionalDiags,
26};
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);
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(
165 &self,
166 cause: &ObligationCause<'tcx>,
167 exp_span: Span,
168 exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
169 diag: &mut Diag<'_>,
170 ) {
171 {
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:171",
"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(171u32),
::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};
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!("suggest_await_on_expect_found: exp_span={0:?}, expected_ty={1:?}, found_ty={2:?}",
exp_span, exp_found.expected, exp_found.found) as
&dyn Value))])
});
} else { ; }
};debug!(
172 "suggest_await_on_expect_found: exp_span={:?}, expected_ty={:?}, found_ty={:?}",
173 exp_span, exp_found.expected, exp_found.found,
174 );
175
176 match self.tcx.coroutine_kind(cause.body_id) {
177 Some(hir::CoroutineKind::Desugared(
178 hir::CoroutineDesugaring::Async | hir::CoroutineDesugaring::AsyncGen,
179 _,
180 )) => (),
181 None
182 | Some(
183 hir::CoroutineKind::Coroutine(_)
184 | hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Gen, _),
185 ) => return,
186 }
187
188 if let ObligationCauseCode::CompareImplItem { .. } = cause.code() {
189 return;
190 }
191
192 let subdiag = match (
193 self.get_impl_future_output_ty(exp_found.expected),
194 self.get_impl_future_output_ty(exp_found.found),
195 ) {
196 (Some(exp), Some(found)) if self.same_type_modulo_infer(exp, found) => match cause
197 .code()
198 {
199 ObligationCauseCode::IfExpression { expr_id, .. } => {
200 let hir::Node::Expr(hir::Expr {
201 kind: hir::ExprKind::If(_, then_expr, _), ..
202 }) = self.tcx.hir_node(*expr_id)
203 else {
204 return;
205 };
206 let then_span = self.find_block_span_from_hir_id(then_expr.hir_id);
207 Some(ConsiderAddingAwait::BothFuturesSugg {
208 first: then_span.shrink_to_hi(),
209 second: exp_span.shrink_to_hi(),
210 })
211 }
212 ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause {
213 prior_non_diverging_arms,
214 ..
215 }) => {
216 if let [.., arm_span] = &prior_non_diverging_arms[..] {
217 Some(ConsiderAddingAwait::BothFuturesSugg {
218 first: arm_span.shrink_to_hi(),
219 second: exp_span.shrink_to_hi(),
220 })
221 } else {
222 Some(ConsiderAddingAwait::BothFuturesHelp)
223 }
224 }
225 _ => Some(ConsiderAddingAwait::BothFuturesHelp),
226 },
227 (_, Some(ty)) if self.same_type_modulo_infer(exp_found.expected, ty) => {
228 diag.subdiagnostic(ConsiderAddingAwait::FutureSugg {
230 span: exp_span.shrink_to_hi(),
231 });
232 Some(ConsiderAddingAwait::FutureSuggNote { span: exp_span })
233 }
234 (Some(ty), _) if self.same_type_modulo_infer(ty, exp_found.found) => match cause.code()
235 {
236 ObligationCauseCode::Pattern { span: Some(then_span), origin_expr, .. } => {
237 origin_expr.is_some().then_some(ConsiderAddingAwait::FutureSugg {
238 span: then_span.shrink_to_hi(),
239 })
240 }
241 ObligationCauseCode::IfExpression { expr_id, .. } => {
242 let hir::Node::Expr(hir::Expr {
243 kind: hir::ExprKind::If(_, then_expr, _), ..
244 }) = self.tcx.hir_node(*expr_id)
245 else {
246 return;
247 };
248 let then_span = self.find_block_span_from_hir_id(then_expr.hir_id);
249 Some(ConsiderAddingAwait::FutureSugg { span: then_span.shrink_to_hi() })
250 }
251 ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause {
252 prior_non_diverging_arms,
253 ..
254 }) => Some({
255 ConsiderAddingAwait::FutureSuggMultiple {
256 spans: prior_non_diverging_arms
257 .iter()
258 .map(|arm| arm.shrink_to_hi())
259 .collect(),
260 }
261 }),
262 _ => None,
263 },
264 _ => None,
265 };
266 if let Some(subdiag) = subdiag {
267 diag.subdiagnostic(subdiag);
268 }
269 }
270
271 pub(super) fn suggest_accessing_field_where_appropriate(
272 &self,
273 cause: &ObligationCause<'tcx>,
274 exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
275 diag: &mut Diag<'_>,
276 ) {
277 {
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:277",
"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(277u32),
::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};
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!("suggest_accessing_field_where_appropriate(cause={0:?}, exp_found={1:?})",
cause, exp_found) as &dyn Value))])
});
} else { ; }
};debug!(
278 "suggest_accessing_field_where_appropriate(cause={:?}, exp_found={:?})",
279 cause, exp_found
280 );
281 if let ty::Adt(expected_def, expected_args) = exp_found.expected.kind() {
282 if expected_def.is_enum() {
283 return;
284 }
285
286 if let Some((name, ty)) = expected_def
287 .non_enum_variant()
288 .fields
289 .iter()
290 .filter(|field| field.vis.is_accessible_from(field.did, self.tcx))
291 .map(|field| (field.name, field.ty(self.tcx, expected_args)))
292 .find(|(_, ty)| self.same_type_modulo_infer(*ty, exp_found.found))
293 && let ObligationCauseCode::Pattern { span: Some(span), .. } = *cause.code()
294 && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span)
295 {
296 let suggestion = if expected_def.is_struct() {
297 SuggestAccessingField::Safe { span, snippet, name, ty }
298 } else if expected_def.is_union() {
299 SuggestAccessingField::Unsafe { span, snippet, name, ty }
300 } else {
301 return;
302 };
303 diag.subdiagnostic(suggestion);
304 }
305 }
306 }
307
308 pub(super) fn suggest_turning_stmt_into_expr(
309 &self,
310 cause: &ObligationCause<'tcx>,
311 exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
312 diag: &mut Diag<'_>,
313 ) {
314 let ty::error::ExpectedFound { expected, found } = exp_found;
315 if !found.peel_refs().is_unit() {
316 return;
317 }
318
319 let ObligationCauseCode::BlockTailExpression(hir_id, MatchSource::Normal) = cause.code()
320 else {
321 return;
322 };
323
324 let node = self.tcx.hir_node(*hir_id);
325 let mut blocks = ::alloc::vec::Vec::new()vec![];
326 if let hir::Node::Block(block) = node
327 && let Some(expr) = block.expr
328 && let hir::ExprKind::Path(QPath::Resolved(_, Path { res, .. })) = expr.kind
329 && let Res::Local(local) = res
330 && let Node::LetStmt(LetStmt { init: Some(init), .. }) =
331 self.tcx.parent_hir_node(*local)
332 {
333 fn collect_blocks<'hir>(expr: &hir::Expr<'hir>, blocks: &mut Vec<&hir::Block<'hir>>) {
334 match expr.kind {
335 hir::ExprKind::If(_, blk1, Some(blk2)) => {
337 collect_blocks(blk1, blocks);
338 collect_blocks(blk2, blocks);
339 }
340 hir::ExprKind::Match(_, arms, _) => {
341 for arm in arms.iter() {
343 collect_blocks(arm.body, blocks);
344 }
345 }
346 hir::ExprKind::Block(blk, _) => {
347 blocks.push(blk);
348 }
349 _ => {}
350 }
351 }
352 collect_blocks(init, &mut blocks);
353 }
354
355 let expected_inner: Ty<'_> = expected.peel_refs();
356 for block in blocks.iter() {
357 self.consider_removing_semicolon(block, expected_inner, diag);
358 }
359 }
360
361 pub fn consider_removing_semicolon(
373 &self,
374 blk: &'tcx hir::Block<'tcx>,
375 expected_ty: Ty<'tcx>,
376 diag: &mut Diag<'_>,
377 ) -> bool {
378 if let Some((span_semi, boxed)) = self.could_remove_semicolon(blk, expected_ty) {
379 if let StatementAsExpression::NeedsBoxing = boxed {
380 diag.span_suggestion_verbose(
381 span_semi,
382 "consider removing this semicolon and boxing the expression",
383 "",
384 Applicability::HasPlaceholders,
385 );
386 } else {
387 diag.span_suggestion_short(
388 span_semi,
389 "remove this semicolon to return this value",
390 "",
391 Applicability::MachineApplicable,
392 );
393 }
394 true
395 } else {
396 false
397 }
398 }
399
400 pub(crate) fn suggest_function_pointers_impl(
401 &self,
402 span: Option<Span>,
403 exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
404 diag: &mut Diag<'_>,
405 ) {
406 let ty::error::ExpectedFound { expected, found } = exp_found;
407 let expected_inner = expected.peel_refs();
408 let found_inner = found.peel_refs();
409 if !expected_inner.is_fn() || !found_inner.is_fn() {
410 return;
411 }
412 match (expected_inner.kind(), found_inner.kind()) {
413 (ty::FnPtr(sig_tys, hdr), ty::FnDef(did, args)) => {
414 let sig = sig_tys.with(*hdr);
415 let expected_sig = &(self.normalize_fn_sig)(Unnormalized::new_wip(sig));
416 let found_sig =
417 &(self.normalize_fn_sig)(self.tcx.fn_sig(*did).instantiate(self.tcx, args));
418
419 let fn_name = self.tcx.def_path_str_with_args(*did, args);
420
421 if !self.same_type_modulo_infer(*found_sig, *expected_sig)
422 || !sig.is_suggestable(self.tcx, true)
423 || self.tcx.intrinsic(*did).is_some()
424 {
425 return;
426 }
427
428 let Some(span) = span else {
429 let casting = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0} as {1}", fn_name, sig))
})format!("{fn_name} as {sig}");
430 diag.subdiagnostic(FnItemsAreDistinct);
431 diag.subdiagnostic(FnConsiderCasting { casting });
432 return;
433 };
434
435 let sugg = match (expected.is_ref(), found.is_ref()) {
436 (true, false) => {
437 FunctionPointerSuggestion::UseRef { span: span.shrink_to_lo() }
438 }
439 (false, true) => FunctionPointerSuggestion::RemoveRef { span, fn_name },
440 (true, true) => {
441 diag.subdiagnostic(FnItemsAreDistinct);
442 FunctionPointerSuggestion::CastRef { span, fn_name, sig }
443 }
444 (false, false) => {
445 diag.subdiagnostic(FnItemsAreDistinct);
446 FunctionPointerSuggestion::Cast { span: span.shrink_to_hi(), sig }
447 }
448 };
449 diag.subdiagnostic(sugg);
450 }
451 (ty::FnDef(did1, args1), ty::FnDef(did2, args2)) => {
452 let expected_sig =
453 &(self.normalize_fn_sig)(self.tcx.fn_sig(*did1).instantiate(self.tcx, args1));
454 let found_sig =
455 &(self.normalize_fn_sig)(self.tcx.fn_sig(*did2).instantiate(self.tcx, args2));
456
457 if self.same_type_modulo_infer(*expected_sig, *found_sig) {
458 diag.subdiagnostic(FnUniqTypes);
459 }
460
461 if !self.same_type_modulo_infer(*found_sig, *expected_sig)
462 || !found_sig.is_suggestable(self.tcx, true)
463 || !expected_sig.is_suggestable(self.tcx, true)
464 || self.tcx.intrinsic(*did1).is_some()
465 || self.tcx.intrinsic(*did2).is_some()
466 {
467 return;
468 }
469
470 let fn_name = self.tcx.def_path_str_with_args(*did2, args2);
471
472 let Some(span) = span else {
473 diag.subdiagnostic(FnConsiderCastingBoth { sig: *expected_sig });
474 return;
475 };
476
477 let sug = if found.is_ref() {
478 FunctionPointerSuggestion::CastBothRef {
479 span,
480 fn_name,
481 found_sig: *found_sig,
482 expected_sig: *expected_sig,
483 }
484 } else {
485 FunctionPointerSuggestion::CastBoth {
486 span: span.shrink_to_hi(),
487 found_sig: *found_sig,
488 expected_sig: *expected_sig,
489 }
490 };
491
492 diag.subdiagnostic(sug);
493 }
494 (ty::FnDef(did, args), ty::FnPtr(sig_tys, hdr)) => {
495 let expected_sig =
496 &(self.normalize_fn_sig)(self.tcx.fn_sig(*did).instantiate(self.tcx, args));
497 let found_sig = &(self.normalize_fn_sig)(Unnormalized::new_wip(sig_tys.with(*hdr)));
498
499 if !self.same_type_modulo_infer(*found_sig, *expected_sig) {
500 return;
501 }
502
503 let fn_name = self.tcx.def_path_str_with_args(*did, args);
504
505 let casting = if expected.is_ref() {
506 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("&({0} as {1})", fn_name,
found_sig))
})format!("&({fn_name} as {found_sig})")
507 } else {
508 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0} as {1}", fn_name, found_sig))
})format!("{fn_name} as {found_sig}")
509 };
510
511 diag.subdiagnostic(FnConsiderCasting { casting });
512 }
513 _ => {
514 return;
515 }
516 };
517 }
518
519 pub(super) fn suggest_function_pointers(
520 &self,
521 cause: &ObligationCause<'tcx>,
522 span: Span,
523 exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
524 terr: TypeError<'tcx>,
525 diag: &mut Diag<'_>,
526 ) {
527 {
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:527",
"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(527u32),
::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};
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!("suggest_function_pointers(cause={0:?}, exp_found={1:?})",
cause, exp_found) as &dyn Value))])
});
} else { ; }
};debug!("suggest_function_pointers(cause={:?}, exp_found={:?})", cause, exp_found);
528
529 if exp_found.expected.peel_refs().is_fn() && exp_found.found.peel_refs().is_fn() {
530 self.suggest_function_pointers_impl(Some(span), exp_found, diag);
531 } else if let TypeError::Sorts(exp_found) = terr {
532 self.suggest_function_pointers_impl(None, &exp_found, diag);
533 }
534 }
535
536 fn should_suggest_as_ref_kind(
537 &self,
538 expected: Ty<'tcx>,
539 found: Ty<'tcx>,
540 ) -> Option<SuggestAsRefKind> {
541 if let (ty::Adt(exp_def, exp_args), ty::Ref(_, found_ty, _)) =
542 (expected.kind(), found.kind())
543 && let ty::Adt(found_def, found_args) = *found_ty.kind()
544 {
545 if exp_def == &found_def {
546 let have_as_ref = &[
547 (sym::Option, SuggestAsRefKind::Option),
548 (sym::Result, SuggestAsRefKind::Result),
549 ];
550 if let Some(msg) = have_as_ref.iter().find_map(|(name, msg)| {
551 self.tcx.is_diagnostic_item(*name, exp_def.did()).then_some(msg)
552 }) {
553 let mut show_suggestion = true;
554 for (exp_ty, found_ty) in std::iter::zip(exp_args.types(), found_args.types()) {
555 match *exp_ty.kind() {
556 ty::Ref(_, exp_ty, _) => {
557 match (exp_ty.kind(), found_ty.kind()) {
558 (_, ty::Param(_))
559 | (_, ty::Infer(_))
560 | (ty::Param(_), _)
561 | (ty::Infer(_), _) => {}
562 _ if self.same_type_modulo_infer(exp_ty, found_ty) => {}
563 _ => show_suggestion = false,
564 };
565 }
566 ty::Param(_) | ty::Infer(_) => {}
567 _ => show_suggestion = false,
568 }
569 }
570 if show_suggestion {
571 return Some(*msg);
572 }
573 }
574 }
575 }
576 None
577 }
578
579 pub fn should_suggest_as_ref(&self, expected: Ty<'tcx>, found: Ty<'tcx>) -> Option<&str> {
581 match self.should_suggest_as_ref_kind(expected, found) {
582 Some(SuggestAsRefKind::Option) => Some(
583 "you can convert from `&Option<T>` to `Option<&T>` using \
584 `.as_ref()`",
585 ),
586 Some(SuggestAsRefKind::Result) => Some(
587 "you can convert from `&Result<T, E>` to \
588 `Result<&T, &E>` using `.as_ref()`",
589 ),
590 None => None,
591 }
592 }
593 pub(super) fn suggest_let_for_letchains(
597 &self,
598 cause: &ObligationCause<'_>,
599 span: Span,
600 ) -> Option<TypeErrorAdditionalDiags> {
601 struct IfVisitor {
603 found_if: bool,
604 err_span: Span,
605 }
606
607 impl<'v> Visitor<'v> for IfVisitor {
608 type Result = ControlFlow<()>;
609 fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) -> Self::Result {
610 match ex.kind {
611 hir::ExprKind::If(cond, _, _) => {
612 self.found_if = true;
613 walk_expr(self, cond)?;
614 self.found_if = false;
615 ControlFlow::Continue(())
616 }
617 _ => walk_expr(self, ex),
618 }
619 }
620
621 fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) -> Self::Result {
622 if let hir::StmtKind::Let(LetStmt {
623 span,
624 pat: hir::Pat { .. },
625 ty: None,
626 init: Some(_),
627 ..
628 }) = &ex.kind
629 && self.found_if
630 && span.eq(&self.err_span)
631 {
632 ControlFlow::Break(())
633 } else {
634 walk_stmt(self, ex)
635 }
636 }
637 }
638
639 self.tcx.hir_maybe_body_owned_by(cause.body_id).and_then(|body| {
640 IfVisitor { err_span: span, found_if: false }
641 .visit_body(&body)
642 .is_break()
643 .then(|| TypeErrorAdditionalDiags::AddLetForLetChains { span: span.shrink_to_lo() })
644 })
645 }
646
647 pub(super) fn suggest_for_all_lifetime_closure(
650 &self,
651 span: Span,
652 hir: hir::Node<'_>,
653 exp_found: &ty::error::ExpectedFound<ty::TraitRef<'tcx>>,
654 diag: &mut Diag<'_>,
655 ) {
656 let hir::Node::Expr(hir::Expr {
658 kind: hir::ExprKind::Closure(hir::Closure { body, fn_decl, .. }),
659 ..
660 }) = hir
661 else {
662 return;
663 };
664 let hir::Body { params, .. } = self.tcx.hir_body(*body);
665
666 let Some(expected) = exp_found.expected.args.get(1) else {
669 return;
670 };
671 let Some(found) = exp_found.found.args.get(1) else {
672 return;
673 };
674 let expected = expected.kind();
675 let found = found.kind();
676 if let GenericArgKind::Type(expected) = expected
678 && let GenericArgKind::Type(found) = found
679 && let ty::Tuple(expected) = expected.kind()
680 && let ty::Tuple(found) = found.kind()
681 && expected.len() == found.len()
682 {
683 let mut suggestion = "|".to_string();
684 let mut is_first = true;
685 let mut has_suggestion = false;
686
687 for (((expected, found), param_hir), arg_hir) in
688 expected.iter().zip(found.iter()).zip(params.iter()).zip(fn_decl.inputs.iter())
689 {
690 if is_first {
691 is_first = false;
692 } else {
693 suggestion += ", ";
694 }
695
696 if let ty::Ref(expected_region, _, _) = expected.kind()
697 && let ty::Ref(found_region, _, _) = found.kind()
698 && expected_region.is_bound()
699 && !found_region.is_bound()
700 && let hir::TyKind::Infer(()) = arg_hir.kind
701 {
702 if param_hir.pat.span == param_hir.ty_span {
705 let Ok(pat) =
707 self.tcx.sess.source_map().span_to_snippet(param_hir.pat.span)
708 else {
709 return;
710 };
711 suggestion += &::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}: &_", pat))
})format!("{pat}: &_");
712 } else {
713 let Ok(pat) =
715 self.tcx.sess.source_map().span_to_snippet(param_hir.pat.span)
716 else {
717 return;
718 };
719 let Ok(ty) = self.tcx.sess.source_map().span_to_snippet(param_hir.ty_span)
720 else {
721 return;
722 };
723 suggestion += &::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}: &{1}", pat, ty))
})format!("{pat}: &{ty}");
724 }
725 has_suggestion = true;
726 } else {
727 let Ok(arg) = self.tcx.sess.source_map().span_to_snippet(param_hir.span) else {
728 return;
729 };
730 suggestion += &arg;
732 }
733 }
734 suggestion += "|";
735
736 if has_suggestion {
737 diag.span_suggestion_verbose(
738 span,
739 "consider specifying the type of the closure parameters",
740 suggestion,
741 Applicability::MaybeIncorrect,
742 );
743 }
744 }
745 }
746}
747
748impl<'tcx> TypeErrCtxt<'_, 'tcx> {
749 fn could_remove_semicolon(
752 &self,
753 blk: &'tcx hir::Block<'tcx>,
754 expected_ty: Ty<'tcx>,
755 ) -> Option<(Span, StatementAsExpression)> {
756 let blk = blk.innermost_block();
757 if blk.expr.is_some() {
759 return None;
760 }
761 let last_stmt = blk.stmts.last()?;
762 let hir::StmtKind::Semi(last_expr) = last_stmt.kind else {
763 return None;
764 };
765 let last_expr_ty = self.typeck_results.as_ref()?.expr_ty_opt(last_expr)?;
766 let needs_box = match (last_expr_ty.kind(), expected_ty.kind()) {
767 _ if last_expr_ty.references_error() => return None,
768 _ if self.same_type_modulo_infer(last_expr_ty, expected_ty) => {
769 StatementAsExpression::CorrectType
770 }
771 (
772 ty::Alias(ty::AliasTy { kind: ty::Opaque { def_id: last_def_id }, .. }),
773 ty::Alias(ty::AliasTy { kind: ty::Opaque { def_id: exp_def_id }, .. }),
774 ) if last_def_id == exp_def_id => StatementAsExpression::CorrectType,
775 (
776 ty::Alias(ty::AliasTy {
777 kind: ty::Opaque { def_id: last_def_id },
778 args: last_bounds,
779 ..
780 }),
781 ty::Alias(ty::AliasTy {
782 kind: ty::Opaque { def_id: exp_def_id },
783 args: exp_bounds,
784 ..
785 }),
786 ) => {
787 {
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:787",
"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(787u32),
::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};
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!("both opaque, likely future {0:?} {1:?} {2:?} {3:?}",
last_def_id, last_bounds, exp_def_id, exp_bounds) as
&dyn Value))])
});
} else { ; }
};debug!(
788 "both opaque, likely future {:?} {:?} {:?} {:?}",
789 last_def_id, last_bounds, exp_def_id, exp_bounds
790 );
791
792 let last_local_id = last_def_id.as_local()?;
793 let exp_local_id = exp_def_id.as_local()?;
794
795 match (
796 &self.tcx.hir_expect_opaque_ty(last_local_id),
797 &self.tcx.hir_expect_opaque_ty(exp_local_id),
798 ) {
799 (
800 hir::OpaqueTy { bounds: last_bounds, .. },
801 hir::OpaqueTy { bounds: exp_bounds, .. },
802 ) if std::iter::zip(*last_bounds, *exp_bounds).all(|(left, right)| match (
803 left, right,
804 ) {
805 (hir::GenericBound::Trait(tl), hir::GenericBound::Trait(tr))
807 if tl.trait_ref.trait_def_id() == tr.trait_ref.trait_def_id()
808 && tl.modifiers == tr.modifiers =>
809 {
810 true
811 }
812 _ => false,
813 }) =>
814 {
815 StatementAsExpression::NeedsBoxing
816 }
817 _ => StatementAsExpression::CorrectType,
818 }
819 }
820 _ => return None,
821 };
822 let span = if last_stmt.span.from_expansion() {
823 let mac_call = rustc_span::source_map::original_sp(last_stmt.span, blk.span);
824 self.tcx.sess.source_map().mac_call_stmt_semi_span(mac_call)?
825 } else {
826 self.tcx
827 .sess
828 .source_map()
829 .span_extend_while_whitespace(last_expr.span)
830 .shrink_to_hi()
831 .with_hi(last_stmt.span.hi())
832 };
833
834 Some((span, needs_box))
835 }
836
837 fn consider_returning_binding_diag(
840 &self,
841 blk: &'tcx hir::Block<'tcx>,
842 expected_ty: Ty<'tcx>,
843 ) -> Option<SuggestRemoveSemiOrReturnBinding> {
844 let blk = blk.innermost_block();
845 if blk.expr.is_some() {
847 return None;
848 }
849 let mut shadowed = FxIndexSet::default();
850 let mut candidate_idents = ::alloc::vec::Vec::new()vec![];
851 let mut find_compatible_candidates = |pat: &hir::Pat<'_>| {
852 if let hir::PatKind::Binding(_, hir_id, ident, _) = &pat.kind
853 && let Some(pat_ty) = self
854 .typeck_results
855 .as_ref()
856 .and_then(|typeck_results| typeck_results.node_type_opt(*hir_id))
857 {
858 let pat_ty = self.resolve_vars_if_possible(pat_ty);
859 if self.same_type_modulo_infer(pat_ty, expected_ty)
860 && !(pat_ty, expected_ty).references_error()
861 && shadowed.insert(ident.name)
862 {
863 candidate_idents.push((*ident, pat_ty));
864 }
865 }
866 true
867 };
868
869 for stmt in blk.stmts.iter().rev() {
870 let hir::StmtKind::Let(local) = &stmt.kind else {
871 continue;
872 };
873 local.pat.walk(&mut find_compatible_candidates);
874 }
875 match self.tcx.parent_hir_node(blk.hir_id) {
876 hir::Node::Expr(hir::Expr { hir_id, .. }) => match self.tcx.parent_hir_node(*hir_id) {
877 hir::Node::Arm(hir::Arm { pat, .. }) => {
878 pat.walk(&mut find_compatible_candidates);
879 }
880
881 hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn { body, .. }, .. })
882 | hir::Node::ImplItem(hir::ImplItem {
883 kind: hir::ImplItemKind::Fn(_, body), ..
884 })
885 | hir::Node::TraitItem(hir::TraitItem {
886 kind: hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(body)),
887 ..
888 })
889 | hir::Node::Expr(hir::Expr {
890 kind: hir::ExprKind::Closure(hir::Closure { body, .. }),
891 ..
892 }) => {
893 for param in self.tcx.hir_body(*body).params {
894 param.pat.walk(&mut find_compatible_candidates);
895 }
896 }
897 hir::Node::Expr(hir::Expr {
898 kind:
899 hir::ExprKind::If(
900 hir::Expr { kind: hir::ExprKind::Let(let_), .. },
901 then_block,
902 _,
903 ),
904 ..
905 }) if then_block.hir_id == *hir_id => {
906 let_.pat.walk(&mut find_compatible_candidates);
907 }
908 _ => {}
909 },
910 _ => {}
911 }
912
913 match &candidate_idents[..] {
914 [(ident, _ty)] => {
915 let sm = self.tcx.sess.source_map();
916 let (span, sugg) = if let Some(stmt) = blk.stmts.last() {
917 let stmt_span = sm.stmt_span(stmt.span, blk.span);
918 let sugg = if sm.is_multiline(blk.span)
919 && let Some(spacing) = sm.indentation_before(stmt_span)
920 {
921 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("\n{0}{1}", spacing, ident))
})format!("\n{spacing}{ident}")
922 } else {
923 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!(" {0}", ident))
})format!(" {ident}")
924 };
925 (stmt_span.shrink_to_hi(), sugg)
926 } else {
927 let sugg = if sm.is_multiline(blk.span)
928 && let Some(spacing) = sm.indentation_before(blk.span.shrink_to_lo())
929 {
930 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("\n{0} {1}\n{0}", spacing,
ident))
})format!("\n{spacing} {ident}\n{spacing}")
931 } else {
932 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!(" {0} ", ident))
})format!(" {ident} ")
933 };
934 let left_span = sm.span_through_char(blk.span, '{').shrink_to_hi();
935 (sm.span_extend_while_whitespace(left_span), sugg)
936 };
937 Some(SuggestRemoveSemiOrReturnBinding::Add { sp: span, code: sugg, ident: *ident })
938 }
939 values if (1..3).contains(&values.len()) => {
940 let spans = values.iter().map(|(ident, _)| ident.span).collect::<Vec<_>>();
941 Some(SuggestRemoveSemiOrReturnBinding::AddOne { spans: spans.into() })
942 }
943 _ => None,
944 }
945 }
946
947 pub fn consider_returning_binding(
948 &self,
949 blk: &'tcx hir::Block<'tcx>,
950 expected_ty: Ty<'tcx>,
951 err: &mut Diag<'_>,
952 ) -> bool {
953 let diag = self.consider_returning_binding_diag(blk, expected_ty);
954 match diag {
955 Some(diag) => {
956 err.subdiagnostic(diag);
957 true
958 }
959 None => false,
960 }
961 }
962}