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