about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2013-09-03 12:22:16 -0700
committerbors <bors@rust-lang.org>2013-09-03 12:22:16 -0700
commitb4ff0bca4c123399f8be8d44877e271767c5871c (patch)
tree1dce179b3e5f546fb7c4cecb7d042dae88dbcb65
parent7ee90a002ab0841f5fcfdf74eb74b58b1c1a8000 (diff)
parent506f69aed7e2f5dfdd2134ae0c74e47559e1c649 (diff)
downloadrust-b4ff0bca4c123399f8be8d44877e271767c5871c.tar.gz
rust-b4ff0bca4c123399f8be8d44877e271767c5871c.zip
auto merge of #8921 : huonw/rust/stability, r=brson
Significant progress on #6875, enough that I'll open new bugs and turn that into a metabug when this lands.

Description & example in the commit message.
-rw-r--r--doc/rust.md58
-rw-r--r--src/librustc/metadata/encoder.rs1
-rw-r--r--src/librustc/middle/lint.rs130
-rw-r--r--src/librustc/middle/ty.rs1
-rw-r--r--src/libsyntax/ast_map.rs20
-rw-r--r--src/libsyntax/attr.rs38
-rw-r--r--src/test/auxiliary/lint_stability.rs162
-rw-r--r--src/test/compile-fail/lint-stability.rs338
8 files changed, 745 insertions, 3 deletions
diff --git a/doc/rust.md b/doc/rust.md
index b33006733ab..cfe0ff4768f 100644
--- a/doc/rust.md
+++ b/doc/rust.md
@@ -1038,7 +1038,7 @@ code_. They are defined in the same way as any other Rust function,
 except that they have the `extern` modifier.
 
 ~~~
-// Declares an extern fn, the ABI defaults to "C" 
+// Declares an extern fn, the ABI defaults to "C"
 extern fn new_vec() -> ~[int] { ~[] }
 
 // Declares an extern fn with "stdcall" ABI
@@ -1723,6 +1723,62 @@ Supported traits for `deriving` are:
   each constituent field of the type must also implement `ToStr` and will have
   `field.to_str()` invoked to build up the result.
 
