1use std::iter;
2use std::path::PathBuf;
3
4use rustc_ast::MetaItemInner;
5use rustc_data_structures::fx::FxHashMap;
6use rustc_errors::codes::*;
7use rustc_errors::{ErrorGuaranteed, struct_span_code_err};
8use rustc_hir::def_id::{DefId, LocalDefId};
9use rustc_hir::{AttrArgs, AttrKind, Attribute};
10use rustc_macros::LintDiagnostic;
11use rustc_middle::bug;
12use rustc_middle::ty::print::PrintTraitRefExt as _;
13use rustc_middle::ty::{self, GenericArgsRef, GenericParamDefKind, TyCtxt};
14use rustc_parse_format::{ParseMode, Parser, Piece, Position};
15use rustc_session::lint::builtin::UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES;
16use rustc_span::{Ident, Span, Symbol, kw, sym};
17use tracing::{debug, info};
18use {rustc_attr_parsing as attr, rustc_hir as hir};
19
20use super::{ObligationCauseCode, PredicateObligation};
21use crate::error_reporting::TypeErrCtxt;
22use crate::errors::{
23 EmptyOnClauseInOnUnimplemented, InvalidOnClauseInOnUnimplemented, NoValueInOnUnimplemented,
24};
25use crate::infer::InferCtxtExt;
26
27static ALLOWED_FORMAT_SYMBOLS: &[Symbol] = &[
29 kw::SelfUpper,
30 sym::ItemContext,
31 sym::from_desugaring,
32 sym::direct,
33 sym::cause,
34 sym::integral,
35 sym::integer_,
36 sym::float,
37 sym::_Self,
38 sym::crate_local,
39 sym::Trait,
40];
41
42impl<'tcx> TypeErrCtxt<'_, 'tcx> {
43 fn impl_similar_to(
44 &self,
45 trait_pred: ty::PolyTraitPredicate<'tcx>,
46 obligation: &PredicateObligation<'tcx>,
47 ) -> Option<(DefId, GenericArgsRef<'tcx>)> {
48 let tcx = self.tcx;
49 let param_env = obligation.param_env;
50 self.enter_forall(trait_pred, |trait_pred| {
51 let trait_self_ty = trait_pred.self_ty();
52
53 let mut self_match_impls = vec![];
54 let mut fuzzy_match_impls = vec![];
55
56 self.tcx.for_each_relevant_impl(trait_pred.def_id(), trait_self_ty, |def_id| {
57 let impl_args = self.fresh_args_for_item(obligation.cause.span, def_id);
58 let impl_trait_ref =
59 tcx.impl_trait_ref(def_id).unwrap().instantiate(tcx, impl_args);
60
61 let impl_self_ty = impl_trait_ref.self_ty();
62
63 if self.can_eq(param_env, trait_self_ty, impl_self_ty) {
64 self_match_impls.push((def_id, impl_args));
65
66 if iter::zip(
67 trait_pred.trait_ref.args.types().skip(1),
68 impl_trait_ref.args.types().skip(1),
69 )
70 .all(|(u, v)| self.fuzzy_match_tys(u, v, false).is_some())
71 {
72 fuzzy_match_impls.push((def_id, impl_args));
73 }
74 }
75 });
76
77 let impl_def_id_and_args = if let [impl_] = self_match_impls[..] {
78 impl_
79 } else if let [impl_] = fuzzy_match_impls[..] {
80 impl_
81 } else {
82 return None;
83 };
84
85 tcx.has_attr(impl_def_id_and_args.0, sym::rustc_on_unimplemented)
86 .then_some(impl_def_id_and_args)
87 })
88 }
89
90 fn describe_enclosure(&self, def_id: LocalDefId) -> Option<&'static str> {
93 match self.tcx.hir_node_by_def_id(def_id) {
94 hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn { .. }, .. }) => Some("a function"),
95 hir::Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Fn(..), .. }) => {
96 Some("a trait method")
97 }
98 hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(..), .. }) => {
99 Some("a method")
100 }
101 hir::Node::Expr(hir::Expr {
102 kind: hir::ExprKind::Closure(hir::Closure { kind, .. }),
103 ..
104 }) => Some(self.describe_closure(*kind)),
105 _ => None,
106 }
107 }
108
109 pub fn on_unimplemented_note(
110 &self,
111 trait_pred: ty::PolyTraitPredicate<'tcx>,
112 obligation: &PredicateObligation<'tcx>,
113 long_ty_file: &mut Option<PathBuf>,
114 ) -> OnUnimplementedNote {
115 if trait_pred.polarity() != ty::PredicatePolarity::Positive {
116 return OnUnimplementedNote::default();
117 }
118
119 let (def_id, args) = self
120 .impl_similar_to(trait_pred, obligation)
121 .unwrap_or_else(|| (trait_pred.def_id(), trait_pred.skip_binder().trait_ref.args));
122 let trait_pred = trait_pred.skip_binder();
123
124 let mut flags = vec![];
125 let enclosure = self.describe_enclosure(obligation.cause.body_id).map(|t| t.to_owned());
129 flags.push((sym::ItemContext, enclosure));
130
131 match obligation.cause.code() {
132 ObligationCauseCode::BuiltinDerived(..)
133 | ObligationCauseCode::ImplDerived(..)
134 | ObligationCauseCode::WellFormedDerived(..) => {}
135 _ => {
136 flags.push((sym::direct, None));
139 }
140 }
141
142 if let Some(k) = obligation.cause.span.desugaring_kind() {
143 flags.push((sym::from_desugaring, None));
144 flags.push((sym::from_desugaring, Some(format!("{k:?}"))));
145 }
146
147 if let ObligationCauseCode::MainFunctionType = obligation.cause.code() {
148 flags.push((sym::cause, Some("MainFunctionType".to_string())));
149 }
150
151 flags.push((sym::Trait, Some(trait_pred.trait_ref.print_trait_sugared().to_string())));
152
153 ty::print::with_no_trimmed_paths!(ty::print::with_no_visible_paths!({
156 let generics = self.tcx.generics_of(def_id);
157 let self_ty = trait_pred.self_ty();
158 flags.push((sym::_Self, Some(self_ty.to_string())));
161 if let Some(def) = self_ty.ty_adt_def() {
162 flags.push((
165 sym::_Self,
166 Some(self.tcx.type_of(def.did()).instantiate_identity().to_string()),
167 ));
168 }
169
170 for param in generics.own_params.iter() {
171 let value = match param.kind {
172 GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => {
173 args[param.index as usize].to_string()
174 }
175 GenericParamDefKind::Lifetime => continue,
176 };
177 let name = param.name;
178 flags.push((name, Some(value)));
179
180 if let GenericParamDefKind::Type { .. } = param.kind {
181 let param_ty = args[param.index as usize].expect_ty();
182 if let Some(def) = param_ty.ty_adt_def() {
183 flags.push((
186 name,
187 Some(self.tcx.type_of(def.did()).instantiate_identity().to_string()),
188 ));
189 }
190 }
191 }
192
193 if let Some(true) = self_ty.ty_adt_def().map(|def| def.did().is_local()) {
194 flags.push((sym::crate_local, None));
195 }
196
197 if self_ty.is_integral() {
199 flags.push((sym::_Self, Some("{integral}".to_owned())));
200 }
201
202 if self_ty.is_array_slice() {
203 flags.push((sym::_Self, Some("&[]".to_owned())));
204 }
205
206 if self_ty.is_fn() {
207 let fn_sig = self_ty.fn_sig(self.tcx);
208 let shortname = if let ty::FnDef(def_id, _) = self_ty.kind()
209 && self.tcx.codegen_fn_attrs(def_id).safe_target_features
210 {
211 "#[target_feature] fn"
212 } else {
213 match fn_sig.safety() {
214 hir::Safety::Safe => "fn",
215 hir::Safety::Unsafe => "unsafe fn",
216 }
217 };
218 flags.push((sym::_Self, Some(shortname.to_owned())));
219 }
220
221 if let ty::Slice(aty) = self_ty.kind() {
223 flags.push((sym::_Self, Some("[]".to_string())));
224 if let Some(def) = aty.ty_adt_def() {
225 flags.push((
228 sym::_Self,
229 Some(format!("[{}]", self.tcx.type_of(def.did()).instantiate_identity())),
230 ));
231 }
232 if aty.is_integral() {
233 flags.push((sym::_Self, Some("[{integral}]".to_string())));
234 }
235 }
236
237 if let ty::Array(aty, len) = self_ty.kind() {
239 flags.push((sym::_Self, Some("[]".to_string())));
240 let len = len.try_to_target_usize(self.tcx);
241 flags.push((sym::_Self, Some(format!("[{aty}; _]"))));
242 if let Some(n) = len {
243 flags.push((sym::_Self, Some(format!("[{aty}; {n}]"))));
244 }
245 if let Some(def) = aty.ty_adt_def() {
246 let def_ty = self.tcx.type_of(def.did()).instantiate_identity();
249 flags.push((sym::_Self, Some(format!("[{def_ty}; _]"))));
250 if let Some(n) = len {
251 flags.push((sym::_Self, Some(format!("[{def_ty}; {n}]"))));
252 }
253 }
254 if aty.is_integral() {
255 flags.push((sym::_Self, Some("[{integral}; _]".to_string())));
256 if let Some(n) = len {
257 flags.push((sym::_Self, Some(format!("[{{integral}}; {n}]"))));
258 }
259 }
260 }
261 if let ty::Dynamic(traits, _, _) = self_ty.kind() {
262 for t in traits.iter() {
263 if let ty::ExistentialPredicate::Trait(trait_ref) = t.skip_binder() {
264 flags.push((sym::_Self, Some(self.tcx.def_path_str(trait_ref.def_id))))
265 }
266 }
267 }
268
269 if let ty::Ref(_, ref_ty, rustc_ast::Mutability::Not) = self_ty.kind()
271 && let ty::Slice(sty) = ref_ty.kind()
272 && sty.is_integral()
273 {
274 flags.push((sym::_Self, Some("&[{integral}]".to_owned())));
275 }
276 }));
277
278 if let Ok(Some(command)) = OnUnimplementedDirective::of_item(self.tcx, def_id) {
279 command.evaluate(self.tcx, trait_pred.trait_ref, &flags, long_ty_file)
280 } else {
281 OnUnimplementedNote::default()
282 }
283 }
284}
285
286#[derive(Clone, Debug)]
287pub struct OnUnimplementedFormatString {
288 symbol: Symbol,
289 span: Span,
290 is_diagnostic_namespace_variant: bool,
291}
292
293#[derive(Debug)]
294pub struct OnUnimplementedDirective {
295 pub condition: Option<MetaItemInner>,
296 pub subcommands: Vec<OnUnimplementedDirective>,
297 pub message: Option<OnUnimplementedFormatString>,
298 pub label: Option<OnUnimplementedFormatString>,
299 pub notes: Vec<OnUnimplementedFormatString>,
300 pub parent_label: Option<OnUnimplementedFormatString>,
301 pub append_const_msg: Option<AppendConstMessage>,
302}
303
304#[derive(Default)]
306pub struct OnUnimplementedNote {
307 pub message: Option<String>,
308 pub label: Option<String>,
309 pub notes: Vec<String>,
310 pub parent_label: Option<String>,
311 pub append_const_msg: Option<AppendConstMessage>,
313}
314
315#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
317pub enum AppendConstMessage {
318 #[default]
319 Default,
320 Custom(Symbol, Span),
321}
322
323#[derive(LintDiagnostic)]
324#[diag(trait_selection_malformed_on_unimplemented_attr)]
325#[help]
326pub struct MalformedOnUnimplementedAttrLint {
327 #[label]
328 pub span: Span,
329}
330
331impl MalformedOnUnimplementedAttrLint {
332 fn new(span: Span) -> Self {
333 Self { span }
334 }
335}
336
337#[derive(LintDiagnostic)]
338#[diag(trait_selection_missing_options_for_on_unimplemented_attr)]
339#[help]
340pub struct MissingOptionsForOnUnimplementedAttr;
341
342#[derive(LintDiagnostic)]
343#[diag(trait_selection_ignored_diagnostic_option)]
344pub struct IgnoredDiagnosticOption {
345 pub option_name: &'static str,
346 #[label]
347 pub span: Span,
348 #[label(trait_selection_other_label)]
349 pub prev_span: Span,
350}
351
352impl IgnoredDiagnosticOption {
353 fn maybe_emit_warning<'tcx>(
354 tcx: TyCtxt<'tcx>,
355 item_def_id: DefId,
356 new: Option<Span>,
357 old: Option<Span>,
358 option_name: &'static str,
359 ) {
360 if let (Some(new_item), Some(old_item)) = (new, old) {
361 if let Some(item_def_id) = item_def_id.as_local() {
362 tcx.emit_node_span_lint(
363 UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
364 tcx.local_def_id_to_hir_id(item_def_id),
365 new_item,
366 IgnoredDiagnosticOption { span: new_item, prev_span: old_item, option_name },
367 );
368 }
369 }
370 }
371}
372
373#[derive(LintDiagnostic)]
374#[diag(trait_selection_unknown_format_parameter_for_on_unimplemented_attr)]
375#[help]
376pub struct UnknownFormatParameterForOnUnimplementedAttr {
377 argument_name: Symbol,
378 trait_name: Ident,
379}
380
381#[derive(LintDiagnostic)]
382#[diag(trait_selection_disallowed_positional_argument)]
383#[help]
384pub struct DisallowedPositionalArgument;
385
386#[derive(LintDiagnostic)]
387#[diag(trait_selection_invalid_format_specifier)]
388#[help]
389pub struct InvalidFormatSpecifier;
390
391#[derive(LintDiagnostic)]
392#[diag(trait_selection_wrapped_parser_error)]
393pub struct WrappedParserError {
394 description: String,
395 label: String,
396}
397
398impl<'tcx> OnUnimplementedDirective {
399 fn parse(
400 tcx: TyCtxt<'tcx>,
401 item_def_id: DefId,
402 items: &[MetaItemInner],
403 span: Span,
404 is_root: bool,
405 is_diagnostic_namespace_variant: bool,
406 ) -> Result<Option<Self>, ErrorGuaranteed> {
407 let mut errored = None;
408 let mut item_iter = items.iter();
409
410 let parse_value = |value_str, value_span| {
411 OnUnimplementedFormatString::try_parse(
412 tcx,
413 item_def_id,
414 value_str,
415 value_span,
416 is_diagnostic_namespace_variant,
417 )
418 .map(Some)
419 };
420
421 let condition = if is_root {
422 None
423 } else {
424 let cond = item_iter
425 .next()
426 .ok_or_else(|| tcx.dcx().emit_err(EmptyOnClauseInOnUnimplemented { span }))?
427 .meta_item_or_bool()
428 .ok_or_else(|| tcx.dcx().emit_err(InvalidOnClauseInOnUnimplemented { span }))?;
429 attr::eval_condition(cond, &tcx.sess, Some(tcx.features()), &mut |cfg| {
430 if let Some(value) = cfg.value
431 && let Err(guar) = parse_value(value, cfg.span)
432 {
433 errored = Some(guar);
434 }
435 true
436 });
437 Some(cond.clone())
438 };
439
440 let mut message = None;
441 let mut label = None;
442 let mut notes = Vec::new();
443 let mut parent_label = None;
444 let mut subcommands = vec![];
445 let mut append_const_msg = None;
446
447 for item in item_iter {
448 if item.has_name(sym::message) && message.is_none() {
449 if let Some(message_) = item.value_str() {
450 message = parse_value(message_, item.span())?;
451 continue;
452 }
453 } else if item.has_name(sym::label) && label.is_none() {
454 if let Some(label_) = item.value_str() {
455 label = parse_value(label_, item.span())?;
456 continue;
457 }
458 } else if item.has_name(sym::note) {
459 if let Some(note_) = item.value_str() {
460 if let Some(note) = parse_value(note_, item.span())? {
461 notes.push(note);
462 continue;
463 }
464 }
465 } else if item.has_name(sym::parent_label)
466 && parent_label.is_none()
467 && !is_diagnostic_namespace_variant
468 {
469 if let Some(parent_label_) = item.value_str() {
470 parent_label = parse_value(parent_label_, item.span())?;
471 continue;
472 }
473 } else if item.has_name(sym::on)
474 && is_root
475 && message.is_none()
476 && label.is_none()
477 && notes.is_empty()
478 && !is_diagnostic_namespace_variant
479 {
481 if let Some(items) = item.meta_item_list() {
482 match Self::parse(
483 tcx,
484 item_def_id,
485 items,
486 item.span(),
487 false,
488 is_diagnostic_namespace_variant,
489 ) {
490 Ok(Some(subcommand)) => subcommands.push(subcommand),
491 Ok(None) => bug!(
492 "This cannot happen for now as we only reach that if `is_diagnostic_namespace_variant` is false"
493 ),
494 Err(reported) => errored = Some(reported),
495 };
496 continue;
497 }
498 } else if item.has_name(sym::append_const_msg)
499 && append_const_msg.is_none()
500 && !is_diagnostic_namespace_variant
501 {
502 if let Some(msg) = item.value_str() {
503 append_const_msg = Some(AppendConstMessage::Custom(msg, item.span()));
504 continue;
505 } else if item.is_word() {
506 append_const_msg = Some(AppendConstMessage::Default);
507 continue;
508 }
509 }
510
511 if is_diagnostic_namespace_variant {
512 if let Some(def_id) = item_def_id.as_local() {
513 tcx.emit_node_span_lint(
514 UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
515 tcx.local_def_id_to_hir_id(def_id),
516 vec![item.span()],
517 MalformedOnUnimplementedAttrLint::new(item.span()),
518 );
519 }
520 } else {
521 tcx.dcx().emit_err(NoValueInOnUnimplemented { span: item.span() });
523 }
524 }
525
526 if let Some(reported) = errored {
527 if is_diagnostic_namespace_variant { Ok(None) } else { Err(reported) }
528 } else {
529 Ok(Some(OnUnimplementedDirective {
530 condition,
531 subcommands,
532 message,
533 label,
534 notes,
535 parent_label,
536 append_const_msg,
537 }))
538 }
539 }
540
541 pub fn of_item(tcx: TyCtxt<'tcx>, item_def_id: DefId) -> Result<Option<Self>, ErrorGuaranteed> {
542 if let Some(attr) = tcx.get_attr(item_def_id, sym::rustc_on_unimplemented) {
543 return Self::parse_attribute(attr, false, tcx, item_def_id);
544 } else {
545 tcx.get_attrs_by_path(item_def_id, &[sym::diagnostic, sym::on_unimplemented])
546 .filter_map(|attr| Self::parse_attribute(attr, true, tcx, item_def_id).transpose())
547 .try_fold(None, |aggr: Option<Self>, directive| {
548 let directive = directive?;
549 if let Some(aggr) = aggr {
550 let mut subcommands = aggr.subcommands;
551 subcommands.extend(directive.subcommands);
552 let mut notes = aggr.notes;
553 notes.extend(directive.notes);
554 IgnoredDiagnosticOption::maybe_emit_warning(
555 tcx,
556 item_def_id,
557 directive.message.as_ref().map(|f| f.span),
558 aggr.message.as_ref().map(|f| f.span),
559 "message",
560 );
561 IgnoredDiagnosticOption::maybe_emit_warning(
562 tcx,
563 item_def_id,
564 directive.label.as_ref().map(|f| f.span),
565 aggr.label.as_ref().map(|f| f.span),
566 "label",
567 );
568 IgnoredDiagnosticOption::maybe_emit_warning(
569 tcx,
570 item_def_id,
571 directive.condition.as_ref().map(|i| i.span()),
572 aggr.condition.as_ref().map(|i| i.span()),
573 "condition",
574 );
575 IgnoredDiagnosticOption::maybe_emit_warning(
576 tcx,
577 item_def_id,
578 directive.parent_label.as_ref().map(|f| f.span),
579 aggr.parent_label.as_ref().map(|f| f.span),
580 "parent_label",
581 );
582 IgnoredDiagnosticOption::maybe_emit_warning(
583 tcx,
584 item_def_id,
585 directive.append_const_msg.as_ref().and_then(|c| {
586 if let AppendConstMessage::Custom(_, s) = c {
587 Some(*s)
588 } else {
589 None
590 }
591 }),
592 aggr.append_const_msg.as_ref().and_then(|c| {
593 if let AppendConstMessage::Custom(_, s) = c {
594 Some(*s)
595 } else {
596 None
597 }
598 }),
599 "append_const_msg",
600 );
601
602 Ok(Some(Self {
603 condition: aggr.condition.or(directive.condition),
604 subcommands,
605 message: aggr.message.or(directive.message),
606 label: aggr.label.or(directive.label),
607 notes,
608 parent_label: aggr.parent_label.or(directive.parent_label),
609 append_const_msg: aggr.append_const_msg.or(directive.append_const_msg),
610 }))
611 } else {
612 Ok(Some(directive))
613 }
614 })
615 }
616 }
617
618 fn parse_attribute(
619 attr: &Attribute,
620 is_diagnostic_namespace_variant: bool,
621 tcx: TyCtxt<'tcx>,
622 item_def_id: DefId,
623 ) -> Result<Option<Self>, ErrorGuaranteed> {
624 let result = if let Some(items) = attr.meta_item_list() {
625 Self::parse(tcx, item_def_id, &items, attr.span, true, is_diagnostic_namespace_variant)
626 } else if let Some(value) = attr.value_str() {
627 if !is_diagnostic_namespace_variant {
628 Ok(Some(OnUnimplementedDirective {
629 condition: None,
630 message: None,
631 subcommands: vec![],
632 label: Some(OnUnimplementedFormatString::try_parse(
633 tcx,
634 item_def_id,
635 value,
636 attr.span,
637 is_diagnostic_namespace_variant,
638 )?),
639 notes: Vec::new(),
640 parent_label: None,
641 append_const_msg: None,
642 }))
643 } else {
644 let item = attr.get_normal_item();
645 let report_span = match &item.args {
646 AttrArgs::Empty => item.path.span,
647 AttrArgs::Delimited(args) => args.dspan.entire(),
648 AttrArgs::Eq { eq_span, expr } => eq_span.to(expr.span),
649 };
650
651 if let Some(item_def_id) = item_def_id.as_local() {
652 tcx.emit_node_span_lint(
653 UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
654 tcx.local_def_id_to_hir_id(item_def_id),
655 report_span,
656 MalformedOnUnimplementedAttrLint::new(report_span),
657 );
658 }
659 Ok(None)
660 }
661 } else if is_diagnostic_namespace_variant {
662 match &attr.kind {
663 AttrKind::Normal(p) if !matches!(p.args, AttrArgs::Empty) => {
664 if let Some(item_def_id) = item_def_id.as_local() {
665 tcx.emit_node_span_lint(
666 UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
667 tcx.local_def_id_to_hir_id(item_def_id),
668 attr.span,
669 MalformedOnUnimplementedAttrLint::new(attr.span),
670 );
671 }
672 }
673 _ => {
674 if let Some(item_def_id) = item_def_id.as_local() {
675 tcx.emit_node_span_lint(
676 UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
677 tcx.local_def_id_to_hir_id(item_def_id),
678 attr.span,
679 MissingOptionsForOnUnimplementedAttr,
680 )
681 }
682 }
683 };
684
685 Ok(None)
686 } else {
687 let reported = tcx.dcx().delayed_bug("of_item: neither meta_item_list nor value_str");
688 return Err(reported);
689 };
690 debug!("of_item({:?}) = {:?}", item_def_id, result);
691 result
692 }
693
694 pub fn evaluate(
695 &self,
696 tcx: TyCtxt<'tcx>,
697 trait_ref: ty::TraitRef<'tcx>,
698 options: &[(Symbol, Option<String>)],
699 long_ty_file: &mut Option<PathBuf>,
700 ) -> OnUnimplementedNote {
701 let mut message = None;
702 let mut label = None;
703 let mut notes = Vec::new();
704 let mut parent_label = None;
705 let mut append_const_msg = None;
706 info!("evaluate({:?}, trait_ref={:?}, options={:?})", self, trait_ref, options);
707
708 let options_map: FxHashMap<Symbol, String> =
709 options.iter().filter_map(|(k, v)| v.clone().map(|v| (*k, v))).collect();
710
711 for command in self.subcommands.iter().chain(Some(self)).rev() {
712 debug!(?command);
713 if let Some(ref condition) = command.condition
714 && !attr::eval_condition(condition, &tcx.sess, Some(tcx.features()), &mut |cfg| {
715 let value = cfg.value.map(|v| {
716 ty::print::with_no_visible_paths!(
719 OnUnimplementedFormatString {
720 symbol: v,
721 span: cfg.span,
722 is_diagnostic_namespace_variant: false
723 }
724 .format(
725 tcx,
726 trait_ref,
727 &options_map,
728 long_ty_file
729 )
730 )
731 });
732
733 options.contains(&(cfg.name, value))
734 })
735 {
736 debug!("evaluate: skipping {:?} due to condition", command);
737 continue;
738 }
739 debug!("evaluate: {:?} succeeded", command);
740 if let Some(ref message_) = command.message {
741 message = Some(message_.clone());
742 }
743
744 if let Some(ref label_) = command.label {
745 label = Some(label_.clone());
746 }
747
748 notes.extend(command.notes.clone());
749
750 if let Some(ref parent_label_) = command.parent_label {
751 parent_label = Some(parent_label_.clone());
752 }
753
754 append_const_msg = command.append_const_msg;
755 }
756
757 OnUnimplementedNote {
758 label: label.map(|l| l.format(tcx, trait_ref, &options_map, long_ty_file)),
759 message: message.map(|m| m.format(tcx, trait_ref, &options_map, long_ty_file)),
760 notes: notes
761 .into_iter()
762 .map(|n| n.format(tcx, trait_ref, &options_map, long_ty_file))
763 .collect(),
764 parent_label: parent_label
765 .map(|e_s| e_s.format(tcx, trait_ref, &options_map, long_ty_file)),
766 append_const_msg,
767 }
768 }
769}
770
771impl<'tcx> OnUnimplementedFormatString {
772 fn try_parse(
773 tcx: TyCtxt<'tcx>,
774 item_def_id: DefId,
775 from: Symbol,
776 value_span: Span,
777 is_diagnostic_namespace_variant: bool,
778 ) -> Result<Self, ErrorGuaranteed> {
779 let result = OnUnimplementedFormatString {
780 symbol: from,
781 span: value_span,
782 is_diagnostic_namespace_variant,
783 };
784 result.verify(tcx, item_def_id)?;
785 Ok(result)
786 }
787
788 fn verify(&self, tcx: TyCtxt<'tcx>, item_def_id: DefId) -> Result<(), ErrorGuaranteed> {
789 let trait_def_id = if tcx.is_trait(item_def_id) {
790 item_def_id
791 } else {
792 tcx.trait_id_of_impl(item_def_id)
793 .expect("expected `on_unimplemented` to correspond to a trait")
794 };
795 let trait_name = tcx.item_ident(trait_def_id);
796 let generics = tcx.generics_of(item_def_id);
797 let s = self.symbol.as_str();
798 let mut parser = Parser::new(s, None, None, false, ParseMode::Format);
799 let mut result = Ok(());
800 for token in &mut parser {
801 match token {
802 Piece::Lit(_) => (), Piece::NextArgument(a) => {
804 let format_spec = a.format;
805 if self.is_diagnostic_namespace_variant
806 && (format_spec.ty_span.is_some()
807 || format_spec.width_span.is_some()
808 || format_spec.precision_span.is_some()
809 || format_spec.fill_span.is_some())
810 {
811 if let Some(item_def_id) = item_def_id.as_local() {
812 tcx.emit_node_span_lint(
813 UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
814 tcx.local_def_id_to_hir_id(item_def_id),
815 self.span,
816 InvalidFormatSpecifier,
817 );
818 }
819 }
820 match a.position {
821 Position::ArgumentNamed(s) => {
822 match Symbol::intern(s) {
823 s if s == trait_name.name
825 && !self.is_diagnostic_namespace_variant =>
826 {
827 ()
828 }
829 s if ALLOWED_FORMAT_SYMBOLS.contains(&s)
830 && !self.is_diagnostic_namespace_variant =>
831 {
832 ()
833 }
834 s if generics.own_params.iter().any(|param| param.name == s) => (),
836 s => {
837 if self.is_diagnostic_namespace_variant {
838 if let Some(item_def_id) = item_def_id.as_local() {
839 tcx.emit_node_span_lint(
840 UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
841 tcx.local_def_id_to_hir_id(item_def_id),
842 self.span,
843 UnknownFormatParameterForOnUnimplementedAttr {
844 argument_name: s,
845 trait_name,
846 },
847 );
848 }
849 } else {
850 result = Err(struct_span_code_err!(
851 tcx.dcx(),
852 self.span,
853 E0230,
854 "there is no parameter `{}` on {}",
855 s,
856 if trait_def_id == item_def_id {
857 format!("trait `{trait_name}`")
858 } else {
859 "impl".to_string()
860 }
861 )
862 .emit());
863 }
864 }
865 }
866 }
867 Position::ArgumentIs(..) | Position::ArgumentImplicitlyIs(_) => {
869 if self.is_diagnostic_namespace_variant {
870 if let Some(item_def_id) = item_def_id.as_local() {
871 tcx.emit_node_span_lint(
872 UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
873 tcx.local_def_id_to_hir_id(item_def_id),
874 self.span,
875 DisallowedPositionalArgument,
876 );
877 }
878 } else {
879 let reported = struct_span_code_err!(
880 tcx.dcx(),
881 self.span,
882 E0231,
883 "only named generic parameters are allowed"
884 )
885 .emit();
886 result = Err(reported);
887 }
888 }
889 }
890 }
891 }
892 }
893 for e in parser.errors {
899 if self.is_diagnostic_namespace_variant {
900 if let Some(item_def_id) = item_def_id.as_local() {
901 tcx.emit_node_span_lint(
902 UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
903 tcx.local_def_id_to_hir_id(item_def_id),
904 self.span,
905 WrappedParserError { description: e.description, label: e.label },
906 );
907 }
908 } else {
909 let reported =
910 struct_span_code_err!(tcx.dcx(), self.span, E0231, "{}", e.description,).emit();
911 result = Err(reported);
912 }
913 }
914
915 result
916 }
917
918 pub fn format(
919 &self,
920 tcx: TyCtxt<'tcx>,
921 trait_ref: ty::TraitRef<'tcx>,
922 options: &FxHashMap<Symbol, String>,
923 long_ty_file: &mut Option<PathBuf>,
924 ) -> String {
925 let name = tcx.item_name(trait_ref.def_id);
926 let trait_str = tcx.def_path_str(trait_ref.def_id);
927 let generics = tcx.generics_of(trait_ref.def_id);
928 let generic_map = generics
929 .own_params
930 .iter()
931 .filter_map(|param| {
932 let value = match param.kind {
933 GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => {
934 if let Some(ty) = trait_ref.args[param.index as usize].as_type() {
935 tcx.short_string(ty, long_ty_file)
936 } else {
937 trait_ref.args[param.index as usize].to_string()
938 }
939 }
940 GenericParamDefKind::Lifetime => return None,
941 };
942 let name = param.name;
943 Some((name, value))
944 })
945 .collect::<FxHashMap<Symbol, String>>();
946 let empty_string = String::new();
947
948 let s = self.symbol.as_str();
949 let mut parser = Parser::new(s, None, None, false, ParseMode::Format);
950 let item_context = (options.get(&sym::ItemContext)).unwrap_or(&empty_string);
951 let constructed_message = (&mut parser)
952 .map(|p| match p {
953 Piece::Lit(s) => s.to_owned(),
954 Piece::NextArgument(a) => match a.position {
955 Position::ArgumentNamed(arg) => {
956 let s = Symbol::intern(arg);
957 match generic_map.get(&s) {
958 Some(val) => val.to_string(),
959 None if self.is_diagnostic_namespace_variant => {
960 format!("{{{arg}}}")
961 }
962 None if s == name => trait_str.clone(),
963 None => {
964 if let Some(val) = options.get(&s) {
965 val.clone()
966 } else if s == sym::from_desugaring {
967 String::new()
969 } else if s == sym::ItemContext
970 && !self.is_diagnostic_namespace_variant
971 {
972 item_context.clone()
973 } else if s == sym::integral {
974 String::from("{integral}")
975 } else if s == sym::integer_ {
976 String::from("{integer}")
977 } else if s == sym::float {
978 String::from("{float}")
979 } else {
980 bug!(
981 "broken on_unimplemented {:?} for {:?}: \
982 no argument matching {:?}",
983 self.symbol,
984 trait_ref,
985 s
986 )
987 }
988 }
989 }
990 }
991 Position::ArgumentImplicitlyIs(_) if self.is_diagnostic_namespace_variant => {
992 String::from("{}")
993 }
994 Position::ArgumentIs(idx) if self.is_diagnostic_namespace_variant => {
995 format!("{{{idx}}}")
996 }
997 _ => bug!("broken on_unimplemented {:?} - bad format arg", self.symbol),
998 },
999 })
1000 .collect();
1001 if self.is_diagnostic_namespace_variant && !parser.errors.is_empty() {
1011 String::from(s)
1012 } else {
1013 constructed_message
1014 }
1015 }
1016}