use std::iter;
use rustc_data_structures::fx::FxIndexSet;
use rustc_errors::{
    Applicability, Diag, E0309, E0310, E0311, E0495, Subdiagnostic, struct_span_code_err,
};
use rustc_hir::def::DefKind;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::intravisit::Visitor;
use rustc_hir::{self as hir, ParamName};
use rustc_middle::bug;
use rustc_middle::traits::ObligationCauseCode;
use rustc_middle::ty::error::TypeError;
use rustc_middle::ty::{self, IsSuggestable, Region, Ty, TyCtxt, TypeVisitableExt as _};
use rustc_span::symbol::kw;
use rustc_span::{BytePos, ErrorGuaranteed, Span, Symbol};
use rustc_type_ir::Upcast as _;
use tracing::{debug, instrument};
use super::ObligationCauseAsDiagArg;
use super::nice_region_error::find_anon_type;
use crate::error_reporting::TypeErrCtxt;
use crate::error_reporting::infer::ObligationCauseExt;
use crate::errors::{
    self, FulfillReqLifetime, LfBoundNotSatisfied, OutlivesBound, OutlivesContent,
    RefLongerThanData, RegionOriginNote, WhereClauseSuggestions, note_and_explain,
};
use crate::fluent_generated as fluent;
use crate::infer::region_constraints::GenericKind;
use crate::infer::{self, InferCtxt, RegionResolutionError, RegionVariableOrigin, SubregionOrigin};
impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
    pub fn report_region_errors(
        &self,
        generic_param_scope: LocalDefId,
        errors: &[RegionResolutionError<'tcx>],
    ) -> ErrorGuaranteed {
        assert!(!errors.is_empty());
        if let Some(guaranteed) = self.infcx.tainted_by_errors() {
            return guaranteed;
        }
        debug!("report_region_errors(): {} errors to start", errors.len());
        let errors = self.process_errors(errors);
        debug!("report_region_errors: {} errors after preprocessing", errors.len());
        let mut guar = None;
        for error in errors {
            debug!("report_region_errors: error = {:?}", error);
            let e = if let Some(guar) =
                self.try_report_nice_region_error(generic_param_scope, &error)
            {
                guar
            } else {
                match error.clone() {
                    RegionResolutionError::ConcreteFailure(origin, sub, sup) => {
                        if sub.is_placeholder() || sup.is_placeholder() {
                            self.report_placeholder_failure(generic_param_scope, origin, sub, sup)
                                .emit()
                        } else {
                            self.report_concrete_failure(generic_param_scope, origin, sub, sup)
                                .emit()
                        }
                    }
                    RegionResolutionError::GenericBoundFailure(origin, param_ty, sub) => self
                        .report_generic_bound_failure(
                            generic_param_scope,
                            origin.span(),
                            Some(origin),
                            param_ty,
                            sub,
                        ),
                    RegionResolutionError::SubSupConflict(
                        _,
                        var_origin,
                        sub_origin,
                        sub_r,
                        sup_origin,
                        sup_r,
                        _,
                    ) => {
                        if sub_r.is_placeholder() {
                            self.report_placeholder_failure(
                                generic_param_scope,
                                sub_origin,
                                sub_r,
                                sup_r,
                            )
                            .emit()
                        } else if sup_r.is_placeholder() {
                            self.report_placeholder_failure(
                                generic_param_scope,
                                sup_origin,
                                sub_r,
                                sup_r,
                            )
                            .emit()
                        } else {
                            self.report_sub_sup_conflict(
                                generic_param_scope,
                                var_origin,
                                sub_origin,
                                sub_r,
                                sup_origin,
                                sup_r,
                            )
                        }
                    }
                    RegionResolutionError::UpperBoundUniverseConflict(
                        _,
                        _,
                        _,
                        sup_origin,
                        sup_r,
                    ) => {
                        assert!(sup_r.is_placeholder());
                        let sub_r = self.tcx.lifetimes.re_erased;
                        self.report_placeholder_failure(
                            generic_param_scope,
                            sup_origin,
                            sub_r,
                            sup_r,
                        )
                        .emit()
                    }
                    RegionResolutionError::CannotNormalize(clause, origin) => {
                        let clause: ty::Clause<'tcx> =
                            clause.map_bound(ty::ClauseKind::TypeOutlives).upcast(self.tcx);
                        self.tcx
                            .dcx()
                            .struct_span_err(origin.span(), format!("cannot normalize `{clause}`"))
                            .emit()
                    }
                }
            };
            guar = Some(e)
        }
        guar.unwrap()
    }
    fn process_errors(
        &self,
        errors: &[RegionResolutionError<'tcx>],
    ) -> Vec<RegionResolutionError<'tcx>> {
        debug!("process_errors()");
        let is_bound_failure = |e: &RegionResolutionError<'tcx>| match *e {
            RegionResolutionError::GenericBoundFailure(..) => true,
            RegionResolutionError::ConcreteFailure(..)
            | RegionResolutionError::SubSupConflict(..)
            | RegionResolutionError::UpperBoundUniverseConflict(..)
            | RegionResolutionError::CannotNormalize(..) => false,
        };
        let mut errors = if errors.iter().all(|e| is_bound_failure(e)) {
            errors.to_owned()
        } else {
            errors.iter().filter(|&e| !is_bound_failure(e)).cloned().collect()
        };
        errors.sort_by_key(|u| match *u {
            RegionResolutionError::ConcreteFailure(ref sro, _, _) => sro.span(),
            RegionResolutionError::GenericBoundFailure(ref sro, _, _) => sro.span(),
            RegionResolutionError::SubSupConflict(_, ref rvo, _, _, _, _, _) => rvo.span(),
            RegionResolutionError::UpperBoundUniverseConflict(_, ref rvo, _, _, _) => rvo.span(),
            RegionResolutionError::CannotNormalize(_, ref sro) => sro.span(),
        });
        errors
    }
    pub(super) fn note_region_origin(&self, err: &mut Diag<'_>, origin: &SubregionOrigin<'tcx>) {
        match *origin {
            infer::Subtype(ref trace) => RegionOriginNote::WithRequirement {
                span: trace.cause.span,
                requirement: ObligationCauseAsDiagArg(trace.cause.clone()),
                expected_found: self.values_str(trace.values).map(|(e, f, _)| (e, f)),
            }
            .add_to_diag(err),
            infer::Reborrow(span) => {
                RegionOriginNote::Plain { span, msg: fluent::trait_selection_reborrow }
                    .add_to_diag(err)
            }
            infer::RelateObjectBound(span) => {
                RegionOriginNote::Plain { span, msg: fluent::trait_selection_relate_object_bound }
                    .add_to_diag(err);
            }
            infer::ReferenceOutlivesReferent(ty, span) => {
                RegionOriginNote::WithName {
                    span,
                    msg: fluent::trait_selection_reference_outlives_referent,
                    name: &self.ty_to_string(ty),
                    continues: false,
                }
                .add_to_diag(err);
            }
            infer::RelateParamBound(span, ty, opt_span) => {
                RegionOriginNote::WithName {
                    span,
                    msg: fluent::trait_selection_relate_param_bound,
                    name: &self.ty_to_string(ty),
                    continues: opt_span.is_some(),
                }
                .add_to_diag(err);
                if let Some(span) = opt_span {
                    RegionOriginNote::Plain {
                        span,
                        msg: fluent::trait_selection_relate_param_bound_2,
                    }
                    .add_to_diag(err);
                }
            }
            infer::RelateRegionParamBound(span, _) => {
                RegionOriginNote::Plain {
                    span,
                    msg: fluent::trait_selection_relate_region_param_bound,
                }
                .add_to_diag(err);
            }
            infer::CompareImplItemObligation { span, .. } => {
                RegionOriginNote::Plain {
                    span,
                    msg: fluent::trait_selection_compare_impl_item_obligation,
                }
                .add_to_diag(err);
            }
            infer::CheckAssociatedTypeBounds { ref parent, .. } => {
                self.note_region_origin(err, parent);
            }
            infer::AscribeUserTypeProvePredicate(span) => {
                RegionOriginNote::Plain {
                    span,
                    msg: fluent::trait_selection_ascribe_user_type_prove_predicate,
                }
                .add_to_diag(err);
            }
        }
    }
    pub(super) fn report_concrete_failure(
        &self,
        generic_param_scope: LocalDefId,
        origin: SubregionOrigin<'tcx>,
        sub: Region<'tcx>,
        sup: Region<'tcx>,
    ) -> Diag<'a> {
        let mut err = match origin {
            infer::Subtype(box trace) => {
                let terr = TypeError::RegionsDoesNotOutlive(sup, sub);
                let mut err = self.report_and_explain_type_error(trace, terr);
                match (*sub, *sup) {
                    (ty::RePlaceholder(_), ty::RePlaceholder(_)) => {}
                    (ty::RePlaceholder(_), _) => {
                        note_and_explain_region(
                            self.tcx,
                            &mut err,
                            generic_param_scope,
                            "",
                            sup,
                            " doesn't meet the lifetime requirements",
                            None,
                        );
                    }
                    (_, ty::RePlaceholder(_)) => {
                        note_and_explain_region(
                            self.tcx,
                            &mut err,
                            generic_param_scope,
                            "the required lifetime does not necessarily outlive ",
                            sub,
                            "",
                            None,
                        );
                    }
                    _ => {
                        note_and_explain_region(
                            self.tcx,
                            &mut err,
                            generic_param_scope,
                            "",
                            sup,
                            "...",
                            None,
                        );
                        note_and_explain_region(
                            self.tcx,
                            &mut err,
                            generic_param_scope,
                            "...does not necessarily outlive ",
                            sub,
                            "",
                            None,
                        );
                    }
                }
                err
            }
            infer::Reborrow(span) => {
                let reference_valid = note_and_explain::RegionExplanation::new(
                    self.tcx,
                    generic_param_scope,
                    sub,
                    None,
                    note_and_explain::PrefixKind::RefValidFor,
                    note_and_explain::SuffixKind::Continues,
                );
                let content_valid = note_and_explain::RegionExplanation::new(
                    self.tcx,
                    generic_param_scope,
                    sup,
                    None,
                    note_and_explain::PrefixKind::ContentValidFor,
                    note_and_explain::SuffixKind::Empty,
                );
                self.dcx().create_err(OutlivesContent {
                    span,
                    notes: reference_valid.into_iter().chain(content_valid).collect(),
                })
            }
            infer::RelateObjectBound(span) => {
                let object_valid = note_and_explain::RegionExplanation::new(
                    self.tcx,
                    generic_param_scope,
                    sub,
                    None,
                    note_and_explain::PrefixKind::TypeObjValidFor,
                    note_and_explain::SuffixKind::Empty,
                );
                let pointer_valid = note_and_explain::RegionExplanation::new(
                    self.tcx,
                    generic_param_scope,
                    sup,
                    None,
                    note_and_explain::PrefixKind::SourcePointerValidFor,
                    note_and_explain::SuffixKind::Empty,
                );
                self.dcx().create_err(OutlivesBound {
                    span,
                    notes: object_valid.into_iter().chain(pointer_valid).collect(),
                })
            }
            infer::RelateParamBound(span, ty, opt_span) => {
                let prefix = match *sub {
                    ty::ReStatic => note_and_explain::PrefixKind::TypeSatisfy,
                    _ => note_and_explain::PrefixKind::TypeOutlive,
                };
                let suffix = if opt_span.is_some() {
                    note_and_explain::SuffixKind::ReqByBinding
                } else {
                    note_and_explain::SuffixKind::Empty
                };
                let note = note_and_explain::RegionExplanation::new(
                    self.tcx,
                    generic_param_scope,
                    sub,
                    opt_span,
                    prefix,
                    suffix,
                );
                self.dcx().create_err(FulfillReqLifetime {
                    span,
                    ty: self.resolve_vars_if_possible(ty),
                    note,
                })
            }
            infer::RelateRegionParamBound(span, ty) => {
                let param_instantiated = note_and_explain::RegionExplanation::new(
                    self.tcx,
                    generic_param_scope,
                    sup,
                    None,
                    note_and_explain::PrefixKind::LfParamInstantiatedWith,
                    note_and_explain::SuffixKind::Empty,
                );
                let mut alt_span = None;
                if let Some(ty) = ty
                    && sub.is_static()
                    && let ty::Dynamic(preds, _, ty::DynKind::Dyn) = ty.kind()
                    && let Some(def_id) = preds.principal_def_id()
                {
                    for (clause, span) in
                        self.tcx.predicates_of(def_id).instantiate_identity(self.tcx)
                    {
                        if let ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(a, b)) =
                            clause.kind().skip_binder()
                            && let ty::Param(param) = a.kind()
                            && param.name == kw::SelfUpper
                            && b.is_static()
                        {
                            alt_span = Some(span);
                        }
                    }
                }
                let param_must_outlive = note_and_explain::RegionExplanation::new(
                    self.tcx,
                    generic_param_scope,
                    sub,
                    alt_span,
                    note_and_explain::PrefixKind::LfParamMustOutlive,
                    note_and_explain::SuffixKind::Empty,
                );
                self.dcx().create_err(LfBoundNotSatisfied {
                    span,
                    notes: param_instantiated.into_iter().chain(param_must_outlive).collect(),
                })
            }
            infer::ReferenceOutlivesReferent(ty, span) => {
                let pointer_valid = note_and_explain::RegionExplanation::new(
                    self.tcx,
                    generic_param_scope,
                    sub,
                    None,
                    note_and_explain::PrefixKind::PointerValidFor,
                    note_and_explain::SuffixKind::Empty,
                );
                let data_valid = note_and_explain::RegionExplanation::new(
                    self.tcx,
                    generic_param_scope,
                    sup,
                    None,
                    note_and_explain::PrefixKind::DataValidFor,
                    note_and_explain::SuffixKind::Empty,
                );
                self.dcx().create_err(RefLongerThanData {
                    span,
                    ty: self.resolve_vars_if_possible(ty),
                    notes: pointer_valid.into_iter().chain(data_valid).collect(),
                })
            }
            infer::CompareImplItemObligation { span, impl_item_def_id, trait_item_def_id } => {
                let mut err = self.report_extra_impl_obligation(
                    span,
                    impl_item_def_id,
                    trait_item_def_id,
                    &format!("`{sup}: {sub}`"),
                );
                if let Some(generics) = self.tcx.hir().get_generics(impl_item_def_id)
                    && generics.where_clause_span.contains(span)
                {
                    self.suggest_copy_trait_method_bounds(
                        trait_item_def_id,
                        impl_item_def_id,
                        &mut err,
                    );
                }
                err
            }
            infer::CheckAssociatedTypeBounds { impl_item_def_id, trait_item_def_id, parent } => {
                let mut err = self.report_concrete_failure(generic_param_scope, *parent, sub, sup);
                if !self.tcx.is_impl_trait_in_trait(impl_item_def_id.to_def_id()) {
                    let trait_item_span = self.tcx.def_span(trait_item_def_id);
                    let item_name = self.tcx.item_name(impl_item_def_id.to_def_id());
                    err.span_label(
                        trait_item_span,
                        format!("definition of `{item_name}` from trait"),
                    );
                }
                self.suggest_copy_trait_method_bounds(
                    trait_item_def_id,
                    impl_item_def_id,
                    &mut err,
                );
                err
            }
            infer::AscribeUserTypeProvePredicate(span) => {
                let instantiated = note_and_explain::RegionExplanation::new(
                    self.tcx,
                    generic_param_scope,
                    sup,
                    None,
                    note_and_explain::PrefixKind::LfInstantiatedWith,
                    note_and_explain::SuffixKind::Empty,
                );
                let must_outlive = note_and_explain::RegionExplanation::new(
                    self.tcx,
                    generic_param_scope,
                    sub,
                    None,
                    note_and_explain::PrefixKind::LfMustOutlive,
                    note_and_explain::SuffixKind::Empty,
                );
                self.dcx().create_err(LfBoundNotSatisfied {
                    span,
                    notes: instantiated.into_iter().chain(must_outlive).collect(),
                })
            }
        };
        if sub.is_error() || sup.is_error() {
            err.downgrade_to_delayed_bug();
        }
        err
    }
    pub fn suggest_copy_trait_method_bounds(
        &self,
        trait_item_def_id: DefId,
        impl_item_def_id: LocalDefId,
        err: &mut Diag<'_>,
    ) {
        let Some(impl_def_id) = self.tcx.associated_item(impl_item_def_id).impl_container(self.tcx)
        else {
            return;
        };
        let Some(trait_ref) = self.tcx.impl_trait_ref(impl_def_id) else {
            return;
        };
        let trait_args = trait_ref
            .instantiate_identity()
            .with_self_ty(self.tcx, Ty::new_param(self.tcx, 0, kw::SelfUpper))
            .args;
        let trait_item_args = ty::GenericArgs::identity_for_item(self.tcx, impl_item_def_id)
            .rebase_onto(self.tcx, impl_def_id, trait_args);
        let Ok(trait_predicates) =
            self.tcx
                .explicit_predicates_of(trait_item_def_id)
                .instantiate_own(self.tcx, trait_item_args)
                .map(|(pred, _)| {
                    if pred.is_suggestable(self.tcx, false) {
                        Ok(pred.to_string())
                    } else {
                        Err(())
                    }
                })
                .collect::<Result<Vec<_>, ()>>()
        else {
            return;
        };
        let Some(generics) = self.tcx.hir().get_generics(impl_item_def_id) else {
            return;
        };
        let suggestion = if trait_predicates.is_empty() {
            WhereClauseSuggestions::Remove { span: generics.where_clause_span }
        } else {
            let space = if generics.where_clause_span.is_empty() { " " } else { "" };
            WhereClauseSuggestions::CopyPredicates {
                span: generics.where_clause_span,
                space,
                trait_predicates: trait_predicates.join(", "),
            }
        };
        err.subdiagnostic(suggestion);
    }
    pub(super) fn report_placeholder_failure(
        &self,
        generic_param_scope: LocalDefId,
        placeholder_origin: SubregionOrigin<'tcx>,
        sub: Region<'tcx>,
        sup: Region<'tcx>,
    ) -> Diag<'a> {
        debug!(?placeholder_origin, ?sub, ?sup, "report_placeholder_failure");
        match placeholder_origin {
            infer::Subtype(box ref trace)
                if matches!(
                    &trace.cause.code().peel_derives(),
                    ObligationCauseCode::WhereClause(..)
                        | ObligationCauseCode::WhereClauseInExpr(..)
                ) =>
            {
                if let ObligationCauseCode::WhereClause(_, span)
                | ObligationCauseCode::WhereClauseInExpr(_, span, ..) =
                    &trace.cause.code().peel_derives()
                {
                    let span = *span;
                    let mut err = self.report_concrete_failure(
                        generic_param_scope,
                        placeholder_origin,
                        sub,
                        sup,
                    );
                    if !span.is_dummy() {
                        err =
                            err.with_span_note(span, "the lifetime requirement is introduced here");
                    }
                    err
                } else {
                    unreachable!(
                        "control flow ensures we have a `BindingObligation` or `WhereClauseInExpr` here..."
                    )
                }
            }
            infer::Subtype(box trace) => {
                let terr = TypeError::RegionsPlaceholderMismatch;
                return self.report_and_explain_type_error(trace, terr);
            }
            _ => {
                return self.report_concrete_failure(
                    generic_param_scope,
                    placeholder_origin,
                    sub,
                    sup,
                );
            }
        }
    }
    pub fn report_generic_bound_failure(
        &self,
        generic_param_scope: LocalDefId,
        span: Span,
        origin: Option<SubregionOrigin<'tcx>>,
        bound_kind: GenericKind<'tcx>,
        sub: Region<'tcx>,
    ) -> ErrorGuaranteed {
        self.construct_generic_bound_failure(generic_param_scope, span, origin, bound_kind, sub)
            .emit()
    }
    pub fn construct_generic_bound_failure(
        &self,
        generic_param_scope: LocalDefId,
        span: Span,
        origin: Option<SubregionOrigin<'tcx>>,
        bound_kind: GenericKind<'tcx>,
        sub: Region<'tcx>,
    ) -> Diag<'a> {
        if let Some(SubregionOrigin::CompareImplItemObligation {
            span,
            impl_item_def_id,
            trait_item_def_id,
        }) = origin
        {
            return self.report_extra_impl_obligation(
                span,
                impl_item_def_id,
                trait_item_def_id,
                &format!("`{bound_kind}: {sub}`"),
            );
        }
        let labeled_user_string = match bound_kind {
            GenericKind::Param(ref p) => format!("the parameter type `{p}`"),
            GenericKind::Placeholder(ref p) => format!("the placeholder type `{p:?}`"),
            GenericKind::Alias(ref p) => match p.kind(self.tcx) {
                ty::Projection | ty::Inherent => {
                    format!("the associated type `{p}`")
                }
                ty::Weak => format!("the type alias `{p}`"),
                ty::Opaque => format!("the opaque type `{p}`"),
            },
        };
        let mut err = self
            .tcx
            .dcx()
            .struct_span_err(span, format!("{labeled_user_string} may not live long enough"));
        err.code(match sub.kind() {
            ty::ReEarlyParam(_) | ty::ReLateParam(_) if sub.has_name() => E0309,
            ty::ReStatic => E0310,
            _ => E0311,
        });
        '_explain: {
            let (description, span) = match sub.kind() {
                ty::ReEarlyParam(_) | ty::ReLateParam(_) | ty::ReStatic => {
                    msg_span_from_named_region(self.tcx, generic_param_scope, sub, Some(span))
                }
                _ => (format!("lifetime `{sub}`"), Some(span)),
            };
            let prefix = format!("{labeled_user_string} must be valid for ");
            label_msg_span(&mut err, &prefix, description, span, "...");
            if let Some(origin) = origin {
                self.note_region_origin(&mut err, &origin);
            }
        }
        'suggestion: {
            let msg = "consider adding an explicit lifetime bound";
            if (bound_kind, sub).has_infer_regions()
                || (bound_kind, sub).has_placeholders()
                || !bound_kind.is_suggestable(self.tcx, false)
            {
                let lt_name = sub.get_name_or_anon().to_string();
                err.help(format!("{msg} `{bound_kind}: {lt_name}`..."));
                break 'suggestion;
            }
            let mut generic_param_scope = generic_param_scope;
            while self.tcx.def_kind(generic_param_scope) == DefKind::OpaqueTy {
                generic_param_scope = self.tcx.local_parent(generic_param_scope);
            }
            let (type_scope, type_param_sugg_span) = match bound_kind {
                GenericKind::Param(param) => {
                    let generics = self.tcx.generics_of(generic_param_scope);
                    let type_param = generics.type_param(param, self.tcx);
                    let def_id = type_param.def_id.expect_local();
                    let scope = self.tcx.local_def_id_to_hir_id(def_id).owner.def_id;
                    let hir_generics = self.tcx.hir().get_generics(scope).unwrap();
                    let sugg_span = match hir_generics.bounds_span_for_suggestions(def_id) {
                        Some((span, open_paren_sp)) => Some((span, true, open_paren_sp)),
                        None if generics.has_self && param.index == 0 => None,
                        None => {
                            let span = if let Some(param) =
                                hir_generics.params.iter().find(|param| param.def_id == def_id)
                                && let ParamName::Plain(ident) = param.name
                            {
                                ident.span.shrink_to_hi()
                            } else {
                                let span = self.tcx.def_span(def_id);
                                span.shrink_to_hi()
                            };
                            Some((span, false, None))
                        }
                    };
                    (scope, sugg_span)
                }
                _ => (generic_param_scope, None),
            };
            let suggestion_scope = {
                let lifetime_scope = match sub.kind() {
                    ty::ReStatic => hir::def_id::CRATE_DEF_ID,
                    _ => match self.tcx.is_suitable_region(generic_param_scope, sub) {
                        Some(info) => info.def_id,
                        None => generic_param_scope,
                    },
                };
                match self.tcx.is_descendant_of(type_scope.into(), lifetime_scope.into()) {
                    true => type_scope,
                    false => lifetime_scope,
                }
            };
            let mut suggs = vec![];
            let lt_name = self.suggest_name_region(generic_param_scope, sub, &mut suggs);
            if let Some((sp, has_lifetimes, open_paren_sp)) = type_param_sugg_span
                && suggestion_scope == type_scope
            {
                let suggestion =
                    if has_lifetimes { format!(" + {lt_name}") } else { format!(": {lt_name}") };
                if let Some(open_paren_sp) = open_paren_sp {
                    suggs.push((open_paren_sp, "(".to_string()));
                    suggs.push((sp, format!("){suggestion}")));
                } else {
                    suggs.push((sp, suggestion))
                }
            } else if let GenericKind::Alias(ref p) = bound_kind
                && let ty::Projection = p.kind(self.tcx)
                && let DefKind::AssocTy = self.tcx.def_kind(p.def_id)
                && let Some(ty::ImplTraitInTraitData::Trait { .. }) =
                    self.tcx.opt_rpitit_info(p.def_id)
            {
                } else if let Some(generics) = self.tcx.hir().get_generics(suggestion_scope) {
                let pred = format!("{bound_kind}: {lt_name}");
                let suggestion = format!("{} {}", generics.add_where_or_trailing_comma(), pred);
                suggs.push((generics.tail_span_for_predicate_suggestion(), suggestion))
            } else {
                let consider = format!("{msg} `{bound_kind}: {sub}`...");
                err.help(consider);
            }
            if !suggs.is_empty() {
                err.multipart_suggestion_verbose(
                    msg,
                    suggs,
                    Applicability::MaybeIncorrect, );
            }
        }
        err
    }
    pub fn suggest_name_region(
        &self,
        generic_param_scope: LocalDefId,
        lifetime: Region<'tcx>,
        add_lt_suggs: &mut Vec<(Span, String)>,
    ) -> String {
        struct LifetimeReplaceVisitor<'a> {
            needle: hir::LifetimeName,
            new_lt: &'a str,
            add_lt_suggs: &'a mut Vec<(Span, String)>,
        }
        impl<'hir> hir::intravisit::Visitor<'hir> for LifetimeReplaceVisitor<'_> {
            fn visit_lifetime(&mut self, lt: &'hir hir::Lifetime) {
                if lt.res == self.needle {
                    self.add_lt_suggs.push(lt.suggestion(self.new_lt));
                }
            }
            fn visit_ty(&mut self, ty: &'hir hir::Ty<'hir>) {
                let hir::TyKind::OpaqueDef(opaque_ty, _) = ty.kind else {
                    return hir::intravisit::walk_ty(self, ty);
                };
                if let Some(&(_, b)) =
                    opaque_ty.lifetime_mapping.iter().find(|&(a, _)| a.res == self.needle)
                {
                    let prev_needle =
                        std::mem::replace(&mut self.needle, hir::LifetimeName::Param(b));
                    for bound in opaque_ty.bounds {
                        self.visit_param_bound(bound);
                    }
                    self.needle = prev_needle;
                }
            }
        }
        let (lifetime_def_id, lifetime_scope) =
            match self.tcx.is_suitable_region(generic_param_scope, lifetime) {
                Some(info) if !lifetime.has_name() => {
                    (info.bound_region.get_id().unwrap().expect_local(), info.def_id)
                }
                _ => return lifetime.get_name_or_anon().to_string(),
            };
        let new_lt = {
            let generics = self.tcx.generics_of(lifetime_scope);
            let mut used_names =
                iter::successors(Some(generics), |g| g.parent.map(|p| self.tcx.generics_of(p)))
                    .flat_map(|g| &g.own_params)
                    .filter(|p| matches!(p.kind, ty::GenericParamDefKind::Lifetime))
                    .map(|p| p.name)
                    .collect::<Vec<_>>();
            let hir_id = self.tcx.local_def_id_to_hir_id(lifetime_scope);
            used_names.extend(self.tcx.late_bound_vars(hir_id).into_iter().filter_map(
                |p| match p {
                    ty::BoundVariableKind::Region(lt) => lt.get_name(),
                    _ => None,
                },
            ));
            (b'a'..=b'z')
                .map(|c| format!("'{}", c as char))
                .find(|candidate| !used_names.iter().any(|e| e.as_str() == candidate))
                .unwrap_or("'lt".to_string())
        };
        let mut visitor = LifetimeReplaceVisitor {
            needle: hir::LifetimeName::Param(lifetime_def_id),
            add_lt_suggs,
            new_lt: &new_lt,
        };
        match self.tcx.expect_hir_owner_node(lifetime_scope) {
            hir::OwnerNode::Item(i) => visitor.visit_item(i),
            hir::OwnerNode::ForeignItem(i) => visitor.visit_foreign_item(i),
            hir::OwnerNode::ImplItem(i) => visitor.visit_impl_item(i),
            hir::OwnerNode::TraitItem(i) => visitor.visit_trait_item(i),
            hir::OwnerNode::Crate(_) => bug!("OwnerNode::Crate doesn't not have generics"),
            hir::OwnerNode::Synthetic => unreachable!(),
        }
        let ast_generics = self.tcx.hir().get_generics(lifetime_scope).unwrap();
        let sugg = ast_generics
            .span_for_lifetime_suggestion()
            .map(|span| (span, format!("{new_lt}, ")))
            .unwrap_or_else(|| (ast_generics.span, format!("<{new_lt}>")));
        add_lt_suggs.push(sugg);
        new_lt
    }
    fn report_sub_sup_conflict(
        &self,
        generic_param_scope: LocalDefId,
        var_origin: RegionVariableOrigin,
        sub_origin: SubregionOrigin<'tcx>,
        sub_region: Region<'tcx>,
        sup_origin: SubregionOrigin<'tcx>,
        sup_region: Region<'tcx>,
    ) -> ErrorGuaranteed {
        let mut err = self.report_inference_failure(var_origin);
        note_and_explain_region(
            self.tcx,
            &mut err,
            generic_param_scope,
            "first, the lifetime cannot outlive ",
            sup_region,
            "...",
            None,
        );
        debug!("report_sub_sup_conflict: var_origin={:?}", var_origin);
        debug!("report_sub_sup_conflict: sub_region={:?}", sub_region);
        debug!("report_sub_sup_conflict: sub_origin={:?}", sub_origin);
        debug!("report_sub_sup_conflict: sup_region={:?}", sup_region);
        debug!("report_sub_sup_conflict: sup_origin={:?}", sup_origin);
        if let infer::Subtype(ref sup_trace) = sup_origin
            && let infer::Subtype(ref sub_trace) = sub_origin
            && let Some((sup_expected, sup_found, _)) = self.values_str(sup_trace.values)
            && let Some((sub_expected, sub_found, _)) = self.values_str(sub_trace.values)
            && sub_expected == sup_expected
            && sub_found == sup_found
        {
            note_and_explain_region(
                self.tcx,
                &mut err,
                generic_param_scope,
                "...but the lifetime must also be valid for ",
                sub_region,
                "...",
                None,
            );
            err.span_note(
                sup_trace.cause.span,
                format!("...so that the {}", sup_trace.cause.as_requirement_str()),
            );
            err.note_expected_found(&"", sup_expected, &"", sup_found);
            return if sub_region.is_error() | sup_region.is_error() {
                err.delay_as_bug()
            } else {
                err.emit()
            };
        }
        self.note_region_origin(&mut err, &sup_origin);
        note_and_explain_region(
            self.tcx,
            &mut err,
            generic_param_scope,
            "but, the lifetime must be valid for ",
            sub_region,
            "...",
            None,
        );
        self.note_region_origin(&mut err, &sub_origin);
        if sub_region.is_error() | sup_region.is_error() { err.delay_as_bug() } else { err.emit() }
    }
    fn report_inference_failure(&self, var_origin: RegionVariableOrigin) -> Diag<'_> {
        let br_string = |br: ty::BoundRegionKind| {
            let mut s = match br {
                ty::BrNamed(_, name) => name.to_string(),
                _ => String::new(),
            };
            if !s.is_empty() {
                s.push(' ');
            }
            s
        };
        let var_description = match var_origin {
            infer::MiscVariable(_) => String::new(),
            infer::PatternRegion(_) => " for pattern".to_string(),
            infer::BorrowRegion(_) => " for borrow expression".to_string(),
            infer::Autoref(_) => " for autoref".to_string(),
            infer::Coercion(_) => " for automatic coercion".to_string(),
            infer::BoundRegion(_, br, infer::FnCall) => {
                format!(" for lifetime parameter {}in function call", br_string(br))
            }
            infer::BoundRegion(_, br, infer::HigherRankedType) => {
                format!(" for lifetime parameter {}in generic type", br_string(br))
            }
            infer::BoundRegion(_, br, infer::AssocTypeProjection(def_id)) => format!(
                " for lifetime parameter {}in trait containing associated type `{}`",
                br_string(br),
                self.tcx.associated_item(def_id).name
            ),
            infer::RegionParameterDefinition(_, name) => {
                format!(" for lifetime parameter `{name}`")
            }
            infer::UpvarRegion(ref upvar_id, _) => {
                let var_name = self.tcx.hir().name(upvar_id.var_path.hir_id);
                format!(" for capture of `{var_name}` by closure")
            }
            infer::Nll(..) => bug!("NLL variable found in lexical phase"),
        };
        struct_span_code_err!(
            self.dcx(),
            var_origin.span(),
            E0495,
            "cannot infer an appropriate lifetime{} due to conflicting requirements",
            var_description
        )
    }
}
pub(super) fn note_and_explain_region<'tcx>(
    tcx: TyCtxt<'tcx>,
    err: &mut Diag<'_>,
    generic_param_scope: LocalDefId,
    prefix: &str,
    region: ty::Region<'tcx>,
    suffix: &str,
    alt_span: Option<Span>,
) {
    let (description, span) = match *region {
        ty::ReEarlyParam(_) | ty::ReLateParam(_) | ty::RePlaceholder(_) | ty::ReStatic => {
            msg_span_from_named_region(tcx, generic_param_scope, region, alt_span)
        }
        ty::ReError(_) => return,
        ty::ReVar(_) => (format!("lifetime `{region}`"), alt_span),
        ty::ReBound(..) | ty::ReErased => {
            bug!("unexpected region for note_and_explain_region: {:?}", region);
        }
    };
    emit_msg_span(err, prefix, description, span, suffix);
}
fn explain_free_region<'tcx>(
    tcx: TyCtxt<'tcx>,
    err: &mut Diag<'_>,
    generic_param_scope: LocalDefId,
    prefix: &str,
    region: ty::Region<'tcx>,
    suffix: &str,
) {
    let (description, span) = msg_span_from_named_region(tcx, generic_param_scope, region, None);
    label_msg_span(err, prefix, description, span, suffix);
}
fn msg_span_from_named_region<'tcx>(
    tcx: TyCtxt<'tcx>,
    generic_param_scope: LocalDefId,
    region: ty::Region<'tcx>,
    alt_span: Option<Span>,
) -> (String, Option<Span>) {
    match *region {
        ty::ReEarlyParam(br) => {
            let param_def_id = tcx.generics_of(generic_param_scope).region_param(br, tcx).def_id;
            let span = tcx.def_span(param_def_id);
            let text = if br.has_name() {
                format!("the lifetime `{}` as defined here", br.name)
            } else {
                "the anonymous lifetime as defined here".to_string()
            };
            (text, Some(span))
        }
        ty::ReLateParam(ref fr) => {
            if !fr.bound_region.is_named()
                && let Some((ty, _)) =
                    find_anon_type(tcx, generic_param_scope, region, &fr.bound_region)
            {
                ("the anonymous lifetime defined here".to_string(), Some(ty.span))
            } else {
                match fr.bound_region {
                    ty::BoundRegionKind::BrNamed(param_def_id, name) => {
                        let span = tcx.def_span(param_def_id);
                        let text = if name == kw::UnderscoreLifetime {
                            "the anonymous lifetime as defined here".to_string()
                        } else {
                            format!("the lifetime `{name}` as defined here")
                        };
                        (text, Some(span))
                    }
                    ty::BrAnon => (
                        "the anonymous lifetime as defined here".to_string(),
                        Some(tcx.def_span(generic_param_scope)),
                    ),
                    _ => (
                        format!("the lifetime `{region}` as defined here"),
                        Some(tcx.def_span(generic_param_scope)),
                    ),
                }
            }
        }
        ty::ReStatic => ("the static lifetime".to_owned(), alt_span),
        ty::RePlaceholder(ty::PlaceholderRegion {
            bound: ty::BoundRegion { kind: ty::BoundRegionKind::BrNamed(def_id, name), .. },
            ..
        }) => (format!("the lifetime `{name}` as defined here"), Some(tcx.def_span(def_id))),
        ty::RePlaceholder(ty::PlaceholderRegion {
            bound: ty::BoundRegion { kind: ty::BoundRegionKind::BrAnon, .. },
            ..
        }) => ("an anonymous lifetime".to_owned(), None),
        _ => bug!("{:?}", region),
    }
}
fn emit_msg_span(
    err: &mut Diag<'_>,
    prefix: &str,
    description: String,
    span: Option<Span>,
    suffix: &str,
) {
    let message = format!("{prefix}{description}{suffix}");
    if let Some(span) = span {
        err.span_note(span, message);
    } else {
        err.note(message);
    }
}
fn label_msg_span(
    err: &mut Diag<'_>,
    prefix: &str,
    description: String,
    span: Option<Span>,
    suffix: &str,
) {
    let message = format!("{prefix}{description}{suffix}");
    if let Some(span) = span {
        err.span_label(span, message);
    } else {
        err.note(message);
    }
}
#[instrument(level = "trace", skip(infcx))]
pub fn unexpected_hidden_region_diagnostic<'a, 'tcx>(
    infcx: &'a InferCtxt<'tcx>,
    generic_param_scope: LocalDefId,
    span: Span,
    hidden_ty: Ty<'tcx>,
    hidden_region: ty::Region<'tcx>,
    opaque_ty_key: ty::OpaqueTypeKey<'tcx>,
) -> Diag<'a> {
    let tcx = infcx.tcx;
    let mut err = infcx.dcx().create_err(errors::OpaqueCapturesLifetime {
        span,
        opaque_ty: Ty::new_opaque(tcx, opaque_ty_key.def_id.to_def_id(), opaque_ty_key.args),
        opaque_ty_span: tcx.def_span(opaque_ty_key.def_id),
    });
    match *hidden_region {
        ty::ReEarlyParam(_) | ty::ReLateParam(_) | ty::ReStatic => {
            explain_free_region(
                tcx,
                &mut err,
                generic_param_scope,
                &format!("hidden type `{hidden_ty}` captures "),
                hidden_region,
                "",
            );
            if let Some(_) = tcx.is_suitable_region(generic_param_scope, hidden_region) {
                suggest_precise_capturing(tcx, opaque_ty_key.def_id, hidden_region, &mut err);
            }
        }
        ty::RePlaceholder(_) => {
            explain_free_region(
                tcx,
                &mut err,
                generic_param_scope,
                &format!("hidden type `{}` captures ", hidden_ty),
                hidden_region,
                "",
            );
        }
        ty::ReError(_) => {
            err.downgrade_to_delayed_bug();
        }
        _ => {
            note_and_explain_region(
                tcx,
                &mut err,
                generic_param_scope,
                &format!("hidden type `{hidden_ty}` captures "),
                hidden_region,
                "",
                None,
            );
        }
    }
    err
}
fn suggest_precise_capturing<'tcx>(
    tcx: TyCtxt<'tcx>,
    opaque_def_id: LocalDefId,
    captured_lifetime: ty::Region<'tcx>,
    diag: &mut Diag<'_>,
) {
    let hir::OpaqueTy { bounds, origin, .. } =
        tcx.hir_node_by_def_id(opaque_def_id).expect_opaque_ty();
    let hir::OpaqueTyOrigin::FnReturn { parent: fn_def_id, .. } = *origin else {
        return;
    };
    let new_lifetime = Symbol::intern(&captured_lifetime.to_string());
    if let Some((args, span)) = bounds.iter().find_map(|bound| match bound {
        hir::GenericBound::Use(args, span) => Some((args, span)),
        _ => None,
    }) {
        let last_lifetime_span = args.iter().rev().find_map(|arg| match arg {
            hir::PreciseCapturingArg::Lifetime(lt) => Some(lt.ident.span),
            _ => None,
        });
        let first_param_span = args.iter().find_map(|arg| match arg {
            hir::PreciseCapturingArg::Param(p) => Some(p.ident.span),
            _ => None,
        });
        let (span, pre, post) = if let Some(last_lifetime_span) = last_lifetime_span {
            (last_lifetime_span.shrink_to_hi(), ", ", "")
        } else if let Some(first_param_span) = first_param_span {
            (first_param_span.shrink_to_lo(), "", ", ")
        } else {
            (span.with_hi(span.hi() - BytePos(1)).shrink_to_hi(), "", "")
        };
        diag.subdiagnostic(errors::AddPreciseCapturing::Existing { span, new_lifetime, pre, post });
    } else {
        let mut captured_lifetimes = FxIndexSet::default();
        let mut captured_non_lifetimes = FxIndexSet::default();
        let variances = tcx.variances_of(opaque_def_id);
        let mut generics = tcx.generics_of(opaque_def_id);
        let mut synthetics = vec![];
        loop {
            for param in &generics.own_params {
                if variances[param.index as usize] == ty::Bivariant {
                    continue;
                }
                match param.kind {
                    ty::GenericParamDefKind::Lifetime => {
                        captured_lifetimes.insert(param.name);
                    }
                    ty::GenericParamDefKind::Type { synthetic: true, .. } => {
                        synthetics.push((tcx.def_span(param.def_id), param.name));
                    }
                    ty::GenericParamDefKind::Type { .. }
                    | ty::GenericParamDefKind::Const { .. } => {
                        captured_non_lifetimes.insert(param.name);
                    }
                }
            }
            if let Some(parent) = generics.parent {
                generics = tcx.generics_of(parent);
            } else {
                break;
            }
        }
        if !captured_lifetimes.insert(new_lifetime) {
            return;
        }
        if synthetics.is_empty() {
            let concatenated_bounds = captured_lifetimes
                .into_iter()
                .chain(captured_non_lifetimes)
                .map(|sym| sym.to_string())
                .collect::<Vec<_>>()
                .join(", ");
            diag.subdiagnostic(errors::AddPreciseCapturing::New {
                span: tcx.def_span(opaque_def_id).shrink_to_hi(),
                new_lifetime,
                concatenated_bounds,
            });
        } else {
            let mut next_fresh_param = || {
                ["T", "U", "V", "W", "X", "Y", "A", "B", "C"]
                    .into_iter()
                    .map(Symbol::intern)
                    .chain((0..).map(|i| Symbol::intern(&format!("T{i}"))))
                    .find(|s| captured_non_lifetimes.insert(*s))
                    .unwrap()
            };
            let mut new_params = String::new();
            let mut suggs = vec![];
            let mut apit_spans = vec![];
            for (i, (span, name)) in synthetics.into_iter().enumerate() {
                apit_spans.push(span);
                let fresh_param = next_fresh_param();
                suggs.push((span, fresh_param.to_string()));
                if i > 0 {
                    new_params += ", ";
                }
                let name_as_bounds = name.as_str().trim_start_matches("impl").trim_start();
                new_params += fresh_param.as_str();
                new_params += ": ";
                new_params += name_as_bounds;
            }
            let Some(generics) = tcx.hir().get_generics(fn_def_id) else {
                return;
            };
            suggs.push(if let Some(params_span) = generics.span_for_param_suggestion() {
                (params_span, format!(", {new_params}"))
            } else {
                (generics.span, format!("<{new_params}>"))
            });
            let concatenated_bounds = captured_lifetimes
                .into_iter()
                .chain(captured_non_lifetimes)
                .map(|sym| sym.to_string())
                .collect::<Vec<_>>()
                .join(", ");
            suggs.push((
                tcx.def_span(opaque_def_id).shrink_to_hi(),
                format!(" + use<{concatenated_bounds}>"),
            ));
            diag.subdiagnostic(errors::AddPreciseCapturingAndParams {
                suggs,
                new_lifetime,
                apit_spans,
            });
        }
    }
}