rustc_trait_selection/error_reporting/infer/nice_region_error/
trait_impl_difference.rs

1//! Error Reporting for `impl` items that do not match the obligations from their `trait`.
2
3use rustc_errors::ErrorGuaranteed;
4use rustc_hir::def::{Namespace, Res};
5use rustc_hir::def_id::DefId;
6use rustc_hir::intravisit::{Visitor, walk_ty};
7use rustc_hir::{self as hir, AmbigArg};
8use rustc_infer::infer::SubregionOrigin;
9use rustc_middle::hir::nested_filter;
10use rustc_middle::traits::ObligationCauseCode;
11use rustc_middle::ty::error::ExpectedFound;
12use rustc_middle::ty::print::RegionHighlightMode;
13use rustc_middle::ty::{self, TyCtxt, TypeVisitable};
14use rustc_span::Span;
15use tracing::debug;
16
17use crate::error_reporting::infer::nice_region_error::NiceRegionError;
18use crate::error_reporting::infer::nice_region_error::placeholder_error::Highlighted;
19use crate::errors::{ConsiderBorrowingParamHelp, RelationshipHelp, TraitImplDiff};
20use crate::infer::{RegionResolutionError, ValuePairs};
21
22impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
23    /// Print the error message for lifetime errors when the `impl` doesn't conform to the `trait`.
24    pub(super) fn try_report_impl_not_conforming_to_trait(&self) -> Option<ErrorGuaranteed> {
25        let error = self.error.as_ref()?;
26        debug!("try_report_impl_not_conforming_to_trait {:?}", error);
27        if let RegionResolutionError::SubSupConflict(
28            _,
29            var_origin,
30            sub_origin,
31            _sub,
32            sup_origin,
33            _sup,
34            _,
35        ) = error.clone()
36            && let (SubregionOrigin::Subtype(sup_trace), SubregionOrigin::Subtype(sub_trace)) =
37                (&sup_origin, &sub_origin)
38            && let &ObligationCauseCode::CompareImplItem { trait_item_def_id, .. } =
39                sub_trace.cause.code()
40            && sub_trace.values == sup_trace.values
41            && let ValuePairs::PolySigs(ExpectedFound { expected, found }) = sub_trace.values
42        {
43            // FIXME(compiler-errors): Don't like that this needs `Ty`s, but
44            // all of the region highlighting machinery only deals with those.
45            let guar = self.emit_err(var_origin.span(), expected, found, trait_item_def_id);
46            return Some(guar);
47        }
48        None
49    }
50
51    fn emit_err(
52        &self,
53        sp: Span,
54        expected: ty::PolyFnSig<'tcx>,
55        found: ty::PolyFnSig<'tcx>,
56        trait_item_def_id: DefId,
57    ) -> ErrorGuaranteed {
58        let trait_sp = self.tcx().def_span(trait_item_def_id);
59
60        // Mark all unnamed regions in the type with a number.
61        // This diagnostic is called in response to lifetime errors, so be informative.
62        struct HighlightBuilder<'tcx> {
63            tcx: TyCtxt<'tcx>,
64            highlight: RegionHighlightMode<'tcx>,
65            counter: usize,
66        }
67
68        impl<'tcx> HighlightBuilder<'tcx> {
69            fn build(tcx: TyCtxt<'tcx>, sig: ty::PolyFnSig<'tcx>) -> RegionHighlightMode<'tcx> {
70                let mut builder =
71                    HighlightBuilder { tcx, highlight: RegionHighlightMode::default(), counter: 1 };
72                sig.visit_with(&mut builder);
73                builder.highlight
74            }
75        }
76
77        impl<'tcx> ty::TypeVisitor<TyCtxt<'tcx>> for HighlightBuilder<'tcx> {
78            fn visit_region(&mut self, r: ty::Region<'tcx>) {
79                if !r.is_named(self.tcx) && self.counter <= 3 {
80                    self.highlight.highlighting_region(r, self.counter);
81                    self.counter += 1;
82                }
83            }
84        }
85
86        let tcx = self.cx.tcx;
87        let expected_highlight = HighlightBuilder::build(tcx, expected);
88        let expected = Highlighted {
89            highlight: expected_highlight,
90            ns: Namespace::TypeNS,
91            tcx,
92            value: expected,
93        }
94        .to_string();
95        let found_highlight = HighlightBuilder::build(tcx, found);
96        let found =
97            Highlighted { highlight: found_highlight, ns: Namespace::TypeNS, tcx, value: found }
98                .to_string();
99
100        // Get the span of all the used type parameters in the method.
101        let assoc_item = self.tcx().associated_item(trait_item_def_id);
102        let mut visitor = TypeParamSpanVisitor { tcx: self.tcx(), types: vec![] };
103        match assoc_item.kind {
104            ty::AssocKind::Fn { .. } => {
105                if let Some(hir_id) =
106                    assoc_item.def_id.as_local().map(|id| self.tcx().local_def_id_to_hir_id(id))
107                {
108                    if let Some(decl) = self.tcx().hir_fn_decl_by_hir_id(hir_id) {
109                        visitor.visit_fn_decl(decl);
110                    }
111                }
112            }
113            _ => {}
114        }
115
116        let diag = TraitImplDiff {
117            sp,
118            trait_sp,
119            note: (),
120            param_help: ConsiderBorrowingParamHelp { spans: visitor.types.to_vec() },
121            rel_help: visitor.types.is_empty().then_some(RelationshipHelp),
122            expected,
123            found,
124        };
125
126        self.tcx().dcx().emit_err(diag)
127    }
128}
129
130struct TypeParamSpanVisitor<'tcx> {
131    tcx: TyCtxt<'tcx>,
132    types: Vec<Span>,
133}
134
135impl<'tcx> Visitor<'tcx> for TypeParamSpanVisitor<'tcx> {
136    type NestedFilter = nested_filter::OnlyBodies;
137
138    fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
139        self.tcx
140    }
141
142    fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx, AmbigArg>) {
143        match arg.kind {
144            hir::TyKind::Ref(_, ref mut_ty) => {
145                // We don't want to suggest looking into borrowing `&T` or `&Self`.
146                if let Some(ambig_ty) = mut_ty.ty.try_as_ambig_ty() {
147                    walk_ty(self, ambig_ty);
148                }
149                return;
150            }
151            hir::TyKind::Path(hir::QPath::Resolved(None, path)) => match &path.segments {
152                [segment]
153                    if matches!(
154                        segment.res,
155                        Res::SelfTyParam { .. }
156                            | Res::SelfTyAlias { .. }
157                            | Res::Def(hir::def::DefKind::TyParam, _)
158                    ) =>
159                {
160                    self.types.push(path.span);
161                }
162                _ => {}
163            },
164            _ => {}
165        }
166        hir::intravisit::walk_ty(self, arg);
167    }
168}