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(
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};
__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!(
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_def_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.tcx.get_impl_future_output_ty(exp_found.expected),
194 self.tcx.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(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(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};
__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!(
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).skip_norm_wip()))
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 args = args.no_bound_vars().unwrap();
415
416 let sig = sig_tys.with(*hdr);
417 let expected_sig = self.normalize_fn_sig(Unnormalized::new_wip(sig));
418 let found_sig =
419 self.normalize_fn_sig(self.tcx.fn_sig(*did).instantiate(self.tcx, args));
420
421 let fn_name = self.tcx.def_path_str_with_args(*did, args);
422
423 if !self.same_type_modulo_infer(found_sig, expected_sig)
424 || !sig.is_suggestable(self.tcx, true)
425 || self.tcx.intrinsic(*did).is_some()
426 {
427 return;
428 }
429
430 let Some(span) = span else {
431 let casting = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0} as {1}", fn_name, sig))
})format!("{fn_name} as {sig}");
432 diag.subdiagnostic(FnItemsAreDistinct);
433 diag.subdiagnostic(FnConsiderCasting { casting });
434 return;
435 };
436
437 let sugg = match (expected.is_ref(), found.is_ref()) {
438 (true, false) => {
439 FunctionPointerSuggestion::UseRef { span: span.shrink_to_lo() }
440 }
441 (false, true) => FunctionPointerSuggestion::RemoveRef { span, fn_name },
442 (true, true) => {
443 diag.subdiagnostic(FnItemsAreDistinct);
444 FunctionPointerSuggestion::CastRef { span, fn_name, sig }
445 }
446 (false, false) => {
447 diag.subdiagnostic(FnItemsAreDistinct);
448 FunctionPointerSuggestion::Cast { span: span.shrink_to_hi(), sig }
449 }
450 };
451 diag.subdiagnostic(sugg);
452 }
453 (ty::FnDef(did1, args1), ty::FnDef(did2, args2)) => {
454 let args1 = args1.no_bound_vars().unwrap();
455 let args2 = args2.no_bound_vars().unwrap();
456
457 let expected_sig =
458 self.normalize_fn_sig(self.tcx.fn_sig(*did1).instantiate(self.tcx, args1));
459 let found_sig =
460 self.normalize_fn_sig(self.tcx.fn_sig(*did2).instantiate(self.tcx, args2));
461
462 if self.same_type_modulo_infer(expected_sig, found_sig) {
463 diag.subdiagnostic(FnUniqTypes);
464 }
465
466 if !self.same_type_modulo_infer(found_sig, expected_sig)
467 || !found_sig.is_suggestable(self.tcx, true)
468 || !expected_sig.is_suggestable(self.tcx, true)
469 || self.tcx.intrinsic(*did1).is_some()
470 || self.tcx.intrinsic(*did2).is_some()
471 {
472 return;
473 }
474
475 let fn_name = self.tcx.def_path_str_with_args(*did2, args2);
476
477 let Some(span) = span else {
478 diag.subdiagnostic(FnConsiderCastingBoth { sig: expected_sig });
479 return;
480 };
481
482 let sug = if found.is_ref() {
483 FunctionPointerSuggestion::CastBothRef {
484 span,
485 fn_name,
486 found_sig,
487 expected_sig,
488 }
489 } else {
490 FunctionPointerSuggestion::CastBoth {
491 span: span.shrink_to_hi(),
492 found_sig,
493 expected_sig,
494 }
495 };
496
497 diag.subdiagnostic(sug);
498 }
499 (ty::FnDef(did, args), ty::FnPtr(sig_tys, hdr)) => {
500 let args = args.no_bound_vars().unwrap();
501
502 let expected_sig =
503 self.normalize_fn_sig(self.tcx.fn_sig(*did).instantiate(self.tcx, args));
504 let found_sig = self.normalize_fn_sig(Unnormalized::new_wip(sig_tys.with(*hdr)));
505
506 if !self.same_type_modulo_infer(found_sig, expected_sig) {
507 return;
508 }
509
510 let fn_name = self.tcx.def_path_str_with_args(*did, args);
511
512 let casting = if expected.is_ref() {
513 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("&({0} as {1})", fn_name,
found_sig))
})format!("&({fn_name} as {found_sig})")
514 } else {
515 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0} as {1}", fn_name, found_sig))
})format!("{fn_name} as {found_sig}")
516 };
517
518 diag.subdiagnostic(FnConsiderCasting { casting });
519 }
520 _ => {
521 return;
522 }
523 };
524 }
525
526 pub(super) fn suggest_function_pointers(
527 &self,
528 cause: &ObligationCause<'tcx>,
529 span: Span,
530 exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
531 terr: TypeError<'tcx>,
532 diag: &mut Diag<'_>,
533 ) {
534 {
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:534",
"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(534u32),
::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);
535
536 if exp_found.expected.peel_refs().is_fn() && exp_found.found.peel_refs().is_fn() {
537 self.suggest_function_pointers_impl(Some(span), exp_found, diag);
538 } else if let TypeError::Sorts(exp_found) = terr {
539 self.suggest_function_pointers_impl(None, &exp_found, diag);
540 }
541 }
542
543 fn should_suggest_as_ref_kind(
544 &self,
545 expected: Ty<'tcx>,
546 found: Ty<'tcx>,
547 ) -> Option<SuggestAsRefKind> {
548 if let (ty::Adt(exp_def, exp_args), ty::Ref(_, found_ty, _)) =
549 (expected.kind(), found.kind())
550 && let ty::Adt(found_def, found_args) = *found_ty.kind()
551 {
552 if exp_def == &found_def {
553 let have_as_ref = &[
554 (sym::Option, SuggestAsRefKind::Option),
555 (sym::Result, SuggestAsRefKind::Result),
556 ];
557 if let Some(msg) = have_as_ref.iter().find_map(|(name, msg)| {
558 self.tcx.is_diagnostic_item(*name, exp_def.did()).then_some(msg)
559 }) {
560 let mut show_suggestion = true;
561 for (exp_ty, found_ty) in std::iter::zip(exp_args.types(), found_args.types()) {
562 match *exp_ty.kind() {
563 ty::Ref(_, exp_ty, _) => {
564 match (exp_ty.kind(), found_ty.kind()) {
565 (_, ty::Param(_))
566 | (_, ty::Infer(_))
567 | (ty::Param(_), _)
568 | (ty::Infer(_), _) => {}
569 _ if self.same_type_modulo_infer(exp_ty, found_ty) => {}
570 _ => show_suggestion = false,
571 };
572 }
573 ty::Param(_) | ty::Infer(_) => {}
574 _ => show_suggestion = false,
575 }
576 }
577 if show_suggestion {
578 return Some(*msg);
579 }
580 }
581 }
582 }
583 None
584 }
585
586 pub fn should_suggest_as_ref(&self, expected: Ty<'tcx>, found: Ty<'tcx>) -> Option<&str> {
588 match self.should_suggest_as_ref_kind(expected, found) {
589 Some(SuggestAsRefKind::Option) => Some(
590 "you can convert from `&Option<T>` to `Option<&T>` using \
591 `.as_ref()`",
592 ),
593 Some(SuggestAsRefKind::Result) => Some(
594 "you can convert from `&Result<T, E>` to \
595 `Result<&T, &E>` using `.as_ref()`",
596 ),
597 None => None,
598 }
599 }
600 pub(super) fn suggest_let_for_letchains(
604 &self,
605 cause: &ObligationCause<'_>,
606 span: Span,
607 ) -> Option<TypeErrorAdditionalDiags> {
608 struct IfVisitor {
610 found_if: bool,
611 err_span: Span,
612 }
613
614 impl<'v> Visitor<'v> for IfVisitor {
615 type Result = ControlFlow<()>;
616 fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) -> Self::Result {
617 match ex.kind {
618 hir::ExprKind::If(cond, _, _) => {
619 self.found_if = true;
620 walk_expr(self, cond)?;
621 self.found_if = false;
622 ControlFlow::Continue(())
623 }
624 _ => walk_expr(self, ex),
625 }
626 }
627
628 fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) -> Self::Result {
629 if let hir::StmtKind::Let(LetStmt {
630 span,
631 pat: hir::Pat { .. },
632 ty: None,
633 init: Some(_),
634 ..
635 }) = &ex.kind
636 && self.found_if
637 && span.eq(&self.err_span)
638 {
639 ControlFlow::Break(())
640 } else {
641 walk_stmt(self, ex)
642 }
643 }
644 }
645
646 self.tcx.hir_maybe_body_owned_by(cause.body_def_id).and_then(|body| {
647 IfVisitor { err_span: span, found_if: false }
648 .visit_body(&body)
649 .is_break()
650 .then(|| TypeErrorAdditionalDiags::AddLetForLetChains { span: span.shrink_to_lo() })
651 })
652 }
653
654 pub(super) fn suggest_for_all_lifetime_closure(
657 &self,
658 span: Span,
659 hir: hir::Node<'_>,
660 exp_found: &ty::error::ExpectedFound<ty::TraitRef<'tcx>>,
661 diag: &mut Diag<'_>,
662 ) {
663 let hir::Node::Expr(hir::Expr {
665 kind: hir::ExprKind::Closure(hir::Closure { body, fn_decl, .. }),
666 ..
667 }) = hir
668 else {
669 return;
670 };
671 let hir::Body { params, .. } = self.tcx.hir_body(*body);
672
673 let Some(expected) = exp_found.expected.args.get(1) else {
676 return;
677 };
678 let Some(found) = exp_found.found.args.get(1) else {
679 return;
680 };
681 let expected = expected.kind();
682 let found = found.kind();
683 if let GenericArgKind::Type(expected) = expected
685 && let GenericArgKind::Type(found) = found
686 && let ty::Tuple(expected) = expected.kind()
687 && let ty::Tuple(found) = found.kind()
688 && expected.len() == found.len()
689 {
690 let mut suggestion = "|".to_string();
691 let mut is_first = true;
692 let mut has_suggestion = false;
693
694 for (((expected, found), param_hir), arg_hir) in
695 expected.iter().zip(found.iter()).zip(params.iter()).zip(fn_decl.inputs.iter())
696 {
697 if is_first {
698 is_first = false;
699 } else {
700 suggestion += ", ";
701 }
702
703 if let ty::Ref(expected_region, _, _) = expected.kind()
704 && let ty::Ref(found_region, _, _) = found.kind()
705 && expected_region.is_bound()
706 && !found_region.is_bound()
707 && let hir::TyKind::Infer(()) = arg_hir.kind
708 {
709 if param_hir.pat.span == param_hir.ty_span {
712 let Ok(pat) =
714 self.tcx.sess.source_map().span_to_snippet(param_hir.pat.span)
715 else {
716 return;
717 };
718 suggestion += &::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}: &_", pat))
})format!("{pat}: &_");
719 } else {
720 let Ok(pat) =
722 self.tcx.sess.source_map().span_to_snippet(param_hir.pat.span)
723 else {
724 return;
725 };
726 let Ok(ty) = self.tcx.sess.source_map().span_to_snippet(param_hir.ty_span)
727 else {
728 return;
729 };
730 suggestion += &::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}: &{1}", pat, ty))
})format!("{pat}: &{ty}");
731 }
732 has_suggestion = true;
733 } else {
734 let Ok(arg) = self.tcx.sess.source_map().span_to_snippet(param_hir.span) else {
735 return;
736 };
737 suggestion += &arg;
739 }
740 }
741 suggestion += "|";
742
743 if has_suggestion {
744 diag.span_suggestion_verbose(
745 span,
746 "consider specifying the type of the closure parameters",
747 suggestion,
748 Applicability::MaybeIncorrect,
749 );
750 }
751 }
752 }
753}
754
755impl<'tcx> TypeErrCtxt<'_, 'tcx> {
756 fn could_remove_semicolon(
759 &self,
760 blk: &'tcx hir::Block<'tcx>,
761 expected_ty: Ty<'tcx>,
762 ) -> Option<(Span, StatementAsExpression)> {
763 let blk = blk.innermost_block();
764 if blk.expr.is_some() {
766 return None;
767 }
768 let last_stmt = blk.stmts.last()?;
769 let hir::StmtKind::Semi(last_expr) = last_stmt.kind else {
770 return None;
771 };
772 let last_expr_ty = self.typeck_results.as_ref()?.expr_ty_opt(last_expr)?;
773 let needs_box = match (last_expr_ty.kind(), expected_ty.kind()) {
774 _ if last_expr_ty.references_error() => return None,
775 _ if self.same_type_modulo_infer(last_expr_ty, expected_ty) => {
776 StatementAsExpression::CorrectType
777 }
778 (
779 ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id: last_def_id }, .. }),
780 ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id: exp_def_id }, .. }),
781 ) if last_def_id == exp_def_id => StatementAsExpression::CorrectType,
782 (
783 ty::Alias(
784 _,
785 ty::AliasTy {
786 kind: ty::Opaque { def_id: last_def_id }, args: last_bounds, ..
787 },
788 ),
789 ty::Alias(
790 _,
791 ty::AliasTy {
792 kind: ty::Opaque { def_id: exp_def_id }, args: exp_bounds, ..
793 },
794 ),
795 ) => {
796 {
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:796",
"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(796u32),
::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!(
797 "both opaque, likely future {:?} {:?} {:?} {:?}",
798 last_def_id, last_bounds, exp_def_id, exp_bounds
799 );
800
801 let last_local_id = last_def_id.as_local()?;
802 let exp_local_id = exp_def_id.as_local()?;
803
804 match (
805 &self.tcx.hir_expect_opaque_ty(last_local_id),
806 &self.tcx.hir_expect_opaque_ty(exp_local_id),
807 ) {
808 (
809 hir::OpaqueTy { bounds: last_bounds, .. },
810 hir::OpaqueTy { bounds: exp_bounds, .. },
811 ) if std::iter::zip(*last_bounds, *exp_bounds).all(|(left, right)| match (
812 left, right,
813 ) {
814 (hir::GenericBound::Trait(tl), hir::GenericBound::Trait(tr))
816 if tl.trait_ref.trait_def_id() == tr.trait_ref.trait_def_id()
817 && tl.modifiers == tr.modifiers =>
818 {
819 true
820 }
821 _ => false,
822 }) =>
823 {
824 StatementAsExpression::NeedsBoxing
825 }
826 _ => StatementAsExpression::CorrectType,
827 }
828 }
829 _ => return None,
830 };
831 let span = if last_stmt.span.from_expansion() {
832 let mac_call = rustc_span::source_map::original_sp(last_stmt.span, blk.span);
833 self.tcx.sess.source_map().mac_call_stmt_semi_span(mac_call)?
834 } else {
835 self.tcx
836 .sess
837 .source_map()
838 .span_extend_while_whitespace(last_expr.span)
839 .shrink_to_hi()
840 .with_hi(last_stmt.span.hi())
841 };
842
843 Some((span, needs_box))
844 }
845
846 fn consider_returning_binding_diag(
849 &self,
850 blk: &'tcx hir::Block<'tcx>,
851 expected_ty: Ty<'tcx>,
852 ) -> Option<SuggestRemoveSemiOrReturnBinding> {
853 let blk = blk.innermost_block();
854 if blk.expr.is_some() {
856 return None;
857 }
858 let mut shadowed = FxIndexSet::default();
859 let mut candidate_idents = ::alloc::vec::Vec::new()vec![];
860 let mut find_compatible_candidates = |pat: &hir::Pat<'_>| {
861 if let hir::PatKind::Binding(_, hir_id, ident, _) = &pat.kind
862 && let Some(pat_ty) = self
863 .typeck_results
864 .as_ref()
865 .and_then(|typeck_results| typeck_results.node_type_opt(*hir_id))
866 {
867 let pat_ty = self.resolve_vars_if_possible(pat_ty);
868 if self.same_type_modulo_infer(pat_ty, expected_ty)
869 && !(pat_ty, expected_ty).references_error()
870 && shadowed.insert(ident.name)
871 {
872 candidate_idents.push((*ident, pat_ty));
873 }
874 }
875 true
876 };
877
878 for stmt in blk.stmts.iter().rev() {
879 let hir::StmtKind::Let(local) = &stmt.kind else {
880 continue;
881 };
882 local.pat.walk(&mut find_compatible_candidates);
883 }
884 match self.tcx.parent_hir_node(blk.hir_id) {
885 hir::Node::Expr(hir::Expr { hir_id, .. }) => match self.tcx.parent_hir_node(*hir_id) {
886 hir::Node::Arm(hir::Arm { pat, .. }) => {
887 pat.walk(&mut find_compatible_candidates);
888 }
889
890 hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn { body, .. }, .. })
891 | hir::Node::ImplItem(hir::ImplItem {
892 kind: hir::ImplItemKind::Fn(_, body), ..
893 })
894 | hir::Node::TraitItem(hir::TraitItem {
895 kind: hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(body)),
896 ..
897 })
898 | hir::Node::Expr(hir::Expr {
899 kind: hir::ExprKind::Closure(hir::Closure { body, .. }),
900 ..
901 }) => {
902 for param in self.tcx.hir_body(*body).params {
903 param.pat.walk(&mut find_compatible_candidates);
904 }
905 }
906 hir::Node::Expr(hir::Expr {
907 kind:
908 hir::ExprKind::If(
909 hir::Expr { kind: hir::ExprKind::Let(let_), .. },
910 then_block,
911 _,
912 ),
913 ..
914 }) if then_block.hir_id == *hir_id => {
915 let_.pat.walk(&mut find_compatible_candidates);
916 }
917 _ => {}
918 },
919 _ => {}
920 }
921
922 match &candidate_idents[..] {
923 [(ident, _ty)] => {
924 let sm = self.tcx.sess.source_map();
925 let (span, sugg) = if let Some(stmt) = blk.stmts.last() {
926 let stmt_span = sm.stmt_span(stmt.span, blk.span);
927 let sugg = if sm.is_multiline(blk.span)
928 && let Some(spacing) = sm.indentation_before(stmt_span)
929 {
930 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("\n{0}{1}", spacing, ident))
})format!("\n{spacing}{ident}")
931 } else {
932 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!(" {0}", ident))
})format!(" {ident}")
933 };
934 (stmt_span.shrink_to_hi(), sugg)
935 } else {
936 let sugg = if sm.is_multiline(blk.span)
937 && let Some(spacing) = sm.indentation_before(blk.span.shrink_to_lo())
938 {
939 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("\n{0} {1}\n{0}", spacing,
ident))
})format!("\n{spacing} {ident}\n{spacing}")
940 } else {
941 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!(" {0} ", ident))
})format!(" {ident} ")
942 };
943 let left_span = sm.span_through_char(blk.span, '{').shrink_to_hi();
944 (sm.span_extend_while_whitespace(left_span), sugg)
945 };
946 Some(SuggestRemoveSemiOrReturnBinding::Add { sp: span, code: sugg, ident: *ident })
947 }
948 values if (1..3).contains(&values.len()) => {
949 let spans = values.iter().map(|(ident, _)| ident.span).collect::<Vec<_>>();
950 Some(SuggestRemoveSemiOrReturnBinding::AddOne { spans: spans.into() })
951 }
952 _ => None,
953 }
954 }
955
956 pub fn consider_returning_binding(
957 &self,
958 blk: &'tcx hir::Block<'tcx>,
959 expected_ty: Ty<'tcx>,
960 err: &mut Diag<'_>,
961 ) -> bool {
962 let diag = self.consider_returning_binding_diag(blk, expected_ty);
963 match diag {
964 Some(diag) => {
965 err.subdiagnostic(diag);
966 true
967 }
968 None => false,
969 }
970 }
971}