Skip to main content

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::{Ident, Span};
15use tracing::debug;
16
17use crate::diagnostics::{ConsiderBorrowingParamHelp, TraitImplDiff};
18use crate::error_reporting::infer::nice_region_error::NiceRegionError;
19use crate::error_reporting::infer::nice_region_error::placeholder_error::Highlighted;
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        {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event /rustc-dev/822182db4c4a1827256881d8c7aa685f0bcf2b11/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs:26",
                        "rustc_trait_selection::error_reporting::infer::nice_region_error::trait_impl_difference",
                        ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("/rustc-dev/822182db4c4a1827256881d8c7aa685f0bcf2b11/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs"),
                        ::tracing_core::__macro_support::Option::Some(26u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_trait_selection::error_reporting::infer::nice_region_error::trait_impl_difference"),
                        ::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!("try_report_impl_not_conforming_to_trait {0:?}",
                                                    error) as &dyn ::tracing::field::Value))])
            });
    } else { ; }
};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 {
39                trait_item_def_id, impl_item_def_id, ..
40            } = sub_trace.cause.code()
41            && sub_trace.values == sup_trace.values
42            && let ValuePairs::PolySigs(ExpectedFound { expected, found }) = sub_trace.values
43        {
44            // FIXME(compiler-errors): Don't like that this needs `Ty`s, but
45            // all of the region highlighting machinery only deals with those.
46            let guar = self.emit_err(
47                var_origin.span(),
48                expected,
49                found,
50                trait_item_def_id,
51                impl_item_def_id.into(),
52            );
53            return Some(guar);
54        }
55        None
56    }
57
58    fn emit_err(
59        &self,
60        sp: Span,
61        expected: ty::PolyFnSig<'tcx>,
62        found: ty::PolyFnSig<'tcx>,
63        trait_item_def_id: DefId,
64        impl_item_def_id: DefId,
65    ) -> ErrorGuaranteed {
66        let trait_sp = self.tcx().def_span(trait_item_def_id);
67
68        // Mark all unnamed regions in the type with a number.
69        // This diagnostic is called in response to lifetime errors, so be informative.
70        struct HighlightBuilder<'tcx> {
71            tcx: TyCtxt<'tcx>,
72            highlight: RegionHighlightMode<'tcx>,
73            counter: usize,
74        }
75
76        impl<'tcx> HighlightBuilder<'tcx> {
77            fn build(tcx: TyCtxt<'tcx>, sig: ty::PolyFnSig<'tcx>) -> RegionHighlightMode<'tcx> {
78                let mut builder =
79                    HighlightBuilder { tcx, highlight: RegionHighlightMode::default(), counter: 1 };
80                sig.visit_with(&mut builder);
81                builder.highlight
82            }
83        }
84
85        impl<'tcx> ty::TypeVisitor<TyCtxt<'tcx>> for HighlightBuilder<'tcx> {
86            fn visit_region(&mut self, r: ty::Region<'tcx>) {
87                if !r.is_named(self.tcx) && self.counter <= 3 {
88                    self.highlight.highlighting_region(r, self.counter);
89                    self.counter += 1;
90                }
91            }
92        }
93
94        let tcx = self.cx.tcx;
95        let mut expected_highlight = HighlightBuilder::build(tcx, expected);
96        let expected_short = Highlighted {
97            highlight: expected_highlight,
98            ns: Namespace::TypeNS,
99            tcx,
100            value: expected,
101        }
102        .to_string();
103        expected_highlight.keep_regions = false;
104        let expected = Highlighted {
105            highlight: expected_highlight,
106            ns: Namespace::TypeNS,
107            tcx,
108            value: expected,
109        }
110        .to_string();
111        let mut found_highlight = HighlightBuilder::build(tcx, found);
112        let found_short =
113            Highlighted { highlight: found_highlight, ns: Namespace::TypeNS, tcx, value: found }
114                .to_string();
115        found_highlight.keep_regions = false;
116        let found =
117            Highlighted { highlight: found_highlight, ns: Namespace::TypeNS, tcx, value: found }
118                .to_string();
119
120        // Get the span of all the used type parameters in the method.
121        let assoc_item = self.tcx().associated_item(trait_item_def_id);
122        let mut visitor =
123            TypeParamSpanVisitor { tcx: self.tcx(), types: ::alloc::vec::Vec::new()vec![], elided_lifetime_paths: ::alloc::vec::Vec::new()vec![] };
124        match assoc_item.kind {
125            ty::AssocKind::Fn { .. } => {
126                if let Some(hir_id) =
127                    assoc_item.def_id.as_local().map(|id| self.tcx().local_def_id_to_hir_id(id))
128                    && let Some(decl) = self.tcx().hir_fn_decl_by_hir_id(hir_id)
129                {
130                    visitor.visit_fn_decl(decl);
131                }
132            }
133            _ => {}
134        }
135
136        let diag = TraitImplDiff {
137            sp,
138            trait_sp,
139            note: (),
140            param_help: ConsiderBorrowingParamHelp { spans: visitor.types.to_vec() },
141            rel_help: visitor.types.is_empty(),
142            expected,
143            found,
144            expected_short,
145            found_short,
146            trait_span: self.tcx().def_span(self.tcx().parent(trait_item_def_id)).shrink_to_lo(),
147            impl_span: self.tcx().def_span(self.tcx().parent(impl_item_def_id)).shrink_to_lo(),
148        };
149
150        let mut diag = self.tcx().dcx().create_err(diag);
151        // A limit not to make diag verbose.
152        const ELIDED_LIFETIME_NOTE_LIMIT: usize = 5;
153        let elided_lifetime_paths = visitor.elided_lifetime_paths;
154        let total_elided_lifetime_paths = elided_lifetime_paths.len();
155        let shown_elided_lifetime_paths = if tcx.sess.opts.verbose {
156            total_elided_lifetime_paths
157        } else {
158            ELIDED_LIFETIME_NOTE_LIMIT
159        };
160
161        for elided in elided_lifetime_paths.into_iter().take(shown_elided_lifetime_paths) {
162            diag.span_note(
163                elided.span,
164                ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("`{0}` here is elided as `{1}`",
                elided.ident, elided.shorthand))
    })format!("`{}` here is elided as `{}`", elided.ident, elided.shorthand),
