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