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 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 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 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 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 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 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 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 if let ty::Slice(aty) = self_ty.kind() {
224 self_types.push("[]".to_owned());
225 if let Some(def) = aty.ty_adt_def() {
226 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 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 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 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 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#[derive(Clone, Debug)]
324pub struct OnUnimplementedFormatString {
325 symbol: Symbol,
327 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#[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 pub append_const_msg: Option<AppendConstMessage>,
352}
353
354#[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 {
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 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 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 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 Err(e) => {
875 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 if let Ok(s) = FormatString::parse(self.symbol, None, self.span, &ctx) {
916 s.format(args)
917 } else {
918 self.symbol.as_str().into()
928 }
929 }
930}