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