//! Error Reporting for `impl` items that do not match the obligations from their `trait`.

use crate::infer::error_reporting::nice_region_error::NiceRegionError;
use crate::infer::lexical_region_resolve::RegionResolutionError;
use crate::infer::{Subtype, ValuePairs};
use crate::traits::ObligationCauseCode::CompareImplMethodObligation;
use crate::ty::Ty;
use crate::util::common::ErrorReported;
use rustc_span::Span;

impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
    /// Print the error message for lifetime errors when the `impl` doesn't conform to the `trait`.
    pub(super) fn try_report_impl_not_conforming_to_trait(&self) -> Option<ErrorReported> {
        if let Some(ref error) = self.error {
            debug!("try_report_impl_not_conforming_to_trait {:?}", error);
            if let RegionResolutionError::SubSupConflict(
                _,
                var_origin,
                sub_origin,
                _sub,
                sup_origin,
                _sup,
            ) = error.clone()
            {
                match (&sup_origin, &sub_origin) {
                    (&Subtype(ref sup_trace), &Subtype(ref sub_trace)) => {
                        if let (
                            ValuePairs::Types(sub_expected_found),
                            ValuePairs::Types(sup_expected_found),
                            CompareImplMethodObligation { trait_item_def_id, .. },
                        ) = (&sub_trace.values, &sup_trace.values, &sub_trace.cause.code)
                        {
                            if sup_expected_found == sub_expected_found {
                                self.emit_err(
                                    var_origin.span(),
                                    sub_expected_found.expected,
                                    sub_expected_found.found,
                                    self.tcx().def_span(*trait_item_def_id),
                                );
                                return Some(ErrorReported);
                            }
                        }
                    }
                    _ => {}
                }
            }
        }
        None
    }

    fn emit_err(&self, sp: Span, expected: Ty<'tcx>, found: Ty<'tcx>, impl_sp: Span) {
        let mut err = self
            .tcx()
            .sess
            .struct_span_err(sp, "`impl` item signature doesn't match `trait` item signature");
        err.note(&format!("expected `{:?}`\n   found `{:?}`", expected, found));
        err.span_label(sp, &format!("found {:?}", found));
        err.span_label(impl_sp, &format!("expected {:?}", expected));
        err.emit();
    }
}
