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