Skip to main content

rustc_trait_selection/error_reporting/infer/
suggest.rs

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        // Heavily inspired by `FnCtxt::suggest_compatible_variants`, with
95        // some modifications due to that being in typeck and this being in infer.
96        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                        // FIXME #56861: DRYer prelude filtering
112                        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                    // More than one matching variant.
135                    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    /// A possible error is to forget to add `.await` when using futures:
147    ///
148    /// ```compile_fail,E0308
149    /// async fn make_u32() -> u32 {
150    ///     22
151    /// }
152    ///
153    /// fn take_u32(x: u32) {}
154    ///
155    /// async fn foo() {
156    ///     let x = make_u32();
157    ///     take_u32(x);
158    /// }
159    /// ```
160    ///
161    /// This routine checks if the found type `T` implements `Future<Output=U>` where `U` is the
162    /// expected type. In an async body, it suggests adding `.await` to the expression. For a
163    /// return expression in a synchronous function, it suggests making the function async and
164    /// awaiting the expression together.
165    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                // FIXME: Seems like we can't have a suggestion and a note with different spans in a single subdiagnostic
233                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                    // `blk1` and `blk2` must be have the same types, it will be reported before reaching here
398                    hir::ExprKind::If(_, blk1, Some(blk2)) => {
399                        collect_blocks(blk1, blocks);
400                        collect_blocks(blk2, blocks);
401                    }
402                    hir::ExprKind::Match(_, arms, _) => {
403                        // all arms must have same types
404                        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    /// A common error is to add an extra semicolon:
424    ///
425    /// ```compile_fail,E0308
426    /// fn foo() -> usize {
427    ///     22;
428    /// }
429    /// ```
430    ///
431    /// This routine checks if the final statement in a block is an
432    /// expression with an explicit semicolon whose type is compatible
433    /// with `expected_ty`. If so, it suggests removing the semicolon.
434    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    // FIXME: Remove once `rustc_hir_typeck` is migrated to diagnostic structs
649    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    /// Try to find code with pattern `if Some(..) = expr`
663    /// use a `visitor` to mark the `if` which its span contains given error span,
664    /// and then try to find a assignment in the `cond` part, which span is equal with error span
665    pub(super) fn suggest_let_for_letchains(
666        &self,
667        cause: &ObligationCause<'_>,
668        span: Span,
669    ) -> Option<TypeErrorAdditionalDiags> {
670        /// Find the if expression with given span
671        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    /// For "one type is more general than the other" errors on closures, suggest changing the lifetime
717    /// of the parameters to accept all lifetimes.
718    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        // 0. Extract fn_decl from hir
726        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        // 1. Get the args of the closure.
736        // 2. Assume exp_found is FnOnce / FnMut / Fn, we can extract function parameters from [1].
737        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        // 3. Extract the tuple type from Fn trait and suggest the change.
746        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 the expected region is late bound, the found region is not, and users are asking compiler
772                    // to infer the type, we can suggest adding `: &_`.
773                    if param_hir.pat.span == param_hir.ty_span {
774                        // for `|x|`, `|_|`, `|x: impl Foo|`
775                        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                        // for `|x: ty|`, `|_: ty|`
783                        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                    // Otherwise, keep it as-is.
800                    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    /// Be helpful when the user wrote `{... expr; }` and taking the `;` off
819    /// is enough to fix the error.
820    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        // Do not suggest if we have a tail expr.
827        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                        // FIXME: Suspicious
877                        (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    /// Suggest returning a local binding with a compatible type if the block
909    /// has no return expression.
910    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        // Do not suggest if we have a tail expr.
917        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}