]> git.lizzy.rs Git - rust.git/blob - src/librustc/traits/on_unimplemented.rs
Do not complain about missing `fn main()` in some cases
[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 #[derive(Clone, Debug)]
14 pub struct OnUnimplementedFormatString(Symbol);
15
16 #[derive(Debug)]
17 pub struct OnUnimplementedDirective {
18     pub condition: Option<MetaItem>,
19     pub subcommands: Vec<OnUnimplementedDirective>,
20     pub message: Option<OnUnimplementedFormatString>,
21     pub label: Option<OnUnimplementedFormatString>,
22     pub note: Option<OnUnimplementedFormatString>,
23 }
24
25 pub struct OnUnimplementedNote {
26     pub message: Option<String>,
27     pub label: Option<String>,
28     pub note: Option<String>,
29 }
30
31 impl OnUnimplementedNote {
32     pub fn empty() -> Self {
33         OnUnimplementedNote { message: None, label: None, note: None }
34     }
35 }
36
37 fn parse_error(
38     tcx: TyCtxt<'_>,
39     span: Span,
40     message: &str,
41     label: &str,
42     note: Option<&str>,
43 ) -> ErrorReported {
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<'tcx> OnUnimplementedDirective {
55     fn parse(
56         tcx: TyCtxt<'tcx>,
57         trait_def_id: DefId,
58         items: &[NestedMetaItem],
59         span: Span,
60         is_root: bool,
61     ) -> Result<Self, ErrorReported> {
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_, 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_, 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_, 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     pub fn of_item(
136         tcx: TyCtxt<'tcx>,
137         trait_def_id: DefId,
138         impl_def_id: DefId,
139     ) -> Result<Option<Self>, ErrorReported> {
140         let attrs = tcx.get_attrs(impl_def_id);
141
142         let attr = if let Some(item) = attr::find_by_name(&attrs, sym::rustc_on_unimplemented) {
143             item
144         } else {
145             return Ok(None);
146         };
147
148         let result = if let Some(items) = attr.meta_item_list() {
149             Self::parse(tcx, trait_def_id, &items, attr.span, true).map(Some)
150         } else if let Some(value) = attr.value_str() {
151             Ok(Some(OnUnimplementedDirective {
152                 condition: None,
153                 message: None,
154                 subcommands: vec![],
155                 label: Some(OnUnimplementedFormatString::try_parse(
156                     tcx, trait_def_id, value, attr.span)?),
157                 note: None,
158             }))
159         } else {
160             return Err(ErrorReported);
161         };
162         debug!("of_item({:?}/{:?}) = {:?}", trait_def_id, impl_def_id, result);
163         result
164     }
165
166     pub fn evaluate(
167         &self,
168         tcx: TyCtxt<'tcx>,
169         trait_ref: ty::TraitRef<'tcx>,
170         options: &[(Symbol, Option<String>)],
171     ) -> OnUnimplementedNote {
172         let mut message = None;
173         let mut label = None;
174         let mut note = None;
175         info!("evaluate({:?}, trait_ref={:?}, options={:?})", self, trait_ref, options);
176
177         for command in self.subcommands.iter().chain(Some(self)).rev() {
178             if let Some(ref condition) = command.condition {
179                 if !attr::eval_condition(condition, &tcx.sess.parse_sess, &mut |c| {
180                     c.ident().map_or(false, |ident| {
181                         options.contains(&(
182                             ident.name,
183                             c.value_str().map(|s| s.as_str().to_string())
184                         ))
185                     })
186                 }) {
187                     debug!("evaluate: skipping {:?} due to condition", command);
188                     continue
189                 }
190             }
191             debug!("evaluate: {:?} succeeded", command);
192             if let Some(ref message_) = command.message {
193                 message = Some(message_.clone());
194             }
195
196             if let Some(ref label_) = command.label {
197                 label = Some(label_.clone());
198             }
199
200             if let Some(ref note_) = command.note {
201                 note = Some(note_.clone());
202             }
203         }
204
205         let options: FxHashMap<Symbol, String> = options.into_iter()
206             .filter_map(|(k, v)| v.as_ref().map(|v| (*k, v.to_owned())))
207             .collect();
208         OnUnimplementedNote {
209             label: label.map(|l| l.format(tcx, trait_ref, &options)),
210             message: message.map(|m| m.format(tcx, trait_ref, &options)),
211             note: note.map(|n| n.format(tcx, trait_ref, &options)),
212         }
213     }
214 }
215
216 impl<'tcx> OnUnimplementedFormatString {
217     fn try_parse(
218         tcx: TyCtxt<'tcx>,
219         trait_def_id: DefId,
220         from: Symbol,
221         err_sp: Span,
222     ) -> Result<Self, ErrorReported> {
223         let result = OnUnimplementedFormatString(from);
224         result.verify(tcx, trait_def_id, err_sp)?;
225         Ok(result)
226     }
227
228     fn verify(
229         &self,
230         tcx: TyCtxt<'tcx>,
231         trait_def_id: DefId,
232         span: Span,
233     ) -> Result<(), ErrorReported> {
234         let name = tcx.item_name(trait_def_id);
235         let generics = tcx.generics_of(trait_def_id);
236         let s = self.0.as_str();
237         let parser = Parser::new(&s, 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 == 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<'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;
293             Some((name, value))
294         }).collect::<FxHashMap<Symbol, String>>();
295         let empty_string = String::new();
296
297         let s = self.0.as_str();
298         let parser = Parser::new(&s, None, vec![], false);
299         parser.map(|p|
300             match p {
301                 Piece::String(s) => s,
302                 Piece::NextArgument(a) => match a.position {
303                     Position::ArgumentNamed(s) => match generic_map.get(&s) {
304                         Some(val) => val,
305                         None if s == name => {
306                             &trait_str
307                         }
308                         None => {
309                             if let Some(val) = options.get(&s) {
310                                 val
311                             } else if s == sym::from_desugaring || s == sym::from_method {
312                                 // don't break messages using these two arguments incorrectly
313                                 &empty_string
314                             } else {
315                                 bug!("broken on_unimplemented {:?} for {:?}: \
316                                       no argument matching {:?}",
317                                      self.0, trait_ref, s)
318                             }
319                         }
320                     },
321                     _ => bug!("broken on_unimplemented {:?} - bad format arg", self.0)
322                 }
323             }
324         ).collect()
325     }
326 }