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, 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(
626 tcx,
627 item_def_id,
628 &items,
629 attr.span(),
630 true,
631 is_diagnostic_namespace_variant,
632 )
633 } else if let Some(value) = attr.value_str() {
634 if !is_diagnostic_namespace_variant {
635 Ok(Some(OnUnimplementedDirective {
636 condition: None,
637 message: None,
638 subcommands: vec![],
639 label: Some(OnUnimplementedFormatString::try_parse(
640 tcx,
641 item_def_id,
642 value,
643 attr.span(),
644 is_diagnostic_namespace_variant,
645 )?),
646 notes: Vec::new(),
647 parent_label: None,
648 append_const_msg: None,
649 }))
650 } else {
651 let item = attr.get_normal_item();
652 let report_span = match &item.args {
653 AttrArgs::Empty => item.path.span,
654 AttrArgs::Delimited(args) => args.dspan.entire(),
655 AttrArgs::Eq { eq_span, expr } => eq_span.to(expr.span),
656 };
657
658 if let Some(item_def_id) = item_def_id.as_local() {
659 tcx.emit_node_span_lint(
660 UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
661 tcx.local_def_id_to_hir_id(item_def_id),
662 report_span,
663 MalformedOnUnimplementedAttrLint::new(report_span),
664 );
665 }
666 Ok(None)
667 }
668 } else if is_diagnostic_namespace_variant {
669 match attr {
670 Attribute::Unparsed(p) if !matches!(p.args, AttrArgs::Empty) => {
671 if let Some(item_def_id) = item_def_id.as_local() {
672 tcx.emit_node_span_lint(
673 UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
674 tcx.local_def_id_to_hir_id(item_def_id),
675 attr.span(),
676 MalformedOnUnimplementedAttrLint::new(attr.span()),
677 );
678 }
679 }
680 _ => {
681 if let Some(item_def_id) = item_def_id.as_local() {
682 tcx.emit_node_span_lint(
683 UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
684 tcx.local_def_id_to_hir_id(item_def_id),
685 attr.span(),
686 MissingOptionsForOnUnimplementedAttr,
687 )
688 }
689 }
690 };
691
692 Ok(None)
693 } else {
694 let reported = tcx.dcx().delayed_bug("of_item: neither meta_item_list nor value_str");
695 return Err(reported);
696 };
697 debug!("of_item({:?}) = {:?}", item_def_id, result);
698 result
699 }
700
701 pub fn evaluate(
702 &self,
703 tcx: TyCtxt<'tcx>,
704 trait_ref: ty::TraitRef<'tcx>,
705 options: &[(Symbol, Option<String>)],
706 long_ty_file: &mut Option<PathBuf>,
707 ) -> OnUnimplementedNote {
708 let mut message = None;
709 let mut label = None;
710 let mut notes = Vec::new();
711 let mut parent_label = None;
712 let mut append_const_msg = None;
713 info!("evaluate({:?}, trait_ref={:?}, options={:?})", self, trait_ref, options);
714
715 let options_map: FxHashMap<Symbol, String> =
716 options.iter().filter_map(|(k, v)| v.clone().map(|v| (*k, v))).collect();
717
718 for command in self.subcommands.iter().chain(Some(self)).rev() {
719 debug!(?command);
720 if let Some(ref condition) = command.condition
721 && !attr::eval_condition(condition, &tcx.sess, Some(tcx.features()), &mut |cfg| {
722 let value = cfg.value.map(|v| {
723 ty::print::with_no_visible_paths!(
726 OnUnimplementedFormatString {
727 symbol: v,
728 span: cfg.span,
729 is_diagnostic_namespace_variant: false
730 }
731 .format(
732 tcx,
733 trait_ref,
734 &options_map,
735 long_ty_file
736 )
737 )
738 });
739
740 options.contains(&(cfg.name, value))
741 })
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.format(tcx, trait_ref, &options_map, long_ty_file)),
766 message: message.map(|m| m.format(tcx, trait_ref, &options_map, long_ty_file)),
767 notes: notes
768 .into_iter()
769 .map(|n| n.format(tcx, trait_ref, &options_map, long_ty_file))
770 .collect(),
771 parent_label: parent_label
772 .map(|e_s| e_s.format(tcx, trait_ref, &options_map, long_ty_file)),
773 append_const_msg,
774 }
775 }
776}
777
778impl<'tcx> OnUnimplementedFormatString {
779 fn try_parse(
780 tcx: TyCtxt<'tcx>,
781 item_def_id: DefId,
782 from: Symbol,
783 value_span: Span,
784 is_diagnostic_namespace_variant: bool,
785 ) -> Result<Self, ErrorGuaranteed> {
786 let result = OnUnimplementedFormatString {
787 symbol: from,
788 span: value_span,
789 is_diagnostic_namespace_variant,
790 };
791 result.verify(tcx, item_def_id)?;
792 Ok(result)
793 }
794
795 fn verify(&self, tcx: TyCtxt<'tcx>, item_def_id: DefId) -> Result<(), ErrorGuaranteed> {
796 let trait_def_id = if tcx.is_trait(item_def_id) {
797 item_def_id
798 } else {
799 tcx.trait_id_of_impl(item_def_id)
800 .expect("expected `on_unimplemented` to correspond to a trait")
801 };
802 let trait_name = tcx.item_ident(trait_def_id);
803 let generics = tcx.generics_of(item_def_id);
804 let s = self.symbol.as_str();
805 let mut parser = Parser::new(s, None, None, false, ParseMode::Format);
806 let mut result = Ok(());
807 for token in &mut parser {
808 match token {
809 Piece::Lit(_) => (), Piece::NextArgument(a) => {
811 let format_spec = a.format;
812 if self.is_diagnostic_namespace_variant
813 && (format_spec.ty_span.is_some()
814 || format_spec.width_span.is_some()
815 || format_spec.precision_span.is_some()
816 || format_spec.fill_span.is_some())
817 {
818 if let Some(item_def_id) = item_def_id.as_local() {
819 tcx.emit_node_span_lint(
820 UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
821 tcx.local_def_id_to_hir_id(item_def_id),
822 self.span,
823 InvalidFormatSpecifier,
824 );
825 }
826 }
827 match a.position {
828 Position::ArgumentNamed(s) => {
829 match Symbol::intern(s) {
830 s if s == trait_name.name
832 && !self.is_diagnostic_namespace_variant =>
833 {
834 ()
835 }
836 s if ALLOWED_FORMAT_SYMBOLS.contains(&s)
837 && !self.is_diagnostic_namespace_variant =>
838 {
839 ()
840 }
841 s if generics.own_params.iter().any(|param| param.name == s) => (),
843 s => {
844 if self.is_diagnostic_namespace_variant {
845 if let Some(item_def_id) = item_def_id.as_local() {
846 tcx.emit_node_span_lint(
847 UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
848 tcx.local_def_id_to_hir_id(item_def_id),
849 self.span,
850 UnknownFormatParameterForOnUnimplementedAttr {
851 argument_name: s,
852 trait_name,
853 },
854 );
855 }
856 } else {
857 result = Err(struct_span_code_err!(
858 tcx.dcx(),
859 self.span,
860 E0230,
861 "there is no parameter `{}` on {}",
862 s,
863 if trait_def_id == item_def_id {
864 format!("trait `{trait_name}`")
865 } else {
866 "impl".to_string()
867 }
868 )
869 .emit());
870 }
871 }
872 }
873 }
874 Position::ArgumentIs(..) | Position::ArgumentImplicitlyIs(_) => {
876 if self.is_diagnostic_namespace_variant {
877 if let Some(item_def_id) = item_def_id.as_local() {
878 tcx.emit_node_span_lint(
879 UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
880 tcx.local_def_id_to_hir_id(item_def_id),
881 self.span,
882 DisallowedPositionalArgument,
883 );
884 }
885 } else {
886 let reported = struct_span_code_err!(
887 tcx.dcx(),
888 self.span,
889 E0231,
890 "only named generic parameters are allowed"
891 )
892 .emit();
893 result = Err(reported);
894 }
895 }
896 }
897 }
898 }
899 }
900 for e in parser.errors {
906 if self.is_diagnostic_namespace_variant {
907 if let Some(item_def_id) = item_def_id.as_local() {
908 tcx.emit_node_span_lint(
909 UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
910 tcx.local_def_id_to_hir_id(item_def_id),
911 self.span,
912 WrappedParserError { description: e.description, label: e.label },
913 );
914 }
915 } else {
916 let reported =
917 struct_span_code_err!(tcx.dcx(), self.span, E0231, "{}", e.description,).emit();
918 result = Err(reported);
919 }
920 }
921
922 result
923 }
924
925 pub fn format(
926 &self,
927 tcx: TyCtxt<'tcx>,
928 trait_ref: ty::TraitRef<'tcx>,
929 options: &FxHashMap<Symbol, String>,
930 long_ty_file: &mut Option<PathBuf>,
931 ) -> String {
932 let name = tcx.item_name(trait_ref.def_id);
933 let trait_str = tcx.def_path_str(trait_ref.def_id);
934 let generics = tcx.generics_of(trait_ref.def_id);
935 let generic_map = generics
936 .own_params
937 .iter()
938 .filter_map(|param| {
939 let value = match param.kind {
940 GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => {
941 if let Some(ty) = trait_ref.args[param.index as usize].as_type() {
942 tcx.short_string(ty, long_ty_file)
943 } else {
944 trait_ref.args[param.index as usize].to_string()
945 }
946 }
947 GenericParamDefKind::Lifetime => return None,
948 };
949 let name = param.name;
950 Some((name, value))
951 })
952 .collect::<FxHashMap<Symbol, String>>();
953 let empty_string = String::new();
954
955 let s = self.symbol.as_str();
956 let mut parser = Parser::new(s, None, None, false, ParseMode::Format);
957 let item_context = (options.get(&sym::ItemContext)).unwrap_or(&empty_string);
958 let constructed_message = (&mut parser)
959 .map(|p| match p {
960 Piece::Lit(s) => s.to_owned(),
961 Piece::NextArgument(a) => match a.position {
962 Position::ArgumentNamed(arg) => {
963 let s = Symbol::intern(arg);
964 match generic_map.get(&s) {
965 Some(val) => val.to_string(),
966 None if self.is_diagnostic_namespace_variant => {
967 format!("{{{arg}}}")
968 }
969 None if s == name => trait_str.clone(),
970 None => {
971 if let Some(val) = options.get(&s) {
972 val.clone()
973 } else if s == sym::from_desugaring {
974 String::new()
976 } else if s == sym::ItemContext
977 && !self.is_diagnostic_namespace_variant
978 {
979 item_context.clone()
980 } else if s == sym::integral {
981 String::from("{integral}")
982 } else if s == sym::integer_ {
983 String::from("{integer}")
984 } else if s == sym::float {
985 String::from("{float}")
986 } else {
987 bug!(
988 "broken on_unimplemented {:?} for {:?}: \
989 no argument matching {:?}",
990 self.symbol,
991 trait_ref,
992 s
993 )
994 }
995 }
996 }
997 }
998 Position::ArgumentImplicitlyIs(_) if self.is_diagnostic_namespace_variant => {
999 String::from("{}")
1000 }
1001 Position::ArgumentIs(idx) if self.is_diagnostic_namespace_variant => {
1002 format!("{{{idx}}}")
1003 }
1004 _ => bug!("broken on_unimplemented {:?} - bad format arg", self.symbol),
1005 },
1006 })
1007 .collect();
1008 if self.is_diagnostic_namespace_variant && !parser.errors.is_empty() {
1018 String::from(s)
1019 } else {
1020 constructed_message
1021 }
1022 }
1023}