]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs
Rollup merge of #101655 - dns2utf8:box_docs, r=dtolnay
[rust.git] / compiler / rustc_trait_selection / src / traits / error_reporting / on_unimplemented.rs
1 use super::{ObligationCauseCode, PredicateObligation};
2 use crate::infer::error_reporting::TypeErrCtxt;
3 use rustc_ast::{MetaItem, NestedMetaItem};
4 use rustc_attr as attr;
5 use rustc_data_structures::fx::FxHashMap;
6 use rustc_errors::{struct_span_err, ErrorGuaranteed};
7 use rustc_hir as hir;
8 use rustc_hir::def_id::DefId;
9 use rustc_middle::ty::SubstsRef;
10 use rustc_middle::ty::{self, GenericParamDefKind, TyCtxt};
11 use rustc_parse_format::{ParseMode, Parser, Piece, Position};
12 use rustc_span::symbol::{kw, sym, Symbol};
13 use rustc_span::{Span, DUMMY_SP};
14 use std::iter;
15
16 use crate::errors::{
17     EmptyOnClauseInOnUnimplemented, InvalidOnClauseInOnUnimplemented, NoValueInOnUnimplemented,
18 };
19
20 use super::InferCtxtPrivExt;
21
22 pub trait TypeErrCtxtExt<'tcx> {
23     /*private*/
24     fn impl_similar_to(
25         &self,
26         trait_ref: ty::PolyTraitRef<'tcx>,
27         obligation: &PredicateObligation<'tcx>,
28     ) -> Option<(DefId, SubstsRef<'tcx>)>;
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
40 impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
41     fn impl_similar_to(
42         &self,
43         trait_ref: ty::PolyTraitRef<'tcx>,
44         obligation: &PredicateObligation<'tcx>,
45     ) -> Option<(DefId, SubstsRef<'tcx>)> {
46         let tcx = self.tcx;
47         let param_env = obligation.param_env;
48         let trait_ref = tcx.erase_late_bound_regions(trait_ref);
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);
56             let impl_trait_ref = tcx.bound_impl_trait_ref(def_id).unwrap().subst(tcx, impl_substs);
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) {
61                 self_match_impls.push((def_id, impl_substs));
62
63                 if iter::zip(
64                     trait_ref.substs.types().skip(1),
65                     impl_trait_ref.substs.types().skip(1),
66                 )
67                 .all(|(u, v)| self.fuzzy_match_tys(u, v, false).is_some())
68                 {
69                     fuzzy_match_impls.push((def_id, impl_substs));
70                 }
71             }
72         });
73
74         let impl_def_id_and_substs = if self_match_impls.len() == 1 {
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
82         tcx.has_attr(impl_def_id_and_substs.0, sym::rustc_on_unimplemented)
83             .then_some(impl_def_id_and_substs)
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> {
89         let hir = self.tcx.hir();
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(|| {
94                     Some(match sig.header {
95                         hir::FnHeader { asyncness: hir::IsAsync::Async, .. } => "an async function",
96                         _ => "a function",
97                     })
98                 })
99             }
100             hir::Node::TraitItem(hir::TraitItem {
101                 kind: hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(body_id)),
102                 ..
103             }) => self.describe_generator(*body_id).or_else(|| Some("a trait method")),
104             hir::Node::ImplItem(hir::ImplItem {
105                 kind: hir::ImplItemKind::Fn(sig, body_id),
106                 ..
107             }) => self.describe_generator(*body_id).or_else(|| {
108                 Some(match sig.header {
109                     hir::FnHeader { asyncness: hir::IsAsync::Async, .. } => "an async method",
110                     _ => "a method",
111                 })
112             }),
113             hir::Node::Expr(hir::Expr {
114                 kind: hir::ExprKind::Closure(hir::Closure { body, movability, .. }),
115                 ..
116             }) => self.describe_generator(*body).or_else(|| {
117                 Some(if movability.is_some() { "an async closure" } else { "a closure" })
118             }),
119             hir::Node::Expr(hir::Expr { .. }) => {
120                 let parent_hid = hir.get_parent_node(hir_id);
121                 if parent_hid != hir_id { self.describe_enclosure(parent_hid) } else { None }
122             }
123             _ => None,
124         }
125     }
126
127     fn on_unimplemented_note(
128         &self,
129         trait_ref: ty::PolyTraitRef<'tcx>,
130         obligation: &PredicateObligation<'tcx>,
131     ) -> OnUnimplementedNote {
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));
135         let trait_ref = trait_ref.skip_binder();
136
137         let mut flags = vec![(
138             sym::ItemContext,
139             self.describe_enclosure(obligation.cause.body_id).map(|s| s.to_owned()),
140         )];
141
142         match obligation.cause.code() {
143             ObligationCauseCode::BuiltinDerivedObligation(..)
144             | ObligationCauseCode::ImplDerivedObligation(..)
145             | ObligationCauseCode::DerivedObligation(..) => {}
146             _ => {
147                 // this is a "direct", user-specified, rather than derived,
148                 // obligation.
149                 flags.push((sym::direct, None));
150             }
151         }
152
153         if let ObligationCauseCode::ItemObligation(item)
154         | ObligationCauseCode::BindingObligation(item, _)
155         | ObligationCauseCode::ExprItemObligation(item, ..)
156         | ObligationCauseCode::ExprBindingObligation(item, ..) = *obligation.cause.code()
157         {
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         }
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         }
174
175         if let ObligationCauseCode::MainFunctionType = obligation.cause.code() {
176             flags.push((sym::cause, Some("MainFunctionType".to_string())));
177         }
178
179         // Add all types without trimmed paths.
180         ty::print::with_no_trimmed_paths!({
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
189                 flags.push((sym::_Self, Some(self.tcx.type_of(def.did()).to_string())));
190             }
191
192             for param in generics.params.iter() {
193                 let value = match param.kind {
194                     GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => {
195                         substs[param.index as usize].to_string()
196                     }
197                     GenericParamDefKind::Lifetime => continue,
198                 };
199                 let name = param.name;
200                 flags.push((name, Some(value)));
201
202                 if let GenericParamDefKind::Type { .. } = param.kind {
203                     let param_ty = substs[param.index as usize].expect_ty();
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
207                         flags.push((name, Some(self.tcx.type_of(def.did()).to_string())));
208                     }
209                 }
210             }
211
212             if let Some(true) = self_ty.ty_adt_def().map(|def| def.did().is_local()) {
213                 flags.push((sym::crate_local, None));
214             }
215
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             }
220
221             if self_ty.is_array_slice() {
222                 flags.push((sym::_Self, Some("&[]".to_owned())));
223             }
224
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
240                     flags.push((sym::_Self, Some(format!("[{}]", self.tcx.type_of(def.did())))));
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]`
248             if let ty::Array(aty, len) = self_ty.kind() {
249                 flags.push((sym::_Self, Some("[]".to_string())));
250                 let len = len.kind().try_to_value().and_then(|v| v.try_to_machine_usize(self.tcx));
251                 flags.push((sym::_Self, Some(format!("[{}; _]", aty))));
252                 if let Some(n) = len {
253                     flags.push((sym::_Self, Some(format!("[{}; {}]", aty, n))));
254                 }
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
258                     let def_ty = self.tcx.type_of(def.did());
259                     flags.push((sym::_Self, Some(format!("[{def_ty}; _]"))));
260                     if let Some(n) = len {
261                         flags.push((sym::_Self, Some(format!("[{def_ty}; {n}]"))));
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                     }
269                 }
270             }
271             if let ty::Dynamic(traits, _, _) = self_ty.kind() {
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                     }
276                 }
277             }
278         });
279
280         if let Ok(Some(command)) = OnUnimplementedDirective::of_item(self.tcx, def_id) {
281             command.evaluate(self.tcx, trait_ref, &flags)
282         } else {
283             OnUnimplementedNote::default()
284         }
285     }
286 }
287
288 #[derive(Clone, Debug)]
289 pub struct OnUnimplementedFormatString(Symbol);
290
291 #[derive(Debug)]
292 pub 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)]
304 pub 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
316 impl<'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.as_ref().map(|v| (*k, v.to_owned()))).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
511 impl<'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 }