+### Stability
+One can indicate the stability of an API using the following attributes:
+
+* `deprecated`: This item should no longer be used, e.g. it has been
+  replaced. No guarantee of backwards-compatibility.
+* `experimental`: This item was only recently introduced or is
+  otherwise in a state of flux. It may change significantly, or even
+  be removed. No guarantee of backwards-compatibility.
+* `unstable`: This item is still under development, but requires more
+  testing to be considered stable. No guarantee of backwards-compatibility.
+* `stable`: This item is considered stable, and will not change
+  significantly. Guarantee of backwards-compatibility.
+* `frozen`: This item is very stable, and is unlikely to
+  change. Guarantee of backwards-compatibility.
+* `locked`: This item will never change unless a serious bug is
+  found. Guarantee of backwards-compatibility.
+
+These levels are directly inspired by
+[Node.js' "stability index"](http://nodejs.org/api/documentation.html).
+
+There are lints for disallowing items marked with certain levels:
+`deprecated`, `experimental` and `unstable`; the first two will warn
+by default. Items with not marked with a stability are considered to
+be unstable for the purposes of the lint. One can give an optional
+string that will be displayed when the lint flags the use of an item.
+
+~~~ {.xfail-test}
+#[warn(unstable)];
+
+#[deprecated="replaced by `best`"]
+fn bad() {
+    // delete everything
+}
+
+fn better() {
+    // delete fewer things
+}
+
+#[stable]
+fn best() {
+    // delete nothing
+}
+
+fn main() {
+    bad(); // "warning: use of deprecated item: replaced by `best`"
+
+    better(); // "warning: use of unmarked item"
+
+    best(); // no warning
+}
+~~~
+
+> **Note:** Currently these are only checked when applied to
+> individual functions, structs, methods and enum variants, *not* to
+> entire modules, traits, impls or enums themselves.
+
 # Statements and expressions
 
 Rust is _primarily_ an expression language. This means that most forms of
diff --git a/src/librustc/metadata/encoder.rs b/src/librustc/metadata/encoder.rs
index 1b469676773..786d1bf3242 100644
--- a/src/librustc/metadata/encoder.rs
+++ b/src/librustc/metadata/encoder.rs
@@ -330,6 +330,7 @@ fn encode_enum_variant_info(ecx: &EncodeContext,
         encode_name(ecx, ebml_w, variant.node.name);
         encode_parent_item(ebml_w, local_def(id));
         encode_visibility(ebml_w, variant.node.vis);
+        encode_attributes(ebml_w, variant.node.attrs);
         match variant.node.kind {
             ast::tuple_variant_kind(ref args)
                     if args.len() > 0 && generics.ty_params.len() == 0 => {
diff --git a/src/librustc/middle/lint.rs b/src/librustc/middle/lint.rs
index 596fd05b262..1e1be5f7a13 100644
--- a/src/librustc/middle/lint.rs
+++ b/src/librustc/middle/lint.rs
@@ -12,6 +12,7 @@
 use driver::session;
 use middle::ty;
 use middle::pat_util;
+use metadata::csearch;
 use util::ppaux::{ty_to_str};
 
 use std::cmp;
@@ -27,7 +28,7 @@ use std::u8;
 use extra::smallintmap::SmallIntMap;
 use syntax::ast_map;
 use syntax::attr;
-use syntax::attr::AttrMetaMethods;
+use syntax::attr::{AttrMetaMethods, AttributeMethods};
 use syntax::codemap::Span;
 use syntax::codemap;
 use syntax::parse::token;
@@ -97,6 +98,10 @@ pub enum lint {
     missing_doc,
     unreachable_code,
 
+    deprecated,
+    experimental,
+    unstable,
+
     warnings,
 }
 
@@ -281,6 +286,27 @@ static lint_table: &'static [(&'static str, LintSpec)] = &[
         default: warn
     }),
 
+    ("deprecated",
+     LintSpec {
+        lint: deprecated,
+        desc: "detects use of #[deprecated] items",
+        default: warn
+    }),
+
+    ("experimental",
+     LintSpec {
+        lint: experimental,
+        desc: "detects use of #[experimental] items",
+        default: warn
+    }),
+
+    ("unstable",
+     LintSpec {
+        lint: unstable,
+        desc: "detects use of #[unstable] items (incl. items with no stability attribute)",
+        default: allow
+    }),
+
     ("warnings",
      LintSpec {
         lint: warnings,
@@ -1375,6 +1401,107 @@ fn lint_missing_doc() -> @mut OuterLint {
     @mut MissingDocLintVisitor { stopping_on_items: false } as @mut OuterLint
 }
 
+/// Checks for use of items with #[deprecated], #[experimental] and
+/// #[unstable] (or none of them) attributes.
+struct StabilityLintVisitor { stopping_on_items: bool }
+
+impl StabilityLintVisitor {
+    fn handle_def(&mut self, sp: Span, def: &ast::Def, cx: @mut Context) {
+        let id = ast_util::def_id_of_def(*def);
+
+        let stability = if ast_util::is_local(id) {
+            // this crate
+            match cx.tcx.items.find(&id.node) {
+                Some(ast_node) => {
+                    let s = do ast_node.with_attrs |attrs| {
+                        do attrs.map_move |a| {
+                            attr::find_stability(a.iter().map(|a| a.meta()))
+                        }
+                    };
+                    match s {
+                        Some(s) => s,
+
+                        // no possibility of having attributes
+                        // (e.g. it's a local variable), so just
+                        // ignore it.
+                        None => return
+                    }
+                }
+                _ => cx.tcx.sess.bug(fmt!("handle_def: %? not found", id))
+            }
+        } else {
+            // cross-crate
+
+            let mut s = None;
+            // run through all the attributes and take the first
+            // stability one.
+            do csearch::get_item_attrs(cx.tcx.cstore, id) |meta_items| {
+                if s.is_none() {
+                    s = attr::find_stability(meta_items.move_iter())
+                }
+            }
+            s
+        };
+
+        let (lint, label) = match stability {
+            // no stability attributes == Unstable
+            None => (unstable, "unmarked"),
+            Some(attr::Stability { level: attr::Unstable, _ }) => (unstable, "unstable"),
+            Some(attr::Stability { level: attr::Experimental, _ }) => {
+                (experimental, "experimental")
+            }
+            Some(attr::Stability { level: attr::Deprecated, _ }) => (deprecated, "deprecated"),
+            _ => return
+        };
+
+        let msg = match stability {
+            Some(attr::Stability { text: Some(ref s), _ }) => {
+                fmt!("use of %s item: %s", label, *s)
+            }
+            _ => fmt!("use of %s item", label)
+        };
+
+        cx.span_lint(lint, sp, msg);
+    }
+}
+
+impl SubitemStoppableVisitor for StabilityLintVisitor {
+    fn is_running_on_items(&mut self) -> bool { !self.stopping_on_items }
+}
+
+impl Visitor<@mut Context> for StabilityLintVisitor {
+    fn visit_item(&mut self, i:@ast::item, e:@mut Context) {
+        self.OVERRIDE_visit_item(i, e);
+    }
+
+    fn visit_fn(&mut self, fk:&visit::fn_kind, fd:&ast::fn_decl,
+                b:&ast::Block, s:Span, n:ast::NodeId, e:@mut Context) {
+        self.OVERRIDE_visit_fn(fk, fd, b, s, n, e);
+    }
+
+    fn visit_expr(&mut self, ex: @ast::Expr, cx: @mut Context) {
+        match ex.node {
+            ast::ExprMethodCall(*) |
+            ast::ExprPath(*) |
+            ast::ExprStruct(*) => {
+                match cx.tcx.def_map.find(&ex.id) {
+                    Some(def) => self.handle_def(ex.span, def, cx),
+                    None => {}
+                }
+            }
+            _ => {}
+        }
+
+        visit::walk_expr(self, ex, cx)
+    }
+}
+
+outer_lint_boilerplate_impl!(StabilityLintVisitor)
+
+fn lint_stability() -> @mut OuterLint {
+    @mut StabilityLintVisitor { stopping_on_items: false } as @mut OuterLint
+}
+
 struct LintCheckVisitor;
 
 impl Visitor<@mut Context> for LintCheckVisitor {
@@ -1458,6 +1585,7 @@ pub fn check_crate(tcx: ty::ctxt, crate: @ast::Crate) {
     cx.add_old_lint(lint_unused_mut());
     cx.add_old_lint(lint_unnecessary_allocations());
     cx.add_old_lint(lint_missing_doc());
+    cx.add_old_lint(lint_stability());
     cx.add_lint(lint_session(cx));
 
     // Actually perform the lint checks (iterating the ast)
diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs
index 3266ec5c9ab..dc26350d88d 100644
--- a/src/librustc/middle/ty.rs
+++ b/src/librustc/middle/ty.rs
@@ -4706,4 +4706,3 @@ pub fn trait_of_method(tcx: ctxt, def_id: ast::DefId)
 
     result
 }
-
diff --git a/src/libsyntax/ast_map.rs b/src/libsyntax/ast_map.rs
index ccaadcbad4d..e3023b919f8 100644
--- a/src/libsyntax/ast_map.rs
+++ b/src/libsyntax/ast_map.rs
@@ -80,6 +80,26 @@ pub enum ast_node {
     node_callee_scope(@Expr)
 }
 
+impl ast_node {
+    pub fn with_attrs<T>(&self, f: &fn(Option<&[Attribute]>) -> T) -> T {
+        let attrs = match *self {
+            node_item(i, _) => Some(i.attrs.as_slice()),
+            node_foreign_item(fi, _, _, _) => Some(fi.attrs.as_slice()),
+            node_trait_method(tm, _, _) => match *tm {
+                required(ref type_m) => Some(type_m.attrs.as_slice()),
+                provided(m) => Some(m.attrs.as_slice())
+            },
+            node_method(m, _, _) => Some(m.attrs.as_slice()),
+            node_variant(ref v, _, _) => Some(v.node.attrs.as_slice()),
+            // unit/tuple structs take the attributes straight from
+            // the struct definition.
+            node_struct_ctor(_, strct, _) => Some(strct.attrs.as_slice()),
+            _ => None
+        };
+        f(attrs)
+    }
+}
+
 pub type map = @mut HashMap<NodeId, ast_node>;
 
 pub struct Ctx {
diff --git a/src/libsyntax/attr.rs b/src/libsyntax/attr.rs
index 2ed03040fa1..fd0887de722 100644
--- a/src/libsyntax/attr.rs
+++ b/src/libsyntax/attr.rs
@@ -313,6 +313,44 @@ pub fn test_cfg<AM: AttrMetaMethods, It: Iterator<AM>>
     no_cfgs || some_cfg_matches
 }
 
+/// Represents the #[deprecated="foo"] (etc) attributes.
+pub struct Stability {
+    level: StabilityLevel,
+    text: Option<@str>
+}
+
+/// The available stability levels.
+#[deriving(Eq,Ord,Clone)]
+pub enum StabilityLevel {
+    Deprecated,
+    Experimental,
+    Unstable,
+    Stable,
+    Frozen,
+    Locked
+}
+
+/// Find the first stability attribute. `None` if none exists.
+pub fn find_stability<AM: AttrMetaMethods, It: Iterator<AM>>(mut metas: It) -> Option<Stability> {
+    for m in metas {
+        let level = match m.name().as_slice() {
+            "deprecated" => Deprecated,
+            "experimental" => Experimental,
+            "unstable" => Unstable,
+            "stable" => Stable,
+            "frozen" => Frozen,
+            "locked" => Locked,
+            _ => loop // not a stability level
+        };
+
+        return Some(Stability {
+                level: level,
+                text: m.value_str()
+            });
+    }
+    None
+}
+
 pub fn require_unique_names(diagnostic: @mut span_handler,
                             metas: &[@MetaItem]) {
     let mut set = HashSet::new();
diff --git a/src/test/auxiliary/lint_stability.rs b/src/test/auxiliary/lint_stability.rs
new file mode 100644
index 00000000000..af00a6876c2
--- /dev/null
+++ b/src/test/auxiliary/lint_stability.rs
@@ -0,0 +1,162 @@
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+#[link(name = "lint_stability",
+       vers = "0.1")];
+#[crate_type = "lib"];
+
+#[deprecated]
+pub fn deprecated() {}
+#[deprecated="text"]
+pub fn deprecated_text() {}
+
+#[experimental]
+pub fn experimental() {}
+#[experimental="text"]
+pub fn experimental_text() {}
+
+#[unstable]
+pub fn unstable() {}
+#[unstable="text"]
+pub fn unstable_text() {}
+
+pub fn unmarked() {}
+
+#[stable]
+pub fn stable() {}
+#[stable="text"]
+pub fn stable_text() {}
+
+#[locked]
+pub fn locked() {}
+#[locked="text"]
+pub fn locked_text() {}
+
+#[frozen]
+pub fn frozen() {}
+#[frozen="text"]
+pub fn frozen_text() {}
+
+#[stable]
+pub struct MethodTester;
+
+impl MethodTester {
+    #[deprecated]
+    pub fn method_deprecated(&self) {}
+    #[deprecated="text"]
+    pub fn method_deprecated_text(&self) {}
+
+    #[experimental]
+    pub fn method_experimental(&self) {}
+    #[experimental="text"]
+    pub fn method_experimental_text(&self) {}
+
+    #[unstable]
+    pub fn method_unstable(&self) {}
+    #[unstable="text"]
+    pub fn method_unstable_text(&self) {}
+
+    pub fn method_unmarked(&self) {}
+
+    #[stable]
+    pub fn method_stable(&self) {}
+    #[stable="text"]
+    pub fn method_stable_text(&self) {}
+
+    #[locked]
+    pub fn method_locked(&self) {}
+    #[locked="text"]
+    pub fn method_locked_text(&self) {}
+
+    #[frozen]
+    pub fn method_frozen(&self) {}
+    #[frozen="text"]
+    pub fn method_frozen_text(&self) {}
+}
+
+pub trait Trait {
+    #[deprecated]
+    fn trait_deprecated(&self) {}
+    #[deprecated="text"]
+    fn trait_deprecated_text(&self) {}
+
+    #[experimental]
+    fn trait_experimental(&self) {}
+    #[experimental="text"]
+    fn trait_experimental_text(&self) {}
+
+    #[unstable]
+    fn trait_unstable(&self) {}
+    #[unstable="text"]
+    fn trait_unstable_text(&self) {}
+
+    fn trait_unmarked(&self) {}
+
+    #[stable]
+    fn trait_stable(&self) {}
+    #[stable="text"]
+    fn trait_stable_text(&self) {}
+
+    #[locked]
+    fn trait_locked(&self) {}
+    #[locked="text"]
+    fn trait_locked_text(&self) {}
+
+    #[frozen]
+    fn trait_frozen(&self) {}
+    #[frozen="text"]
+    fn trait_frozen_text(&self) {}
+}
+
+impl Trait for MethodTester {}
+
+#[deprecated]
+pub struct DeprecatedStruct { i: int }
+#[experimental]
+pub struct ExperimentalStruct { i: int }
+#[unstable]
+pub struct UnstableStruct { i: int }
+pub struct UnmarkedStruct { i: int }
+#[stable]
+pub struct StableStruct { i: int }
+#[frozen]
+pub struct FrozenStruct { i: int }
+#[locked]
+pub struct LockedStruct { i: int }
+
+#[deprecated]
+pub struct DeprecatedUnitStruct;
+#[experimental]
+pub struct ExperimentalUnitStruct;
+#[unstable]
+pub struct UnstableUnitStruct;
+pub struct UnmarkedUnitStruct;
+#[stable]
+pub struct StableUnitStruct;
+#[frozen]
+pub struct FrozenUnitStruct;
+#[locked]
+pub struct LockedUnitStruct;
+
+pub enum Enum {
+    #[deprecated]
+    DeprecatedVariant,
+    #[experimental]
+    ExperimentalVariant,
+    #[unstable]
+    UnstableVariant,
+
+    UnmarkedVariant,
+    #[stable]
+    StableVariant,
+    #[frozen]
+    FrozenVariant,
+    #[locked]
+    LockedVariant,
+}
diff --git a/src/test/compile-fail/lint-stability.rs b/src/test/compile-fail/lint-stability.rs
new file mode 100644
index 00000000000..2c8b7685875
--- /dev/null
+++ b/src/test/compile-fail/lint-stability.rs
@@ -0,0 +1,338 @@
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// xfail-fast aux-build
+// aux-build:lint_stability.rs
+
+#[deny(unstable)];
+#[deny(deprecated)];
+#[deny(experimental)];
+
+mod cross_crate {
+    extern mod lint_stability;
+    use self::lint_stability::*;
+
+    fn test() {
+        // XXX: attributes on methods are not encoded cross crate.
+        let foo = MethodTester;
+
+        deprecated(); //~ ERROR use of deprecated item
+        foo.method_deprecated(); // ~ ERROR use of deprecated item
+        foo.trait_deprecated(); // ~ ERROR use of deprecated item
+
+        deprecated_text(); //~ ERROR use of deprecated item: text
+        foo.method_deprecated_text(); // ~ ERROR use of deprecated item: text
+        foo.trait_deprecated_text(); // ~ ERROR use of deprecated item: text
+
+        experimental(); //~ ERROR use of experimental item
+        foo.method_experimental(); // ~ ERROR use of experimental item
+        foo.trait_experimental(); // ~ ERROR use of experimental item
+
+        experimental_text(); //~ ERROR use of experimental item: text
+        foo.method_experimental_text(); // ~ ERROR use of experimental item: text
+        foo.trait_experimental_text(); // ~ ERROR use of experimental item: text
+
+        unstable(); //~ ERROR use of unstable item
+        foo.method_unstable(); // ~ ERROR use of unstable item
+        foo.trait_unstable(); // ~ ERROR use of unstable item
+
+        unstable_text(); //~ ERROR use of unstable item: text
+        foo.method_unstable_text(); // ~ ERROR use of unstable item: text
+        foo.trait_unstable_text(); // ~ ERROR use of unstable item: text
+
+        unmarked(); //~ ERROR use of unmarked item
+        foo.method_unmarked(); // ~ ERROR use of unmarked item
+        foo.trait_unmarked(); // ~ ERROR use of unmarked item
+
+        stable();
+        foo.method_stable();
+        foo.trait_stable();
+
+        stable_text();
+        foo.method_stable_text();
+        foo.trait_stable_text();
+
+        frozen();
+        foo.method_frozen();
+        foo.trait_frozen();
+
+        frozen_text();
+        foo.method_frozen_text();
+        foo.trait_frozen_text();
+
+        locked();
+        foo.method_locked();
+        foo.trait_locked();
+
+        locked_text();
+        foo.method_locked_text();
+        foo.trait_locked_text();
+
+
+        let _ = DeprecatedStruct { i: 0 }; //~ ERROR use of deprecated item
+        let _ = ExperimentalStruct { i: 0 }; //~ ERROR use of experimental item
+        let _ = UnstableStruct { i: 0 }; //~ ERROR use of unstable item
+        let _ = UnmarkedStruct { i: 0 }; //~ ERROR use of unmarked item
+        let _ = StableStruct { i: 0 };
+        let _ = FrozenStruct { i: 0 };
+        let _ = LockedStruct { i: 0 };
+
+        let _ = DeprecatedUnitStruct; //~ ERROR use of deprecated item
+        let _ = ExperimentalUnitStruct; //~ ERROR use of experimental item
+        let _ = UnstableUnitStruct; //~ ERROR use of unstable item
+        let _ = UnmarkedUnitStruct; //~ ERROR use of unmarked item
+        let _ = StableUnitStruct;
+        let _ = FrozenUnitStruct;
+        let _ = LockedUnitStruct;
+
+        let _ = DeprecatedVariant; //~ ERROR use of deprecated item
+        let _ = ExperimentalVariant; //~ ERROR use of experimental item
+        let _ = UnstableVariant; //~ ERROR use of unstable item
+        let _ = UnmarkedVariant; //~ ERROR use of unmarked item
+        let _ = StableVariant;
+        let _ = FrozenVariant;
+        let _ = LockedVariant;
+    }
+}
+
+mod this_crate {
+    #[deprecated]
+    pub fn deprecated() {}
+    #[deprecated="text"]
+    pub fn deprecated_text() {}
+
+    #[experimental]
+    pub fn experimental() {}
+    #[experimental="text"]
+    pub fn experimental_text() {}
+
+    #[unstable]
+    pub fn unstable() {}
+    #[unstable="text"]
+    pub fn unstable_text() {}
+
+    pub fn unmarked() {}
+
+    #[stable]
+    pub fn stable() {}
+    #[stable="text"]
+    pub fn stable_text() {}
+
+    #[locked]
+    pub fn locked() {}
+    #[locked="text"]
+    pub fn locked_text() {}
+
+    #[frozen]
+    pub fn frozen() {}
+    #[frozen="text"]
+    pub fn frozen_text() {}
+
+    #[stable]
+    pub struct MethodTester;
+
+    impl MethodTester {
+        #[deprecated]
+        pub fn method_deprecated(&self) {}
+        #[deprecated="text"]
+        pub fn method_deprecated_text(&self) {}
+
+        #[experimental]
+        pub fn method_experimental(&self) {}
+        #[experimental="text"]
+        pub fn method_experimental_text(&self) {}
+
+        #[unstable]
+        pub fn method_unstable(&self) {}
+        #[unstable="text"]
+        pub fn method_unstable_text(&self) {}
+
+        pub fn method_unmarked(&self) {}
+
+        #[stable]
+        pub fn method_stable(&self) {}
+        #[stable="text"]
+        pub fn method_stable_text(&self) {}
+
+        #[locked]
+        pub fn method_locked(&self) {}
+        #[locked="text"]
+        pub fn method_locked_text(&self) {}
+
+        #[frozen]
+        pub fn method_frozen(&self) {}
+        #[frozen="text"]
+        pub fn method_frozen_text(&self) {}
+    }
+
+    pub trait Trait {
+        #[deprecated]
+        fn trait_deprecated(&self) {}
+        #[deprecated="text"]
+        fn trait_deprecated_text(&self) {}
+
+        #[experimental]
+        fn trait_experimental(&self) {}
+        #[experimental="text"]
+        fn trait_experimental_text(&self) {}
+
+        #[unstable]
+        fn trait_unstable(&self) {}
+        #[unstable="text"]
+        fn trait_unstable_text(&self) {}
+
+        fn trait_unmarked(&self) {}
+
+        #[stable]
+        fn trait_stable(&self) {}
+        #[stable="text"]
+        fn trait_stable_text(&self) {}
+
+        #[locked]
+        fn trait_locked(&self) {}
+        #[locked="text"]
+        fn trait_locked_text(&self) {}
+
+        #[frozen]
+        fn trait_frozen(&self) {}
+        #[frozen="text"]
+        fn trait_frozen_text(&self) {}
+    }
+
+    impl Trait for MethodTester {}
+
+    #[deprecated]
+    pub struct DeprecatedStruct { i: int }
+    #[experimental]
+    pub struct ExperimentalStruct { i: int }
+    #[unstable]
+    pub struct UnstableStruct { i: int }
+    pub struct UnmarkedStruct { i: int }
+    #[stable]
+    pub struct StableStruct { i: int }
+    #[frozen]
+    pub struct FrozenStruct { i: int }
+    #[locked]
+    pub struct LockedStruct { i: int }
+
+    #[deprecated]
+    pub struct DeprecatedUnitStruct;
+    #[experimental]
+    pub struct ExperimentalUnitStruct;
+    #[unstable]
+    pub struct UnstableUnitStruct;
+    pub struct UnmarkedUnitStruct;
+    #[stable]
+    pub struct StableUnitStruct;
+    #[frozen]
+    pub struct FrozenUnitStruct;
+    #[locked]
+    pub struct LockedUnitStruct;
+
+    pub enum Enum {
+        #[deprecated]
+        DeprecatedVariant,
+        #[experimental]
+        ExperimentalVariant,
+        #[unstable]
+        UnstableVariant,
+
+        UnmarkedVariant,
+        #[stable]
+        StableVariant,
+        #[frozen]
+        FrozenVariant,
+        #[locked]
+        LockedVariant,
+    }
+
+    fn test() {
+        let foo = MethodTester;
+
+        deprecated(); //~ ERROR use of deprecated item
+        foo.method_deprecated(); // ~ ERROR use of deprecated item
+        foo.trait_deprecated(); // ~ ERROR use of deprecated item
+
+        deprecated_text(); //~ ERROR use of deprecated item: text
+        foo.method_deprecated_text(); // ~ ERROR use of deprecated item: text
+        foo.trait_deprecated_text(); // ~ ERROR use of deprecated item: text
+
+        experimental(); //~ ERROR use of experimental item
+        foo.method_experimental(); // ~ ERROR use of experimental item
+        foo.trait_experimental(); // ~ ERROR use of experimental item
+
+        experimental_text(); //~ ERROR use of experimental item: text
+        foo.method_experimental_text(); // ~ ERROR use of experimental item: text
+        foo.trait_experimental_text(); // ~ ERROR use of experimental item: text
+
+        unstable(); //~ ERROR use of unstable item
+        foo.method_unstable(); // ~ ERROR use of unstable item
+        foo.trait_unstable(); // ~ ERROR use of unstable item
+
+        unstable_text(); //~ ERROR use of unstable item: text
+        foo.method_unstable_text(); // ~ ERROR use of unstable item: text
+        foo.trait_unstable_text(); // ~ ERROR use of unstable item: text
+
+        unmarked(); //~ ERROR use of unmarked item
+        foo.method_unmarked(); // ~ ERROR use of unmarked item
+        foo.trait_unmarked(); // ~ ERROR use of unmarked item
+
+        stable();
+        foo.method_stable();
+        foo.trait_stable();
+
+        stable_text();
+        foo.method_stable_text();
+        foo.trait_stable_text();
+
+        frozen();
+        foo.method_frozen();
+        foo.trait_frozen();
+
+        frozen_text();
+        foo.method_frozen_text();
+        foo.trait_frozen_text();
+
+        locked();
+        foo.method_locked();
+        foo.trait_locked();
+
+        locked_text();
+        foo.method_locked_text();
+        foo.trait_locked_text();
+
+
+        let _ = DeprecatedStruct { i: 0 }; //~ ERROR use of deprecated item
+        let _ = ExperimentalStruct { i: 0 }; //~ ERROR use of experimental item
+        let _ = UnstableStruct { i: 0 }; //~ ERROR use of unstable item
+        let _ = UnmarkedStruct { i: 0 }; //~ ERROR use of unmarked item
+        let _ = StableStruct { i: 0 };
+        let _ = FrozenStruct { i: 0 };
+        let _ = LockedStruct { i: 0 };
+
+        let _ = DeprecatedUnitStruct; //~ ERROR use of deprecated item
+        let _ = ExperimentalUnitStruct; //~ ERROR use of experimental item
+        let _ = UnstableUnitStruct; //~ ERROR use of unstable item
+        let _ = UnmarkedUnitStruct; //~ ERROR use of unmarked item
+        let _ = StableUnitStruct;
+        let _ = FrozenUnitStruct;
+        let _ = LockedUnitStruct;
+
+        let _ = DeprecatedVariant; //~ ERROR use of deprecated item
+        let _ = ExperimentalVariant; //~ ERROR use of experimental item
+        let _ = UnstableVariant; //~ ERROR use of unstable item
+        let _ = UnmarkedVariant; //~ ERROR use of unmarked item
+        let _ = StableVariant;
+        let _ = FrozenVariant;
+        let _ = LockedVariant;
+    }
+}
+
+fn main() {}