]>
Commit | Line | Data |
---|---|---|
487cf647 | 1 | use super::{ObligationCauseCode, PredicateObligation}; |
2b03887a | 2 | use crate::infer::error_reporting::TypeErrCtxt; |
487cf647 FG |
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}; | |
dfeec247 XL |
7 | use rustc_hir as hir; |
8 | use rustc_hir::def_id::DefId; | |
2b03887a | 9 | use rustc_middle::ty::SubstsRef; |
487cf647 FG |
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}; | |
cdc7bbd5 | 14 | use std::iter; |
dfeec247 | 15 | |
487cf647 FG |
16 | use crate::errors::{ |
17 | EmptyOnClauseInOnUnimplemented, InvalidOnClauseInOnUnimplemented, NoValueInOnUnimplemented, | |
18 | }; | |
19 | ||
ba9703b0 XL |
20 | use super::InferCtxtPrivExt; |
21 | ||
2b03887a | 22 | pub 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 | 40 | impl<'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)] | |
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.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 | ||
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 | } |