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