rustc_trait_selection/error_reporting/traits/
on_unimplemented.rs

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