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