]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs
New upstream version 1.67.1+dfsg1
[rustc.git] / compiler / rustc_trait_selection / src / traits / error_reporting / on_unimplemented.rs
CommitLineData
487cf647 1use super::{ObligationCauseCode, PredicateObligation};
2b03887a 2use crate::infer::error_reporting::TypeErrCtxt;
487cf647
FG
3use rustc_ast::{MetaItem, NestedMetaItem};
4use rustc_attr as attr;
5use rustc_data_structures::fx::FxHashMap;
6use rustc_errors::{struct_span_err, ErrorGuaranteed};
dfeec247
XL
7use rustc_hir as hir;
8use rustc_hir::def_id::DefId;
2b03887a 9use rustc_middle::ty::SubstsRef;
487cf647
FG
10use rustc_middle::ty::{self, GenericParamDefKind, TyCtxt};
11use rustc_parse_format::{ParseMode, Parser, Piece, Position};
12use rustc_span::symbol::{kw, sym, Symbol};
13use rustc_span::{Span, DUMMY_SP};
cdc7bbd5 14use std::iter;
dfeec247 15
487cf647
FG
16use crate::errors::{
17 EmptyOnClauseInOnUnimplemented, InvalidOnClauseInOnUnimplemented, NoValueInOnUnimplemented,
18};
19
ba9703b0
XL
20use super::InferCtxtPrivExt;
21
2b03887a 22pub trait TypeErrCtxtExt<'tcx> {
ba9703b0
XL
23 /*private*/
24 fn impl_similar_to(
25 &self,
26 trait_ref: ty::PolyTraitRef<'tcx>,
27 obligation: &PredicateObligation<'tcx>,
5e7ed085 28 ) -> Option<(DefId, SubstsRef<'tcx>)>;
ba9703b0
XL
29
30 /*private*/
31 fn describe_enclosure(&self, hir_id: hir::HirId) -> Option<&'static str>;
32
33 fn on_unimplemented_note(
34 &self,
35 trait_ref: ty::PolyTraitRef<'tcx>,
36 obligation: &PredicateObligation<'tcx>,
37 ) -> OnUnimplementedNote;
38}
39
2b03887a 40impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
dfeec247
XL
41 fn impl_similar_to(
42 &self,
43 trait_ref: ty::PolyTraitRef<'tcx>,
44 obligation: &PredicateObligation<'tcx>,
5e7ed085 45 ) -> Option<(DefId, SubstsRef<'tcx>)> {
dfeec247
XL
46 let tcx = self.tcx;
47 let param_env = obligation.param_env;
fc512014 48 let trait_ref = tcx.erase_late_bound_regions(trait_ref);
dfeec247
XL
49 let trait_self_ty = trait_ref.self_ty();
50
51 let mut self_match_impls = vec![];
52 let mut fuzzy_match_impls = vec![];
53
54 self.tcx.for_each_relevant_impl(trait_ref.def_id, trait_self_ty, |def_id| {
55 let impl_substs = self.fresh_substs_for_item(obligation.cause.span, def_id);
04454e1e 56 let impl_trait_ref = tcx.bound_impl_trait_ref(def_id).unwrap().subst(tcx, impl_substs);
dfeec247
XL
57
58 let impl_self_ty = impl_trait_ref.self_ty();
59
60 if let Ok(..) = self.can_eq(param_env, trait_self_ty, impl_self_ty) {
5e7ed085 61 self_match_impls.push((def_id, impl_substs));
dfeec247 62
cdc7bbd5
XL
63 if iter::zip(
64 trait_ref.substs.types().skip(1),
65 impl_trait_ref.substs.types().skip(1),
66 )
5099ac24 67 .all(|(u, v)| self.fuzzy_match_tys(u, v, false).is_some())
dfeec247 68 {
5e7ed085 69 fuzzy_match_impls.push((def_id, impl_substs));
dfeec247
XL
70 }
71 }
72 });
73
5e7ed085 74 let impl_def_id_and_substs = if self_match_impls.len() == 1 {
dfeec247
XL
75 self_match_impls[0]
76 } else if fuzzy_match_impls.len() == 1 {
77 fuzzy_match_impls[0]
78 } else {
79 return None;
80 };
81
5e7ed085
FG
82 tcx.has_attr(impl_def_id_and_substs.0, sym::rustc_on_unimplemented)
83 .then_some(impl_def_id_and_substs)
dfeec247
XL
84 }
85
86 /// Used to set on_unimplemented's `ItemContext`
87 /// to be the enclosing (async) block/function/closure
88 fn describe_enclosure(&self, hir_id: hir::HirId) -> Option<&'static str> {
5099ac24 89 let hir = self.tcx.hir();
dfeec247
XL
90 let node = hir.find(hir_id)?;
91 match &node {
92 hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, _, body_id), .. }) => {
93 self.describe_generator(*body_id).or_else(|| {
ba9703b0
XL
94 Some(match sig.header {
95 hir::FnHeader { asyncness: hir::IsAsync::Async, .. } => "an async function",
96 _ => "a function",
dfeec247
XL
97 })
98 })
99 }
100 hir::Node::TraitItem(hir::TraitItem {
ba9703b0 101 kind: hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(body_id)),
dfeec247
XL
102 ..
103 }) => self.describe_generator(*body_id).or_else(|| Some("a trait method")),
104 hir::Node::ImplItem(hir::ImplItem {
ba9703b0 105 kind: hir::ImplItemKind::Fn(sig, body_id),
dfeec247
XL
106 ..
107 }) => self.describe_generator(*body_id).or_else(|| {
ba9703b0
XL
108 Some(match sig.header {
109 hir::FnHeader { asyncness: hir::IsAsync::Async, .. } => "an async method",
110 _ => "a method",
dfeec247
XL
111 })
112 }),
113 hir::Node::Expr(hir::Expr {
064997fb 114 kind: hir::ExprKind::Closure(hir::Closure { body, movability, .. }),
dfeec247 115 ..
923072b8
FG
116 }) => self.describe_generator(*body).or_else(|| {
117 Some(if movability.is_some() { "an async closure" } else { "a closure" })
dfeec247
XL
118 }),
119 hir::Node::Expr(hir::Expr { .. }) => {
120 let parent_hid = hir.get_parent_node(hir_id);
ba9703b0 121 if parent_hid != hir_id { self.describe_enclosure(parent_hid) } else { None }
dfeec247
XL
122 }
123 _ => None,
124 }
125 }
126
ba9703b0 127 fn on_unimplemented_note(
dfeec247
XL
128 &self,
129 trait_ref: ty::PolyTraitRef<'tcx>,
130 obligation: &PredicateObligation<'tcx>,
131 ) -> OnUnimplementedNote {
5e7ed085
FG
132 let (def_id, substs) = self
133 .impl_similar_to(trait_ref, obligation)
134 .unwrap_or_else(|| (trait_ref.def_id(), trait_ref.skip_binder().substs));
f035d41b 135 let trait_ref = trait_ref.skip_binder();
dfeec247 136
94222f64 137 let mut flags = vec![(
3dfed10e 138 sym::ItemContext,
dfeec247 139 self.describe_enclosure(obligation.cause.body_id).map(|s| s.to_owned()),
94222f64 140 )];
dfeec247 141
a2a8927a 142 match obligation.cause.code() {
dfeec247 143 ObligationCauseCode::BuiltinDerivedObligation(..)
ba9703b0
XL
144 | ObligationCauseCode::ImplDerivedObligation(..)
145 | ObligationCauseCode::DerivedObligation(..) => {}
dfeec247
XL
146 _ => {
147 // this is a "direct", user-specified, rather than derived,
148 // obligation.
149 flags.push((sym::direct, None));
150 }
151 }
152
ba9703b0 153 if let ObligationCauseCode::ItemObligation(item)
f2b60f7d
FG
154 | ObligationCauseCode::BindingObligation(item, _)
155 | ObligationCauseCode::ExprItemObligation(item, ..)
156 | ObligationCauseCode::ExprBindingObligation(item, ..) = *obligation.cause.code()
ba9703b0 157 {
dfeec247
XL
158 // FIXME: maybe also have some way of handling methods
159 // from other traits? That would require name resolution,
160 // which we might want to be some sort of hygienic.
161 //
162 // Currently I'm leaving it for what I need for `try`.
163 if self.tcx.trait_of_item(item) == Some(trait_ref.def_id) {
164 let method = self.tcx.item_name(item);
165 flags.push((sym::from_method, None));
166 flags.push((sym::from_method, Some(method.to_string())));
167 }
168 }
dfeec247
XL
169
170 if let Some(k) = obligation.cause.span.desugaring_kind() {
171 flags.push((sym::from_desugaring, None));
172 flags.push((sym::from_desugaring, Some(format!("{:?}", k))));
173 }
dfeec247 174
2b03887a
FG
175 if let ObligationCauseCode::MainFunctionType = obligation.cause.code() {
176 flags.push((sym::cause, Some("MainFunctionType".to_string())));
177 }
178
cdc7bbd5 179 // Add all types without trimmed paths.
5e7ed085 180 ty::print::with_no_trimmed_paths!({
cdc7bbd5
XL
181 let generics = self.tcx.generics_of(def_id);
182 let self_ty = trait_ref.self_ty();
183 // This is also included through the generics list as `Self`,
184 // but the parser won't allow you to use it
185 flags.push((sym::_Self, Some(self_ty.to_string())));
186 if let Some(def) = self_ty.ty_adt_def() {
187 // We also want to be able to select self's original
188 // signature with no type arguments resolved
5e7ed085 189 flags.push((sym::_Self, Some(self.tcx.type_of(def.did()).to_string())));
cdc7bbd5 190 }
dfeec247 191
cdc7bbd5
XL
192 for param in generics.params.iter() {
193 let value = match param.kind {
194 GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => {
5e7ed085 195 substs[param.index as usize].to_string()
cdc7bbd5
XL
196 }
197 GenericParamDefKind::Lifetime => continue,
198 };
199 let name = param.name;
200 flags.push((name, Some(value)));
17df50a5
XL
201
202 if let GenericParamDefKind::Type { .. } = param.kind {
5e7ed085 203 let param_ty = substs[param.index as usize].expect_ty();
17df50a5
XL
204 if let Some(def) = param_ty.ty_adt_def() {
205 // We also want to be able to select the parameter's
206 // original signature with no type arguments resolved
5e7ed085 207 flags.push((name, Some(self.tcx.type_of(def.did()).to_string())));
17df50a5
XL
208 }
209 }
cdc7bbd5 210 }
dfeec247 211
5e7ed085 212 if let Some(true) = self_ty.ty_adt_def().map(|def| def.did().is_local()) {
cdc7bbd5
XL
213 flags.push((sym::crate_local, None));
214 }
dfeec247 215
cdc7bbd5
XL
216 // Allow targeting all integers using `{integral}`, even if the exact type was resolved
217 if self_ty.is_integral() {
218 flags.push((sym::_Self, Some("{integral}".to_owned())));
219 }
6a06907d 220
5e7ed085
FG
221 if self_ty.is_array_slice() {
222 flags.push((sym::_Self, Some("&[]".to_owned())));
223 }
224
04454e1e
FG
225 if self_ty.is_fn() {
226 let fn_sig = self_ty.fn_sig(self.tcx);
227 let shortname = match fn_sig.unsafety() {
228 hir::Unsafety::Normal => "fn",
229 hir::Unsafety::Unsafe => "unsafe fn",
230 };
231 flags.push((sym::_Self, Some(shortname.to_owned())));
232 }
233
234 // Slices give us `[]`, `[{ty}]`
235 if let ty::Slice(aty) = self_ty.kind() {
236 flags.push((sym::_Self, Some("[]".to_string())));
237 if let Some(def) = aty.ty_adt_def() {
238 // We also want to be able to select the slice's type's original
239 // signature with no type arguments resolved
064997fb 240 flags.push((sym::_Self, Some(format!("[{}]", self.tcx.type_of(def.did())))));
04454e1e
FG
241 }
242 if aty.is_integral() {
243 flags.push((sym::_Self, Some("[{integral}]".to_string())));
244 }
245 }
246
247 // Arrays give us `[]`, `[{ty}; _]` and `[{ty}; N]`
cdc7bbd5 248 if let ty::Array(aty, len) = self_ty.kind() {
04454e1e 249 flags.push((sym::_Self, Some("[]".to_string())));
923072b8 250 let len = len.kind().try_to_value().and_then(|v| v.try_to_machine_usize(self.tcx));
04454e1e
FG
251 flags.push((sym::_Self, Some(format!("[{}; _]", aty))));
252 if let Some(n) = len {
253 flags.push((sym::_Self, Some(format!("[{}; {}]", aty, n))));
254 }
cdc7bbd5
XL
255 if let Some(def) = aty.ty_adt_def() {
256 // We also want to be able to select the array's type's original
257 // signature with no type arguments resolved
064997fb
FG
258 let def_ty = self.tcx.type_of(def.did());
259 flags.push((sym::_Self, Some(format!("[{def_ty}; _]"))));
04454e1e 260 if let Some(n) = len {
064997fb 261 flags.push((sym::_Self, Some(format!("[{def_ty}; {n}]"))));
04454e1e
FG
262 }
263 }
264 if aty.is_integral() {
265 flags.push((sym::_Self, Some("[{integral}; _]".to_string())));
266 if let Some(n) = len {
267 flags.push((sym::_Self, Some(format!("[{{integral}}; {n}]"))));
268 }
cdc7bbd5 269 }
dfeec247 270 }
f2b60f7d 271 if let ty::Dynamic(traits, _, _) = self_ty.kind() {
cdc7bbd5
XL
272 for t in traits.iter() {
273 if let ty::ExistentialPredicate::Trait(trait_ref) = t.skip_binder() {
274 flags.push((sym::_Self, Some(self.tcx.def_path_str(trait_ref.def_id))))
275 }
74b04a01
XL
276 }
277 }
cdc7bbd5 278 });
dfeec247 279
5e7ed085 280 if let Ok(Some(command)) = OnUnimplementedDirective::of_item(self.tcx, def_id) {
a2a8927a 281 command.evaluate(self.tcx, trait_ref, &flags)
dfeec247
XL
282 } else {
283 OnUnimplementedNote::default()
284 }
285 }
286}
487cf647
FG
287
288#[derive(Clone, Debug)]
289pub struct OnUnimplementedFormatString(Symbol);
290
291#[derive(Debug)]
292pub struct OnUnimplementedDirective {
293 pub condition: Option<MetaItem>,
294 pub subcommands: Vec<OnUnimplementedDirective>,
295 pub message: Option<OnUnimplementedFormatString>,
296 pub label: Option<OnUnimplementedFormatString>,
297 pub note: Option<OnUnimplementedFormatString>,
298 pub parent_label: Option<OnUnimplementedFormatString>,
299 pub append_const_msg: Option<Option<Symbol>>,
300}
301
302/// For the `#[rustc_on_unimplemented]` attribute
303#[derive(Default)]
304pub struct OnUnimplementedNote {
305 pub message: Option<String>,
306 pub label: Option<String>,
307 pub note: Option<String>,
308 pub parent_label: Option<String>,
309 /// Append a message for `~const Trait` errors. `None` means not requested and
310 /// should fallback to a generic message, `Some(None)` suggests using the default
311 /// appended message, `Some(Some(s))` suggests use the `s` message instead of the
312 /// default one..
313 pub append_const_msg: Option<Option<Symbol>>,
314}
315
316impl<'tcx> OnUnimplementedDirective {
317 fn parse(
318 tcx: TyCtxt<'tcx>,
319 item_def_id: DefId,
320 items: &[NestedMetaItem],
321 span: Span,
322 is_root: bool,
323 ) -> Result<Self, ErrorGuaranteed> {
324 let mut errored = None;
325 let mut item_iter = items.iter();
326
327 let parse_value = |value_str| {
328 OnUnimplementedFormatString::try_parse(tcx, item_def_id, value_str, span).map(Some)
329 };
330
331 let condition = if is_root {
332 None
333 } else {
334 let cond = item_iter
335 .next()
336 .ok_or_else(|| tcx.sess.emit_err(EmptyOnClauseInOnUnimplemented { span }))?
337 .meta_item()
338 .ok_or_else(|| tcx.sess.emit_err(InvalidOnClauseInOnUnimplemented { span }))?;
339 attr::eval_condition(cond, &tcx.sess.parse_sess, Some(tcx.features()), &mut |cfg| {
340 if let Some(value) = cfg.value && let Err(guar) = parse_value(value) {
341 errored = Some(guar);
342 }
343 true
344 });
345 Some(cond.clone())
346 };
347
348 let mut message = None;
349 let mut label = None;
350 let mut note = None;
351 let mut parent_label = None;
352 let mut subcommands = vec![];
353 let mut append_const_msg = None;
354
355 for item in item_iter {
356 if item.has_name(sym::message) && message.is_none() {
357 if let Some(message_) = item.value_str() {
358 message = parse_value(message_)?;
359 continue;
360 }
361 } else if item.has_name(sym::label) && label.is_none() {
362 if let Some(label_) = item.value_str() {
363 label = parse_value(label_)?;
364 continue;
365 }
366 } else if item.has_name(sym::note) && note.is_none() {
367 if let Some(note_) = item.value_str() {
368 note = parse_value(note_)?;
369 continue;
370 }
371 } else if item.has_name(sym::parent_label) && parent_label.is_none() {
372 if let Some(parent_label_) = item.value_str() {
373 parent_label = parse_value(parent_label_)?;
374 continue;
375 }
376 } else if item.has_name(sym::on)
377 && is_root
378 && message.is_none()
379 && label.is_none()
380 && note.is_none()
381 {
382 if let Some(items) = item.meta_item_list() {
383 match Self::parse(tcx, item_def_id, &items, item.span(), false) {
384 Ok(subcommand) => subcommands.push(subcommand),
385 Err(reported) => errored = Some(reported),
386 };
387 continue;
388 }
389 } else if item.has_name(sym::append_const_msg) && append_const_msg.is_none() {
390 if let Some(msg) = item.value_str() {
391 append_const_msg = Some(Some(msg));
392 continue;
393 } else if item.is_word() {
394 append_const_msg = Some(None);
395 continue;
396 }
397 }
398
399 // nothing found
400 tcx.sess.emit_err(NoValueInOnUnimplemented { span: item.span() });
401 }
402
403 if let Some(reported) = errored {
404 Err(reported)
405 } else {
406 Ok(OnUnimplementedDirective {
407 condition,
408 subcommands,
409 message,
410 label,
411 note,
412 parent_label,
413 append_const_msg,
414 })
415 }
416 }
417
418 pub fn of_item(tcx: TyCtxt<'tcx>, item_def_id: DefId) -> Result<Option<Self>, ErrorGuaranteed> {
419 let Some(attr) = tcx.get_attr(item_def_id, sym::rustc_on_unimplemented) else {
420 return Ok(None);
421 };
422
423 let result = if let Some(items) = attr.meta_item_list() {
424 Self::parse(tcx, item_def_id, &items, attr.span, true).map(Some)
425 } else if let Some(value) = attr.value_str() {
426 Ok(Some(OnUnimplementedDirective {
427 condition: None,
428 message: None,
429 subcommands: vec![],
430 label: Some(OnUnimplementedFormatString::try_parse(
431 tcx,
432 item_def_id,
433 value,
434 attr.span,
435 )?),
436 note: None,
437 parent_label: None,
438 append_const_msg: None,
439 }))
440 } else {
441 let reported =
442 tcx.sess.delay_span_bug(DUMMY_SP, "of_item: neither meta_item_list nor value_str");
443 return Err(reported);
444 };
445 debug!("of_item({:?}) = {:?}", item_def_id, result);
446 result
447 }
448
449 pub fn evaluate(
450 &self,
451 tcx: TyCtxt<'tcx>,
452 trait_ref: ty::TraitRef<'tcx>,
453 options: &[(Symbol, Option<String>)],
454 ) -> OnUnimplementedNote {
455 let mut message = None;
456 let mut label = None;
457 let mut note = None;
458 let mut parent_label = None;
459 let mut append_const_msg = None;
460 info!("evaluate({:?}, trait_ref={:?}, options={:?})", self, trait_ref, options);
461
462 let options_map: FxHashMap<Symbol, String> =
463 options.iter().filter_map(|(k, v)| v.clone().map(|v| (*k, v))).collect();
464
465 for command in self.subcommands.iter().chain(Some(self)).rev() {
466 if let Some(ref condition) = command.condition && !attr::eval_condition(
467 condition,
468 &tcx.sess.parse_sess,
469 Some(tcx.features()),
470 &mut |cfg| {
471 let value = cfg.value.map(|v| {
472 OnUnimplementedFormatString(v).format(tcx, trait_ref, &options_map)
473 });
474
475 options.contains(&(cfg.name, value))
476 },
477 ) {
478 debug!("evaluate: skipping {:?} due to condition", command);
479 continue;
480 }
481 debug!("evaluate: {:?} succeeded", command);
482 if let Some(ref message_) = command.message {
483 message = Some(message_.clone());
484 }
485
486 if let Some(ref label_) = command.label {
487 label = Some(label_.clone());
488 }
489
490 if let Some(ref note_) = command.note {
491 note = Some(note_.clone());
492 }
493
494 if let Some(ref parent_label_) = command.parent_label {
495 parent_label = Some(parent_label_.clone());
496 }
497
498 append_const_msg = command.append_const_msg;
499 }
500
501 OnUnimplementedNote {
502 label: label.map(|l| l.format(tcx, trait_ref, &options_map)),
503 message: message.map(|m| m.format(tcx, trait_ref, &options_map)),
504 note: note.map(|n| n.format(tcx, trait_ref, &options_map)),
505 parent_label: parent_label.map(|e_s| e_s.format(tcx, trait_ref, &options_map)),
506 append_const_msg,
507 }
508 }
509}
510
511impl<'tcx> OnUnimplementedFormatString {
512 fn try_parse(
513 tcx: TyCtxt<'tcx>,
514 item_def_id: DefId,
515 from: Symbol,
516 err_sp: Span,
517 ) -> Result<Self, ErrorGuaranteed> {
518 let result = OnUnimplementedFormatString(from);
519 result.verify(tcx, item_def_id, err_sp)?;
520 Ok(result)
521 }
522
523 fn verify(
524 &self,
525 tcx: TyCtxt<'tcx>,
526 item_def_id: DefId,
527 span: Span,
528 ) -> Result<(), ErrorGuaranteed> {
529 let trait_def_id = if tcx.is_trait(item_def_id) {
530 item_def_id
531 } else {
532 tcx.trait_id_of_impl(item_def_id)
533 .expect("expected `on_unimplemented` to correspond to a trait")
534 };
535 let trait_name = tcx.item_name(trait_def_id);
536 let generics = tcx.generics_of(item_def_id);
537 let s = self.0.as_str();
538 let parser = Parser::new(s, None, None, false, ParseMode::Format);
539 let mut result = Ok(());
540 for token in parser {
541 match token {
542 Piece::String(_) => (), // Normal string, no need to check it
543 Piece::NextArgument(a) => match a.position {
544 Position::ArgumentNamed(s) => {
545 match Symbol::intern(s) {
546 // `{Self}` is allowed
547 kw::SelfUpper => (),
548 // `{ThisTraitsName}` is allowed
549 s if s == trait_name => (),
550 // `{from_method}` is allowed
551 sym::from_method => (),
552 // `{from_desugaring}` is allowed
553 sym::from_desugaring => (),
554 // `{ItemContext}` is allowed
555 sym::ItemContext => (),
556 // `{integral}` and `{integer}` and `{float}` are allowed
557 sym::integral | sym::integer_ | sym::float => (),
558 // So is `{A}` if A is a type parameter
559 s => match generics.params.iter().find(|param| param.name == s) {
560 Some(_) => (),
561 None => {
562 let reported = struct_span_err!(
563 tcx.sess,
564 span,
565 E0230,
566 "there is no parameter `{}` on {}",
567 s,
568 if trait_def_id == item_def_id {
569 format!("trait `{}`", trait_name)
570 } else {
571 "impl".to_string()
572 }
573 )
574 .emit();
575 result = Err(reported);
576 }
577 },
578 }
579 }
580 // `{:1}` and `{}` are not to be used
581 Position::ArgumentIs(..) | Position::ArgumentImplicitlyIs(_) => {
582 let reported = struct_span_err!(
583 tcx.sess,
584 span,
585 E0231,
586 "only named substitution parameters are allowed"
587 )
588 .emit();
589 result = Err(reported);
590 }
591 },
592 }
593 }
594
595 result
596 }
597
598 pub fn format(
599 &self,
600 tcx: TyCtxt<'tcx>,
601 trait_ref: ty::TraitRef<'tcx>,
602 options: &FxHashMap<Symbol, String>,
603 ) -> String {
604 let name = tcx.item_name(trait_ref.def_id);
605 let trait_str = tcx.def_path_str(trait_ref.def_id);
606 let generics = tcx.generics_of(trait_ref.def_id);
607 let generic_map = generics
608 .params
609 .iter()
610 .filter_map(|param| {
611 let value = match param.kind {
612 GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => {
613 trait_ref.substs[param.index as usize].to_string()
614 }
615 GenericParamDefKind::Lifetime => return None,
616 };
617 let name = param.name;
618 Some((name, value))
619 })
620 .collect::<FxHashMap<Symbol, String>>();
621 let empty_string = String::new();
622
623 let s = self.0.as_str();
624 let parser = Parser::new(s, None, None, false, ParseMode::Format);
625 let item_context = (options.get(&sym::ItemContext)).unwrap_or(&empty_string);
626 parser
627 .map(|p| match p {
628 Piece::String(s) => s,
629 Piece::NextArgument(a) => match a.position {
630 Position::ArgumentNamed(s) => {
631 let s = Symbol::intern(s);
632 match generic_map.get(&s) {
633 Some(val) => val,
634 None if s == name => &trait_str,
635 None => {
636 if let Some(val) = options.get(&s) {
637 val
638 } else if s == sym::from_desugaring || s == sym::from_method {
639 // don't break messages using these two arguments incorrectly
640 &empty_string
641 } else if s == sym::ItemContext {
642 &item_context
643 } else if s == sym::integral {
644 "{integral}"
645 } else if s == sym::integer_ {
646 "{integer}"
647 } else if s == sym::float {
648 "{float}"
649 } else {
650 bug!(
651 "broken on_unimplemented {:?} for {:?}: \
652 no argument matching {:?}",
653 self.0,
654 trait_ref,
655 s
656 )
657 }
658 }
659 }
660 }
661 _ => bug!("broken on_unimplemented {:?} - bad format arg", self.0),
662 },
663 })
664 .collect()
665 }
666}