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 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 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 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 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 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 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 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 if let ty::Slice(aty) = self_ty.kind() {
202 self_types.push("[]".to_owned());
203 if let Some(def) = aty.ty_adt_def() {
204 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 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 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 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 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#[derive(Clone, Debug)]
307pub struct OnUnimplementedFormatString {
308 symbol: Symbol,
310 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#[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 pub append_const_msg: Option<AppendConstMessage>,
335}
336
337#[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 match OnUnimplementedCondition::parse(cond) {
433 Ok(condition) => Some(condition),
434 Err(e) => return Err(tcx.dcx().emit_err(e)),
435 }
436 };
437
438 let mut message = None;
439 let mut label = None;
440 let mut notes = Vec::new();
441 let mut parent_label = None;
442 let mut subcommands = vec![];
443 let mut append_const_msg = None;
444
445 let get_value_and_span = |item: &_, key| {
446 if let MetaItemInner::MetaItem(MetaItem {
447 path,
448 kind: MetaItemKind::NameValue(MetaItemLit { span, kind: LitKind::Str(s, _), .. }),
449 ..
450 }) = item
451 && *path == key
452 {
453 Some((*s, *span))
454 } else {
455 None
456 }
457 };
458
459 for item in item_iter {
460 if let Some((message_, span)) = get_value_and_span(item, sym::message)
461 && message.is_none()
462 {
463 message = parse_value(message_, span)?.map(|l| (item.span(), l));
464 continue;
465 } else if let Some((label_, span)) = get_value_and_span(item, sym::label)
466 && label.is_none()
467 {
468 label = parse_value(label_, span)?.map(|l| (item.span(), l));
469 continue;
470 } else if let Some((note_, span)) = get_value_and_span(item, sym::note) {
471 if let Some(note) = parse_value(note_, span)? {
472 notes.push(note);
473 continue;
474 }
475 } else if item.has_name(sym::parent_label)
476 && parent_label.is_none()
477 && !is_diagnostic_namespace_variant
478 {
479 if let Some(parent_label_) = item.value_str() {
480 parent_label = parse_value(parent_label_, item.span())?;
481 continue;
482 }
483 } else if item.has_name(sym::on)
484 && is_root
485 && message.is_none()
486 && label.is_none()
487 && notes.is_empty()
488 && !is_diagnostic_namespace_variant
489 {
491 if let Some(items) = item.meta_item_list() {
492 match Self::parse(
493 tcx,
494 item_def_id,
495 items,
496 item.span(),
497 false,
498 is_diagnostic_namespace_variant,
499 ) {
500 Ok(Some(subcommand)) => subcommands.push(subcommand),
501 Ok(None) => bug!(
502 "This cannot happen for now as we only reach that if `is_diagnostic_namespace_variant` is false"
503 ),
504 Err(reported) => errored = Some(reported),
505 };
506 continue;
507 }
508 } else if item.has_name(sym::append_const_msg)
509 && append_const_msg.is_none()
510 && !is_diagnostic_namespace_variant
511 {
512 if let Some(msg) = item.value_str() {
513 append_const_msg = Some(AppendConstMessage::Custom(msg, item.span()));
514 continue;
515 } else if item.is_word() {
516 append_const_msg = Some(AppendConstMessage::Default);
517 continue;
518 }
519 }
520
521 if is_diagnostic_namespace_variant {
522 if let Some(def_id) = item_def_id.as_local() {
523 tcx.emit_node_span_lint(
524 UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
525 tcx.local_def_id_to_hir_id(def_id),
526 vec![item.span()],
527 MalformedOnUnimplementedAttrLint::new(item.span()),
528 );
529 }
530 } else {
531 tcx.dcx().emit_err(NoValueInOnUnimplemented { span: item.span() });
533 }
534 }
535
536 if let Some(reported) = errored {
537 if is_diagnostic_namespace_variant { Ok(None) } else { Err(reported) }
538 } else {
539 Ok(Some(OnUnimplementedDirective {
540 condition,
541 subcommands,
542 message,
543 label,
544 notes,
545 parent_label,
546 append_const_msg,
547 }))
548 }
549 }
550
551 pub fn of_item(tcx: TyCtxt<'tcx>, item_def_id: DefId) -> Result<Option<Self>, ErrorGuaranteed> {
552 if !tcx.is_trait(item_def_id) {
553 return Ok(None);
558 }
559 if let Some(attr) = tcx.get_attr(item_def_id, sym::rustc_on_unimplemented) {
560 return Self::parse_attribute(attr, false, tcx, item_def_id);
561 } else {
562 tcx.get_attrs_by_path(item_def_id, &[sym::diagnostic, sym::on_unimplemented])
563 .filter_map(|attr| Self::parse_attribute(attr, true, tcx, item_def_id).transpose())
564 .try_fold(None, |aggr: Option<Self>, directive| {
565 let directive = directive?;
566 if let Some(aggr) = aggr {
567 let mut subcommands = aggr.subcommands;
568 subcommands.extend(directive.subcommands);
569 let mut notes = aggr.notes;
570 notes.extend(directive.notes);
571 IgnoredDiagnosticOption::maybe_emit_warning(
572 tcx,
573 item_def_id,
574 directive.message.as_ref().map(|f| f.0),
575 aggr.message.as_ref().map(|f| f.0),
576 "message",
577 );
578 IgnoredDiagnosticOption::maybe_emit_warning(
579 tcx,
580 item_def_id,
581 directive.label.as_ref().map(|f| f.0),
582 aggr.label.as_ref().map(|f| f.0),
583 "label",
584 );
585 IgnoredDiagnosticOption::maybe_emit_warning(
586 tcx,
587 item_def_id,
588 directive.condition.as_ref().map(|i| i.span()),
589 aggr.condition.as_ref().map(|i| i.span()),
590 "condition",
591 );
592 IgnoredDiagnosticOption::maybe_emit_warning(
593 tcx,
594 item_def_id,
595 directive.parent_label.as_ref().map(|f| f.span),
596 aggr.parent_label.as_ref().map(|f| f.span),
597 "parent_label",
598 );
599 IgnoredDiagnosticOption::maybe_emit_warning(
600 tcx,
601 item_def_id,
602 directive.append_const_msg.as_ref().and_then(|c| {
603 if let AppendConstMessage::Custom(_, s) = c {
604 Some(*s)
605 } else {
606 None
607 }
608 }),
609 aggr.append_const_msg.as_ref().and_then(|c| {
610 if let AppendConstMessage::Custom(_, s) = c {
611 Some(*s)
612 } else {
613 None
614 }
615 }),
616 "append_const_msg",
617 );
618
619 Ok(Some(Self {
620 condition: aggr.condition.or(directive.condition),
621 subcommands,
622 message: aggr.message.or(directive.message),
623 label: aggr.label.or(directive.label),
624 notes,
625 parent_label: aggr.parent_label.or(directive.parent_label),
626 append_const_msg: aggr.append_const_msg.or(directive.append_const_msg),
627 }))
628 } else {
629 Ok(Some(directive))
630 }
631 })
632 }
633 }
634
635 fn parse_attribute(
636 attr: &Attribute,
637 is_diagnostic_namespace_variant: bool,
638 tcx: TyCtxt<'tcx>,
639 item_def_id: DefId,
640 ) -> Result<Option<Self>, ErrorGuaranteed> {
641 let result = if let Some(items) = attr.meta_item_list() {
642 Self::parse(
643 tcx,
644 item_def_id,
645 &items,
646 attr.span(),
647 true,
648 is_diagnostic_namespace_variant,
649 )
650 } else if let Some(value) = attr.value_str() {
651 if !is_diagnostic_namespace_variant {
652 Ok(Some(OnUnimplementedDirective {
653 condition: None,
654 message: None,
655 subcommands: vec![],
656 label: Some((
657 attr.span(),
658 OnUnimplementedFormatString::try_parse(
659 tcx,
660 item_def_id,
661 value,
662 attr.value_span().unwrap_or(attr.span()),
663 is_diagnostic_namespace_variant,
664 )?,
665 )),
666 notes: Vec::new(),
667 parent_label: None,
668 append_const_msg: None,
669 }))
670 } else {
671 let item = attr.get_normal_item();
672 let report_span = match &item.args {
673 AttrArgs::Empty => item.path.span,
674 AttrArgs::Delimited(args) => args.dspan.entire(),
675 AttrArgs::Eq { eq_span, expr } => eq_span.to(expr.span),
676 };
677
678 if let Some(item_def_id) = item_def_id.as_local() {
679 tcx.emit_node_span_lint(
680 UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
681 tcx.local_def_id_to_hir_id(item_def_id),
682 report_span,
683 MalformedOnUnimplementedAttrLint::new(report_span),
684 );
685 }
686 Ok(None)
687 }
688 } else if is_diagnostic_namespace_variant {
689 match attr {
690 Attribute::Unparsed(p) if !matches!(p.args, AttrArgs::Empty) => {
691 if let Some(item_def_id) = item_def_id.as_local() {
692 tcx.emit_node_span_lint(
693 UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
694 tcx.local_def_id_to_hir_id(item_def_id),
695 attr.span(),
696 MalformedOnUnimplementedAttrLint::new(attr.span()),
697 );
698 }
699 }
700 _ => {
701 if let Some(item_def_id) = item_def_id.as_local() {
702 tcx.emit_node_span_lint(
703 UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
704 tcx.local_def_id_to_hir_id(item_def_id),
705 attr.span(),
706 MissingOptionsForOnUnimplementedAttr,
707 )
708 }
709 }
710 };
711
712 Ok(None)
713 } else {
714 let reported = tcx.dcx().delayed_bug("of_item: neither meta_item_list nor value_str");
715 return Err(reported);
716 };
717 debug!("of_item({:?}) = {:?}", item_def_id, result);
718 result
719 }
720
721 pub(crate) fn evaluate(
722 &self,
723 tcx: TyCtxt<'tcx>,
724 trait_ref: ty::TraitRef<'tcx>,
725 condition_options: &ConditionOptions,
726 args: &FormatArgs<'tcx>,
727 ) -> OnUnimplementedNote {
728 let mut message = None;
729 let mut label = None;
730 let mut notes = Vec::new();
731 let mut parent_label = None;
732 let mut append_const_msg = None;
733 info!(
734 "evaluate({:?}, trait_ref={:?}, options={:?}, args ={:?})",
735 self, trait_ref, condition_options, args
736 );
737
738 for command in self.subcommands.iter().chain(Some(self)).rev() {
739 debug!(?command);
740 if let Some(ref condition) = command.condition
741 && !condition.matches_predicate(condition_options)
742 {
743 debug!("evaluate: skipping {:?} due to condition", command);
744 continue;
745 }
746 debug!("evaluate: {:?} succeeded", command);
747 if let Some(ref message_) = command.message {
748 message = Some(message_.clone());
749 }
750
751 if let Some(ref label_) = command.label {
752 label = Some(label_.clone());
753 }
754
755 notes.extend(command.notes.clone());
756
757 if let Some(ref parent_label_) = command.parent_label {
758 parent_label = Some(parent_label_.clone());
759 }
760
761 append_const_msg = command.append_const_msg;
762 }
763
764 OnUnimplementedNote {
765 label: label.map(|l| l.1.format(tcx, trait_ref, args)),
766 message: message.map(|m| m.1.format(tcx, trait_ref, args)),
767 notes: notes.into_iter().map(|n| n.format(tcx, trait_ref, args)).collect(),
768 parent_label: parent_label.map(|e_s| e_s.format(tcx, trait_ref, args)),
769 append_const_msg,
770 }
771 }
772}
773
774impl<'tcx> OnUnimplementedFormatString {
775 fn try_parse(
776 tcx: TyCtxt<'tcx>,
777 item_def_id: DefId,
778 from: Symbol,
779 span: Span,
780 is_diagnostic_namespace_variant: bool,
781 ) -> Result<Self, ErrorGuaranteed> {
782 let result =
783 OnUnimplementedFormatString { symbol: from, span, is_diagnostic_namespace_variant };
784 result.verify(tcx, item_def_id)?;
785 Ok(result)
786 }
787
788 fn verify(&self, tcx: TyCtxt<'tcx>, trait_def_id: DefId) -> Result<(), ErrorGuaranteed> {
789 if !tcx.is_trait(trait_def_id) {
790 return Ok(());
791 };
792
793 let ctx = if self.is_diagnostic_namespace_variant {
794 Ctx::DiagnosticOnUnimplemented { tcx, trait_def_id }
795 } else {
796 Ctx::RustcOnUnimplemented { tcx, trait_def_id }
797 };
798
799 let mut result = Ok(());
800
801 match FormatString::parse(self.symbol, self.span, &ctx) {
802 Ok(FormatString { warnings, .. }) => {
805 if self.is_diagnostic_namespace_variant {
806 for w in warnings {
807 w.emit_warning(tcx, trait_def_id)
808 }
809 } else {
810 for w in warnings {
811 match w {
812 FormatWarning::UnknownParam { argument_name, span } => {
813 let reported = struct_span_code_err!(
814 tcx.dcx(),
815 span,
816 E0230,
817 "cannot find parameter {} on this trait",
818 argument_name,
819 )
820 .emit();
821 result = Err(reported);
822 }
823 FormatWarning::PositionalArgument { span, .. } => {
824 let reported = struct_span_code_err!(
825 tcx.dcx(),
826 span,
827 E0231,
828 "positional format arguments are not allowed here"
829 )
830 .emit();
831 result = Err(reported);
832 }
833 FormatWarning::InvalidSpecifier { .. }
834 | FormatWarning::FutureIncompat { .. } => {}
835 }
836 }
837 }
838 }
839 Err(errors) => {
841 for e in errors {
847 if self.is_diagnostic_namespace_variant {
848 if let Some(trait_def_id) = trait_def_id.as_local() {
849 tcx.emit_node_span_lint(
850 UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
851 tcx.local_def_id_to_hir_id(trait_def_id),
852 self.span,
853 WrappedParserError { description: e.description, label: e.label },
854 );
855 }
856 } else {
857 let reported = struct_span_code_err!(
858 tcx.dcx(),
859 self.span,
860 E0231,
861 "{}",
862 e.description,
863 )
864 .emit();
865 result = Err(reported);
866 }
867 }
868 }
869 }
870
871 result
872 }
873
874 pub fn format(
875 &self,
876 tcx: TyCtxt<'tcx>,
877 trait_ref: ty::TraitRef<'tcx>,
878 args: &FormatArgs<'tcx>,
879 ) -> String {
880 let trait_def_id = trait_ref.def_id;
881 let ctx = if self.is_diagnostic_namespace_variant {
882 Ctx::DiagnosticOnUnimplemented { tcx, trait_def_id }
883 } else {
884 Ctx::RustcOnUnimplemented { tcx, trait_def_id }
885 };
886
887 if let Ok(s) = FormatString::parse(self.symbol, self.span, &ctx) {
888 s.format(args)
889 } else {
890 self.symbol.as_str().into()
900 }
901 }
902}