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