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::{ConditionOptions, FormatArgs, OnUnimplementedNote};
5use rustc_hir::def_id::LocalDefId;
6use rustc_hir::find_attr;
7pub use rustc_hir::lints::FormatWarning;
8use rustc_middle::ty::print::PrintTraitRefExt;
9use rustc_middle::ty::{self, GenericParamDef, GenericParamDefKind};
10use rustc_span::Symbol;
11
12use super::{ObligationCauseCode, PredicateObligation};
13use crate::error_reporting::TypeErrCtxt;
14
15impl<'tcx> TypeErrCtxt<'_, 'tcx> {
16    /// Used to set on_unimplemented's `ItemContext`
17    /// to be the enclosing (async) block/function/closure
18    fn describe_enclosure(&self, def_id: LocalDefId) -> Option<&'static str> {
19        match self.tcx.hir_node_by_def_id(def_id) {
20            hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn { .. }, .. }) => Some("a function"),
21            hir::Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Fn(..), .. }) => {
22                Some("a trait method")
23            }
24            hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(..), .. }) => {
25                Some("a method")
26            }
27            hir::Node::Expr(hir::Expr {
28                kind: hir::ExprKind::Closure(hir::Closure { kind, .. }),
29                ..
30            }) => Some(self.describe_closure(*kind)),
31            _ => None,
32        }
33    }
34
35    pub fn on_unimplemented_note(
36        &self,
37        trait_pred: ty::PolyTraitPredicate<'tcx>,
38        obligation: &PredicateObligation<'tcx>,
39        long_ty_path: &mut Option<PathBuf>,
40    ) -> OnUnimplementedNote {
41        if trait_pred.polarity() != ty::PredicatePolarity::Positive {
42            return OnUnimplementedNote::default();
43        }
44        let (condition_options, format_args) =
45            self.on_unimplemented_components(trait_pred, obligation, long_ty_path);
46        if let Some(command) = {

    #[allow(deprecated)]
    {
        {
            'done:
                {
                for i in self.tcx.get_all_attrs(trait_pred.def_id()) {
                    #[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() {
47            command.evaluate_directive(
48                trait_pred.skip_binder().trait_ref,
49                &condition_options,
50                &format_args,
51            )
52        } else {
53            OnUnimplementedNote::default()
54        }
55    }
56
57    pub(crate) fn on_unimplemented_components(
58        &self,
59        trait_pred: ty::PolyTraitPredicate<'tcx>,
60        obligation: &PredicateObligation<'tcx>,
61        long_ty_path: &mut Option<PathBuf>,
62    ) -> (ConditionOptions, FormatArgs) {
63        let (def_id, args) = (trait_pred.def_id(), trait_pred.skip_binder().trait_ref.args);
64        let trait_pred = trait_pred.skip_binder();
65
66        let mut self_types = ::alloc::vec::Vec::new()vec![];
67        let mut generic_args: Vec<(Symbol, String)> = ::alloc::vec::Vec::new()vec![];
68        let mut crate_local = false;
69        // FIXME(-Zlower-impl-trait-in-trait-to-assoc-ty): HIR is not present for RPITITs,
70        // but I guess we could synthesize one here. We don't see any errors that rely on
71        // that yet, though.
72        let item_context = self.describe_enclosure(obligation.cause.body_id).unwrap_or("");
73
74        let direct = match obligation.cause.code() {
75            ObligationCauseCode::BuiltinDerived(..)
76            | ObligationCauseCode::ImplDerived(..)
77            | ObligationCauseCode::WellFormedDerived(..) => false,
78            _ => {
79                // this is a "direct", user-specified, rather than derived,
80                // obligation.
81                true
82            }
83        };
84
85        let from_desugaring = obligation.cause.span.desugaring_kind();
86
87        let cause = if let ObligationCauseCode::MainFunctionType = obligation.cause.code() {
88            Some("MainFunctionType".to_string())
89        } else {
90            None
91        };
92
93        // Add all types without trimmed paths or visible paths, ensuring they end up with
94        // their "canonical" def path.
95        {
    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().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().to_string()));
                    }
                }
            }
            if let Some(true) =
                    self_ty.ty_adt_def().map(|def| def.did().is_local()) {
                crate_local = true;
            }
            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()))
                            }));
                }
                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();
                    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!({
96            let generics = self.tcx.generics_of(def_id);
97            let self_ty = trait_pred.self_ty();
98            self_types.push(self_ty.to_string());
99            if let Some(def) = self_ty.ty_adt_def() {
100                // We also want to be able to select self's original
101                // signature with no type arguments resolved
102                self_types.push(self.tcx.type_of(def.did()).instantiate_identity().to_string());
103            }
104
105            for GenericParamDef { name, kind, index, .. } in generics.own_params.iter() {
106                let value = match kind {
107                    GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => {
108                        args[*index as usize].to_string()
109                    }
110                    GenericParamDefKind::Lifetime => continue,
111                };
112                generic_args.push((*name, value));
113
114                if let GenericParamDefKind::Type { .. } = kind {
115                    let param_ty = args[*index as usize].expect_ty();
116                    if let Some(def) = param_ty.ty_adt_def() {
117                        // We also want to be able to select the parameter's
118                        // original signature with no type arguments resolved
119                        generic_args.push((
120                            *name,
121                            self.tcx.type_of(def.did()).instantiate_identity().to_string(),
122                        ));
123                    }
124                }
125            }
126
127            if let Some(true) = self_ty.ty_adt_def().map(|def| def.did().is_local()) {
128                crate_local = true;
129            }
130
131            // Allow targeting all integers using `{integral}`, even if the exact type was resolved
132            if self_ty.is_integral() {
133                self_types.push("{integral}".to_owned());
134            }
135
136            if self_ty.is_array_slice() {
137                self_types.push("&[]".to_owned());
138            }
139
140            if self_ty.is_fn() {
141                let fn_sig = self_ty.fn_sig(self.tcx);
142                let shortname = if let ty::FnDef(def_id, _) = *self_ty.kind()
143                    && self.tcx.codegen_fn_attrs(def_id).safe_target_features
144                {
145                    "#[target_feature] fn"
146                } else {
147                    match fn_sig.safety() {
148                        hir::Safety::Safe => "fn",
149                        hir::Safety::Unsafe => "unsafe fn",
150                    }
151                };
152                self_types.push(shortname.to_owned());
153            }
154
155            // Slices give us `[]`, `[{ty}]`
156            if let ty::Slice(aty) = self_ty.kind() {
157                self_types.push("[]".to_owned());
158                if let Some(def) = aty.ty_adt_def() {
159                    // We also want to be able to select the slice's type's original
160                    // signature with no type arguments resolved
161                    self_types
162                        .push(format!("[{}]", self.tcx.type_of(def.did()).instantiate_identity()));
163                }
164                if aty.is_integral() {
165                    self_types.push("[{integral}]".to_string());
166                }
167            }
168
169            // Arrays give us `[]`, `[{ty}; _]` and `[{ty}; N]`
170            if let ty::Array(aty, len) = self_ty.kind() {
171                self_types.push("[]".to_string());
172                let len = len.try_to_target_usize(self.tcx);
173                self_types.push(format!("[{aty}; _]"));
174                if let Some(n) = len {
175                    self_types.push(format!("[{aty}; {n}]"));
176                }
177                if let Some(def) = aty.ty_adt_def() {
178                    // We also want to be able to select the array's type's original
179                    // signature with no type arguments resolved
180                    let def_ty = self.tcx.type_of(def.did()).instantiate_identity();
181                    self_types.push(format!("[{def_ty}; _]"));
182                    if let Some(n) = len {
183                        self_types.push(format!("[{def_ty}; {n}]"));
184                    }
185                }
186                if aty.is_integral() {
187                    self_types.push("[{integral}; _]".to_string());
188                    if let Some(n) = len {
189                        self_types.push(format!("[{{integral}}; {n}]"));
190                    }
191                }
192            }
193            if let ty::Dynamic(traits, _) = self_ty.kind() {
194                for t in traits.iter() {
195                    if let ty::ExistentialPredicate::Trait(trait_ref) = t.skip_binder() {
196                        self_types.push(self.tcx.def_path_str(trait_ref.def_id));
197                    }
198                }
199            }
200
201            // `&[{integral}]` - `FromIterator` needs that.
202            if let ty::Ref(_, ref_ty, rustc_ast::Mutability::Not) = self_ty.kind()
203                && let ty::Slice(sty) = ref_ty.kind()
204                && sty.is_integral()
205            {
206                self_types.push("&[{integral}]".to_owned());
207            }
208        }));
209
210        let this = self.tcx.def_path_str(trait_pred.trait_ref.def_id);
211        let trait_sugared = trait_pred.trait_ref.print_trait_sugared().to_string();
212
213        let condition_options = ConditionOptions {
214            self_types,
215            from_desugaring,
216            cause,
217            crate_local,
218            direct,
219            generic_args,
220        };
221
222        // Unlike the generic_args earlier,
223        // this one is *not* collected under `with_no_trimmed_paths!`
224        // for printing the type to the user
225        //
226        // This includes `Self`, as it is the first parameter in `own_params`.
227        let generic_args = self
228            .tcx
229            .generics_of(trait_pred.trait_ref.def_id)
230            .own_params
231            .iter()
232            .filter_map(|param| {
233                let value = match param.kind {
234                    GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => {
235                        if let Some(ty) = trait_pred.trait_ref.args[param.index as usize].as_type()
236                        {
237                            self.tcx.short_string(ty, long_ty_path)
238                        } else {
239                            trait_pred.trait_ref.args[param.index as usize].to_string()
240                        }
241                    }
242                    GenericParamDefKind::Lifetime => return None,
243                };
244                let name = param.name;
245                Some((name, value))
246            })
247            .collect();
248
249        let format_args = FormatArgs { this, trait_sugared, generic_args, item_context };
250        (condition_options, format_args)
251    }
252}