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