165            );
166        }
167        if total_elided_lifetime_paths > shown_elided_lifetime_paths {
168            diag.note(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("and {0} more elided lifetime{1} in type paths",
                total_elided_lifetime_paths - shown_elided_lifetime_paths,
                if total_elided_lifetime_paths - shown_elided_lifetime_paths
                        == 1 {
                    ""
                } else { "s" }))
    })format!(
169                "and {} more elided lifetime{} in type paths",
170                total_elided_lifetime_paths - shown_elided_lifetime_paths,
171                if total_elided_lifetime_paths - shown_elided_lifetime_paths == 1 {
172                    ""
173                } else {
174                    "s"
175                },
176            ));
177        }
178        diag.emit_err()
179    }
180}
181
182#[derive(#[automatically_derived]
impl ::core::clone::Clone for ElidedLifetimeInPath {
    #[inline]
    fn clone(&self) -> Self {
        ElidedLifetimeInPath {
            span: ::core::clone::Clone::clone(&self.span),
            ident: ::core::clone::Clone::clone(&self.ident),
            shorthand: ::core::clone::Clone::clone(&self.shorthand),
        }
    }
}Clone)]
183struct ElidedLifetimeInPath {
184    span: Span,
185    ident: Ident,
186    shorthand: String,
187}
188
189struct TypeParamSpanVisitor<'tcx> {
190    tcx: TyCtxt<'tcx>,
191    types: Vec<Span>,
192    elided_lifetime_paths: Vec<ElidedLifetimeInPath>,
193}
194
195impl<'tcx> Visitor<'tcx> for TypeParamSpanVisitor<'tcx> {
196    type NestedFilter = nested_filter::OnlyBodies;
197
198    fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
199        self.tcx
200    }
201
202    fn visit_qpath(&mut self, qpath: &'tcx hir::QPath<'tcx>, id: hir::HirId, _span: Span) {
203        fn record_elided_lifetimes(
204            tcx: TyCtxt<'_>,
205            elided_lifetime_paths: &mut Vec<ElidedLifetimeInPath>,
206            segment: &hir::PathSegment<'_>,
207        ) {
208            let Some(args) = segment.args else { return };
209            if args.parenthesized != hir::GenericArgsParentheses::No {
210                // Our diagnostic rendering below uses `<...>` syntax; skip cases like `Fn(..) -> ..`.
211                return;
212            }
213            let elided_count = args
214                .args
215                .iter()
216                .filter(|arg| {
217                    let hir::GenericArg::Lifetime(l) = arg else { return false };
218                    l.syntax == hir::LifetimeSyntax::Implicit
219                        && #[allow(non_exhaustive_omitted_patterns)] match l.source {
    hir::LifetimeSource::Path { .. } => true,
    _ => false,
}matches!(l.source, hir::LifetimeSource::Path { .. })
220                })
221                .count();
222            if elided_count == 0
223                || elided_lifetime_paths.iter().any(|p| p.span == segment.ident.span)
224            {
225                return;
226            }
227
228            let sm = tcx.sess.source_map();
229            let mut parts = args
230                .args
231                .iter()
232                .map(|arg| match arg {
233                    hir::GenericArg::Lifetime(l) => {
234                        if l.syntax == hir::LifetimeSyntax::Implicit
235                            && #[allow(non_exhaustive_omitted_patterns)] match l.source {
    hir::LifetimeSource::Path { .. } => true,
    _ => false,
}matches!(l.source, hir::LifetimeSource::Path { .. })
236                        {
237                            "'_".to_string()
238                        } else {
239                            sm.span_to_snippet(l.ident.span)
240                                .unwrap_or_else(|_| ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("\'{0}", l.ident.name))
    })format!("'{}", l.ident.name))
241                        }
242                    }
243                    hir::GenericArg::Type(ty) => {
244                        sm.span_to_snippet(ty.span).unwrap_or_else(|_| "..".to_string())
245                    }
246                    hir::GenericArg::Const(ct) => {
247                        sm.span_to_snippet(ct.span).unwrap_or_else(|_| "..".to_string())
248                    }
249                    hir::GenericArg::Infer(_) => "_".to_string(),
250                })
251                .collect::<Vec<_>>();
252            parts.extend(args.constraints.iter().map(|constraint| {
253                sm.span_to_snippet(constraint.span)
254                    .unwrap_or_else(|_| ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0} = ..", constraint.ident))
    })format!("{} = ..", constraint.ident))
