]>
Commit | Line | Data |
---|---|---|
1a4d82fc JJ |
1 | // Copyright 2013 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 | //! Feature gating | |
12 | //! | |
d9579d0f | 13 | //! This module implements the gating necessary for preventing certain compiler |
1a4d82fc JJ |
14 | //! features from being used by default. This module will crawl a pre-expanded |
15 | //! AST to ensure that there are no features which are used that are not | |
16 | //! enabled. | |
17 | //! | |
18 | //! Features are enabled in programs via the crate-level attributes of | |
19 | //! `#![feature(...)]` with a comma-separated list of features. | |
85aaf69f SL |
20 | //! |
21 | //! For the purpose of future feature-tracking, once code for detection of feature | |
22 | //! gate usage is added, *do not remove it again* even once the feature | |
23 | //! becomes stable. | |
24 | ||
1a4d82fc | 25 | use self::Status::*; |
85aaf69f | 26 | use self::AttributeType::*; |
1a4d82fc | 27 | |
c34b1796 | 28 | use abi::Abi; |
1a4d82fc JJ |
29 | use ast::NodeId; |
30 | use ast; | |
31 | use attr; | |
32 | use attr::AttrMetaMethods; | |
33 | use codemap::{CodeMap, Span}; | |
34 | use diagnostic::SpanHandler; | |
35 | use visit; | |
36 | use visit::Visitor; | |
85aaf69f | 37 | use parse::token::{self, InternedString}; |
1a4d82fc | 38 | |
1a4d82fc JJ |
39 | use std::ascii::AsciiExt; |
40 | ||
85aaf69f SL |
41 | // If you change this list without updating src/doc/reference.md, @cmr will be sad |
42 | // Don't ever remove anything from this list; set them to 'Removed'. | |
43 | // The version numbers here correspond to the version in which the current status | |
44 | // was set. This is most important for knowing when a particular feature became | |
45 | // stable (active). | |
46 | // NB: The featureck.py script parses this information directly out of the source | |
47 | // so take care when modifying it. | |
c34b1796 | 48 | const KNOWN_FEATURES: &'static [(&'static str, &'static str, Status)] = &[ |
85aaf69f SL |
49 | ("globs", "1.0.0", Accepted), |
50 | ("macro_rules", "1.0.0", Accepted), | |
51 | ("struct_variant", "1.0.0", Accepted), | |
52 | ("asm", "1.0.0", Active), | |
53 | ("managed_boxes", "1.0.0", Removed), | |
54 | ("non_ascii_idents", "1.0.0", Active), | |
55 | ("thread_local", "1.0.0", Active), | |
56 | ("link_args", "1.0.0", Active), | |
85aaf69f SL |
57 | ("plugin_registrar", "1.0.0", Active), |
58 | ("log_syntax", "1.0.0", Active), | |
59 | ("trace_macros", "1.0.0", Active), | |
60 | ("concat_idents", "1.0.0", Active), | |
85aaf69f SL |
61 | ("intrinsics", "1.0.0", Active), |
62 | ("lang_items", "1.0.0", Active), | |
63 | ||
64 | ("simd", "1.0.0", Active), | |
65 | ("default_type_params", "1.0.0", Accepted), | |
66 | ("quote", "1.0.0", Active), | |
67 | ("link_llvm_intrinsics", "1.0.0", Active), | |
68 | ("linkage", "1.0.0", Active), | |
69 | ("struct_inherit", "1.0.0", Removed), | |
70 | ||
71 | ("quad_precision_float", "1.0.0", Removed), | |
72 | ||
73 | ("rustc_diagnostic_macros", "1.0.0", Active), | |
74 | ("unboxed_closures", "1.0.0", Active), | |
c34b1796 | 75 | ("reflect", "1.0.0", Active), |
85aaf69f SL |
76 | ("import_shadowing", "1.0.0", Removed), |
77 | ("advanced_slice_patterns", "1.0.0", Active), | |
78 | ("tuple_indexing", "1.0.0", Accepted), | |
79 | ("associated_types", "1.0.0", Accepted), | |
80 | ("visible_private_types", "1.0.0", Active), | |
81 | ("slicing_syntax", "1.0.0", Accepted), | |
82 | ("box_syntax", "1.0.0", Active), | |
83 | ("on_unimplemented", "1.0.0", Active), | |
84 | ("simd_ffi", "1.0.0", Active), | |
c34b1796 | 85 | ("allocator", "1.0.0", Active), |
85aaf69f SL |
86 | |
87 | ("if_let", "1.0.0", Accepted), | |
88 | ("while_let", "1.0.0", Accepted), | |
89 | ||
90 | ("plugin", "1.0.0", Active), | |
91 | ("start", "1.0.0", Active), | |
92 | ("main", "1.0.0", Active), | |
1a4d82fc | 93 | |
c34b1796 AL |
94 | ("fundamental", "1.0.0", Active), |
95 | ||
1a4d82fc JJ |
96 | // A temporary feature gate used to enable parser extensions needed |
97 | // to bootstrap fix for #5723. | |
85aaf69f | 98 | ("issue_5723_bootstrap", "1.0.0", Accepted), |
1a4d82fc JJ |
99 | |
100 | // A way to temporarily opt out of opt in copy. This will *never* be accepted. | |
85aaf69f | 101 | ("opt_out_copy", "1.0.0", Removed), |
1a4d82fc | 102 | |
1a4d82fc | 103 | // OIBIT specific features |
85aaf69f | 104 | ("optin_builtin_traits", "1.0.0", Active), |
1a4d82fc | 105 | |
85aaf69f SL |
106 | // macro reexport needs more discussion and stabilization |
107 | ("macro_reexport", "1.0.0", Active), | |
1a4d82fc JJ |
108 | |
109 | // These are used to test this portion of the compiler, they don't actually | |
110 | // mean anything | |
85aaf69f SL |
111 | ("test_accepted_feature", "1.0.0", Accepted), |
112 | ("test_removed_feature", "1.0.0", Removed), | |
113 | ||
114 | // Allows use of #[staged_api] | |
115 | ("staged_api", "1.0.0", Active), | |
116 | ||
117 | // Allows using items which are missing stability attributes | |
118 | ("unmarked_api", "1.0.0", Active), | |
119 | ||
120 | // Allows using #![no_std] | |
121 | ("no_std", "1.0.0", Active), | |
122 | ||
123 | // Allows using `box` in patterns; RFC 469 | |
124 | ("box_patterns", "1.0.0", Active), | |
125 | ||
126 | // Allows using the unsafe_no_drop_flag attribute (unlikely to | |
127 | // switch to Accepted; see RFC 320) | |
128 | ("unsafe_no_drop_flag", "1.0.0", Active), | |
129 | ||
130 | // Allows the use of custom attributes; RFC 572 | |
131 | ("custom_attribute", "1.0.0", Active), | |
132 | ||
c34b1796 AL |
133 | // Allows the use of #[derive(Anything)] as sugar for |
134 | // #[derive_Anything]. | |
135 | ("custom_derive", "1.0.0", Active), | |
136 | ||
85aaf69f SL |
137 | // Allows the use of rustc_* attributes; RFC 572 |
138 | ("rustc_attrs", "1.0.0", Active), | |
c34b1796 | 139 | |
c34b1796 AL |
140 | // Allows the use of #[allow_internal_unstable]. This is an |
141 | // attribute on macro_rules! and can't use the attribute handling | |
142 | // below (it has to be checked before expansion possibly makes | |
143 | // macros disappear). | |
144 | ("allow_internal_unstable", "1.0.0", Active), | |
145 | ||
146 | // #23121. Array patterns have some hazards yet. | |
147 | ("slice_patterns", "1.0.0", Active), | |
148 | ||
149 | // Allows use of unary negate on unsigned integers, e.g. -e for e: u8 | |
150 | ("negate_unsigned", "1.0.0", Active), | |
d9579d0f AL |
151 | |
152 | // Allows the definition of associated constants in `trait` or `impl` | |
153 | // blocks. | |
154 | ("associated_consts", "1.0.0", Active), | |
62682a34 SL |
155 | |
156 | // Allows the definition of `const fn` functions. | |
157 | ("const_fn", "1.2.0", Active), | |
158 | ||
159 | // Allows associated type defaults | |
160 | ("associated_type_defaults", "1.2.0", Active), | |
1a4d82fc | 161 | ]; |
85aaf69f | 162 | // (changing above list without updating src/doc/reference.md makes @cmr sad) |
1a4d82fc JJ |
163 | |
164 | enum Status { | |
165 | /// Represents an active feature that is currently being implemented or | |
166 | /// currently being considered for addition/removal. | |
167 | Active, | |
168 | ||
1a4d82fc JJ |
169 | /// Represents a feature which has since been removed (it was once Active) |
170 | Removed, | |
171 | ||
172 | /// This language feature has since been Accepted (it was once Active) | |
173 | Accepted, | |
174 | } | |
175 | ||
85aaf69f | 176 | // Attributes that have a special meaning to rustc or rustdoc |
c34b1796 | 177 | pub const KNOWN_ATTRIBUTES: &'static [(&'static str, AttributeType)] = &[ |
85aaf69f SL |
178 | // Normal attributes |
179 | ||
180 | ("warn", Normal), | |
181 | ("allow", Normal), | |
182 | ("forbid", Normal), | |
183 | ("deny", Normal), | |
184 | ||
185 | ("macro_reexport", Normal), | |
186 | ("macro_use", Normal), | |
187 | ("macro_export", Normal), | |
188 | ("plugin_registrar", Normal), | |
189 | ||
190 | ("cfg", Normal), | |
c34b1796 | 191 | ("cfg_attr", Normal), |
85aaf69f SL |
192 | ("main", Normal), |
193 | ("start", Normal), | |
194 | ("test", Normal), | |
195 | ("bench", Normal), | |
196 | ("simd", Normal), | |
197 | ("repr", Normal), | |
198 | ("path", Normal), | |
199 | ("abi", Normal), | |
85aaf69f SL |
200 | ("automatically_derived", Normal), |
201 | ("no_mangle", Normal), | |
202 | ("no_link", Normal), | |
203 | ("derive", Normal), | |
c34b1796 | 204 | ("should_panic", Normal), |
85aaf69f SL |
205 | ("ignore", Normal), |
206 | ("no_implicit_prelude", Normal), | |
207 | ("reexport_test_harness_main", Normal), | |
208 | ("link_args", Normal), | |
209 | ("macro_escape", Normal), | |
210 | ||
85aaf69f SL |
211 | ("staged_api", Gated("staged_api", |
212 | "staged_api is for use by rustc only")), | |
213 | ("plugin", Gated("plugin", | |
214 | "compiler plugins are experimental \ | |
215 | and possibly buggy")), | |
216 | ("no_std", Gated("no_std", | |
217 | "no_std is experimental")), | |
218 | ("lang", Gated("lang_items", | |
219 | "language items are subject to change")), | |
220 | ("linkage", Gated("linkage", | |
221 | "the `linkage` attribute is experimental \ | |
222 | and not portable across platforms")), | |
223 | ("thread_local", Gated("thread_local", | |
224 | "`#[thread_local]` is an experimental feature, and does not \ | |
225 | currently handle destructors. There is no corresponding \ | |
226 | `#[task_local]` mapping to the task model")), | |
227 | ||
228 | ("rustc_on_unimplemented", Gated("on_unimplemented", | |
229 | "the `#[rustc_on_unimplemented]` attribute \ | |
230 | is an experimental feature")), | |
c34b1796 AL |
231 | ("allocator", Gated("allocator", |
232 | "the `#[allocator]` attribute is an experimental feature")), | |
85aaf69f SL |
233 | ("rustc_variance", Gated("rustc_attrs", |
234 | "the `#[rustc_variance]` attribute \ | |
235 | is an experimental feature")), | |
236 | ("rustc_error", Gated("rustc_attrs", | |
237 | "the `#[rustc_error]` attribute \ | |
238 | is an experimental feature")), | |
239 | ("rustc_move_fragments", Gated("rustc_attrs", | |
240 | "the `#[rustc_move_fragments]` attribute \ | |
241 | is an experimental feature")), | |
242 | ||
c34b1796 AL |
243 | ("allow_internal_unstable", Gated("allow_internal_unstable", |
244 | EXPLAIN_ALLOW_INTERNAL_UNSTABLE)), | |
245 | ||
246 | ("fundamental", Gated("fundamental", | |
247 | "the `#[fundamental]` attribute \ | |
248 | is an experimental feature")), | |
249 | ||
85aaf69f SL |
250 | // FIXME: #14408 whitelist docs since rustdoc looks at them |
251 | ("doc", Whitelisted), | |
252 | ||
253 | // FIXME: #14406 these are processed in trans, which happens after the | |
254 | // lint pass | |
255 | ("cold", Whitelisted), | |
256 | ("export_name", Whitelisted), | |
257 | ("inline", Whitelisted), | |
258 | ("link", Whitelisted), | |
259 | ("link_name", Whitelisted), | |
260 | ("link_section", Whitelisted), | |
261 | ("no_builtins", Whitelisted), | |
262 | ("no_mangle", Whitelisted), | |
85aaf69f | 263 | ("no_stack_check", Whitelisted), |
85aaf69f SL |
264 | ("no_debug", Whitelisted), |
265 | ("omit_gdb_pretty_printer_section", Whitelisted), | |
c34b1796 AL |
266 | ("unsafe_no_drop_flag", Gated("unsafe_no_drop_flag", |
267 | "unsafe_no_drop_flag has unstable semantics \ | |
268 | and may be removed in the future")), | |
85aaf69f SL |
269 | |
270 | // used in resolve | |
271 | ("prelude_import", Whitelisted), | |
272 | ||
273 | // FIXME: #14407 these are only looked at on-demand so we can't | |
274 | // guarantee they'll have already been checked | |
275 | ("deprecated", Whitelisted), | |
276 | ("must_use", Whitelisted), | |
277 | ("stable", Whitelisted), | |
278 | ("unstable", Whitelisted), | |
279 | ||
c34b1796 AL |
280 | ("rustc_paren_sugar", Gated("unboxed_closures", |
281 | "unboxed_closures are still evolving")), | |
282 | ("rustc_reflect_like", Gated("reflect", | |
283 | "defining reflective traits is still evolving")), | |
85aaf69f SL |
284 | |
285 | // Crate level attributes | |
286 | ("crate_name", CrateLevel), | |
287 | ("crate_type", CrateLevel), | |
288 | ("crate_id", CrateLevel), | |
289 | ("feature", CrateLevel), | |
290 | ("no_start", CrateLevel), | |
291 | ("no_main", CrateLevel), | |
292 | ("no_builtins", CrateLevel), | |
293 | ("recursion_limit", CrateLevel), | |
294 | ]; | |
295 | ||
c34b1796 | 296 | #[derive(PartialEq, Copy, Clone, Debug)] |
85aaf69f SL |
297 | pub enum AttributeType { |
298 | /// Normal, builtin attribute that is consumed | |
299 | /// by the compiler before the unused_attribute check | |
300 | Normal, | |
301 | ||
302 | /// Builtin attribute that may not be consumed by the compiler | |
303 | /// before the unused_attribute check. These attributes | |
304 | /// will be ignored by the unused_attribute lint | |
305 | Whitelisted, | |
306 | ||
307 | /// Is gated by a given feature gate and reason | |
308 | /// These get whitelisted too | |
309 | Gated(&'static str, &'static str), | |
310 | ||
311 | /// Builtin attribute that is only allowed at the crate level | |
312 | CrateLevel, | |
313 | } | |
314 | ||
1a4d82fc | 315 | /// A set of features to be used by later passes. |
1a4d82fc JJ |
316 | pub struct Features { |
317 | pub unboxed_closures: bool, | |
318 | pub rustc_diagnostic_macros: bool, | |
1a4d82fc | 319 | pub visible_private_types: bool, |
85aaf69f SL |
320 | pub allow_quote: bool, |
321 | pub allow_asm: bool, | |
322 | pub allow_log_syntax: bool, | |
323 | pub allow_concat_idents: bool, | |
324 | pub allow_trace_macros: bool, | |
c34b1796 AL |
325 | pub allow_internal_unstable: bool, |
326 | pub allow_custom_derive: bool, | |
85aaf69f SL |
327 | pub simd_ffi: bool, |
328 | pub unmarked_api: bool, | |
c34b1796 | 329 | pub negate_unsigned: bool, |
85aaf69f SL |
330 | /// spans of #![feature] attrs for stable language features. for error reporting |
331 | pub declared_stable_lang_features: Vec<Span>, | |
332 | /// #![feature] attrs for non-language (library) features | |
62682a34 SL |
333 | pub declared_lib_features: Vec<(InternedString, Span)>, |
334 | pub const_fn: bool, | |
1a4d82fc JJ |
335 | } |
336 | ||
337 | impl Features { | |
338 | pub fn new() -> Features { | |
339 | Features { | |
340 | unboxed_closures: false, | |
341 | rustc_diagnostic_macros: false, | |
1a4d82fc | 342 | visible_private_types: false, |
85aaf69f SL |
343 | allow_quote: false, |
344 | allow_asm: false, | |
345 | allow_log_syntax: false, | |
346 | allow_concat_idents: false, | |
347 | allow_trace_macros: false, | |
c34b1796 AL |
348 | allow_internal_unstable: false, |
349 | allow_custom_derive: false, | |
85aaf69f SL |
350 | simd_ffi: false, |
351 | unmarked_api: false, | |
c34b1796 | 352 | negate_unsigned: false, |
85aaf69f | 353 | declared_stable_lang_features: Vec::new(), |
62682a34 SL |
354 | declared_lib_features: Vec::new(), |
355 | const_fn: false, | |
1a4d82fc JJ |
356 | } |
357 | } | |
358 | } | |
359 | ||
360 | struct Context<'a> { | |
361 | features: Vec<&'static str>, | |
362 | span_handler: &'a SpanHandler, | |
363 | cm: &'a CodeMap, | |
62682a34 | 364 | plugin_attributes: &'a [(String, AttributeType)], |
1a4d82fc JJ |
365 | } |
366 | ||
367 | impl<'a> Context<'a> { | |
368 | fn gate_feature(&self, feature: &str, span: Span, explain: &str) { | |
c34b1796 AL |
369 | let has_feature = self.has_feature(feature); |
370 | debug!("gate_feature(feature = {:?}, span = {:?}); has? {}", feature, span, has_feature); | |
371 | if !has_feature { | |
85aaf69f | 372 | emit_feature_err(self.span_handler, feature, span, explain); |
1a4d82fc JJ |
373 | } |
374 | } | |
1a4d82fc JJ |
375 | fn has_feature(&self, feature: &str) -> bool { |
376 | self.features.iter().any(|&n| n == feature) | |
377 | } | |
c34b1796 | 378 | |
62682a34 | 379 | fn check_attribute(&self, attr: &ast::Attribute, is_macro: bool) { |
c34b1796 AL |
380 | debug!("check_attribute(attr = {:?})", attr); |
381 | let name = &*attr.name(); | |
382 | for &(n, ty) in KNOWN_ATTRIBUTES { | |
383 | if n == name { | |
384 | if let Gated(gate, desc) = ty { | |
385 | self.gate_feature(gate, attr.span, desc); | |
386 | } | |
387 | debug!("check_attribute: {:?} is known, {:?}", name, ty); | |
388 | return; | |
389 | } | |
390 | } | |
62682a34 SL |
391 | for &(ref n, ref ty) in self.plugin_attributes { |
392 | if &*n == name { | |
393 | // Plugins can't gate attributes, so we don't check for it | |
394 | // unlike the code above; we only use this loop to | |
395 | // short-circuit to avoid the checks below | |
396 | debug!("check_attribute: {:?} is registered by a plugin, {:?}", name, ty); | |
397 | return; | |
398 | } | |
399 | } | |
c34b1796 AL |
400 | if name.starts_with("rustc_") { |
401 | self.gate_feature("rustc_attrs", attr.span, | |
402 | "unless otherwise specified, attributes \ | |
403 | with the prefix `rustc_` \ | |
404 | are reserved for internal compiler diagnostics"); | |
405 | } else if name.starts_with("derive_") { | |
406 | self.gate_feature("custom_derive", attr.span, | |
d9579d0f | 407 | "attributes of the form `#[derive_*]` are reserved \ |
c34b1796 AL |
408 | for the compiler"); |
409 | } else { | |
62682a34 SL |
410 | // Only run the custom attribute lint during regular |
411 | // feature gate checking. Macro gating runs | |
412 | // before the plugin attributes are registered | |
413 | // so we skip this then | |
414 | if !is_macro { | |
415 | self.gate_feature("custom_attribute", attr.span, | |
416 | &format!("The attribute `{}` is currently \ | |
417 | unknown to the compiler and \ | |
418 | may have meaning \ | |
419 | added to it in the future", | |
420 | name)); | |
421 | } | |
c34b1796 AL |
422 | } |
423 | } | |
1a4d82fc JJ |
424 | } |
425 | ||
85aaf69f SL |
426 | pub fn emit_feature_err(diag: &SpanHandler, feature: &str, span: Span, explain: &str) { |
427 | diag.span_err(span, explain); | |
9346a6ac AL |
428 | |
429 | // #23973: do not suggest `#![feature(...)]` if we are in beta/stable | |
430 | if option_env!("CFG_DISABLE_UNSTABLE_FEATURES").is_some() { return; } | |
c34b1796 | 431 | diag.fileline_help(span, &format!("add #![feature({})] to the \ |
85aaf69f SL |
432 | crate attributes to enable", |
433 | feature)); | |
434 | } | |
435 | ||
85aaf69f SL |
436 | pub const EXPLAIN_ASM: &'static str = |
437 | "inline assembly is not stable enough for use and is subject to change"; | |
438 | ||
439 | pub const EXPLAIN_LOG_SYNTAX: &'static str = | |
440 | "`log_syntax!` is not stable enough for use and is subject to change"; | |
441 | ||
442 | pub const EXPLAIN_CONCAT_IDENTS: &'static str = | |
443 | "`concat_idents` is not stable enough for use and is subject to change"; | |
444 | ||
445 | pub const EXPLAIN_TRACE_MACROS: &'static str = | |
446 | "`trace_macros` is not stable enough for use and is subject to change"; | |
c34b1796 AL |
447 | pub const EXPLAIN_ALLOW_INTERNAL_UNSTABLE: &'static str = |
448 | "allow_internal_unstable side-steps feature gating and stability checks"; | |
449 | ||
450 | pub const EXPLAIN_CUSTOM_DERIVE: &'static str = | |
451 | "`#[derive]` for custom traits is not stable enough for use and is subject to change"; | |
85aaf69f | 452 | |
1a4d82fc JJ |
453 | struct MacroVisitor<'a> { |
454 | context: &'a Context<'a> | |
455 | } | |
456 | ||
457 | impl<'a, 'v> Visitor<'v> for MacroVisitor<'a> { | |
458 | fn visit_mac(&mut self, mac: &ast::Mac) { | |
459 | let ast::MacInvocTT(ref path, _, _) = mac.node; | |
460 | let id = path.segments.last().unwrap().identifier; | |
461 | ||
85aaf69f SL |
462 | // Issue 22234: If you add a new case here, make sure to also |
463 | // add code to catch the macro during or after expansion. | |
464 | // | |
465 | // We still keep this MacroVisitor (rather than *solely* | |
466 | // relying on catching cases during or after expansion) to | |
467 | // catch uses of these macros within conditionally-compiled | |
468 | // code, e.g. `#[cfg]`-guarded functions. | |
469 | ||
1a4d82fc | 470 | if id == token::str_to_ident("asm") { |
85aaf69f | 471 | self.context.gate_feature("asm", path.span, EXPLAIN_ASM); |
1a4d82fc JJ |
472 | } |
473 | ||
474 | else if id == token::str_to_ident("log_syntax") { | |
85aaf69f | 475 | self.context.gate_feature("log_syntax", path.span, EXPLAIN_LOG_SYNTAX); |
1a4d82fc JJ |
476 | } |
477 | ||
478 | else if id == token::str_to_ident("trace_macros") { | |
85aaf69f | 479 | self.context.gate_feature("trace_macros", path.span, EXPLAIN_TRACE_MACROS); |
1a4d82fc JJ |
480 | } |
481 | ||
482 | else if id == token::str_to_ident("concat_idents") { | |
85aaf69f | 483 | self.context.gate_feature("concat_idents", path.span, EXPLAIN_CONCAT_IDENTS); |
1a4d82fc JJ |
484 | } |
485 | } | |
c34b1796 AL |
486 | |
487 | fn visit_attribute(&mut self, attr: &'v ast::Attribute) { | |
62682a34 | 488 | self.context.check_attribute(attr, true); |
c34b1796 | 489 | } |
1a4d82fc JJ |
490 | } |
491 | ||
492 | struct PostExpansionVisitor<'a> { | |
493 | context: &'a Context<'a> | |
494 | } | |
495 | ||
496 | impl<'a> PostExpansionVisitor<'a> { | |
497 | fn gate_feature(&self, feature: &str, span: Span, explain: &str) { | |
c34b1796 | 498 | if !self.context.cm.span_allows_unstable(span) { |
1a4d82fc JJ |
499 | self.context.gate_feature(feature, span, explain) |
500 | } | |
501 | } | |
502 | } | |
503 | ||
504 | impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> { | |
c34b1796 AL |
505 | fn visit_attribute(&mut self, attr: &ast::Attribute) { |
506 | if !self.context.cm.span_allows_unstable(attr.span) { | |
62682a34 | 507 | self.context.check_attribute(attr, false); |
c34b1796 AL |
508 | } |
509 | } | |
510 | ||
1a4d82fc | 511 | fn visit_name(&mut self, sp: Span, name: ast::Name) { |
85aaf69f | 512 | if !token::get_name(name).is_ascii() { |
1a4d82fc JJ |
513 | self.gate_feature("non_ascii_idents", sp, |
514 | "non-ascii idents are not fully supported."); | |
515 | } | |
516 | } | |
517 | ||
85aaf69f | 518 | fn visit_item(&mut self, i: &ast::Item) { |
1a4d82fc | 519 | match i.node { |
85aaf69f SL |
520 | ast::ItemExternCrate(_) => { |
521 | if attr::contains_name(&i.attrs[..], "macro_reexport") { | |
522 | self.gate_feature("macro_reexport", i.span, | |
523 | "macros reexports are experimental \ | |
524 | and possibly buggy"); | |
1a4d82fc JJ |
525 | } |
526 | } | |
1a4d82fc | 527 | |
1a4d82fc | 528 | ast::ItemForeignMod(ref foreign_module) => { |
85aaf69f | 529 | if attr::contains_name(&i.attrs[..], "link_args") { |
1a4d82fc JJ |
530 | self.gate_feature("link_args", i.span, |
531 | "the `link_args` attribute is not portable \ | |
532 | across platforms, it is recommended to \ | |
533 | use `#[link(name = \"foo\")]` instead") | |
534 | } | |
c34b1796 | 535 | if foreign_module.abi == Abi::RustIntrinsic { |
1a4d82fc JJ |
536 | self.gate_feature("intrinsics", |
537 | i.span, | |
538 | "intrinsics are subject to change") | |
539 | } | |
540 | } | |
541 | ||
542 | ast::ItemFn(..) => { | |
85aaf69f | 543 | if attr::contains_name(&i.attrs[..], "plugin_registrar") { |
1a4d82fc JJ |
544 | self.gate_feature("plugin_registrar", i.span, |
545 | "compiler plugins are experimental and possibly buggy"); | |
546 | } | |
85aaf69f SL |
547 | if attr::contains_name(&i.attrs[..], "start") { |
548 | self.gate_feature("start", i.span, | |
549 | "a #[start] function is an experimental \ | |
550 | feature whose signature may change \ | |
551 | over time"); | |
552 | } | |
553 | if attr::contains_name(&i.attrs[..], "main") { | |
554 | self.gate_feature("main", i.span, | |
555 | "declaration of a nonstandard #[main] \ | |
556 | function may change over time, for now \ | |
557 | a top-level `fn main()` is required"); | |
558 | } | |
1a4d82fc JJ |
559 | } |
560 | ||
561 | ast::ItemStruct(..) => { | |
85aaf69f | 562 | if attr::contains_name(&i.attrs[..], "simd") { |
1a4d82fc JJ |
563 | self.gate_feature("simd", i.span, |
564 | "SIMD types are experimental and possibly buggy"); | |
565 | } | |
566 | } | |
567 | ||
c34b1796 AL |
568 | ast::ItemDefaultImpl(..) => { |
569 | self.gate_feature("optin_builtin_traits", | |
570 | i.span, | |
571 | "default trait implementations are experimental \ | |
572 | and possibly buggy"); | |
573 | } | |
574 | ||
1a4d82fc JJ |
575 | ast::ItemImpl(_, polarity, _, _, _, _) => { |
576 | match polarity { | |
577 | ast::ImplPolarity::Negative => { | |
578 | self.gate_feature("optin_builtin_traits", | |
579 | i.span, | |
580 | "negative trait bounds are not yet fully implemented; \ | |
581 | use marker types for now"); | |
582 | }, | |
583 | _ => {} | |
584 | } | |
1a4d82fc JJ |
585 | } |
586 | ||
587 | _ => {} | |
588 | } | |
589 | ||
590 | visit::walk_item(self, i); | |
591 | } | |
592 | ||
593 | fn visit_foreign_item(&mut self, i: &ast::ForeignItem) { | |
85aaf69f | 594 | let links_to_llvm = match attr::first_attr_value_str_by_name(&i.attrs, |
1a4d82fc | 595 | "link_name") { |
85aaf69f | 596 | Some(val) => val.starts_with("llvm."), |
1a4d82fc JJ |
597 | _ => false |
598 | }; | |
599 | if links_to_llvm { | |
600 | self.gate_feature("link_llvm_intrinsics", i.span, | |
601 | "linking to LLVM intrinsics is experimental"); | |
602 | } | |
603 | ||
604 | visit::walk_foreign_item(self, i) | |
605 | } | |
606 | ||
1a4d82fc JJ |
607 | fn visit_expr(&mut self, e: &ast::Expr) { |
608 | match e.node { | |
609 | ast::ExprBox(..) | ast::ExprUnary(ast::UnOp::UnUniq, _) => { | |
610 | self.gate_feature("box_syntax", | |
611 | e.span, | |
85aaf69f | 612 | "box expression syntax is experimental; \ |
1a4d82fc JJ |
613 | you can call `Box::new` instead."); |
614 | } | |
1a4d82fc JJ |
615 | _ => {} |
616 | } | |
617 | visit::walk_expr(self, e); | |
618 | } | |
619 | ||
1a4d82fc JJ |
620 | fn visit_pat(&mut self, pattern: &ast::Pat) { |
621 | match pattern.node { | |
622 | ast::PatVec(_, Some(_), ref last) if !last.is_empty() => { | |
623 | self.gate_feature("advanced_slice_patterns", | |
624 | pattern.span, | |
625 | "multiple-element slice matches anywhere \ | |
626 | but at the end of a slice (e.g. \ | |
d9579d0f | 627 | `[0, ..xs, 0]`) are experimental") |
1a4d82fc | 628 | } |
c34b1796 AL |
629 | ast::PatVec(..) => { |
630 | self.gate_feature("slice_patterns", | |
631 | pattern.span, | |
632 | "slice pattern syntax is experimental"); | |
633 | } | |
1a4d82fc | 634 | ast::PatBox(..) => { |
85aaf69f | 635 | self.gate_feature("box_patterns", |
1a4d82fc | 636 | pattern.span, |
85aaf69f | 637 | "box pattern syntax is experimental"); |
1a4d82fc JJ |
638 | } |
639 | _ => {} | |
640 | } | |
641 | visit::walk_pat(self, pattern) | |
642 | } | |
643 | ||
644 | fn visit_fn(&mut self, | |
645 | fn_kind: visit::FnKind<'v>, | |
646 | fn_decl: &'v ast::FnDecl, | |
647 | block: &'v ast::Block, | |
648 | span: Span, | |
649 | _node_id: NodeId) { | |
62682a34 SL |
650 | // check for const fn declarations |
651 | match fn_kind { | |
652 | visit::FkItemFn(_, _, _, ast::Constness::Const, _, _) => { | |
653 | self.gate_feature("const_fn", span, "const fn is unstable"); | |
654 | } | |
655 | _ => { | |
656 | // stability of const fn methods are covered in | |
657 | // visit_trait_item and visit_impl_item below; this is | |
658 | // because default methods don't pass through this | |
659 | // point. | |
660 | } | |
661 | } | |
662 | ||
1a4d82fc | 663 | match fn_kind { |
62682a34 | 664 | visit::FkItemFn(_, _, _, _, abi, _) if abi == Abi::RustIntrinsic => { |
1a4d82fc JJ |
665 | self.gate_feature("intrinsics", |
666 | span, | |
667 | "intrinsics are subject to change") | |
668 | } | |
62682a34 | 669 | visit::FkItemFn(_, _, _, _, abi, _) | |
9346a6ac | 670 | visit::FkMethod(_, &ast::MethodSig { abi, .. }, _) if abi == Abi::RustCall => { |
c34b1796 AL |
671 | self.gate_feature("unboxed_closures", |
672 | span, | |
673 | "rust-call ABI is subject to change") | |
674 | } | |
1a4d82fc JJ |
675 | _ => {} |
676 | } | |
677 | visit::walk_fn(self, fn_kind, fn_decl, block, span); | |
678 | } | |
d9579d0f AL |
679 | |
680 | fn visit_trait_item(&mut self, ti: &'v ast::TraitItem) { | |
681 | match ti.node { | |
682 | ast::ConstTraitItem(..) => { | |
683 | self.gate_feature("associated_consts", | |
684 | ti.span, | |
685 | "associated constants are experimental") | |
686 | } | |
62682a34 SL |
687 | ast::MethodTraitItem(ref sig, _) => { |
688 | if sig.constness == ast::Constness::Const { | |
689 | self.gate_feature("const_fn", ti.span, "const fn is unstable"); | |
690 | } | |
691 | } | |
692 | ast::TypeTraitItem(_, Some(_)) => { | |
693 | self.gate_feature("associated_type_defaults", ti.span, | |
694 | "associated type defaults are unstable"); | |
695 | } | |
d9579d0f AL |
696 | _ => {} |
697 | } | |
698 | visit::walk_trait_item(self, ti); | |
699 | } | |
700 | ||
701 | fn visit_impl_item(&mut self, ii: &'v ast::ImplItem) { | |
702 | match ii.node { | |
703 | ast::ConstImplItem(..) => { | |
704 | self.gate_feature("associated_consts", | |
705 | ii.span, | |
706 | "associated constants are experimental") | |
707 | } | |
62682a34 SL |
708 | ast::MethodImplItem(ref sig, _) => { |
709 | if sig.constness == ast::Constness::Const { | |
710 | self.gate_feature("const_fn", ii.span, "const fn is unstable"); | |
711 | } | |
712 | } | |
d9579d0f AL |
713 | _ => {} |
714 | } | |
715 | visit::walk_impl_item(self, ii); | |
716 | } | |
1a4d82fc JJ |
717 | } |
718 | ||
c34b1796 AL |
719 | fn check_crate_inner<F>(cm: &CodeMap, span_handler: &SpanHandler, |
720 | krate: &ast::Crate, | |
62682a34 | 721 | plugin_attributes: &[(String, AttributeType)], |
1a4d82fc | 722 | check: F) |
85aaf69f | 723 | -> Features |
1a4d82fc JJ |
724 | where F: FnOnce(&mut Context, &ast::Crate) |
725 | { | |
726 | let mut cx = Context { | |
727 | features: Vec::new(), | |
728 | span_handler: span_handler, | |
729 | cm: cm, | |
62682a34 | 730 | plugin_attributes: plugin_attributes, |
1a4d82fc JJ |
731 | }; |
732 | ||
85aaf69f | 733 | let mut accepted_features = Vec::new(); |
1a4d82fc JJ |
734 | let mut unknown_features = Vec::new(); |
735 | ||
85aaf69f | 736 | for attr in &krate.attrs { |
1a4d82fc JJ |
737 | if !attr.check_name("feature") { |
738 | continue | |
739 | } | |
740 | ||
741 | match attr.meta_item_list() { | |
742 | None => { | |
743 | span_handler.span_err(attr.span, "malformed feature attribute, \ | |
744 | expected #![feature(...)]"); | |
745 | } | |
746 | Some(list) => { | |
85aaf69f | 747 | for mi in list { |
1a4d82fc JJ |
748 | let name = match mi.node { |
749 | ast::MetaWord(ref word) => (*word).clone(), | |
750 | _ => { | |
751 | span_handler.span_err(mi.span, | |
752 | "malformed feature, expected just \ | |
753 | one word"); | |
754 | continue | |
755 | } | |
756 | }; | |
757 | match KNOWN_FEATURES.iter() | |
85aaf69f SL |
758 | .find(|& &(n, _, _)| name == n) { |
759 | Some(&(name, _, Active)) => { | |
1a4d82fc JJ |
760 | cx.features.push(name); |
761 | } | |
85aaf69f | 762 | Some(&(_, _, Removed)) => { |
1a4d82fc JJ |
763 | span_handler.span_err(mi.span, "feature has been removed"); |
764 | } | |
85aaf69f SL |
765 | Some(&(_, _, Accepted)) => { |
766 | accepted_features.push(mi.span); | |
1a4d82fc JJ |
767 | } |
768 | None => { | |
85aaf69f | 769 | unknown_features.push((name, mi.span)); |
1a4d82fc JJ |
770 | } |
771 | } | |
772 | } | |
773 | } | |
774 | } | |
775 | } | |
776 | ||
777 | check(&mut cx, krate); | |
778 | ||
85aaf69f SL |
779 | // FIXME (pnkfelix): Before adding the 99th entry below, change it |
780 | // to a single-pass (instead of N calls to `.has_feature`). | |
781 | ||
782 | Features { | |
1a4d82fc JJ |
783 | unboxed_closures: cx.has_feature("unboxed_closures"), |
784 | rustc_diagnostic_macros: cx.has_feature("rustc_diagnostic_macros"), | |
1a4d82fc | 785 | visible_private_types: cx.has_feature("visible_private_types"), |
85aaf69f SL |
786 | allow_quote: cx.has_feature("quote"), |
787 | allow_asm: cx.has_feature("asm"), | |
788 | allow_log_syntax: cx.has_feature("log_syntax"), | |
789 | allow_concat_idents: cx.has_feature("concat_idents"), | |
790 | allow_trace_macros: cx.has_feature("trace_macros"), | |
c34b1796 AL |
791 | allow_internal_unstable: cx.has_feature("allow_internal_unstable"), |
792 | allow_custom_derive: cx.has_feature("custom_derive"), | |
85aaf69f SL |
793 | simd_ffi: cx.has_feature("simd_ffi"), |
794 | unmarked_api: cx.has_feature("unmarked_api"), | |
c34b1796 | 795 | negate_unsigned: cx.has_feature("negate_unsigned"), |
85aaf69f | 796 | declared_stable_lang_features: accepted_features, |
62682a34 SL |
797 | declared_lib_features: unknown_features, |
798 | const_fn: cx.has_feature("const_fn"), | |
85aaf69f | 799 | } |
1a4d82fc JJ |
800 | } |
801 | ||
802 | pub fn check_crate_macros(cm: &CodeMap, span_handler: &SpanHandler, krate: &ast::Crate) | |
85aaf69f | 803 | -> Features { |
62682a34 | 804 | check_crate_inner(cm, span_handler, krate, &[] as &'static [_], |
1a4d82fc JJ |
805 | |ctx, krate| visit::walk_crate(&mut MacroVisitor { context: ctx }, krate)) |
806 | } | |
807 | ||
62682a34 SL |
808 | pub fn check_crate(cm: &CodeMap, span_handler: &SpanHandler, krate: &ast::Crate, |
809 | plugin_attributes: &[(String, AttributeType)], | |
810 | unstable: UnstableFeatures) -> Features | |
c34b1796 | 811 | { |
62682a34 SL |
812 | maybe_stage_features(span_handler, krate, unstable); |
813 | ||
814 | check_crate_inner(cm, span_handler, krate, plugin_attributes, | |
1a4d82fc JJ |
815 | |ctx, krate| visit::walk_crate(&mut PostExpansionVisitor { context: ctx }, |
816 | krate)) | |
817 | } | |
62682a34 SL |
818 | |
819 | #[derive(Clone, Copy)] | |
820 | pub enum UnstableFeatures { | |
821 | /// Hard errors for unstable features are active, as on | |
822 | /// beta/stable channels. | |
823 | Disallow, | |
824 | /// Allow features to me activated, as on nightly. | |
825 | Allow, | |
826 | /// Errors are bypassed for bootstrapping. This is required any time | |
827 | /// during the build that feature-related lints are set to warn or above | |
828 | /// because the build turns on warnings-as-errors and uses lots of unstable | |
829 | /// features. As a result, this this is always required for building Rust | |
830 | /// itself. | |
831 | Cheat | |
832 | } | |
833 | ||
834 | fn maybe_stage_features(span_handler: &SpanHandler, krate: &ast::Crate, | |
835 | unstable: UnstableFeatures) { | |
836 | let allow_features = match unstable { | |
837 | UnstableFeatures::Allow => true, | |
838 | UnstableFeatures::Disallow => false, | |
839 | UnstableFeatures::Cheat => true | |
840 | }; | |
841 | if !allow_features { | |
842 | for attr in &krate.attrs { | |
843 | if attr.check_name("feature") { | |
844 | let release_channel = option_env!("CFG_RELEASE_CHANNEL").unwrap_or("(unknown)"); | |
845 | let ref msg = format!("#[feature] may not be used on the {} release channel", | |
846 | release_channel); | |
847 | span_handler.span_err(attr.span, msg); | |
848 | } | |
849 | } | |
850 | } | |
851 | } |