rustc_trait_selection/error_reporting/traits/
on_unimplemented.rs

1use std::iter;
2use std::path::PathBuf;
3
4use rustc_ast::MetaItemInner;
5use rustc_data_structures::fx::FxHashMap;
6use rustc_errors::codes::*;
7use rustc_errors::{ErrorGuaranteed, struct_span_code_err};
8use rustc_hir::def_id::{DefId, LocalDefId};
9use rustc_hir::{AttrArgs, Attribute};
10use rustc_macros::LintDiagnostic;
11use rustc_middle::bug;
12use rustc_middle::ty::print::PrintTraitRefExt as _;
13use rustc_middle::ty::{self, GenericArgsRef, GenericParamDefKind, TyCtxt};
14use rustc_parse_format::{ParseMode, Parser, Piece, Position};
15use rustc_session::lint::builtin::UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES;
16use rustc_span::{Ident, Span, Symbol, kw, sym};
17use tracing::{debug, info};
18use {rustc_attr_parsing as attr, rustc_hir as hir};
19
20use super::{ObligationCauseCode, PredicateObligation};
21use crate::error_reporting::TypeErrCtxt;
22use crate::errors::{
23    EmptyOnClauseInOnUnimplemented, InvalidOnClauseInOnUnimplemented, NoValueInOnUnimplemented,
24};
25use crate::infer::InferCtxtExt;
26
27/// The symbols which are always allowed in a format string
28static ALLOWED_FORMAT_SYMBOLS: &[Symbol] = &[
29    kw::SelfUpper,
30    sym::ItemContext,
31    sym::from_desugaring,
32    sym::direct,
33    sym::cause,
34    sym::integral,
35    sym::integer_,
36    sym::float,
37    sym::_Self,
38    sym::crate_local,
39    sym::Trait,
40];
41
42impl<'tcx> TypeErrCtxt<'_, 'tcx> {
43    fn impl_similar_to(
44        &self,
45        trait_pred: ty::PolyTraitPredicate<'tcx>,
46        obligation: &PredicateObligation<'tcx>,
47    ) -> Option<(DefId, GenericArgsRef<'tcx>)> {
48        let tcx = self.tcx;
49        let param_env = obligation.param_env;
50        self.enter_forall(trait_pred, |trait_pred| {
51            let trait_self_ty = trait_pred.self_ty();
52
53            let mut self_match_impls = vec![];
54            let mut fuzzy_match_impls = vec![];
55
56            self.tcx.for_each_relevant_impl(trait_pred.def_id(), trait_self_ty, |def_id| {
57                let impl_args = self.fresh_args_for_item(obligation.cause.span, def_id);
58                let impl_trait_ref =
59                    tcx.impl_trait_ref(def_id).unwrap().instantiate(tcx, impl_args);
60
61                let impl_self_ty = impl_trait_ref.self_ty();
62
63                if self.can_eq(param_env, trait_self_ty, impl_self_ty) {
64                    self_match_impls.push((def_id, impl_args));
65
66                    if iter::zip(
67                        trait_pred.trait_ref.args.types().skip(1),
68                        impl_trait_ref.args.types().skip(1),
69                    )
70                    .all(|(u, v)| self.fuzzy_match_tys(u, v, false).is_some())
71                    {
72                        fuzzy_match_impls.push((def_id, impl_args));
73                    }
74                }
75            });
76
77            let impl_def_id_and_args = if let [impl_] = self_match_impls[..] {
78                impl_
79            } else if let [impl_] = fuzzy_match_impls[..] {
80                impl_
81            } else {
82                return None;
83            };
84
85            tcx.has_attr(impl_def_id_and_args.0, sym::rustc_on_unimplemented)
86                .then_some(impl_def_id_and_args)
87        })
88    }
89
90    /// Used to set on_unimplemented's `ItemContext`
91    /// to be the enclosing (async) block/function/closure
92    fn describe_enclosure(&self, def_id: LocalDefId) -> Option<&'static str> {
93        match self.tcx.hir_node_by_def_id(def_id) {
94            hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn { .. }, .. }) => Some("a function"),
95            hir::Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Fn(..), .. }) => {
96                Some("a trait method")
97            }
98            hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(..), .. }) => {
99                Some("a method")
100            }
101            hir::Node::Expr(hir::Expr {
102                kind: hir::ExprKind::Closure(hir::Closure { kind, .. }),
103                ..
104            }) => Some(self.describe_closure(*kind)),
105            _ => None,
106        }
107    }
108
109    pub fn on_unimplemented_note(
110        &self,
111        trait_pred: ty::PolyTraitPredicate<'tcx>,
112        obligation: &PredicateObligation<'tcx>,
113        long_ty_file: &mut Option<PathBuf>,
114    ) -> OnUnimplementedNote {
115        if trait_pred.polarity() != ty::PredicatePolarity::Positive {
116            return OnUnimplementedNote::default();
117        }
118
119        let (def_id, args) = self
120            .impl_similar_to(trait_pred, obligation)
121            .unwrap_or_else(|| (trait_pred.def_id(), trait_pred.skip_binder().trait_ref.args));
122        let trait_pred = trait_pred.skip_binder();
123
124        let mut flags = vec![];
125        // FIXME(-Zlower-impl-trait-in-trait-to-assoc-ty): HIR is not present for RPITITs,
126        // but I guess we could synthesize one here. We don't see any errors that rely on
127        // that yet, though.
128        let enclosure = self.describe_enclosure(obligation.cause.body_id).map(|t| t.to_owned());
129        flags.push((sym::ItemContext, enclosure));
130
131        match obligation.cause.code() {
132            ObligationCauseCode::BuiltinDerived(..)
133            | ObligationCauseCode::ImplDerived(..)
134            | ObligationCauseCode::WellFormedDerived(..) => {}
135            _ => {
136                // this is a "direct", user-specified, rather than derived,
137                // obligation.
138                flags.push((sym::direct, None));
139            }
140        }
141
142        if let Some(k) = obligation.cause.span.desugaring_kind() {
143            flags.push((sym::from_desugaring, None));
144            flags.push((sym::from_desugaring, Some(format!("{k:?}"))));
145        }
146
147        if let ObligationCauseCode::MainFunctionType = obligation.cause.code() {
148            flags.push((sym::cause, Some("MainFunctionType".to_string())));
149        }
150
151        flags.push((sym::Trait, Some(trait_pred.trait_ref.print_trait_sugared().to_string())));
152
153        // Add all types without trimmed paths or visible paths, ensuring they end up with
154        // their "canonical" def path.
155        ty::print::with_no_trimmed_paths!(ty::print::with_no_visible_paths!({
156            let generics = self.tcx.generics_of(def_id);
157            let self_ty = trait_pred.self_ty();
158            // This is also included through the generics list as `Self`,
159            // but the parser won't allow you to use it
160            flags.push((sym::_Self, Some(self_ty.to_string())));
161            if let Some(def) = self_ty.ty_adt_def() {
162                // We also want to be able to select self's original
163                // signature with no type arguments resolved
164                flags.push((
165                    sym::_Self,
166                    Some(self.tcx.type_of(def.did()).instantiate_identity().to_string()),
167                ));
168            }
169
170            for param in generics.own_params.iter() {
171                let value = match param.kind {
172                    GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => {
173                        args[param.index as usize].to_string()
174                    }
175                    GenericParamDefKind::Lifetime => continue,
176                };
177                let name = param.name;
178                flags.push((name, Some(value)));
179
180                if let GenericParamDefKind::Type { .. } = param.kind {
181                    let param_ty = args[param.index as usize].expect_ty();
182                    if let Some(def) = param_ty.ty_adt_def() {
183                        // We also want to be able to select the parameter's
184                        // original signature with no type arguments resolved
185                        flags.push((
186                            name,
187                            Some(self.tcx.type_of(def.did()).instantiate_identity().to_string()),
188                        ));
189                    }
190                }
191            }
192
193            if let Some(true) = self_ty.ty_adt_def().map(|def| def.did().is_local()) {
194                flags.push((sym::crate_local, None));
195            }
196
197            // Allow targeting all integers using `{integral}`, even if the exact type was resolved
198            if self_ty.is_integral() {
199                flags.push((sym::_Self, Some("{integral}".to_owned())));
200            }
201
202            if self_ty.is_array_slice() {
203                flags.push((sym::_Self, Some("&[]".to_owned())));
204            }
205
206            if self_ty.is_fn() {
207                let fn_sig = self_ty.fn_sig(self.tcx);
208                let shortname = if let ty::FnDef(def_id, _) = self_ty.kind()
209                    && self.tcx.codegen_fn_attrs(def_id).safe_target_features
210                {
211                    "#[target_feature] fn"
212                } else {
213                    match fn_sig.safety() {
214                        hir::Safety::Safe => "fn",
215                        hir::Safety::Unsafe => "unsafe fn",
216                    }
217                };
218                flags.push((sym::_Self, Some(shortname.to_owned())));
219            }
220
221            // Slices give us `[]`, `[{ty}]`
222            if let ty::Slice(aty) = self_ty.kind() {
223                flags.push((sym::_Self, Some("[]".to_string())));
224                if let Some(def) = aty.ty_adt_def() {
225                    // We also want to be able to select the slice's type's original
226                    // signature with no type arguments resolved
227                    flags.push((
228                        sym::_Self,
229                        Some(format!("[{}]", self.tcx.type_of(def.did()).instantiate_identity())),
230                    ));
231                }
232                if aty.is_integral() {
233                    flags.push((sym::_Self, Some("[{integral}]".to_string())));
234                }
235            }
236
237            // Arrays give us `[]`, `[{ty}; _]` and `[{ty}; N]`
238            if let ty::Array(aty, len) = self_ty.kind() {
239                flags.push((sym::_Self, Some("[]".to_string())));
240                let len = len.try_to_target_usize(self.tcx);
241                flags.push((sym::_Self, Some(format!("[{aty}; _]"))));
242                if let Some(n) = len {
243                    flags.push((sym::_Self, Some(format!("[{aty}; {n}]"))));
244                }
245                if let Some(def) = aty.ty_adt_def() {
246                    // We also want to be able to select the array's type's original
247                    // signature with no type arguments resolved
248                    let def_ty = self.tcx.type_of(def.did()).instantiate_identity();
249                    flags.push((sym::_Self, Some(format!("[{def_ty}; _]"))));
250                    if let Some(n) = len {
251                        flags.push((sym::_Self, Some(format!("[{def_ty}; {n}]"))));
252                    }
253                }
254                if aty.is_integral() {
255                    flags.push((sym::_Self, Some("[{integral}; _]".to_string())));
256                    if let Some(n) = len {
257                        flags.push((sym::_Self, Some(format!("[{{integral}}; {n}]"))));
258                    }
259                }
260            }
261            if let ty::Dynamic(traits, _, _) = self_ty.kind() {
262                for t in traits.iter() {
263                    if let ty::ExistentialPredicate::Trait(trait_ref) = t.skip_binder() {
264                        flags.push((sym::_Self, Some(self.tcx.def_path_str(trait_ref.def_id))))
265                    }
266                }
267            }
268
269            // `&[{integral}]` - `FromIterator` needs that.
270            if let ty::Ref(_, ref_ty, rustc_ast::Mutability::Not) = self_ty.kind()
271                && let ty::Slice(sty) = ref_ty.kind()
272                && sty.is_integral()
273            {
274                flags.push((sym::_Self, Some("&[{integral}]".to_owned())));
275            }
276        }));
277
278        if let Ok(Some(command)) = OnUnimplementedDirective::of_item(self.tcx, def_id) {
279            command.evaluate(self.tcx, trait_pred.trait_ref, &flags, long_ty_file)
280        } else {
281            OnUnimplementedNote::default()
282        }
283    }
284}
285
286#[derive(Clone, Debug)]
287pub struct OnUnimplementedFormatString {
288    symbol: Symbol,
289    span: Span,
290    is_diagnostic_namespace_variant: bool,
291}
292
293#[derive(Debug)]
294pub struct OnUnimplementedDirective {
295    pub condition: Option<MetaItemInner>,
296    pub subcommands: Vec<OnUnimplementedDirective>,
297    pub message: Option<OnUnimplementedFormatString>,
298    pub label: Option<OnUnimplementedFormatString>,
299    pub notes: Vec<OnUnimplementedFormatString>,
300    pub parent_label: Option<OnUnimplementedFormatString>,
301    pub append_const_msg: Option<AppendConstMessage>,
302}
303
304/// For the `#[rustc_on_unimplemented]` attribute
305#[derive(Default)]
306pub struct OnUnimplementedNote {
307    pub message: Option<String>,
308    pub label: Option<String>,
309    pub notes: Vec<String>,
310    pub parent_label: Option<String>,
311    // If none, should fall back to a generic message
312    pub append_const_msg: Option<AppendConstMessage>,
313}
314
315/// Append a message for `~const Trait` errors.
316#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
317pub enum AppendConstMessage {
318    #[default]
319    Default,
320    Custom(Symbol, Span),
321}
322
323#[derive(LintDiagnostic)]
324#[diag(trait_selection_malformed_on_unimplemented_attr)]
325#[help]
326pub struct MalformedOnUnimplementedAttrLint {
327    #[label]
328    pub span: Span,
329}
330
331impl MalformedOnUnimplementedAttrLint {
332    fn new(span: Span) -> Self {
333        Self { span }
334    }
335}
336
337#[derive(LintDiagnostic)]
338#[diag(trait_selection_missing_options_for_on_unimplemented_attr)]
339#[help]
340pub struct MissingOptionsForOnUnimplementedAttr;
341
342#[derive(LintDiagnostic)]
343#[diag(trait_selection_ignored_diagnostic_option)]
344pub struct IgnoredDiagnosticOption {
345    pub option_name: &'static str,
346    #[label]
347    pub span: Span,
348    #[label(trait_selection_other_label)]
349    pub prev_span: Span,
350}
351
352impl IgnoredDiagnosticOption {
353    fn maybe_emit_warning<'tcx>(
354        tcx: TyCtxt<'tcx>,
355        item_def_id: DefId,
356        new: Option<Span>,
357        old: Option<Span>,
358        option_name: &'static str,
359    ) {
360        if let (Some(new_item), Some(old_item)) = (new, old) {
361            if let Some(item_def_id) = item_def_id.as_local() {
362                tcx.emit_node_span_lint(
363                    UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
364                    tcx.local_def_id_to_hir_id(item_def_id),
365                    new_item,
366                    IgnoredDiagnosticOption { span: new_item, prev_span: old_item, option_name },
367                );
368            }
369        }
370    }
371}
372
373#[derive(LintDiagnostic)]
374#[diag(trait_selection_unknown_format_parameter_for_on_unimplemented_attr)]
375#[help]
376pub struct UnknownFormatParameterForOnUnimplementedAttr {
377    argument_name: Symbol,
378    trait_name: Ident,
379}
380
381#[derive(LintDiagnostic)]
382#[diag(trait_selection_disallowed_positional_argument)]
383#[help]
384pub struct DisallowedPositionalArgument;
385
386#[derive(LintDiagnostic)]
387#[diag(trait_selection_invalid_format_specifier)]
388#[help]
389pub struct InvalidFormatSpecifier;
390
391#[derive(LintDiagnostic)]
392#[diag(trait_selection_wrapped_parser_error)]
393pub struct WrappedParserError {
394    description: String,
395    label: String,
396}
397
398impl<'tcx> OnUnimplementedDirective {
399    fn parse(
400        tcx: TyCtxt<'tcx>,
401        item_def_id: DefId,
402        items: &[MetaItemInner],
403        span: Span,
404        is_root: bool,
405        is_diagnostic_namespace_variant: bool,
406    ) -> Result<Option<Self>, ErrorGuaranteed> {
407        let mut errored = None;
408        let mut item_iter = items.iter();
409
410        let parse_value = |value_str, value_span| {
411            OnUnimplementedFormatString::try_parse(
412                tcx,
413                item_def_id,
414                value_str,
415                value_span,
416                is_diagnostic_namespace_variant,
417            )
418            .map(Some)
419        };
420
421        let condition = if is_root {
422            None
423        } else {
424            let cond = item_iter
425                .next()
426                .ok_or_else(|| tcx.dcx().emit_err(EmptyOnClauseInOnUnimplemented { span }))?
427                .meta_item_or_bool()
428                .ok_or_else(|| tcx.dcx().emit_err(InvalidOnClauseInOnUnimplemented { span }))?;
429            attr::eval_condition(cond, &tcx.sess, Some(tcx.features()), &mut |cfg| {
430                if let Some(value) = cfg.value
431                    && let Err(guar) = parse_value(value, cfg.span)
432                {
433                    errored = Some(guar);
434                }
435                true
436            });
437            Some(cond.clone())
438        };
439
440        let mut message = None;
441        let mut label = None;
442        let mut notes = Vec::new();
443        let mut parent_label = None;
444        let mut subcommands = vec![];
445        let mut append_const_msg = None;
446
447        for item in item_iter {
448            if item.has_name(sym::message) && message.is_none() {
449                if let Some(message_) = item.value_str() {
450                    message = parse_value(message_, item.span())?;
451                    continue;
452                }
453            } else if item.has_name(sym::label) && label.is_none() {
454                if let Some(label_) = item.value_str() {
455                    label = parse_value(label_, item.span())?;
456                    continue;
457                }
458            } else if item.has_name(sym::note) {
459                if let Some(note_) = item.value_str() {
460                    if let Some(note) = parse_value(note_, item.span())? {
461                        notes.push(note);
462                        continue;
463                    }
464                }
465            } else if item.has_name(sym::parent_label)
466                && parent_label.is_none()
467                && !is_diagnostic_namespace_variant
468            {
469                if let Some(parent_label_) = item.value_str() {
470                    parent_label = parse_value(parent_label_, item.span())?;
471                    continue;
472                }
473            } else if item.has_name(sym::on)
474                && is_root
475                && message.is_none()
476                && label.is_none()
477                && notes.is_empty()
478                && !is_diagnostic_namespace_variant
479            // FIXME(diagnostic_namespace): disallow filters for now
480            {
481                if let Some(items) = item.meta_item_list() {
482                    match Self::parse(
483                        tcx,
484                        item_def_id,
485                        items,
486                        item.span(),
487                        false,
488                        is_diagnostic_namespace_variant,
489                    ) {
490                        Ok(Some(subcommand)) => subcommands.push(subcommand),
491                        Ok(None) => bug!(
492                            "This cannot happen for now as we only reach that if `is_diagnostic_namespace_variant` is false"
493                        ),
494                        Err(reported) => errored = Some(reported),
495                    };
496                    continue;
497                }
498            } else if item.has_name(sym::append_const_msg)
499                && append_const_msg.is_none()
500                && !is_diagnostic_namespace_variant
501            {
502                if let Some(msg) = item.value_str() {
503                    append_const_msg = Some(AppendConstMessage::Custom(msg, item.span()));
504                    continue;
505                } else if item.is_word() {
506                    append_const_msg = Some(AppendConstMessage::Default);
507                    continue;
508                }
509            }
510
511            if is_diagnostic_namespace_variant {
512                if let Some(def_id) = item_def_id.as_local() {
513                    tcx.emit_node_span_lint(
514                        UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
515                        tcx.local_def_id_to_hir_id(def_id),
516                        vec![item.span()],
517                        MalformedOnUnimplementedAttrLint::new(item.span()),
518                    );
519                }
520            } else {
521                // nothing found
522                tcx.dcx().emit_err(NoValueInOnUnimplemented { span: item.span() });
523            }
524        }
525
526        if let Some(reported) = errored {
527            if is_diagnostic_namespace_variant { Ok(None) } else { Err(reported) }
528        } else {
529            Ok(Some(OnUnimplementedDirective {
530                condition,
531                subcommands,
532                message,
533                label,
534                notes,
535                parent_label,
536                append_const_msg,
537            }))
538        }
539    }
540
541    pub fn of_item(tcx: TyCtxt<'tcx>, item_def_id: DefId) -> Result<Option<Self>, ErrorGuaranteed> {
542        if let Some(attr) = tcx.get_attr(item_def_id, sym::rustc_on_unimplemented) {
543            return Self::parse_attribute(attr, false, tcx, item_def_id);
544        } else {
545            tcx.get_attrs_by_path(item_def_id, &[sym::diagnostic, sym::on_unimplemented])
546                .filter_map(|attr| Self::parse_attribute(attr, true, tcx, item_def_id).transpose())
547                .try_fold(None, |aggr: Option<Self>, directive| {
548                    let directive = directive?;
549                    if let Some(aggr) = aggr {
550                        let mut subcommands = aggr.subcommands;
551                        subcommands.extend(directive.subcommands);
552                        let mut notes = aggr.notes;
553                        notes.extend(directive.notes);
554                        IgnoredDiagnosticOption::maybe_emit_warning(
555                            tcx,
556                            item_def_id,
557                            directive.message.as_ref().map(|f| f.span),
558                            aggr.message.as_ref().map(|f| f.span),
559                            "message",
560                        );
561                        IgnoredDiagnosticOption::maybe_emit_warning(
562                            tcx,
563                            item_def_id,
564                            directive.label.as_ref().map(|f| f.span),
565                            aggr.label.as_ref().map(|f| f.span),
566                            "label",
567                        );
568                        IgnoredDiagnosticOption::maybe_emit_warning(
569                            tcx,
570                            item_def_id,
571                            directive.condition.as_ref().map(|i| i.span()),
572                            aggr.condition.as_ref().map(|i| i.span()),
573                            "condition",
574                        );
575                        IgnoredDiagnosticOption::maybe_emit_warning(
576                            tcx,
577                            item_def_id,
578                            directive.parent_label.as_ref().map(|f| f.span),
579                            aggr.parent_label.as_ref().map(|f| f.span),
580                            "parent_label",
581                        );
582                        IgnoredDiagnosticOption::maybe_emit_warning(
583                            tcx,
584                            item_def_id,
585                            directive.append_const_msg.as_ref().and_then(|c| {
586                                if let AppendConstMessage::Custom(_, s) = c {
587                                    Some(*s)
588                                } else {
589                                    None
590                                }
591                            }),
592                            aggr.append_const_msg.as_ref().and_then(|c| {
593                                if let AppendConstMessage::Custom(_, s) = c {
594                                    Some(*s)
595                                } else {
596                                    None
597                                }
598                            }),
599                            "append_const_msg",
600                        );
601
602                        Ok(Some(Self {
603                            condition: aggr.condition.or(directive.condition),
604                            subcommands,
605                            message: aggr.message.or(directive.message),
606                            label: aggr.label.or(directive.label),
607                            notes,
608                            parent_label: aggr.parent_label.or(directive.parent_label),
609                            append_const_msg: aggr.append_const_msg.or(directive.append_const_msg),
610                        }))
611                    } else {
612                        Ok(Some(directive))
613                    }
614                })
615        }
616    }
617
618    fn parse_attribute(
619        attr: &Attribute,
620        is_diagnostic_namespace_variant: bool,
621        tcx: TyCtxt<'tcx>,
622        item_def_id: DefId,
623    ) -> Result<Option<Self>, ErrorGuaranteed> {
624        let result = if let Some(items) = attr.meta_item_list() {
625            Self::parse(
626                tcx,
627                item_def_id,
628                &items,
629                attr.span(),
630                true,
631                is_diagnostic_namespace_variant,
632            )
633        } else if let Some(value) = attr.value_str() {
634            if !is_diagnostic_namespace_variant {
635                Ok(Some(OnUnimplementedDirective {
636                    condition: None,
637                    message: None,
638                    subcommands: vec![],
639                    label: Some(OnUnimplementedFormatString::try_parse(
640                        tcx,
641                        item_def_id,
642                        value,
643                        attr.span(),
644                        is_diagnostic_namespace_variant,
645                    )?),
646                    notes: Vec::new(),
647                    parent_label: None,
648                    append_const_msg: None,
649                }))
650            } else {
651                let item = attr.get_normal_item();
652                let report_span = match &item.args {
653                    AttrArgs::Empty => item.path.span,
654                    AttrArgs::Delimited(args) => args.dspan.entire(),
655                    AttrArgs::Eq { eq_span, expr } => eq_span.to(expr.span),
656                };
657
658                if let Some(item_def_id) = item_def_id.as_local() {
659                    tcx.emit_node_span_lint(
660                        UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
661                        tcx.local_def_id_to_hir_id(item_def_id),
662                        report_span,
663                        MalformedOnUnimplementedAttrLint::new(report_span),
664                    );
665                }
666                Ok(None)
667            }
668        } else if is_diagnostic_namespace_variant {
669            match attr {
670                Attribute::Unparsed(p) if !matches!(p.args, AttrArgs::Empty) => {
671                    if let Some(item_def_id) = item_def_id.as_local() {
672                        tcx.emit_node_span_lint(
673                            UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
674                            tcx.local_def_id_to_hir_id(item_def_id),
675                            attr.span(),
676                            MalformedOnUnimplementedAttrLint::new(attr.span()),
677                        );
678                    }
679                }
680                _ => {
681                    if let Some(item_def_id) = item_def_id.as_local() {
682                        tcx.emit_node_span_lint(
683                            UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
684                            tcx.local_def_id_to_hir_id(item_def_id),
685                            attr.span(),
686                            MissingOptionsForOnUnimplementedAttr,
687                        )
688                    }
689                }
690            };
691
692            Ok(None)
693        } else {
694            let reported = tcx.dcx().delayed_bug("of_item: neither meta_item_list nor value_str");
695            return Err(reported);
696        };
697        debug!("of_item({:?}) = {:?}", item_def_id, result);
698        result
699    }
700
701    pub fn evaluate(
702        &self,
703        tcx: TyCtxt<'tcx>,
704        trait_ref: ty::TraitRef<'tcx>,
705        options: &[(Symbol, Option<String>)],
706        long_ty_file: &mut Option<PathBuf>,
707    ) -> OnUnimplementedNote {
708        let mut message = None;
709        let mut label = None;
710        let mut notes = Vec::new();
711        let mut parent_label = None;
712        let mut append_const_msg = None;
713        info!("evaluate({:?}, trait_ref={:?}, options={:?})", self, trait_ref, options);
714
715        let options_map: FxHashMap<Symbol, String> =
716            options.iter().filter_map(|(k, v)| v.clone().map(|v| (*k, v))).collect();
717
718        for command in self.subcommands.iter().chain(Some(self)).rev() {
719            debug!(?command);
720            if let Some(ref condition) = command.condition
721                && !attr::eval_condition(condition, &tcx.sess, Some(tcx.features()), &mut |cfg| {
722                    let value = cfg.value.map(|v| {
723                        // `with_no_visible_paths` is also used when generating the options,
724                        // so we need to match it here.
725                        ty::print::with_no_visible_paths!(
726                            OnUnimplementedFormatString {
727                                symbol: v,
728                                span: cfg.span,
729                                is_diagnostic_namespace_variant: false
730                            }
731                            .format(
732                                tcx,
733                                trait_ref,
734                                &options_map,
735                                long_ty_file
736                            )
737                        )
738                    });
739
740                    options.contains(&(cfg.name, value))
741                })
742            {
743                debug!("evaluate: skipping {:?} due to condition", command);
744                continue;
745            }
746            debug!("evaluate: {:?} succeeded", command);
747            if let Some(ref message_) = command.message {
748                message = Some(message_.clone());
749            }
750
751            if let Some(ref label_) = command.label {
752                label = Some(label_.clone());
753            }
754
755            notes.extend(command.notes.clone());
756
757            if let Some(ref parent_label_) = command.parent_label {
758                parent_label = Some(parent_label_.clone());
759            }
760
761            append_const_msg = command.append_const_msg;
762        }
763
764        OnUnimplementedNote {
765            label: label.map(|l| l.format(tcx, trait_ref, &options_map, long_ty_file)),
766            message: message.map(|m| m.format(tcx, trait_ref, &options_map, long_ty_file)),
767            notes: notes
768                .into_iter()
769                .map(|n| n.format(tcx, trait_ref, &options_map, long_ty_file))
770                .collect(),
771            parent_label: parent_label
772                .map(|e_s| e_s.format(tcx, trait_ref, &options_map, long_ty_file)),
773            append_const_msg,
774        }
775    }
776}
777
778impl<'tcx> OnUnimplementedFormatString {
779    fn try_parse(
780        tcx: TyCtxt<'tcx>,
781        item_def_id: DefId,
782        from: Symbol,
783        value_span: Span,
784        is_diagnostic_namespace_variant: bool,
785    ) -> Result<Self, ErrorGuaranteed> {
786        let result = OnUnimplementedFormatString {
787            symbol: from,
788            span: value_span,
789            is_diagnostic_namespace_variant,
790        };
791        result.verify(tcx, item_def_id)?;
792        Ok(result)
793    }
794
795    fn verify(&self, tcx: TyCtxt<'tcx>, item_def_id: DefId) -> Result<(), ErrorGuaranteed> {
796        let trait_def_id = if tcx.is_trait(item_def_id) {
797            item_def_id
798        } else {
799            tcx.trait_id_of_impl(item_def_id)
800                .expect("expected `on_unimplemented` to correspond to a trait")
801        };
802        let trait_name = tcx.item_ident(trait_def_id);
803        let generics = tcx.generics_of(item_def_id);
804        let s = self.symbol.as_str();
805        let mut parser = Parser::new(s, None, None, false, ParseMode::Format);
806        let mut result = Ok(());
807        for token in &mut parser {
808            match token {
809                Piece::Lit(_) => (), // Normal string, no need to check it
810                Piece::NextArgument(a) => {
811                    let format_spec = a.format;
812                    if self.is_diagnostic_namespace_variant
813                        && (format_spec.ty_span.is_some()
814                            || format_spec.width_span.is_some()
815                            || format_spec.precision_span.is_some()
816                            || format_spec.fill_span.is_some())
817                    {
818                        if let Some(item_def_id) = item_def_id.as_local() {
819                            tcx.emit_node_span_lint(
820                                UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
821                                tcx.local_def_id_to_hir_id(item_def_id),
822                                self.span,
823                                InvalidFormatSpecifier,
824                            );
825                        }
826                    }
827                    match a.position {
828                        Position::ArgumentNamed(s) => {
829                            match Symbol::intern(s) {
830                                // `{ThisTraitsName}` is allowed
831                                s if s == trait_name.name
832                                    && !self.is_diagnostic_namespace_variant =>
833                                {
834                                    ()
835                                }
836                                s if ALLOWED_FORMAT_SYMBOLS.contains(&s)
837                                    && !self.is_diagnostic_namespace_variant =>
838                                {
839                                    ()
840                                }
841                                // So is `{A}` if A is a type parameter
842                                s if generics.own_params.iter().any(|param| param.name == s) => (),
843                                s => {
844                                    if self.is_diagnostic_namespace_variant {
845                                        if let Some(item_def_id) = item_def_id.as_local() {
846                                            tcx.emit_node_span_lint(
847                                                UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
848                                                tcx.local_def_id_to_hir_id(item_def_id),
849                                                self.span,
850                                                UnknownFormatParameterForOnUnimplementedAttr {
851                                                    argument_name: s,
852                                                    trait_name,
853                                                },
854                                            );
855                                        }
856                                    } else {
857                                        result = Err(struct_span_code_err!(
858                                            tcx.dcx(),
859                                            self.span,
860                                            E0230,
861                                            "there is no parameter `{}` on {}",
862                                            s,
863                                            if trait_def_id == item_def_id {
864                                                format!("trait `{trait_name}`")
865                                            } else {
866                                                "impl".to_string()
867                                            }
868                                        )
869                                        .emit());
870                                    }
871                                }
872                            }
873                        }
874                        // `{:1}` and `{}` are not to be used
875                        Position::ArgumentIs(..) | Position::ArgumentImplicitlyIs(_) => {
876                            if self.is_diagnostic_namespace_variant {
877                                if let Some(item_def_id) = item_def_id.as_local() {
878                                    tcx.emit_node_span_lint(
879                                        UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
880                                        tcx.local_def_id_to_hir_id(item_def_id),
881                                        self.span,
882                                        DisallowedPositionalArgument,
883                                    );
884                                }
885                            } else {
886                                let reported = struct_span_code_err!(
887                                    tcx.dcx(),
888                                    self.span,
889                                    E0231,
890                                    "only named generic parameters are allowed"
891                                )
892                                .emit();
893                                result = Err(reported);
894                            }
895                        }
896                    }
897                }
898            }
899        }
900        // we cannot return errors from processing the format string as hard error here
901        // as the diagnostic namespace guarantees that malformed input cannot cause an error
902        //
903        // if we encounter any error while processing we nevertheless want to show it as warning
904        // so that users are aware that something is not correct
905        for e in parser.errors {
906            if self.is_diagnostic_namespace_variant {
907                if let Some(item_def_id) = item_def_id.as_local() {
908                    tcx.emit_node_span_lint(
909                        UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
910                        tcx.local_def_id_to_hir_id(item_def_id),
911                        self.span,
912                        WrappedParserError { description: e.description, label: e.label },
913                    );
914                }
915            } else {
916                let reported =
917                    struct_span_code_err!(tcx.dcx(), self.span, E0231, "{}", e.description,).emit();
918                result = Err(reported);
919            }
920        }
921
922        result
923    }
924
925    pub fn format(
926        &self,
927        tcx: TyCtxt<'tcx>,
928        trait_ref: ty::TraitRef<'tcx>,
929        options: &FxHashMap<Symbol, String>,
930        long_ty_file: &mut Option<PathBuf>,
931    ) -> String {
932        let name = tcx.item_name(trait_ref.def_id);
933        let trait_str = tcx.def_path_str(trait_ref.def_id);
934        let generics = tcx.generics_of(trait_ref.def_id);
935        let generic_map = generics
936            .own_params
937            .iter()
938            .filter_map(|param| {
939                let value = match param.kind {
940                    GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => {
941                        if let Some(ty) = trait_ref.args[param.index as usize].as_type() {
942                            tcx.short_string(ty, long_ty_file)
943                        } else {
944                            trait_ref.args[param.index as usize].to_string()
945                        }
946                    }
947                    GenericParamDefKind::Lifetime => return None,
948                };
949                let name = param.name;
950                Some((name, value))
951            })
952            .collect::<FxHashMap<Symbol, String>>();
953        let empty_string = String::new();
954
955        let s = self.symbol.as_str();
956        let mut parser = Parser::new(s, None, None, false, ParseMode::Format);
957        let item_context = (options.get(&sym::ItemContext)).unwrap_or(&empty_string);
958        let constructed_message = (&mut parser)
959            .map(|p| match p {
960                Piece::Lit(s) => s.to_owned(),
961                Piece::NextArgument(a) => match a.position {
962                    Position::ArgumentNamed(arg) => {
963                        let s = Symbol::intern(arg);
964                        match generic_map.get(&s) {
965                            Some(val) => val.to_string(),
966                            None if self.is_diagnostic_namespace_variant => {
967                                format!("{{{arg}}}")
968                            }
969                            None if s == name => trait_str.clone(),
970                            None => {
971                                if let Some(val) = options.get(&s) {
972                                    val.clone()
973                                } else if s == sym::from_desugaring {
974                                    // don't break messages using these two arguments incorrectly
975                                    String::new()
976                                } else if s == sym::ItemContext
977                                    && !self.is_diagnostic_namespace_variant
978                                {
979                                    item_context.clone()
980                                } else if s == sym::integral {
981                                    String::from("{integral}")
982                                } else if s == sym::integer_ {
983                                    String::from("{integer}")
984                                } else if s == sym::float {
985                                    String::from("{float}")
986                                } else {
987                                    bug!(
988                                        "broken on_unimplemented {:?} for {:?}: \
989                                      no argument matching {:?}",
990                                        self.symbol,
991                                        trait_ref,
992                                        s
993                                    )
994                                }
995                            }
996                        }
997                    }
998                    Position::ArgumentImplicitlyIs(_) if self.is_diagnostic_namespace_variant => {
999                        String::from("{}")
1000                    }
1001                    Position::ArgumentIs(idx) if self.is_diagnostic_namespace_variant => {
1002                        format!("{{{idx}}}")
1003                    }
1004                    _ => bug!("broken on_unimplemented {:?} - bad format arg", self.symbol),
1005                },
1006            })
1007            .collect();
1008        // we cannot return errors from processing the format string as hard error here
1009        // as the diagnostic namespace guarantees that malformed input cannot cause an error
1010        //
1011        // if we encounter any error while processing the format string
1012        // we don't want to show the potentially half assembled formatted string,
1013        // therefore we fall back to just showing the input string in this case
1014        //
1015        // The actual parser errors are emitted earlier
1016        // as lint warnings in OnUnimplementedFormatString::verify
1017        if self.is_diagnostic_namespace_variant && !parser.errors.is_empty() {
1018            String::from(s)
1019        } else {
1020            constructed_message
1021        }
1022    }
1023}