]> git.lizzy.rs Git - rust.git/blob - src/librustc/traits/on_unimplemented.rs
Rollup merge of #65665 - tspiteri:italic-and-update-SourceCodePro, r=GuillaumeGomez
[rust.git] / src / librustc / traits / on_unimplemented.rs
1 use fmt_macros::{Parser, Piece, Position};
2
3 use crate::hir::def_id::DefId;
4 use crate::ty::{self, TyCtxt, GenericParamDefKind};
5 use crate::util::common::ErrorReported;
6 use crate::util::nodemap::FxHashMap;
7
8 use syntax::ast::{MetaItem, NestedMetaItem};
9 use syntax::attr;
10 use syntax::symbol::{Symbol, kw, sym};
11 use syntax_pos::Span;
12
13 use rustc_error_codes::*;
14
15 #[derive(Clone, Debug)]
16 pub struct OnUnimplementedFormatString(Symbol);
17
18 #[derive(Debug)]
19 pub struct OnUnimplementedDirective {
20     pub condition: Option<MetaItem>,
21     pub subcommands: Vec<OnUnimplementedDirective>,
22     pub message: Option<OnUnimplementedFormatString>,
23     pub label: Option<OnUnimplementedFormatString>,
24     pub note: Option<OnUnimplementedFormatString>,
25 }
26
27 pub struct OnUnimplementedNote {
28     pub message: Option<String>,
29     pub label: Option<String>,
30     pub note: Option<String>,
31 }
32
33 impl OnUnimplementedNote {
34     pub fn empty() -> Self {
35         OnUnimplementedNote { message: None, label: None, note: None }
36     }
37 }
38
39 fn parse_error(
40     tcx: TyCtxt<'_>,
41     span: Span,
42     message: &str,
43     label: &str,
44     note: Option<&str>,
45 ) -> ErrorReported {
46     let mut diag = struct_span_err!(
47         tcx.sess, span, E0232, "{}", message);
48     diag.span_label(span, label);
49     if let Some(note) = note {
50         diag.note(note);
51     }
52     diag.emit();
53     ErrorReported
54 }
55
56 impl<'tcx> OnUnimplementedDirective {
57     fn parse(
58         tcx: TyCtxt<'tcx>,
59         trait_def_id: DefId,
60         items: &[NestedMetaItem],
61         span: Span,
62         is_root: bool,
63     ) -> Result<Self, ErrorReported> {
64         let mut errored = false;
65         let mut item_iter = items.iter();
66
67         let condition = if is_root {
68             None
69         } else {
70             let cond = item_iter.next().ok_or_else(||
71                 parse_error(tcx, span,
72                             "empty `on`-clause in `#[rustc_on_unimplemented]`",
73                             "empty on-clause here",
74                             None)
75             )?.meta_item().ok_or_else(||
76                 parse_error(tcx, span,
77                             "invalid `on`-clause in `#[rustc_on_unimplemented]`",
78                             "invalid on-clause here",
79                             None)
80             )?;
81             attr::eval_condition(cond, &tcx.sess.parse_sess, &mut |_| true);
82             Some(cond.clone())
83         };
84
85         let mut message = None;
86         let mut label = None;
87         let mut note = None;
88         let mut subcommands = vec![];
89         for item in item_iter {
90             if item.check_name(sym::message) && message.is_none() {
91                 if let Some(message_) = item.value_str() {
92                     message = Some(OnUnimplementedFormatString::try_parse(
93                         tcx, trait_def_id, message_, span)?);
94                     continue;
95                 }
96             } else if item.check_name(sym::label) && label.is_none() {
97                 if let Some(label_) = item.value_str() {
98                     label = Some(OnUnimplementedFormatString::try_parse(
99                         tcx, trait_def_id, label_, span)?);
100                     continue;
101                 }
102             } else if item.check_name(sym::note) && note.is_none() {
103                 if let Some(note_) = item.value_str() {
104                     note = Some(OnUnimplementedFormatString::try_parse(
105                         tcx, trait_def_id, note_, span)?);
106                     continue;
107                 }
108             } else if item.check_name(sym::on) && is_root &&
109                 message.is_none() && label.is_none() && note.is_none()
110             {
111                 if let Some(items) = item.meta_item_list() {
112                     if let Ok(subcommand) =
113                         Self::parse(tcx, trait_def_id, &items, item.span(), false)
114                     {
115                         subcommands.push(subcommand);
116                     } else {
117                         errored = true;
118                     }
119                     continue
120                 }
121             }
122
123             // nothing found
124             parse_error(tcx, item.span(),
125                         "this attribute must have a valid value",
126                         "expected value here",
127                         Some(r#"eg `#[rustc_on_unimplemented(message="foo")]`"#));
128         }
129
130         if errored {
131             Err(ErrorReported)
132         } else {
133             Ok(OnUnimplementedDirective { condition, message, label, subcommands, note })
134         }
135     }
136
137     pub fn of_item(
138         tcx: TyCtxt<'tcx>,
139         trait_def_id: DefId,
140         impl_def_id: DefId,
141     ) -> Result<Option<Self>, ErrorReported> {
142         let attrs = tcx.get_attrs(impl_def_id);
143
144         let attr = if let Some(item) = attr::find_by_name(&attrs, sym::rustc_on_unimplemented) {
145             item
146         } else {
147             return Ok(None);
148         };
149
150         let result = if let Some(items) = attr.meta_item_list() {
151             Self::parse(tcx, trait_def_id, &items, attr.span, true).map(Some)
152         } else if let Some(value) = attr.value_str() {
153             Ok(Some(OnUnimplementedDirective {
154                 condition: None,
155                 message: None,
156                 subcommands: vec![],
157                 label: Some(OnUnimplementedFormatString::try_parse(
158                     tcx, trait_def_id, value, attr.span)?),
159                 note: None,
160             }))
161         } else {
162             return Err(ErrorReported);
163         };
164         debug!("of_item({:?}/{:?}) = {:?}", trait_def_id, impl_def_id, result);
165         result
166     }
167
168     pub fn evaluate(
169         &self,
170         tcx: TyCtxt<'tcx>,
171         trait_ref: ty::TraitRef<'tcx>,
172         options: &[(Symbol, Option<String>)],
173     ) -> OnUnimplementedNote {
174         let mut message = None;
175         let mut label = None;
176         let mut note = None;
177         info!("evaluate({:?}, trait_ref={:?}, options={:?})", self, trait_ref, options);
178
179         for command in self.subcommands.iter().chain(Some(self)).rev() {
180             if let Some(ref condition) = command.condition {
181                 if !attr::eval_condition(condition, &tcx.sess.parse_sess, &mut |c| {
182                     c.ident().map_or(false, |ident| {
183                         options.contains(&(
184                             ident.name,
185                             c.value_str().map(|s| s.to_string())
186                         ))
187                     })
188                 }) {
189                     debug!("evaluate: skipping {:?} due to condition", command);
190                     continue
191                 }
192             }
193             debug!("evaluate: {:?} succeeded", command);
194             if let Some(ref message_) = command.message {
195                 message = Some(message_.clone());
196             }
197
198             if let Some(ref label_) = command.label {
199                 label = Some(label_.clone());
200             }
201
202             if let Some(ref note_) = command.note {
203                 note = Some(note_.clone());
204             }
205         }
206
207         let options: FxHashMap<Symbol, String> = options.into_iter()
208             .filter_map(|(k, v)| v.as_ref().map(|v| (*k, v.to_owned())))
209             .collect();
210         OnUnimplementedNote {
211             label: label.map(|l| l.format(tcx, trait_ref, &options)),
212             message: message.map(|m| m.format(tcx, trait_ref, &options)),
213             note: note.map(|n| n.format(tcx, trait_ref, &options)),
214         }
215     }
216 }
217
218 impl<'tcx> OnUnimplementedFormatString {
219     fn try_parse(
220         tcx: TyCtxt<'tcx>,
221         trait_def_id: DefId,
222         from: Symbol,
223         err_sp: Span,
224     ) -> Result<Self, ErrorReported> {
225         let result = OnUnimplementedFormatString(from);
226         result.verify(tcx, trait_def_id, err_sp)?;
227         Ok(result)
228     }
229
230     fn verify(
231         &self,
232         tcx: TyCtxt<'tcx>,
233         trait_def_id: DefId,
234         span: Span,
235     ) -> Result<(), ErrorReported> {
236         let name = tcx.item_name(trait_def_id);
237         let generics = tcx.generics_of(trait_def_id);
238         let s = self.0.as_str();
239         let parser = Parser::new(&s, None, vec![], false);
240         let mut result = Ok(());
241         for token in parser {
242             match token {
243                 Piece::String(_) => (), // Normal string, no need to check it
244                 Piece::NextArgument(a) => match a.position {
245                     // `{Self}` is allowed
246                     Position::ArgumentNamed(s) if s == kw::SelfUpper => (),
247                     // `{ThisTraitsName}` is allowed
248                     Position::ArgumentNamed(s) if s == name => (),
249                     // `{from_method}` is allowed
250                     Position::ArgumentNamed(s) if s == sym::from_method => (),
251                     // `{from_desugaring}` is allowed
252                     Position::ArgumentNamed(s) if s == sym::from_desugaring => (),
253                     // `{ItemContext}` is allowed
254                     Position::ArgumentNamed(s) if s == sym::item_context => (),
255                     // So is `{A}` if A is a type parameter
256                     Position::ArgumentNamed(s) => match generics.params.iter().find(|param| {
257                         param.name == s
258                     }) {
259                         Some(_) => (),
260                         None => {
261                             span_err!(tcx.sess, span, E0230,
262                                       "there is no parameter `{}` on trait `{}`", s, name);
263                             result = Err(ErrorReported);
264                         }
265                     },
266                     // `{:1}` and `{}` are not to be used
267                     Position::ArgumentIs(_) | Position::ArgumentImplicitlyIs(_) => {
268                         span_err!(tcx.sess, span, E0231,
269                                   "only named substitution parameters are allowed");
270                         result = Err(ErrorReported);
271                     }
272                 }
273             }
274         }
275
276         result
277     }
278
279     pub fn format(
280         &self,
281         tcx: TyCtxt<'tcx>,
282         trait_ref: ty::TraitRef<'tcx>,
283         options: &FxHashMap<Symbol, String>,
284     ) -> String {
285         let name = tcx.item_name(trait_ref.def_id);
286         let trait_str = tcx.def_path_str(trait_ref.def_id);
287         let generics = tcx.generics_of(trait_ref.def_id);
288         let generic_map = generics.params.iter().filter_map(|param| {
289             let value = match param.kind {
290                 GenericParamDefKind::Type { .. } |
291                 GenericParamDefKind::Const => {
292                     trait_ref.substs[param.index as usize].to_string()
293                 },
294                 GenericParamDefKind::Lifetime => return None
295             };
296             let name = param.name;
297             Some((name, value))
298         }).collect::<FxHashMap<Symbol, String>>();
299         let empty_string = String::new();
300
301         let s = self.0.as_str();
302         let parser = Parser::new(&s, None, vec![], false);
303         let item_context = (options.get(&sym::item_context)).unwrap_or(&empty_string);
304         parser.map(|p|
305             match p {
306                 Piece::String(s) => s,
307                 Piece::NextArgument(a) => match a.position {
308                     Position::ArgumentNamed(s) => match generic_map.get(&s) {
309                         Some(val) => val,
310                         None if s == name => {
311                             &trait_str
312                         }
313                         None => {
314                             if let Some(val) = options.get(&s) {
315                                 val
316                             } else if s == sym::from_desugaring || s == sym::from_method {
317                                 // don't break messages using these two arguments incorrectly
318                                 &empty_string
319                             } else if s == sym::item_context {
320                                 &item_context
321                             } else {
322                                 bug!("broken on_unimplemented {:?} for {:?}: \
323                                       no argument matching {:?}",
324                                      self.0, trait_ref, s)
325                             }
326                         }
327                     },
328                     _ => bug!("broken on_unimplemented {:?} - bad format arg", self.0)
329                 }
330             }
331         ).collect()
332     }
333 }