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