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 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_file: &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 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 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 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 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 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 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 if let ty::Slice(aty) = self_ty.kind() {
204 self_types.push("[]".to_owned());
205 if let Some(def) = aty.ty_adt_def() {
206 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 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 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 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 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_file)
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#[derive(Clone, Debug)]
309pub struct OnUnimplementedFormatString {
310 symbol: Symbol,
312 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#[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 pub append_const_msg: Option<AppendConstMessage>,
337}
338
339#[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 if let Some(item_def_id) = item_def_id.as_local() {
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
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 {
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 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 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 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 Err(e) => {
856 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 if let Ok(s) = FormatString::parse(self.symbol, None, self.span, &ctx) {
897 s.format(args)
898 } else {
899 self.symbol.as_str().into()
909 }
910 }
911}