Skip to main content

rustc_trait_selection/error_reporting/traits/
on_unimplemented.rs

1use std::path::PathBuf;
2
3use rustc_hir as hir;
4use rustc_hir::attrs::diagnostic::{CustomDiagnostic, FilterOptions, FormatArgs};
5use rustc_hir::def_id::LocalDefId;
6use rustc_hir::find_attr;
7use rustc_middle::ty::print::PrintTraitRefExt;
8use rustc_middle::ty::{self, GenericParamDef, GenericParamDefKind};
9use rustc_span::Symbol;
10
11use super::{ObligationCauseCode, PredicateObligation};
12use crate::error_reporting::TypeErrCtxt;
13
14impl<'tcx> TypeErrCtxt<'_, 'tcx> {
15    /// Used to set on_unimplemented's `ItemContext`
16    /// to be the enclosing (async) block/function/closure
17    fn describe_enclosure(&self, def_id: LocalDefId) -> Option<&'static str> {
18        match self.tcx.hir_node_by_def_id(def_id) {
19            hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn { .. }, .. }) => Some("a function"),
20            hir::Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Fn(..), .. }) => {
21                Some("a trait method")
22            }
23            hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(..), .. }) => {
24                Some("a method")
25            }
26            hir::Node::Expr(hir::Expr {
27                kind: hir::ExprKind::Closure(hir::Closure { kind, .. }),
28                ..
29            }) => Some(self.describe_closure(*kind)),
30            _ => None,
31        }
32    }
33
34    pub fn on_unimplemented_note(
35        &self,
36        trait_pred: ty::PolyTraitPredicate<'tcx>,
37        obligation: &PredicateObligation<'tcx>,
38        long_ty_path: &mut Option<PathBuf>,
39    ) -> CustomDiagnostic {
40        if trait_pred.polarity() != ty::PredicatePolarity::Positive {
41            return CustomDiagnostic::default();
42        }
43        // This is needed as `on_unimplemented` is currently not allowed on trait aliases,
44        // but the "not allowed" is a warning, and this check ensures the attribute has no effect
45        if self.tcx.is_trait_alias(trait_pred.def_id()) {
46            return CustomDiagnostic::default();
47        }
48        let (filter_options, format_args) =
49            self.on_unimplemented_components(trait_pred, obligation, long_ty_path, true);
50        if let Some(command) = {
    {
        'done:
            {
            for i in
                ::rustc_hir::attrs::HasAttrs::get_attrs(trait_pred.def_id(),
                    &self.tcx) {
                #[allow(unused_imports)]
                use ::rustc_hir::attrs::AttributeKind::*;
                let i: &::rustc_hir::Attribute = i;
                match i {
                    ::rustc_hir::Attribute::Parsed(OnUnimplemented { directive,
                        .. }) => {
                        break 'done Some(directive.as_deref());
                    }
                    ::rustc_hir::Attribute::Unparsed(..) =>
                        {}
                        #[deny(unreachable_patterns)]
                        _ => {}
                }
            }
            None
        }
    }
}find_attr!(self.tcx, trait_pred.def_id(), OnUnimplemented {directive, ..} => directive.as_deref()).flatten() {
51            command.eval(
52                Some(&filter_options),
53                &format_args,
54            )
55        } else {
56            CustomDiagnostic::default()
57        }
58    }
59
60    pub(crate) fn on_unimplemented_components(
61        &self,
62        trait_pred: ty::PolyTraitPredicate<'tcx>,
63        obligation: &PredicateObligation<'tcx>,
64        long_ty_path: &mut Option<PathBuf>,
65        print_infer_ty_var: bool,
66    ) -> (FilterOptions, FormatArgs) {
67        let (def_id, args) = (trait_pred.def_id(), trait_pred.skip_binder().trait_ref.args);
68        let trait_pred = trait_pred.skip_binder();
69
70        let mut self_types = ::alloc::vec::Vec::new()vec![];
71        let mut generic_args: Vec<(Symbol, String)> = ::alloc::vec::Vec::new()vec![];
72        let mut crate_local = false;
73        // FIXME(-Zlower-impl-trait-in-trait-to-assoc-ty): HIR is not present for RPITITs,
74        // but I guess we could synthesize one here. We don't see any errors that rely on
75        // that yet, though.
76        let item_context = self.describe_enclosure(obligation.cause.body_def_id).unwrap_or("");
77
78        let direct = match obligation.cause.code() {
79            ObligationCauseCode::BuiltinDerived(..)
80            | ObligationCauseCode::ImplDerived(..)
81            | ObligationCauseCode::WellFormedDerived(..) => false,
82            _ => {
83                // this is a "direct", user-specified, rather than derived,
84                // obligation.
85                true
86            }
87        };
88
89        let from_desugaring = obligation.cause.span.desugaring_kind();
90
91        let cause = if let ObligationCauseCode::MainFunctionType = obligation.cause.code() {
92            Some("MainFunctionType".to_string())
93        } else {
94            None
95        };
96
97        // Add all types without trimmed paths or visible paths, ensuring they end up with
98        // their "canonical" def path.
99        {
    let _guard = NoTrimmedGuard::new();
    {
        let _guard = NoVisibleGuard::new();
        {
            let generics = self.tcx.generics_of(def_id);
            let self_ty = trait_pred.self_ty();
            self_types.push(self_ty.to_string());
            if let Some(def) = self_ty.ty_adt_def() {
                self_types.push(self.tcx.type_of(def.did()).instantiate_identity().skip_norm_wip().to_string());
            }
            for GenericParamDef { name, kind, index, .. } in
                generics.own_params.iter() {
                let value =
                    match kind {
                        GenericParamDefKind::Type { .. } |
                            GenericParamDefKind::Const { .. } => {
                            args[*index as usize].to_string()
                        }
                        GenericParamDefKind::Lifetime => continue,
                    };
                generic_args.push((*name, value));
                if let GenericParamDefKind::Type { .. } = kind {
                    let param_ty = args[*index as usize].expect_ty();
                    if let Some(def) = param_ty.ty_adt_def() {
                        generic_args.push((*name,
                                self.tcx.type_of(def.did()).instantiate_identity().skip_norm_wip().to_string()));
                    }
                }
            }
            if let Some(adt) = self_ty.ty_adt_def() {
                if adt.did().is_local() { crate_local = true; }
                self_types.push(::alloc::__export::must_use({
                            ::alloc::fmt::format(format_args!("{{{0}}}", adt.descr()))
                        }))
            }
            if self_ty.is_integral() {
                self_types.push("{integral}".to_owned());
            }
            if self_ty.is_array_slice() { self_types.push("&[]".to_owned()); }
            if self_ty.is_fn() {
                let fn_sig = self_ty.fn_sig(self.tcx);
                let shortname =
                    if let ty::FnDef(def_id, _) = *self_ty.kind() &&
                            self.tcx.codegen_fn_attrs(def_id).safe_target_features {
                        "#[target_feature] fn"
                    } else {
                        match fn_sig.safety() {
                            hir::Safety::Safe => "fn",
                            hir::Safety::Unsafe => "unsafe fn",
                        }
                    };
                self_types.push(shortname.to_owned());
            }
            if let ty::Slice(aty) = self_ty.kind() {
                self_types.push("[]".to_owned());
                if let Some(def) = aty.ty_adt_def() {
                    self_types.push(::alloc::__export::must_use({
                                ::alloc::fmt::format(format_args!("[{0}]",
                                        self.tcx.type_of(def.did()).instantiate_identity().skip_norm_wip()))
                            }));
                }
                if aty.is_integral() {
                    self_types.push("[{integral}]".to_string());
                }
            }
            if let ty::Array(aty, len) = self_ty.kind() {
                self_types.push("[]".to_string());
                let len = len.try_to_target_usize(self.tcx);
                self_types.push(::alloc::__export::must_use({
                            ::alloc::fmt::format(format_args!("[{0}; _]", aty))
                        }));
                if let Some(n) = len {
                    self_types.push(::alloc::__export::must_use({
                                ::alloc::fmt::format(format_args!("[{0}; {1}]", aty, n))
                            }));
                }
                if let Some(def) = aty.ty_adt_def() {
                    let def_ty =
                        self.tcx.type_of(def.did()).instantiate_identity().skip_norm_wip();
                    self_types.push(::alloc::__export::must_use({
                                ::alloc::fmt::format(format_args!("[{0}; _]", def_ty))
                            }));
                    if let Some(n) = len {
                        self_types.push(::alloc::__export::must_use({
                                    ::alloc::fmt::format(format_args!("[{0}; {1}]", def_ty, n))
                                }));
                    }
                }
                if aty.is_integral() {
                    self_types.push("[{integral}; _]".to_string());
                    if let Some(n) = len {
                        self_types.push(::alloc::__export::must_use({
                                    ::alloc::fmt::format(format_args!("[{{integral}}; {0}]", n))
                                }));
                    }
                }
            }
            if let ty::Dynamic(traits, _) = self_ty.kind() {
                for t in traits.iter() {
                    if let ty::ExistentialPredicate::Trait(trait_ref) =
                            t.skip_binder() {
                        self_types.push(self.tcx.def_path_str(trait_ref.def_id));
                    }
                }
            }
            if let ty::Ref(_, ref_ty, rustc_ast::Mutability::Not) =
                            self_ty.kind() && let ty::Slice(sty) = ref_ty.kind() &&
                    sty.is_integral() {
                self_types.push("&[{integral}]".to_owned());
            }
        }
    }
};ty::print::with_no_trimmed_paths!(ty::print::with_no_visible_paths!({
100            let generics = self.tcx.generics_of(def_id);
101            let self_ty = trait_pred.self_ty();
102            self_types.push(self_ty.to_string());
103            if let Some(def) = self_ty.ty_adt_def() {
104                // We also want to be able to select self's original
105                // signature with no type arguments resolved
106                self_types.push(
107                    self.tcx.type_of(def.did()).instantiate_identity().skip_norm_wip().to_string(),
108                );
109            }
110
111            for GenericParamDef { name, kind, index, .. } in generics.own_params.iter() {
112                let value = match kind {
113                    GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => {
114                        args[*index as usize].to_string()
115                    }
116                    GenericParamDefKind::Lifetime => continue,
117                };
118                generic_args.push((*name, value));
119
120                if let GenericParamDefKind::Type { .. } = kind {
121                    let param_ty = args[*index as usize].expect_ty();
122                    if let Some(def) = param_ty.ty_adt_def() {
123                        // We also want to be able to select the parameter's
124                        // original signature with no type arguments resolved
125                        generic_args.push((
126                            *name,
127                            self.tcx
128                                .type_of(def.did())
129                                .instantiate_identity()
130                                .skip_norm_wip()
131                                .to_string(),
132                        ));
133                    }
134                }
135            }
136
137            if let Some(adt) = self_ty.ty_adt_def() {
138                if adt.did().is_local() {
139                    crate_local = true;
140                }
141                self_types.push(format!("{{{}}}", adt.descr()))
142            }
143
144            // Allow targeting all integers using `{integral}`, even if the exact type was resolved
145            if self_ty.is_integral() {
146                self_types.push("{integral}".to_owned());
147            }
148
149            if self_ty.is_array_slice() {
150                self_types.push("&[]".to_owned());
151            }
152
153            if self_ty.is_fn() {
154                let fn_sig = self_ty.fn_sig(self.tcx);
155                let shortname = if let ty::FnDef(def_id, _) = *self_ty.kind()
156                    && self.tcx.codegen_fn_attrs(def_id).safe_target_features
157                {
158                    "#[target_feature] fn"
159                } else {
160                    match fn_sig.safety() {
161                        hir::Safety::Safe => "fn",
162                        hir::Safety::Unsafe => "unsafe fn",
163                    }
164                };
165                self_types.push(shortname.to_owned());
166            }
167
168            // Slices give us `[]`, `[{ty}]`
169            if let ty::Slice(aty) = self_ty.kind() {
170                self_types.push("[]".to_owned());
171                if let Some(def) = aty.ty_adt_def() {
172                    // We also want to be able to select the slice's type's original
173                    // signature with no type arguments resolved
174                    self_types.push(format!(
175                        "[{}]",
176                        self.tcx.type_of(def.did()).instantiate_identity().skip_norm_wip()
177                    ));
178                }
179                if aty.is_integral() {
180                    self_types.push("[{integral}]".to_string());
181                }
182            }
183
184            // Arrays give us `[]`, `[{ty}; _]` and `[{ty}; N]`
185            if let ty::Array(aty, len) = self_ty.kind() {
186                self_types.push("[]".to_string());
187                let len = len.try_to_target_usize(self.tcx);
188                self_types.push(format!("[{aty}; _]"));
189                if let Some(n) = len {
190                    self_types.push(format!("[{aty}; {n}]"));
191                }
192                if let Some(def) = aty.ty_adt_def() {
193                    // We also want to be able to select the array's type's original
194                    // signature with no type arguments resolved
195                    let def_ty = self.tcx.type_of(def.did()).instantiate_identity().skip_norm_wip();
196                    self_types.push(format!("[{def_ty}; _]"));
197                    if let Some(n) = len {
198                        self_types.push(format!("[{def_ty}; {n}]"));
199                    }
200                }
201                if aty.is_integral() {
202                    self_types.push("[{integral}; _]".to_string());
203                    if let Some(n) = len {
204                        self_types.push(format!("[{{integral}}; {n}]"));
205                    }
206                }
207            }
208            if let ty::Dynamic(traits, _) = self_ty.kind() {
209                for t in traits.iter() {
210                    if let ty::ExistentialPredicate::Trait(trait_ref) = t.skip_binder() {
211                        self_types.push(self.tcx.def_path_str(trait_ref.def_id));
212                    }
213                }
214            }
215
216            // `&[{integral}]` - `FromIterator` needs that.
217            if let ty::Ref(_, ref_ty, rustc_ast::Mutability::Not) = self_ty.kind()
218                && let ty::Slice(sty) = ref_ty.kind()
219                && sty.is_integral()
220            {
221                self_types.push("&[{integral}]".to_owned());
222            }
223        }));
224
225        let this = self.tcx.def_path_str(trait_pred.trait_ref.def_id);
226        let this_resolved = trait_pred.trait_ref.print_trait_sugared().to_string();
227        let this_path =
228            ty::TraitRef::identity(self.tcx, def_id).print_only_trait_path().to_string();
229
230        let filter_options =
231            FilterOptions { self_types, from_desugaring, cause, crate_local, direct, generic_args };
232
233        // Unlike the generic_args earlier,
234        // this one is *not* collected under `with_no_trimmed_paths!`
235        // for printing the type to the user
236        //
237        // This includes `Self`, as it is the first parameter in `own_params`.
238        let generic_args = self
239            .tcx
240            .generics_of(trait_pred.trait_ref.def_id)
241            .own_params
242            .iter()
243            .filter_map(|param| {
244                let value = match param.kind {
245                    GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => {
246                        if let Some(ty) = trait_pred.trait_ref.args[param.index as usize].as_type()
247                        {
248                            if print_infer_ty_var == false
249                                && let ty::Infer(ty::TyVar(_)) = ty.kind()
250                            {
251                                ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0}", param.name))
    })format!("{}", param.name)
252                            } else {
253                                self.tcx.short_string(ty, long_ty_path)
254                            }
255                        } else {
256                            trait_pred.trait_ref.args[param.index as usize].to_string()
257                        }
258                    }
259                    GenericParamDefKind::Lifetime => return None,
260                };
261                let name = param.name;
262                Some((name, value))
263            })
264            .collect();
265
266        let format_args =
267            FormatArgs { this, this_path, this_resolved, generic_args, item_context, .. };
268        (filter_options, format_args)
269    }
270}