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