255            }));
256            let shorthand = ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0}<{1}>", segment.ident,
                parts.join(", ")))
    })format!("{}<{}>", segment.ident, parts.join(", "));
257
258            elided_lifetime_paths.push(ElidedLifetimeInPath {
259                span: segment.ident.span,
260                ident: segment.ident,
261                shorthand,
262            });
263        }
264
265        match qpath {
266            hir::QPath::Resolved(_, path) => {
267                for segment in path.segments {
268                    record_elided_lifetimes(self.tcx, &mut self.elided_lifetime_paths, segment);
269                }
270            }
271            hir::QPath::TypeRelative(_, segment) => {
272                record_elided_lifetimes(self.tcx, &mut self.elided_lifetime_paths, segment);
273            }
274        }
275
276        hir::intravisit::walk_qpath(self, qpath, id);
277    }
278
279    fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx, AmbigArg>) {
280        match arg.kind {
281            hir::TyKind::Ref(_, ref ty, _) => {
282                // We don't want to suggest looking into borrowing `&T` or `&Self`.
283                if let Some(ambig_ty) = ty.try_as_ambig_ty() {
284                    walk_ty(self, ambig_ty);
285                }
286                return;
287            }
288            hir::TyKind::Path(hir::QPath::Resolved(None, path)) => match &path.segments {
289                [segment]
290                    if #[allow(non_exhaustive_omitted_patterns)] match segment.res {
    Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } |
        Res::Def(hir::def::DefKind::TyParam, _) => true,
    _ => false,
}matches!(
291                        segment.res,
292                        Res::SelfTyParam { .. }
293                            | Res::SelfTyAlias { .. }
294                            | Res::Def(hir::def::DefKind::TyParam, _)
295                    ) =>
296                {
297                    self.types.push(path.span);
298                }
299                _ => {}
300            },
301            _ => {}
302        }
303        hir::intravisit::walk_ty(self, arg);
304    }
305}