]>
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::*; |
e9174d1e | 27 | use self::AttributeGate::*; |
1a4d82fc | 28 | |
c34b1796 | 29 | use abi::Abi; |
1a4d82fc JJ |
30 | use ast::NodeId; |
31 | use ast; | |
32 | use attr; | |
33 | use attr::AttrMetaMethods; | |
34 | use codemap::{CodeMap, Span}; | |
35 | use diagnostic::SpanHandler; | |
36 | use visit; | |
e9174d1e | 37 | use visit::{FnKind, Visitor}; |
b039eaaf | 38 | use parse::token::InternedString; |
1a4d82fc | 39 | |
1a4d82fc | 40 | use std::ascii::AsciiExt; |
e9174d1e | 41 | use std::cmp; |
1a4d82fc | 42 | |
85aaf69f SL |
43 | // If you change this list without updating src/doc/reference.md, @cmr will be sad |
44 | // Don't ever remove anything from this list; set them to 'Removed'. | |
45 | // The version numbers here correspond to the version in which the current status | |
46 | // was set. This is most important for knowing when a particular feature became | |
47 | // stable (active). | |
48 | // NB: The featureck.py script parses this information directly out of the source | |
49 | // so take care when modifying it. | |
e9174d1e SL |
50 | const KNOWN_FEATURES: &'static [(&'static str, &'static str, Option<u32>, Status)] = &[ |
51 | ("globs", "1.0.0", None, Accepted), | |
52 | ("macro_rules", "1.0.0", None, Accepted), | |
53 | ("struct_variant", "1.0.0", None, Accepted), | |
92a42be0 | 54 | ("asm", "1.0.0", Some(29722), Active), |
e9174d1e | 55 | ("managed_boxes", "1.0.0", None, Removed), |
92a42be0 SL |
56 | ("non_ascii_idents", "1.0.0", Some(28979), Active), |
57 | ("thread_local", "1.0.0", Some(29594), Active), | |
58 | ("link_args", "1.0.0", Some(29596), Active), | |
59 | ("plugin_registrar", "1.0.0", Some(29597), Active), | |
60 | ("log_syntax", "1.0.0", Some(29598), Active), | |
61 | ("trace_macros", "1.0.0", Some(29598), Active), | |
62 | ("concat_idents", "1.0.0", Some(29599), Active), | |
63 | ||
64 | // rustc internal, for now: | |
e9174d1e SL |
65 | ("intrinsics", "1.0.0", None, Active), |
66 | ("lang_items", "1.0.0", None, Active), | |
67 | ||
68 | ("simd", "1.0.0", Some(27731), Active), | |
69 | ("default_type_params", "1.0.0", None, Accepted), | |
92a42be0 SL |
70 | ("quote", "1.0.0", Some(29601), Active), |
71 | ("link_llvm_intrinsics", "1.0.0", Some(29602), Active), | |
72 | ("linkage", "1.0.0", Some(29603), Active), | |
e9174d1e SL |
73 | ("struct_inherit", "1.0.0", None, Removed), |
74 | ||
75 | ("quad_precision_float", "1.0.0", None, Removed), | |
76 | ||
92a42be0 | 77 | // rustc internal |
e9174d1e | 78 | ("rustc_diagnostic_macros", "1.0.0", None, Active), |
92a42be0 SL |
79 | ("unboxed_closures", "1.0.0", Some(29625), Active), |
80 | ("reflect", "1.0.0", Some(27749), Active), | |
e9174d1e | 81 | ("import_shadowing", "1.0.0", None, Removed), |
92a42be0 | 82 | ("advanced_slice_patterns", "1.0.0", Some(23121), Active), |
e9174d1e SL |
83 | ("tuple_indexing", "1.0.0", None, Accepted), |
84 | ("associated_types", "1.0.0", None, Accepted), | |
92a42be0 | 85 | ("visible_private_types", "1.0.0", Some(29627), Active), |
e9174d1e | 86 | ("slicing_syntax", "1.0.0", None, Accepted), |
b039eaaf SL |
87 | ("box_syntax", "1.0.0", Some(27779), Active), |
88 | ("placement_in_syntax", "1.0.0", Some(27779), Active), | |
92a42be0 SL |
89 | |
90 | // rustc internal. | |
e9174d1e | 91 | ("pushpop_unsafe", "1.2.0", None, Active), |
92a42be0 SL |
92 | |
93 | ("on_unimplemented", "1.0.0", Some(29628), Active), | |
94 | ("simd_ffi", "1.0.0", Some(27731), Active), | |
95 | ("allocator", "1.0.0", Some(27389), Active), | |
96 | ("needs_allocator", "1.4.0", Some(27389), Active), | |
97 | ("linked_from", "1.3.0", Some(29629), Active), | |
e9174d1e SL |
98 | |
99 | ("if_let", "1.0.0", None, Accepted), | |
100 | ("while_let", "1.0.0", None, Accepted), | |
101 | ||
92a42be0 SL |
102 | ("plugin", "1.0.0", Some(29597), Active), |
103 | ("start", "1.0.0", Some(29633), Active), | |
104 | ("main", "1.0.0", Some(29634), Active), | |
e9174d1e | 105 | |
92a42be0 | 106 | ("fundamental", "1.0.0", Some(29635), Active), |
c34b1796 | 107 | |
1a4d82fc JJ |
108 | // A temporary feature gate used to enable parser extensions needed |
109 | // to bootstrap fix for #5723. | |
e9174d1e | 110 | ("issue_5723_bootstrap", "1.0.0", None, Accepted), |
1a4d82fc JJ |
111 | |
112 | // A way to temporarily opt out of opt in copy. This will *never* be accepted. | |
e9174d1e | 113 | ("opt_out_copy", "1.0.0", None, Removed), |
1a4d82fc | 114 | |
1a4d82fc | 115 | // OIBIT specific features |
92a42be0 | 116 | ("optin_builtin_traits", "1.0.0", Some(13231), Active), |
1a4d82fc | 117 | |
85aaf69f | 118 | // macro reexport needs more discussion and stabilization |
92a42be0 | 119 | ("macro_reexport", "1.0.0", Some(29638), Active), |
1a4d82fc JJ |
120 | |
121 | // These are used to test this portion of the compiler, they don't actually | |
122 | // mean anything | |
e9174d1e SL |
123 | ("test_accepted_feature", "1.0.0", None, Accepted), |
124 | ("test_removed_feature", "1.0.0", None, Removed), | |
85aaf69f SL |
125 | |
126 | // Allows use of #[staged_api] | |
92a42be0 | 127 | // rustc internal |
e9174d1e | 128 | ("staged_api", "1.0.0", None, Active), |
85aaf69f SL |
129 | |
130 | // Allows using items which are missing stability attributes | |
92a42be0 | 131 | // rustc internal |
e9174d1e | 132 | ("unmarked_api", "1.0.0", None, Active), |
85aaf69f SL |
133 | |
134 | // Allows using #![no_std] | |
92a42be0 | 135 | ("no_std", "1.0.0", None, Accepted), |
e9174d1e SL |
136 | |
137 | // Allows using #![no_core] | |
92a42be0 | 138 | ("no_core", "1.3.0", Some(29639), Active), |
85aaf69f SL |
139 | |
140 | // Allows using `box` in patterns; RFC 469 | |
92a42be0 | 141 | ("box_patterns", "1.0.0", Some(29641), Active), |
85aaf69f SL |
142 | |
143 | // Allows using the unsafe_no_drop_flag attribute (unlikely to | |
144 | // switch to Accepted; see RFC 320) | |
e9174d1e | 145 | ("unsafe_no_drop_flag", "1.0.0", None, Active), |
85aaf69f | 146 | |
b039eaaf SL |
147 | // Allows using the unsafe_destructor_blind_to_params attribute; |
148 | // RFC 1238 | |
149 | ("dropck_parametricity", "1.3.0", Some(28498), Active), | |
150 | ||
85aaf69f | 151 | // Allows the use of custom attributes; RFC 572 |
92a42be0 | 152 | ("custom_attribute", "1.0.0", Some(29642), Active), |
85aaf69f | 153 | |
c34b1796 AL |
154 | // Allows the use of #[derive(Anything)] as sugar for |
155 | // #[derive_Anything]. | |
92a42be0 | 156 | ("custom_derive", "1.0.0", Some(29644), Active), |
c34b1796 | 157 | |
85aaf69f | 158 | // Allows the use of rustc_* attributes; RFC 572 |
92a42be0 | 159 | ("rustc_attrs", "1.0.0", Some(29642), Active), |
c34b1796 | 160 | |
c34b1796 AL |
161 | // Allows the use of #[allow_internal_unstable]. This is an |
162 | // attribute on macro_rules! and can't use the attribute handling | |
163 | // below (it has to be checked before expansion possibly makes | |
164 | // macros disappear). | |
92a42be0 SL |
165 | // |
166 | // rustc internal | |
e9174d1e | 167 | ("allow_internal_unstable", "1.0.0", None, Active), |
c34b1796 AL |
168 | |
169 | // #23121. Array patterns have some hazards yet. | |
92a42be0 | 170 | ("slice_patterns", "1.0.0", Some(23121), Active), |
c34b1796 AL |
171 | |
172 | // Allows use of unary negate on unsigned integers, e.g. -e for e: u8 | |
92a42be0 | 173 | ("negate_unsigned", "1.0.0", Some(29645), Active), |
d9579d0f AL |
174 | |
175 | // Allows the definition of associated constants in `trait` or `impl` | |
176 | // blocks. | |
92a42be0 | 177 | ("associated_consts", "1.0.0", Some(29646), Active), |
62682a34 SL |
178 | |
179 | // Allows the definition of `const fn` functions. | |
92a42be0 SL |
180 | ("const_fn", "1.2.0", Some(24111), Active), |
181 | ||
182 | // Allows indexing into constant arrays. | |
183 | ("const_indexing", "1.4.0", Some(29947), Active), | |
62682a34 | 184 | |
c1a9b12d | 185 | // Allows using #[prelude_import] on glob `use` items. |
92a42be0 SL |
186 | // |
187 | // rustc internal | |
e9174d1e | 188 | ("prelude_import", "1.2.0", None, Active), |
c1a9b12d SL |
189 | |
190 | // Allows the definition recursive static items. | |
92a42be0 | 191 | ("static_recursion", "1.3.0", Some(29719), Active), |
c1a9b12d SL |
192 | |
193 | // Allows default type parameters to influence type inference. | |
92a42be0 | 194 | ("default_type_parameter_fallback", "1.3.0", Some(27336), Active), |
c1a9b12d | 195 | |
62682a34 | 196 | // Allows associated type defaults |
92a42be0 | 197 | ("associated_type_defaults", "1.2.0", Some(29661), Active), |
e9174d1e | 198 | |
92a42be0 | 199 | // Allows macros to appear in the type position. |
e9174d1e SL |
200 | ("type_macros", "1.3.0", Some(27336), Active), |
201 | ||
202 | // allow `repr(simd)`, and importing the various simd intrinsics | |
203 | ("repr_simd", "1.4.0", Some(27731), Active), | |
204 | ||
205 | // Allows cfg(target_feature = "..."). | |
92a42be0 | 206 | ("cfg_target_feature", "1.4.0", Some(29717), Active), |
e9174d1e SL |
207 | |
208 | // allow `extern "platform-intrinsic" { ... }` | |
209 | ("platform_intrinsics", "1.4.0", Some(27731), Active), | |
210 | ||
211 | // allow `#[unwind]` | |
92a42be0 | 212 | // rust runtime internal |
e9174d1e | 213 | ("unwind_attributes", "1.4.0", None, Active), |
b039eaaf SL |
214 | |
215 | // allow empty structs and enum variants with braces | |
92a42be0 | 216 | ("braced_empty_structs", "1.5.0", Some(29720), Active), |
b039eaaf SL |
217 | |
218 | // allow overloading augmented assignment operations like `a += b` | |
92a42be0 | 219 | ("augmented_assignments", "1.5.0", Some(28235), Active), |
b039eaaf SL |
220 | |
221 | // allow `#[no_debug]` | |
92a42be0 | 222 | ("no_debug", "1.5.0", Some(29721), Active), |
b039eaaf SL |
223 | |
224 | // allow `#[omit_gdb_pretty_printer_section]` | |
92a42be0 | 225 | // rustc internal. |
b039eaaf SL |
226 | ("omit_gdb_pretty_printer_section", "1.5.0", None, Active), |
227 | ||
228 | // Allows cfg(target_vendor = "..."). | |
92a42be0 SL |
229 | ("cfg_target_vendor", "1.5.0", Some(29718), Active), |
230 | ||
231 | // Allow attributes on expressions and non-item statements | |
232 | ("stmt_expr_attributes", "1.6.0", Some(15701), Active), | |
1a4d82fc | 233 | ]; |
85aaf69f | 234 | // (changing above list without updating src/doc/reference.md makes @cmr sad) |
1a4d82fc JJ |
235 | |
236 | enum Status { | |
237 | /// Represents an active feature that is currently being implemented or | |
238 | /// currently being considered for addition/removal. | |
239 | Active, | |
240 | ||
1a4d82fc JJ |
241 | /// Represents a feature which has since been removed (it was once Active) |
242 | Removed, | |
243 | ||
244 | /// This language feature has since been Accepted (it was once Active) | |
245 | Accepted, | |
246 | } | |
247 | ||
85aaf69f | 248 | // Attributes that have a special meaning to rustc or rustdoc |
e9174d1e | 249 | pub const KNOWN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeGate)] = &[ |
85aaf69f SL |
250 | // Normal attributes |
251 | ||
e9174d1e SL |
252 | ("warn", Normal, Ungated), |
253 | ("allow", Normal, Ungated), | |
254 | ("forbid", Normal, Ungated), | |
255 | ("deny", Normal, Ungated), | |
256 | ||
257 | ("macro_reexport", Normal, Ungated), | |
258 | ("macro_use", Normal, Ungated), | |
259 | ("macro_export", Normal, Ungated), | |
260 | ("plugin_registrar", Normal, Ungated), | |
261 | ||
262 | ("cfg", Normal, Ungated), | |
263 | ("cfg_attr", Normal, Ungated), | |
264 | ("main", Normal, Ungated), | |
265 | ("start", Normal, Ungated), | |
266 | ("test", Normal, Ungated), | |
267 | ("bench", Normal, Ungated), | |
268 | ("simd", Normal, Ungated), | |
269 | ("repr", Normal, Ungated), | |
270 | ("path", Normal, Ungated), | |
271 | ("abi", Normal, Ungated), | |
272 | ("automatically_derived", Normal, Ungated), | |
273 | ("no_mangle", Normal, Ungated), | |
274 | ("no_link", Normal, Ungated), | |
275 | ("derive", Normal, Ungated), | |
276 | ("should_panic", Normal, Ungated), | |
277 | ("ignore", Normal, Ungated), | |
278 | ("no_implicit_prelude", Normal, Ungated), | |
279 | ("reexport_test_harness_main", Normal, Ungated), | |
280 | ("link_args", Normal, Ungated), | |
281 | ("macro_escape", Normal, Ungated), | |
282 | ||
283 | // Not used any more, but we can't feature gate it | |
284 | ("no_stack_check", Normal, Ungated), | |
285 | ||
e9174d1e SL |
286 | ("plugin", CrateLevel, Gated("plugin", |
287 | "compiler plugins are experimental \ | |
288 | and possibly buggy")), | |
92a42be0 | 289 | ("no_std", CrateLevel, Ungated), |
e9174d1e SL |
290 | ("no_core", CrateLevel, Gated("no_core", |
291 | "no_core is experimental")), | |
292 | ("lang", Normal, Gated("lang_items", | |
293 | "language items are subject to change")), | |
294 | ("linkage", Whitelisted, Gated("linkage", | |
295 | "the `linkage` attribute is experimental \ | |
296 | and not portable across platforms")), | |
297 | ("thread_local", Whitelisted, Gated("thread_local", | |
298 | "`#[thread_local]` is an experimental feature, and does \ | |
299 | not currently handle destructors. There is no \ | |
300 | corresponding `#[task_local]` mapping to the task \ | |
301 | model")), | |
302 | ||
303 | ("rustc_on_unimplemented", Normal, Gated("on_unimplemented", | |
304 | "the `#[rustc_on_unimplemented]` attribute \ | |
305 | is an experimental feature")), | |
306 | ("allocator", Whitelisted, Gated("allocator", | |
307 | "the `#[allocator]` attribute is an experimental feature")), | |
308 | ("needs_allocator", Normal, Gated("needs_allocator", | |
309 | "the `#[needs_allocator]` \ | |
310 | attribute is an experimental \ | |
311 | feature")), | |
312 | ("rustc_variance", Normal, Gated("rustc_attrs", | |
313 | "the `#[rustc_variance]` attribute \ | |
314 | is just used for rustc unit tests \ | |
315 | and will never be stable")), | |
316 | ("rustc_error", Whitelisted, Gated("rustc_attrs", | |
317 | "the `#[rustc_error]` attribute \ | |
318 | is just used for rustc unit tests \ | |
319 | and will never be stable")), | |
320 | ("rustc_move_fragments", Normal, Gated("rustc_attrs", | |
321 | "the `#[rustc_move_fragments]` attribute \ | |
322 | is just used for rustc unit tests \ | |
323 | and will never be stable")), | |
324 | ("rustc_mir", Normal, Gated("rustc_attrs", | |
325 | "the `#[rustc_mir]` attribute \ | |
326 | is just used for rustc unit tests \ | |
327 | and will never be stable")), | |
328 | ||
329 | ("allow_internal_unstable", Normal, Gated("allow_internal_unstable", | |
330 | EXPLAIN_ALLOW_INTERNAL_UNSTABLE)), | |
331 | ||
332 | ("fundamental", Whitelisted, Gated("fundamental", | |
333 | "the `#[fundamental]` attribute \ | |
334 | is an experimental feature")), | |
335 | ||
336 | ("linked_from", Normal, Gated("linked_from", | |
337 | "the `#[linked_from]` attribute \ | |
338 | is an experimental feature")), | |
c34b1796 | 339 | |
85aaf69f | 340 | // FIXME: #14408 whitelist docs since rustdoc looks at them |
e9174d1e | 341 | ("doc", Whitelisted, Ungated), |
85aaf69f SL |
342 | |
343 | // FIXME: #14406 these are processed in trans, which happens after the | |
344 | // lint pass | |
e9174d1e SL |
345 | ("cold", Whitelisted, Ungated), |
346 | ("export_name", Whitelisted, Ungated), | |
347 | ("inline", Whitelisted, Ungated), | |
348 | ("link", Whitelisted, Ungated), | |
349 | ("link_name", Whitelisted, Ungated), | |
350 | ("link_section", Whitelisted, Ungated), | |
351 | ("no_builtins", Whitelisted, Ungated), | |
352 | ("no_mangle", Whitelisted, Ungated), | |
b039eaaf SL |
353 | ("no_debug", Whitelisted, Gated("no_debug", |
354 | "the `#[no_debug]` attribute \ | |
355 | is an experimental feature")), | |
356 | ("omit_gdb_pretty_printer_section", Whitelisted, Gated("omit_gdb_pretty_printer_section", | |
357 | "the `#[omit_gdb_pretty_printer_section]` \ | |
358 | attribute is just used for the Rust test \ | |
359 | suite")), | |
e9174d1e SL |
360 | ("unsafe_no_drop_flag", Whitelisted, Gated("unsafe_no_drop_flag", |
361 | "unsafe_no_drop_flag has unstable semantics \ | |
362 | and may be removed in the future")), | |
b039eaaf SL |
363 | ("unsafe_destructor_blind_to_params", |
364 | Normal, | |
365 | Gated("dropck_parametricity", | |
366 | "unsafe_destructor_blind_to_params has unstable semantics \ | |
367 | and may be removed in the future")), | |
e9174d1e | 368 | ("unwind", Whitelisted, Gated("unwind_attributes", "#[unwind] is experimental")), |
85aaf69f SL |
369 | |
370 | // used in resolve | |
e9174d1e SL |
371 | ("prelude_import", Whitelisted, Gated("prelude_import", |
372 | "`#[prelude_import]` is for use by rustc only")), | |
85aaf69f SL |
373 | |
374 | // FIXME: #14407 these are only looked at on-demand so we can't | |
375 | // guarantee they'll have already been checked | |
92a42be0 | 376 | ("rustc_deprecated", Whitelisted, Ungated), |
e9174d1e SL |
377 | ("must_use", Whitelisted, Ungated), |
378 | ("stable", Whitelisted, Ungated), | |
379 | ("unstable", Whitelisted, Ungated), | |
85aaf69f | 380 | |
e9174d1e SL |
381 | ("rustc_paren_sugar", Normal, Gated("unboxed_closures", |
382 | "unboxed_closures are still evolving")), | |
383 | ("rustc_reflect_like", Whitelisted, Gated("reflect", | |
384 | "defining reflective traits is still evolving")), | |
85aaf69f SL |
385 | |
386 | // Crate level attributes | |
e9174d1e SL |
387 | ("crate_name", CrateLevel, Ungated), |
388 | ("crate_type", CrateLevel, Ungated), | |
389 | ("crate_id", CrateLevel, Ungated), | |
390 | ("feature", CrateLevel, Ungated), | |
391 | ("no_start", CrateLevel, Ungated), | |
392 | ("no_main", CrateLevel, Ungated), | |
393 | ("no_builtins", CrateLevel, Ungated), | |
394 | ("recursion_limit", CrateLevel, Ungated), | |
85aaf69f SL |
395 | ]; |
396 | ||
e9174d1e SL |
397 | macro_rules! cfg_fn { |
398 | (|$x: ident| $e: expr) => {{ | |
399 | fn f($x: &Features) -> bool { | |
400 | $e | |
401 | } | |
402 | f as fn(&Features) -> bool | |
403 | }} | |
404 | } | |
405 | // cfg(...)'s that are feature gated | |
406 | const GATED_CFGS: &'static [(&'static str, &'static str, fn(&Features) -> bool)] = &[ | |
407 | // (name in cfg, feature, function to check if the feature is enabled) | |
408 | ("target_feature", "cfg_target_feature", cfg_fn!(|x| x.cfg_target_feature)), | |
b039eaaf | 409 | ("target_vendor", "cfg_target_vendor", cfg_fn!(|x| x.cfg_target_vendor)), |
e9174d1e SL |
410 | ]; |
411 | ||
92a42be0 SL |
412 | #[derive(Debug, Eq, PartialEq)] |
413 | pub enum GatedCfgAttr { | |
414 | GatedCfg(GatedCfg), | |
415 | GatedAttr(Span), | |
416 | } | |
417 | ||
e9174d1e SL |
418 | #[derive(Debug, Eq, PartialEq)] |
419 | pub struct GatedCfg { | |
420 | span: Span, | |
421 | index: usize, | |
422 | } | |
423 | ||
92a42be0 SL |
424 | impl Ord for GatedCfgAttr { |
425 | fn cmp(&self, other: &GatedCfgAttr) -> cmp::Ordering { | |
426 | let to_tup = |s: &GatedCfgAttr| match *s { | |
427 | GatedCfgAttr::GatedCfg(ref gated_cfg) => { | |
428 | (gated_cfg.span.lo.0, gated_cfg.span.hi.0, gated_cfg.index) | |
429 | } | |
430 | GatedCfgAttr::GatedAttr(ref span) => { | |
431 | (span.lo.0, span.hi.0, GATED_CFGS.len()) | |
432 | } | |
433 | }; | |
434 | to_tup(self).cmp(&to_tup(other)) | |
e9174d1e SL |
435 | } |
436 | } | |
437 | ||
92a42be0 SL |
438 | impl PartialOrd for GatedCfgAttr { |
439 | fn partial_cmp(&self, other: &GatedCfgAttr) -> Option<cmp::Ordering> { | |
e9174d1e SL |
440 | Some(self.cmp(other)) |
441 | } | |
442 | } | |
443 | ||
92a42be0 SL |
444 | impl GatedCfgAttr { |
445 | pub fn check_and_emit(&self, diagnostic: &SpanHandler, features: &Features) { | |
446 | match *self { | |
447 | GatedCfgAttr::GatedCfg(ref cfg) => { | |
448 | cfg.check_and_emit(diagnostic, features); | |
449 | } | |
450 | GatedCfgAttr::GatedAttr(span) => { | |
451 | if !features.stmt_expr_attributes { | |
452 | emit_feature_err(diagnostic, | |
453 | "stmt_expr_attributes", | |
454 | span, | |
455 | GateIssue::Language, | |
456 | EXPLAIN_STMT_ATTR_SYNTAX); | |
457 | } | |
458 | } | |
459 | } | |
460 | } | |
461 | } | |
462 | ||
e9174d1e SL |
463 | impl GatedCfg { |
464 | pub fn gate(cfg: &ast::MetaItem) -> Option<GatedCfg> { | |
465 | let name = cfg.name(); | |
466 | GATED_CFGS.iter() | |
467 | .position(|info| info.0 == name) | |
468 | .map(|idx| { | |
469 | GatedCfg { | |
470 | span: cfg.span, | |
471 | index: idx | |
472 | } | |
473 | }) | |
474 | } | |
92a42be0 | 475 | fn check_and_emit(&self, diagnostic: &SpanHandler, features: &Features) { |
e9174d1e SL |
476 | let (cfg, feature, has_feature) = GATED_CFGS[self.index]; |
477 | if !has_feature(features) { | |
478 | let explain = format!("`cfg({})` is experimental and subject to change", cfg); | |
479 | emit_feature_err(diagnostic, feature, self.span, GateIssue::Language, &explain); | |
480 | } | |
481 | } | |
482 | } | |
483 | ||
484 | ||
c34b1796 | 485 | #[derive(PartialEq, Copy, Clone, Debug)] |
85aaf69f SL |
486 | pub enum AttributeType { |
487 | /// Normal, builtin attribute that is consumed | |
488 | /// by the compiler before the unused_attribute check | |
489 | Normal, | |
490 | ||
491 | /// Builtin attribute that may not be consumed by the compiler | |
492 | /// before the unused_attribute check. These attributes | |
493 | /// will be ignored by the unused_attribute lint | |
494 | Whitelisted, | |
495 | ||
e9174d1e SL |
496 | /// Builtin attribute that is only allowed at the crate level |
497 | CrateLevel, | |
498 | } | |
499 | ||
500 | #[derive(PartialEq, Copy, Clone, Debug)] | |
501 | pub enum AttributeGate { | |
85aaf69f | 502 | /// Is gated by a given feature gate and reason |
85aaf69f SL |
503 | Gated(&'static str, &'static str), |
504 | ||
e9174d1e SL |
505 | /// Ungated attribute, can be used on all release channels |
506 | Ungated, | |
85aaf69f SL |
507 | } |
508 | ||
1a4d82fc | 509 | /// A set of features to be used by later passes. |
1a4d82fc JJ |
510 | pub struct Features { |
511 | pub unboxed_closures: bool, | |
512 | pub rustc_diagnostic_macros: bool, | |
1a4d82fc | 513 | pub visible_private_types: bool, |
85aaf69f SL |
514 | pub allow_quote: bool, |
515 | pub allow_asm: bool, | |
516 | pub allow_log_syntax: bool, | |
517 | pub allow_concat_idents: bool, | |
518 | pub allow_trace_macros: bool, | |
c34b1796 AL |
519 | pub allow_internal_unstable: bool, |
520 | pub allow_custom_derive: bool, | |
c1a9b12d SL |
521 | pub allow_placement_in: bool, |
522 | pub allow_box: bool, | |
523 | pub allow_pushpop_unsafe: bool, | |
85aaf69f SL |
524 | pub simd_ffi: bool, |
525 | pub unmarked_api: bool, | |
c34b1796 | 526 | pub negate_unsigned: bool, |
85aaf69f SL |
527 | /// spans of #![feature] attrs for stable language features. for error reporting |
528 | pub declared_stable_lang_features: Vec<Span>, | |
529 | /// #![feature] attrs for non-language (library) features | |
62682a34 SL |
530 | pub declared_lib_features: Vec<(InternedString, Span)>, |
531 | pub const_fn: bool, | |
92a42be0 | 532 | pub const_indexing: bool, |
c1a9b12d SL |
533 | pub static_recursion: bool, |
534 | pub default_type_parameter_fallback: bool, | |
e9174d1e SL |
535 | pub type_macros: bool, |
536 | pub cfg_target_feature: bool, | |
b039eaaf SL |
537 | pub cfg_target_vendor: bool, |
538 | pub augmented_assignments: bool, | |
539 | pub braced_empty_structs: bool, | |
92a42be0 SL |
540 | pub staged_api: bool, |
541 | pub stmt_expr_attributes: bool, | |
1a4d82fc JJ |
542 | } |
543 | ||
544 | impl Features { | |
545 | pub fn new() -> Features { | |
546 | Features { | |
547 | unboxed_closures: false, | |
548 | rustc_diagnostic_macros: false, | |
1a4d82fc | 549 | visible_private_types: false, |
85aaf69f SL |
550 | allow_quote: false, |
551 | allow_asm: false, | |
552 | allow_log_syntax: false, | |
553 | allow_concat_idents: false, | |
554 | allow_trace_macros: false, | |
c34b1796 AL |
555 | allow_internal_unstable: false, |
556 | allow_custom_derive: false, | |
c1a9b12d SL |
557 | allow_placement_in: false, |
558 | allow_box: false, | |
559 | allow_pushpop_unsafe: false, | |
85aaf69f SL |
560 | simd_ffi: false, |
561 | unmarked_api: false, | |
c34b1796 | 562 | negate_unsigned: false, |
85aaf69f | 563 | declared_stable_lang_features: Vec::new(), |
62682a34 SL |
564 | declared_lib_features: Vec::new(), |
565 | const_fn: false, | |
92a42be0 | 566 | const_indexing: false, |
c1a9b12d SL |
567 | static_recursion: false, |
568 | default_type_parameter_fallback: false, | |
e9174d1e SL |
569 | type_macros: false, |
570 | cfg_target_feature: false, | |
b039eaaf SL |
571 | cfg_target_vendor: false, |
572 | augmented_assignments: false, | |
573 | braced_empty_structs: false, | |
92a42be0 SL |
574 | staged_api: false, |
575 | stmt_expr_attributes: false, | |
1a4d82fc JJ |
576 | } |
577 | } | |
578 | } | |
579 | ||
c1a9b12d SL |
580 | const EXPLAIN_BOX_SYNTAX: &'static str = |
581 | "box expression syntax is experimental; you can call `Box::new` instead."; | |
582 | ||
583 | const EXPLAIN_PLACEMENT_IN: &'static str = | |
584 | "placement-in expression syntax is experimental and subject to change."; | |
585 | ||
586 | const EXPLAIN_PUSHPOP_UNSAFE: &'static str = | |
587 | "push/pop_unsafe macros are experimental and subject to change."; | |
588 | ||
92a42be0 SL |
589 | const EXPLAIN_STMT_ATTR_SYNTAX: &'static str = |
590 | "attributes on non-item statements and expressions are experimental."; | |
591 | ||
c1a9b12d SL |
592 | pub fn check_for_box_syntax(f: Option<&Features>, diag: &SpanHandler, span: Span) { |
593 | if let Some(&Features { allow_box: true, .. }) = f { | |
594 | return; | |
595 | } | |
e9174d1e | 596 | emit_feature_err(diag, "box_syntax", span, GateIssue::Language, EXPLAIN_BOX_SYNTAX); |
c1a9b12d SL |
597 | } |
598 | ||
599 | pub fn check_for_placement_in(f: Option<&Features>, diag: &SpanHandler, span: Span) { | |
600 | if let Some(&Features { allow_placement_in: true, .. }) = f { | |
601 | return; | |
602 | } | |
e9174d1e | 603 | emit_feature_err(diag, "placement_in_syntax", span, GateIssue::Language, EXPLAIN_PLACEMENT_IN); |
c1a9b12d SL |
604 | } |
605 | ||
606 | pub fn check_for_pushpop_syntax(f: Option<&Features>, diag: &SpanHandler, span: Span) { | |
607 | if let Some(&Features { allow_pushpop_unsafe: true, .. }) = f { | |
608 | return; | |
609 | } | |
e9174d1e | 610 | emit_feature_err(diag, "pushpop_unsafe", span, GateIssue::Language, EXPLAIN_PUSHPOP_UNSAFE); |
c1a9b12d SL |
611 | } |
612 | ||
1a4d82fc JJ |
613 | struct Context<'a> { |
614 | features: Vec<&'static str>, | |
615 | span_handler: &'a SpanHandler, | |
616 | cm: &'a CodeMap, | |
62682a34 | 617 | plugin_attributes: &'a [(String, AttributeType)], |
1a4d82fc JJ |
618 | } |
619 | ||
620 | impl<'a> Context<'a> { | |
c1a9b12d SL |
621 | fn enable_feature(&mut self, feature: &'static str) { |
622 | debug!("enabling feature: {}", feature); | |
623 | self.features.push(feature); | |
624 | } | |
625 | ||
1a4d82fc | 626 | fn gate_feature(&self, feature: &str, span: Span, explain: &str) { |
c34b1796 AL |
627 | let has_feature = self.has_feature(feature); |
628 | debug!("gate_feature(feature = {:?}, span = {:?}); has? {}", feature, span, has_feature); | |
629 | if !has_feature { | |
e9174d1e | 630 | emit_feature_err(self.span_handler, feature, span, GateIssue::Language, explain); |
1a4d82fc JJ |
631 | } |
632 | } | |
1a4d82fc JJ |
633 | fn has_feature(&self, feature: &str) -> bool { |
634 | self.features.iter().any(|&n| n == feature) | |
635 | } | |
c34b1796 | 636 | |
62682a34 | 637 | fn check_attribute(&self, attr: &ast::Attribute, is_macro: bool) { |
c34b1796 AL |
638 | debug!("check_attribute(attr = {:?})", attr); |
639 | let name = &*attr.name(); | |
e9174d1e | 640 | for &(n, ty, gateage) in KNOWN_ATTRIBUTES { |
c34b1796 | 641 | if n == name { |
e9174d1e | 642 | if let Gated(gate, desc) = gateage { |
c34b1796 AL |
643 | self.gate_feature(gate, attr.span, desc); |
644 | } | |
e9174d1e | 645 | debug!("check_attribute: {:?} is known, {:?}, {:?}", name, ty, gateage); |
c34b1796 AL |
646 | return; |
647 | } | |
648 | } | |
62682a34 SL |
649 | for &(ref n, ref ty) in self.plugin_attributes { |
650 | if &*n == name { | |
651 | // Plugins can't gate attributes, so we don't check for it | |
652 | // unlike the code above; we only use this loop to | |
653 | // short-circuit to avoid the checks below | |
654 | debug!("check_attribute: {:?} is registered by a plugin, {:?}", name, ty); | |
655 | return; | |
656 | } | |
657 | } | |
c34b1796 AL |
658 | if name.starts_with("rustc_") { |
659 | self.gate_feature("rustc_attrs", attr.span, | |
660 | "unless otherwise specified, attributes \ | |
661 | with the prefix `rustc_` \ | |
662 | are reserved for internal compiler diagnostics"); | |
663 | } else if name.starts_with("derive_") { | |
664 | self.gate_feature("custom_derive", attr.span, | |
d9579d0f | 665 | "attributes of the form `#[derive_*]` are reserved \ |
c34b1796 AL |
666 | for the compiler"); |
667 | } else { | |
62682a34 SL |
668 | // Only run the custom attribute lint during regular |
669 | // feature gate checking. Macro gating runs | |
670 | // before the plugin attributes are registered | |
671 | // so we skip this then | |
672 | if !is_macro { | |
673 | self.gate_feature("custom_attribute", attr.span, | |
674 | &format!("The attribute `{}` is currently \ | |
675 | unknown to the compiler and \ | |
676 | may have meaning \ | |
677 | added to it in the future", | |
678 | name)); | |
679 | } | |
c34b1796 AL |
680 | } |
681 | } | |
1a4d82fc JJ |
682 | } |
683 | ||
e9174d1e SL |
684 | fn find_lang_feature_issue(feature: &str) -> Option<u32> { |
685 | let info = KNOWN_FEATURES.iter() | |
686 | .find(|t| t.0 == feature) | |
687 | .unwrap(); | |
688 | let issue = info.2; | |
689 | if let Active = info.3 { | |
690 | // FIXME (#28244): enforce that active features have issue numbers | |
691 | // assert!(issue.is_some()) | |
692 | } | |
693 | issue | |
694 | } | |
695 | ||
696 | pub enum GateIssue { | |
697 | Language, | |
698 | Library(Option<u32>) | |
699 | } | |
700 | ||
701 | pub fn emit_feature_err(diag: &SpanHandler, feature: &str, span: Span, issue: GateIssue, | |
702 | explain: &str) { | |
703 | let issue = match issue { | |
704 | GateIssue::Language => find_lang_feature_issue(feature), | |
705 | GateIssue::Library(lib) => lib, | |
706 | }; | |
707 | ||
708 | if let Some(n) = issue { | |
709 | diag.span_err(span, &format!("{} (see issue #{})", explain, n)); | |
710 | } else { | |
711 | diag.span_err(span, explain); | |
712 | } | |
9346a6ac AL |
713 | |
714 | // #23973: do not suggest `#![feature(...)]` if we are in beta/stable | |
715 | if option_env!("CFG_DISABLE_UNSTABLE_FEATURES").is_some() { return; } | |
c34b1796 | 716 | diag.fileline_help(span, &format!("add #![feature({})] to the \ |
85aaf69f SL |
717 | crate attributes to enable", |
718 | feature)); | |
719 | } | |
720 | ||
85aaf69f SL |
721 | pub const EXPLAIN_ASM: &'static str = |
722 | "inline assembly is not stable enough for use and is subject to change"; | |
723 | ||
724 | pub const EXPLAIN_LOG_SYNTAX: &'static str = | |
725 | "`log_syntax!` is not stable enough for use and is subject to change"; | |
726 | ||
727 | pub const EXPLAIN_CONCAT_IDENTS: &'static str = | |
728 | "`concat_idents` is not stable enough for use and is subject to change"; | |
729 | ||
730 | pub const EXPLAIN_TRACE_MACROS: &'static str = | |
731 | "`trace_macros` is not stable enough for use and is subject to change"; | |
c34b1796 AL |
732 | pub const EXPLAIN_ALLOW_INTERNAL_UNSTABLE: &'static str = |
733 | "allow_internal_unstable side-steps feature gating and stability checks"; | |
734 | ||
735 | pub const EXPLAIN_CUSTOM_DERIVE: &'static str = | |
736 | "`#[derive]` for custom traits is not stable enough for use and is subject to change"; | |
85aaf69f | 737 | |
1a4d82fc JJ |
738 | struct MacroVisitor<'a> { |
739 | context: &'a Context<'a> | |
740 | } | |
741 | ||
742 | impl<'a, 'v> Visitor<'v> for MacroVisitor<'a> { | |
743 | fn visit_mac(&mut self, mac: &ast::Mac) { | |
b039eaaf SL |
744 | let path = &mac.node.path; |
745 | let name = path.segments.last().unwrap().identifier.name.as_str(); | |
1a4d82fc | 746 | |
85aaf69f SL |
747 | // Issue 22234: If you add a new case here, make sure to also |
748 | // add code to catch the macro during or after expansion. | |
749 | // | |
750 | // We still keep this MacroVisitor (rather than *solely* | |
751 | // relying on catching cases during or after expansion) to | |
752 | // catch uses of these macros within conditionally-compiled | |
753 | // code, e.g. `#[cfg]`-guarded functions. | |
754 | ||
b039eaaf | 755 | if name == "asm" { |
85aaf69f | 756 | self.context.gate_feature("asm", path.span, EXPLAIN_ASM); |
1a4d82fc JJ |
757 | } |
758 | ||
b039eaaf | 759 | else if name == "log_syntax" { |
85aaf69f | 760 | self.context.gate_feature("log_syntax", path.span, EXPLAIN_LOG_SYNTAX); |
1a4d82fc JJ |
761 | } |
762 | ||
b039eaaf | 763 | else if name == "trace_macros" { |
85aaf69f | 764 | self.context.gate_feature("trace_macros", path.span, EXPLAIN_TRACE_MACROS); |
1a4d82fc JJ |
765 | } |
766 | ||
b039eaaf | 767 | else if name == "concat_idents" { |
85aaf69f | 768 | self.context.gate_feature("concat_idents", path.span, EXPLAIN_CONCAT_IDENTS); |
1a4d82fc JJ |
769 | } |
770 | } | |
c34b1796 AL |
771 | |
772 | fn visit_attribute(&mut self, attr: &'v ast::Attribute) { | |
62682a34 | 773 | self.context.check_attribute(attr, true); |
c34b1796 | 774 | } |
c1a9b12d SL |
775 | |
776 | fn visit_expr(&mut self, e: &ast::Expr) { | |
777 | // Issue 22181: overloaded-`box` and placement-`in` are | |
778 | // implemented via a desugaring expansion, so their feature | |
779 | // gates go into MacroVisitor since that works pre-expansion. | |
780 | // | |
781 | // Issue 22234: we also check during expansion as well. | |
782 | // But we keep these checks as a pre-expansion check to catch | |
783 | // uses in e.g. conditionalized code. | |
784 | ||
b039eaaf | 785 | if let ast::ExprBox(_) = e.node { |
c1a9b12d SL |
786 | self.context.gate_feature("box_syntax", e.span, EXPLAIN_BOX_SYNTAX); |
787 | } | |
788 | ||
b039eaaf | 789 | if let ast::ExprInPlace(..) = e.node { |
c1a9b12d SL |
790 | self.context.gate_feature("placement_in_syntax", e.span, EXPLAIN_PLACEMENT_IN); |
791 | } | |
792 | ||
793 | visit::walk_expr(self, e); | |
794 | } | |
1a4d82fc JJ |
795 | } |
796 | ||
797 | struct PostExpansionVisitor<'a> { | |
b039eaaf | 798 | context: &'a Context<'a>, |
1a4d82fc JJ |
799 | } |
800 | ||
801 | impl<'a> PostExpansionVisitor<'a> { | |
802 | fn gate_feature(&self, feature: &str, span: Span, explain: &str) { | |
c34b1796 | 803 | if !self.context.cm.span_allows_unstable(span) { |
1a4d82fc JJ |
804 | self.context.gate_feature(feature, span, explain) |
805 | } | |
806 | } | |
807 | } | |
808 | ||
809 | impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> { | |
c34b1796 AL |
810 | fn visit_attribute(&mut self, attr: &ast::Attribute) { |
811 | if !self.context.cm.span_allows_unstable(attr.span) { | |
62682a34 | 812 | self.context.check_attribute(attr, false); |
c34b1796 AL |
813 | } |
814 | } | |
815 | ||
1a4d82fc | 816 | fn visit_name(&mut self, sp: Span, name: ast::Name) { |
c1a9b12d | 817 | if !name.as_str().is_ascii() { |
1a4d82fc JJ |
818 | self.gate_feature("non_ascii_idents", sp, |
819 | "non-ascii idents are not fully supported."); | |
820 | } | |
821 | } | |
822 | ||
85aaf69f | 823 | fn visit_item(&mut self, i: &ast::Item) { |
1a4d82fc | 824 | match i.node { |
85aaf69f SL |
825 | ast::ItemExternCrate(_) => { |
826 | if attr::contains_name(&i.attrs[..], "macro_reexport") { | |
827 | self.gate_feature("macro_reexport", i.span, | |
828 | "macros reexports are experimental \ | |
829 | and possibly buggy"); | |
1a4d82fc JJ |
830 | } |
831 | } | |
1a4d82fc | 832 | |
1a4d82fc | 833 | ast::ItemForeignMod(ref foreign_module) => { |
85aaf69f | 834 | if attr::contains_name(&i.attrs[..], "link_args") { |
1a4d82fc JJ |
835 | self.gate_feature("link_args", i.span, |
836 | "the `link_args` attribute is not portable \ | |
837 | across platforms, it is recommended to \ | |
838 | use `#[link(name = \"foo\")]` instead") | |
839 | } | |
e9174d1e SL |
840 | let maybe_feature = match foreign_module.abi { |
841 | Abi::RustIntrinsic => Some(("intrinsics", "intrinsics are subject to change")), | |
842 | Abi::PlatformIntrinsic => { | |
843 | Some(("platform_intrinsics", | |
844 | "platform intrinsics are experimental and possibly buggy")) | |
845 | } | |
846 | _ => None | |
847 | }; | |
848 | if let Some((feature, msg)) = maybe_feature { | |
849 | self.gate_feature(feature, i.span, msg) | |
1a4d82fc JJ |
850 | } |
851 | } | |
852 | ||
853 | ast::ItemFn(..) => { | |
85aaf69f | 854 | if attr::contains_name(&i.attrs[..], "plugin_registrar") { |
1a4d82fc JJ |
855 | self.gate_feature("plugin_registrar", i.span, |
856 | "compiler plugins are experimental and possibly buggy"); | |
857 | } | |
85aaf69f SL |
858 | if attr::contains_name(&i.attrs[..], "start") { |
859 | self.gate_feature("start", i.span, | |
860 | "a #[start] function is an experimental \ | |
861 | feature whose signature may change \ | |
862 | over time"); | |
863 | } | |
864 | if attr::contains_name(&i.attrs[..], "main") { | |
865 | self.gate_feature("main", i.span, | |
866 | "declaration of a nonstandard #[main] \ | |
867 | function may change over time, for now \ | |
868 | a top-level `fn main()` is required"); | |
869 | } | |
1a4d82fc JJ |
870 | } |
871 | ||
872 | ast::ItemStruct(..) => { | |
85aaf69f | 873 | if attr::contains_name(&i.attrs[..], "simd") { |
1a4d82fc JJ |
874 | self.gate_feature("simd", i.span, |
875 | "SIMD types are experimental and possibly buggy"); | |
e9174d1e SL |
876 | self.context.span_handler.span_warn(i.span, |
877 | "the `#[simd]` attribute is deprecated, \ | |
878 | use `#[repr(simd)]` instead"); | |
879 | } | |
880 | for attr in &i.attrs { | |
881 | if attr.name() == "repr" { | |
882 | for item in attr.meta_item_list().unwrap_or(&[]) { | |
883 | if item.name() == "simd" { | |
884 | self.gate_feature("repr_simd", i.span, | |
885 | "SIMD types are experimental and possibly buggy"); | |
886 | ||
887 | } | |
888 | } | |
889 | } | |
1a4d82fc JJ |
890 | } |
891 | } | |
892 | ||
c34b1796 AL |
893 | ast::ItemDefaultImpl(..) => { |
894 | self.gate_feature("optin_builtin_traits", | |
895 | i.span, | |
896 | "default trait implementations are experimental \ | |
897 | and possibly buggy"); | |
898 | } | |
899 | ||
1a4d82fc JJ |
900 | ast::ItemImpl(_, polarity, _, _, _, _) => { |
901 | match polarity { | |
902 | ast::ImplPolarity::Negative => { | |
903 | self.gate_feature("optin_builtin_traits", | |
904 | i.span, | |
905 | "negative trait bounds are not yet fully implemented; \ | |
906 | use marker types for now"); | |
907 | }, | |
908 | _ => {} | |
909 | } | |
1a4d82fc JJ |
910 | } |
911 | ||
912 | _ => {} | |
913 | } | |
914 | ||
915 | visit::walk_item(self, i); | |
916 | } | |
917 | ||
b039eaaf SL |
918 | fn visit_variant_data(&mut self, s: &'v ast::VariantData, _: ast::Ident, |
919 | _: &'v ast::Generics, _: ast::NodeId, span: Span) { | |
920 | if s.fields().is_empty() { | |
921 | if s.is_struct() { | |
922 | self.gate_feature("braced_empty_structs", span, | |
923 | "empty structs and enum variants with braces are unstable"); | |
924 | } else if s.is_tuple() { | |
925 | self.context.span_handler.span_err(span, "empty tuple structs and enum variants \ | |
926 | are not allowed, use unit structs and \ | |
927 | enum variants instead"); | |
92a42be0 SL |
928 | self.context.span_handler.span_help(span, "remove trailing `()` to make a unit \ |
929 | struct or unit enum variant"); | |
b039eaaf SL |
930 | } |
931 | } | |
932 | visit::walk_struct_def(self, s) | |
933 | } | |
934 | ||
1a4d82fc | 935 | fn visit_foreign_item(&mut self, i: &ast::ForeignItem) { |
85aaf69f | 936 | let links_to_llvm = match attr::first_attr_value_str_by_name(&i.attrs, |
1a4d82fc | 937 | "link_name") { |
85aaf69f | 938 | Some(val) => val.starts_with("llvm."), |
1a4d82fc JJ |
939 | _ => false |
940 | }; | |
941 | if links_to_llvm { | |
942 | self.gate_feature("link_llvm_intrinsics", i.span, | |
943 | "linking to LLVM intrinsics is experimental"); | |
944 | } | |
945 | ||
946 | visit::walk_foreign_item(self, i) | |
947 | } | |
948 | ||
1a4d82fc JJ |
949 | fn visit_expr(&mut self, e: &ast::Expr) { |
950 | match e.node { | |
b039eaaf | 951 | ast::ExprBox(_) => { |
1a4d82fc JJ |
952 | self.gate_feature("box_syntax", |
953 | e.span, | |
85aaf69f | 954 | "box expression syntax is experimental; \ |
1a4d82fc JJ |
955 | you can call `Box::new` instead."); |
956 | } | |
1a4d82fc JJ |
957 | _ => {} |
958 | } | |
959 | visit::walk_expr(self, e); | |
960 | } | |
961 | ||
1a4d82fc JJ |
962 | fn visit_pat(&mut self, pattern: &ast::Pat) { |
963 | match pattern.node { | |
964 | ast::PatVec(_, Some(_), ref last) if !last.is_empty() => { | |
965 | self.gate_feature("advanced_slice_patterns", | |
966 | pattern.span, | |
967 | "multiple-element slice matches anywhere \ | |
968 | but at the end of a slice (e.g. \ | |
d9579d0f | 969 | `[0, ..xs, 0]`) are experimental") |
1a4d82fc | 970 | } |
c34b1796 AL |
971 | ast::PatVec(..) => { |
972 | self.gate_feature("slice_patterns", | |
973 | pattern.span, | |
974 | "slice pattern syntax is experimental"); | |
975 | } | |
1a4d82fc | 976 | ast::PatBox(..) => { |
85aaf69f | 977 | self.gate_feature("box_patterns", |
1a4d82fc | 978 | pattern.span, |
85aaf69f | 979 | "box pattern syntax is experimental"); |
1a4d82fc JJ |
980 | } |
981 | _ => {} | |
982 | } | |
983 | visit::walk_pat(self, pattern) | |
984 | } | |
985 | ||
986 | fn visit_fn(&mut self, | |
e9174d1e | 987 | fn_kind: FnKind<'v>, |
1a4d82fc JJ |
988 | fn_decl: &'v ast::FnDecl, |
989 | block: &'v ast::Block, | |
990 | span: Span, | |
991 | _node_id: NodeId) { | |
62682a34 SL |
992 | // check for const fn declarations |
993 | match fn_kind { | |
e9174d1e | 994 | FnKind::ItemFn(_, _, _, ast::Constness::Const, _, _) => { |
62682a34 SL |
995 | self.gate_feature("const_fn", span, "const fn is unstable"); |
996 | } | |
997 | _ => { | |
998 | // stability of const fn methods are covered in | |
999 | // visit_trait_item and visit_impl_item below; this is | |
1000 | // because default methods don't pass through this | |
1001 | // point. | |
1002 | } | |
1003 | } | |
1004 | ||
1a4d82fc | 1005 | match fn_kind { |
e9174d1e | 1006 | FnKind::ItemFn(_, _, _, _, abi, _) if abi == Abi::RustIntrinsic => { |
1a4d82fc JJ |
1007 | self.gate_feature("intrinsics", |
1008 | span, | |
1009 | "intrinsics are subject to change") | |
1010 | } | |
e9174d1e SL |
1011 | FnKind::ItemFn(_, _, _, _, abi, _) | |
1012 | FnKind::Method(_, &ast::MethodSig { abi, .. }, _) if abi == Abi::RustCall => { | |
c34b1796 AL |
1013 | self.gate_feature("unboxed_closures", |
1014 | span, | |
1015 | "rust-call ABI is subject to change") | |
1016 | } | |
1a4d82fc JJ |
1017 | _ => {} |
1018 | } | |
1019 | visit::walk_fn(self, fn_kind, fn_decl, block, span); | |
1020 | } | |
d9579d0f AL |
1021 | |
1022 | fn visit_trait_item(&mut self, ti: &'v ast::TraitItem) { | |
1023 | match ti.node { | |
1024 | ast::ConstTraitItem(..) => { | |
1025 | self.gate_feature("associated_consts", | |
1026 | ti.span, | |
1027 | "associated constants are experimental") | |
1028 | } | |
62682a34 SL |
1029 | ast::MethodTraitItem(ref sig, _) => { |
1030 | if sig.constness == ast::Constness::Const { | |
1031 | self.gate_feature("const_fn", ti.span, "const fn is unstable"); | |
1032 | } | |
1033 | } | |
1034 | ast::TypeTraitItem(_, Some(_)) => { | |
1035 | self.gate_feature("associated_type_defaults", ti.span, | |
1036 | "associated type defaults are unstable"); | |
1037 | } | |
d9579d0f AL |
1038 | _ => {} |
1039 | } | |
1040 | visit::walk_trait_item(self, ti); | |
1041 | } | |
1042 | ||
1043 | fn visit_impl_item(&mut self, ii: &'v ast::ImplItem) { | |
1044 | match ii.node { | |
92a42be0 | 1045 | ast::ImplItemKind::Const(..) => { |
d9579d0f AL |
1046 | self.gate_feature("associated_consts", |
1047 | ii.span, | |
1048 | "associated constants are experimental") | |
1049 | } | |
92a42be0 | 1050 | ast::ImplItemKind::Method(ref sig, _) => { |
62682a34 SL |
1051 | if sig.constness == ast::Constness::Const { |
1052 | self.gate_feature("const_fn", ii.span, "const fn is unstable"); | |
1053 | } | |
1054 | } | |
d9579d0f AL |
1055 | _ => {} |
1056 | } | |
1057 | visit::walk_impl_item(self, ii); | |
1058 | } | |
1a4d82fc JJ |
1059 | } |
1060 | ||
c34b1796 AL |
1061 | fn check_crate_inner<F>(cm: &CodeMap, span_handler: &SpanHandler, |
1062 | krate: &ast::Crate, | |
62682a34 | 1063 | plugin_attributes: &[(String, AttributeType)], |
1a4d82fc | 1064 | check: F) |
85aaf69f | 1065 | -> Features |
1a4d82fc JJ |
1066 | where F: FnOnce(&mut Context, &ast::Crate) |
1067 | { | |
1068 | let mut cx = Context { | |
1069 | features: Vec::new(), | |
1070 | span_handler: span_handler, | |
1071 | cm: cm, | |
62682a34 | 1072 | plugin_attributes: plugin_attributes, |
1a4d82fc JJ |
1073 | }; |
1074 | ||
85aaf69f | 1075 | let mut accepted_features = Vec::new(); |
1a4d82fc JJ |
1076 | let mut unknown_features = Vec::new(); |
1077 | ||
85aaf69f | 1078 | for attr in &krate.attrs { |
1a4d82fc JJ |
1079 | if !attr.check_name("feature") { |
1080 | continue | |
1081 | } | |
1082 | ||
1083 | match attr.meta_item_list() { | |
1084 | None => { | |
1085 | span_handler.span_err(attr.span, "malformed feature attribute, \ | |
1086 | expected #![feature(...)]"); | |
1087 | } | |
1088 | Some(list) => { | |
85aaf69f | 1089 | for mi in list { |
1a4d82fc JJ |
1090 | let name = match mi.node { |
1091 | ast::MetaWord(ref word) => (*word).clone(), | |
1092 | _ => { | |
1093 | span_handler.span_err(mi.span, | |
1094 | "malformed feature, expected just \ | |
1095 | one word"); | |
1096 | continue | |
1097 | } | |
1098 | }; | |
1099 | match KNOWN_FEATURES.iter() | |
e9174d1e SL |
1100 | .find(|& &(n, _, _, _)| name == n) { |
1101 | Some(&(name, _, _, Active)) => { | |
c1a9b12d | 1102 | cx.enable_feature(name); |
1a4d82fc | 1103 | } |
e9174d1e | 1104 | Some(&(_, _, _, Removed)) => { |
1a4d82fc JJ |
1105 | span_handler.span_err(mi.span, "feature has been removed"); |
1106 | } | |
e9174d1e | 1107 | Some(&(_, _, _, Accepted)) => { |
85aaf69f | 1108 | accepted_features.push(mi.span); |
1a4d82fc JJ |
1109 | } |
1110 | None => { | |
85aaf69f | 1111 | unknown_features.push((name, mi.span)); |
1a4d82fc JJ |
1112 | } |
1113 | } | |
1114 | } | |
1115 | } | |
1116 | } | |
1117 | } | |
1118 | ||
1119 | check(&mut cx, krate); | |
1120 | ||
85aaf69f SL |
1121 | // FIXME (pnkfelix): Before adding the 99th entry below, change it |
1122 | // to a single-pass (instead of N calls to `.has_feature`). | |
1123 | ||
1124 | Features { | |
1a4d82fc JJ |
1125 | unboxed_closures: cx.has_feature("unboxed_closures"), |
1126 | rustc_diagnostic_macros: cx.has_feature("rustc_diagnostic_macros"), | |
1a4d82fc | 1127 | visible_private_types: cx.has_feature("visible_private_types"), |
85aaf69f SL |
1128 | allow_quote: cx.has_feature("quote"), |
1129 | allow_asm: cx.has_feature("asm"), | |
1130 | allow_log_syntax: cx.has_feature("log_syntax"), | |
1131 | allow_concat_idents: cx.has_feature("concat_idents"), | |
1132 | allow_trace_macros: cx.has_feature("trace_macros"), | |
c34b1796 AL |
1133 | allow_internal_unstable: cx.has_feature("allow_internal_unstable"), |
1134 | allow_custom_derive: cx.has_feature("custom_derive"), | |
c1a9b12d SL |
1135 | allow_placement_in: cx.has_feature("placement_in_syntax"), |
1136 | allow_box: cx.has_feature("box_syntax"), | |
1137 | allow_pushpop_unsafe: cx.has_feature("pushpop_unsafe"), | |
85aaf69f SL |
1138 | simd_ffi: cx.has_feature("simd_ffi"), |
1139 | unmarked_api: cx.has_feature("unmarked_api"), | |
c34b1796 | 1140 | negate_unsigned: cx.has_feature("negate_unsigned"), |
85aaf69f | 1141 | declared_stable_lang_features: accepted_features, |
62682a34 SL |
1142 | declared_lib_features: unknown_features, |
1143 | const_fn: cx.has_feature("const_fn"), | |
92a42be0 | 1144 | const_indexing: cx.has_feature("const_indexing"), |
c1a9b12d SL |
1145 | static_recursion: cx.has_feature("static_recursion"), |
1146 | default_type_parameter_fallback: cx.has_feature("default_type_parameter_fallback"), | |
e9174d1e SL |
1147 | type_macros: cx.has_feature("type_macros"), |
1148 | cfg_target_feature: cx.has_feature("cfg_target_feature"), | |
b039eaaf SL |
1149 | cfg_target_vendor: cx.has_feature("cfg_target_vendor"), |
1150 | augmented_assignments: cx.has_feature("augmented_assignments"), | |
1151 | braced_empty_structs: cx.has_feature("braced_empty_structs"), | |
92a42be0 SL |
1152 | staged_api: cx.has_feature("staged_api"), |
1153 | stmt_expr_attributes: cx.has_feature("stmt_expr_attributes"), | |
85aaf69f | 1154 | } |
1a4d82fc JJ |
1155 | } |
1156 | ||
1157 | pub fn check_crate_macros(cm: &CodeMap, span_handler: &SpanHandler, krate: &ast::Crate) | |
85aaf69f | 1158 | -> Features { |
62682a34 | 1159 | check_crate_inner(cm, span_handler, krate, &[] as &'static [_], |
1a4d82fc JJ |
1160 | |ctx, krate| visit::walk_crate(&mut MacroVisitor { context: ctx }, krate)) |
1161 | } | |
1162 | ||
62682a34 SL |
1163 | pub fn check_crate(cm: &CodeMap, span_handler: &SpanHandler, krate: &ast::Crate, |
1164 | plugin_attributes: &[(String, AttributeType)], | |
1165 | unstable: UnstableFeatures) -> Features | |
c34b1796 | 1166 | { |
62682a34 SL |
1167 | maybe_stage_features(span_handler, krate, unstable); |
1168 | ||
1169 | check_crate_inner(cm, span_handler, krate, plugin_attributes, | |
1a4d82fc JJ |
1170 | |ctx, krate| visit::walk_crate(&mut PostExpansionVisitor { context: ctx }, |
1171 | krate)) | |
1172 | } | |
62682a34 SL |
1173 | |
1174 | #[derive(Clone, Copy)] | |
1175 | pub enum UnstableFeatures { | |
1176 | /// Hard errors for unstable features are active, as on | |
1177 | /// beta/stable channels. | |
1178 | Disallow, | |
1179 | /// Allow features to me activated, as on nightly. | |
1180 | Allow, | |
1181 | /// Errors are bypassed for bootstrapping. This is required any time | |
1182 | /// during the build that feature-related lints are set to warn or above | |
1183 | /// because the build turns on warnings-as-errors and uses lots of unstable | |
b039eaaf | 1184 | /// features. As a result, this is always required for building Rust itself. |
62682a34 SL |
1185 | Cheat |
1186 | } | |
1187 | ||
1188 | fn maybe_stage_features(span_handler: &SpanHandler, krate: &ast::Crate, | |
1189 | unstable: UnstableFeatures) { | |
1190 | let allow_features = match unstable { | |
1191 | UnstableFeatures::Allow => true, | |
1192 | UnstableFeatures::Disallow => false, | |
1193 | UnstableFeatures::Cheat => true | |
1194 | }; | |
1195 | if !allow_features { | |
1196 | for attr in &krate.attrs { | |
1197 | if attr.check_name("feature") { | |
1198 | let release_channel = option_env!("CFG_RELEASE_CHANNEL").unwrap_or("(unknown)"); | |
1199 | let ref msg = format!("#[feature] may not be used on the {} release channel", | |
1200 | release_channel); | |
1201 | span_handler.span_err(attr.span, msg); | |
1202 | } | |
1203 | } | |
1204 | } | |
1205 | } |