about summary refs log tree commit diff
diff options
context:
space:
mode:
authorVadim Petrochenkov <vadim.petrochenkov@gmail.com>2015-10-13 06:01:31 +0300
committerVadim Petrochenkov <vadim.petrochenkov@gmail.com>2015-10-13 06:01:31 +0300
commitab671552c3b44906b63c07f2676492b766f2a7c3 (patch)
tree5fcfa78f184afcc1bd1cf0cbf5be83f25ec720b9
parent843e528fd0c09666cc5f2896258f88c25eaacb29 (diff)
downloadrust-ab671552c3b44906b63c07f2676492b766f2a7c3.tar.gz
rust-ab671552c3b44906b63c07f2676492b766f2a7c3.zip
Refactor attr::Stability
Stricter checking + enforcement of invariants at compile time
-rw-r--r--src/librustc/middle/stability.rs48
-rw-r--r--src/librustc_front/attr.rs625
-rw-r--r--src/librustc_lint/builtin.rs35
-rw-r--r--src/librustdoc/clean/mod.rs45
-rw-r--r--src/librustdoc/html/render.rs9
-rw-r--r--src/libsyntax/attr.rs294
-rw-r--r--src/libsyntax/ext/base.rs2
-rw-r--r--src/libsyntax/util/small_vector.rs2
-rw-r--r--src/test/auxiliary/inherited_stability.rs2
-rw-r--r--src/test/auxiliary/lint_output_format.rs2
-rw-r--r--src/test/auxiliary/lint_stability.rs34
-rw-r--r--src/test/auxiliary/lint_stability_fields.rs12
-rw-r--r--src/test/compile-fail/issue-17337.rs4
-rw-r--r--src/test/compile-fail/lint-output-format.rs2
-rw-r--r--src/test/compile-fail/lint-stability-fields.rs12
-rw-r--r--src/test/compile-fail/lint-stability.rs26
-rw-r--r--src/test/compile-fail/stability-attribute-sanity.rs31
17 files changed, 299 insertions, 886 deletions
diff --git a/src/librustc/middle/stability.rs b/src/librustc/middle/stability.rs
index c2235591cee..f38ee19a274 100644
--- a/src/librustc/middle/stability.rs
+++ b/src/librustc/middle/stability.rs
@@ -11,6 +11,8 @@
 //! A pass that annotates every item and method with its stability level,
 //! propagating default levels lexically from parent to children ast nodes.
 
+pub use self::StabilityLevel::*;
+
 use session::Session;
 use lint;
 use metadata::cstore::LOCAL_CRATE;
@@ -34,6 +36,18 @@ use rustc_front::visit::{self, FnKind, Visitor};
 use std::mem::replace;
 use std::cmp::Ordering;
 
+#[derive(RustcEncodable, RustcDecodable, PartialEq, PartialOrd, Clone, Copy, Debug, Eq, Hash)]
+pub enum StabilityLevel {
+    Unstable,
+    Stable,
+}
+
+impl StabilityLevel {
+    pub fn from_attr_level(level: &attr::StabilityLevel) -> Self {
+        if level.is_stable() { Stable } else { Unstable }
+    }
+}
+
 /// A stability index, giving the stability level for items and methods.
 pub struct Index<'tcx> {
     /// This is mostly a cache, except the stabilities of local items
@@ -67,10 +81,9 @@ impl<'a, 'tcx: 'a> Annotator<'a, 'tcx> {
                     // if parent is deprecated and we're not, inherit this by merging
                     // deprecated_since and its reason.
                     if let Some(parent_stab) = self.parent {
-                        if parent_stab.deprecated_since.is_some()
-                        && stab.deprecated_since.is_none() {
-                            stab.deprecated_since = parent_stab.deprecated_since.clone();
-                            stab.reason = parent_stab.reason.clone();
+                        if parent_stab.depr.is_some()
+                        && stab.depr.is_none() {
+                            stab.depr = parent_stab.depr.clone()
                         }
                     }
 
@@ -78,9 +91,9 @@ impl<'a, 'tcx: 'a> Annotator<'a, 'tcx> {
 
                     // Check if deprecated_since < stable_since. If it is,
                     // this is *almost surely* an accident.
-                    let deprecated_predates_stable = match (stab.deprecated_since.as_ref(),
-                                                            stab.since.as_ref()) {
-                        (Some(dep_since), Some(stab_since)) => {
+                    let deprecated_predates_stable = match (&stab.depr, &stab.level) {
+                        (&Some(attr::Deprecation {since: ref dep_since, ..}),
+                               &attr::Stable {since: ref stab_since}) => {
                             // explicit version of iter::order::lt to handle parse errors properly
                             let mut is_less = false;
                             for (dep_v, stab_v) in dep_since.split(".").zip(stab_since.split(".")) {
@@ -117,7 +130,7 @@ impl<'a, 'tcx: 'a> Annotator<'a, 'tcx> {
                     self.index.map.insert(def_id, Some(stab));
 
                     // Don't inherit #[stable(feature = "rust1", since = "1.0.0")]
-                    if stab.level != attr::Stable {
+                    if !stab.level.is_stable() {
                         let parent = replace(&mut self.parent, Some(stab));
                         f(self);
                         self.parent = parent;
@@ -261,7 +274,7 @@ impl<'tcx> Index<'tcx> {
 /// features and possibly prints errors. Returns a list of all
 /// features used.
 pub fn check_unstable_api_usage(tcx: &ty::ctxt)
-                                -> FnvHashMap<InternedString, attr::StabilityLevel> {
+                                -> FnvHashMap<InternedString, StabilityLevel> {
     let ref active_lib_features = tcx.sess.features.borrow().declared_lib_features;
 
     // Put the active features into a map for quick lookup
@@ -284,7 +297,7 @@ pub fn check_unstable_api_usage(tcx: &ty::ctxt)
 struct Checker<'a, 'tcx: 'a> {
     tcx: &'a ty::ctxt<'tcx>,
     active_features: FnvHashSet<InternedString>,
-    used_features: FnvHashMap<InternedString, attr::StabilityLevel>,
+    used_features: FnvHashMap<InternedString, StabilityLevel>,
     // Within a block where feature gate checking can be skipped.
     in_skip_block: u32,
 }
@@ -303,8 +316,8 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
         }
 
         match *stab {
-            Some(&Stability { level: attr::Unstable, ref feature, ref reason, issue, .. }) => {
-                self.used_features.insert(feature.clone(), attr::Unstable);
+            Some(&Stability { level: attr::Unstable {ref reason, issue}, ref feature, .. }) => {
+                self.used_features.insert(feature.clone(), Unstable);
 
                 if !self.active_features.contains(feature) {
                     let msg = match *reason {
@@ -312,13 +325,12 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
                                                &feature, &r),
                         None => format!("use of unstable library feature '{}'", &feature)
                     };
-
                     emit_feature_err(&self.tcx.sess.parse_sess.span_diagnostic,
-                                      &feature, span, GateIssue::Library(issue), &msg);
+                                      &feature, span, GateIssue::Library(Some(issue)), &msg);
                 }
             }
-            Some(&Stability { level, ref feature, .. }) => {
-                self.used_features.insert(feature.clone(), level);
+            Some(&Stability { ref level, ref feature, .. }) => {
+                self.used_features.insert(feature.clone(), StabilityLevel::from_attr_level(level));
 
                 // Stable APIs are always ok to call and deprecated APIs are
                 // handled by a lint.
@@ -636,7 +648,7 @@ fn lookup_uncached<'tcx>(tcx: &ty::ctxt<'tcx>, id: DefId) -> Option<&'tcx Stabil
 /// libraries, identify activated features that don't exist and error about them.
 pub fn check_unused_or_stable_features(sess: &Session,
                                        lib_features_used: &FnvHashMap<InternedString,
-                                                                      attr::StabilityLevel>) {
+                                                                      StabilityLevel>) {
     let ref declared_lib_features = sess.features.borrow().declared_lib_features;
     let mut remaining_lib_features: FnvHashMap<InternedString, Span>
         = declared_lib_features.clone().into_iter().collect();
@@ -653,7 +665,7 @@ pub fn check_unused_or_stable_features(sess: &Session,
     for (used_lib_feature, level) in lib_features_used {
         match remaining_lib_features.remove(used_lib_feature) {
             Some(span) => {
-                if *level == attr::Stable {
+                if *level == Stable {
                     sess.add_lint(lint::builtin::STABLE_FEATURES,
                                   ast::CRATE_NODE_ID,
                                   span,
diff --git a/src/librustc_front/attr.rs b/src/librustc_front/attr.rs
deleted file mode 100644
index 1a564eb28a3..00000000000
--- a/src/librustc_front/attr.rs
+++ /dev/null
@@ -1,625 +0,0 @@
-// Copyright 2012-2015 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.
-
-// Functions dealing with attributes and meta items
-
-pub use self::StabilityLevel::*;
-pub use self::ReprAttr::*;
-pub use self::IntType::*;
-
-use hir;
-use syntax::codemap::{Span, Spanned, spanned, dummy_spanned};
-use syntax::codemap::BytePos;
-use syntax::diagnostic::SpanHandler;
-use syntax::attr as syntax_attr;
-use syntax::parse::lexer::comments::{doc_comment_style, strip_doc_comment_decoration};
-use syntax::parse::token::{InternedString, intern_and_get_ident};
-use syntax::parse::token;
-use syntax::ptr::P;
-
-use std::cell::Cell;
-use std::collections::HashSet;
-use std::fmt;
-
-pub fn mark_used(attr: &Attribute) {
-    syntax_attr::mark_used(&unlower_attribute(attr))
-}
-
-pub trait AttrMetaMethods {
-    fn check_name(&self, name: &str) -> bool {
-        name == &self.name()[..]
-    }
-
-    /// Retrieve the name of the meta item, e.g. `foo` in `#[foo]`,
-    /// `#[foo="bar"]` and `#[foo(bar)]`
-    fn name(&self) -> InternedString;
-
-    /// Gets the string value if self is a MetaNameValue variant
-    /// containing a string, otherwise None.
-    fn value_str(&self) -> Option<InternedString>;
-    /// Gets a list of inner meta items from a list MetaItem type.
-    fn meta_item_list<'a>(&'a self) -> Option<&'a [P<MetaItem>]>;
-
-    fn span(&self) -> Span;
-}
-
-impl AttrMetaMethods for Attribute {
-    fn check_name(&self, name: &str) -> bool {
-        let matches = name == &self.name()[..];
-        if matches {
-            syntax_attr::mark_used(&unlower_attribute(self));
-        }
-        matches
-    }
-    fn name(&self) -> InternedString { self.meta().name() }
-    fn value_str(&self) -> Option<InternedString> {
-        self.meta().value_str()
-    }
-    fn meta_item_list<'a>(&'a self) -> Option<&'a [P<MetaItem>]> {
-        self.node.value.meta_item_list()
-    }
-    fn span(&self) -> Span { self.meta().span }
-}
-
-impl AttrMetaMethods for MetaItem {
-    fn name(&self) -> InternedString {
-        match self.node {
-            MetaWord(ref n) => (*n).clone(),
-            MetaNameValue(ref n, _) => (*n).clone(),
-            MetaList(ref n, _) => (*n).clone(),
-        }
-    }
-
-    fn value_str(&self) -> Option<InternedString> {
-        match self.node {
-            MetaNameValue(_, ref v) => {
-                match v.node {
-                    hir::LitStr(ref s, _) => Some((*s).clone()),
-                    _ => None,
-                }
-            },
-            _ => None
-        }
-    }
-
-    fn meta_item_list<'a>(&'a self) -> Option<&'a [P<MetaItem>]> {
-        match self.node {
-            MetaList(_, ref l) => Some(&l[..]),
-            _ => None
-        }
-    }
-    fn span(&self) -> Span { self.span }
-}
-
-// Annoying, but required to get test_cfg to work
-impl AttrMetaMethods for P<MetaItem> {
-    fn name(&self) -> InternedString { (**self).name() }
-    fn value_str(&self) -> Option<InternedString> { (**self).value_str() }
-    fn meta_item_list<'a>(&'a self) -> Option<&'a [P<MetaItem>]> {
-        (**self).meta_item_list()
-    }
-    fn span(&self) -> Span { (**self).span() }
-}
-
-
-pub trait AttributeMethods {
-    fn meta<'a>(&'a self) -> &'a MetaItem;
-    fn with_desugared_doc<T, F>(&self, f: F) -> T where
-        F: FnOnce(&Attribute) -> T;
-}
-
-impl AttributeMethods for Attribute {
-    /// Extract the MetaItem from inside this Attribute.
-    fn meta<'a>(&'a self) -> &'a MetaItem {
-        &*self.node.value
-    }
-
-    /// Convert self to a normal #[doc="foo"] comment, if it is a
-    /// comment like `///` or `/** */`. (Returns self unchanged for
-    /// non-sugared doc attributes.)
-    fn with_desugared_doc<T, F>(&self, f: F) -> T where
-        F: FnOnce(&Attribute) -> T,
-    {
-        if self.node.is_sugared_doc {
-            let comment = self.value_str().unwrap();
-            let meta = mk_name_value_item_str(
-                InternedString::new("doc"),
-                token::intern_and_get_ident(&strip_doc_comment_decoration(
-                        &comment)));
-            if self.node.style == hir::AttrOuter {
-                f(&mk_attr_outer(self.node.id, meta))
-            } else {
-                f(&mk_attr_inner(self.node.id, meta))
-            }
-        } else {
-            f(self)
-        }
-    }
-}
-
-/* Constructors */
-
-pub fn mk_name_value_item_str(name: InternedString, value: InternedString)
-                              -> P<MetaItem> {
-    let value_lit = dummy_spanned(hir::LitStr(value, hir::CookedStr));
-    mk_name_value_item(name, value_lit)
-}
-
-pub fn mk_name_value_item(name: InternedString, value: hir::Lit)
-                          -> P<MetaItem> {
-    P(dummy_spanned(MetaNameValue(name, value)))
-}
-
-pub fn mk_list_item(name: InternedString, items: Vec<P<MetaItem>>) -> P<MetaItem> {
-    P(dummy_spanned(MetaList(name, items)))
-}
-
-pub fn mk_word_item(name: InternedString) -> P<MetaItem> {
-    P(dummy_spanned(MetaWord(name)))
-}
-
-thread_local! { static NEXT_ATTR_ID: Cell<usize> = Cell::new(0) }
-
-pub fn mk_attr_id() -> AttrId {
-    let id = NEXT_ATTR_ID.with(|slot| {
-        let r = slot.get();
-        slot.set(r + 1);
-        r
-    });
-    AttrId(id)
-}
-
-/// Returns an inner attribute with the given value.
-pub fn mk_attr_inner(id: AttrId, item: P<MetaItem>) -> Attribute {
-    dummy_spanned(Attribute_ {
-        id: id,
-        style: hir::AttrStyle::Inner,
-        value: item,
-        is_sugared_doc: false,
-    })
-}
-
-/// Returns an outer attribute with the given value.
-pub fn mk_attr_outer(id: AttrId, item: P<MetaItem>) -> Attribute {
-    dummy_spanned(Attribute_ {
-        id: id,
-        style: hir::AttrStyle::Outer,
-        value: item,
-        is_sugared_doc: false,
-    })
-}
-
-pub fn mk_sugared_doc_attr(id: AttrId, text: InternedString, lo: BytePos,
-                           hi: BytePos)
-                           -> Attribute {
-    let style = lower_attr_style(doc_comment_style(&text));
-    let lit = spanned(lo, hi, hir::LitStr(text, hir::CookedStr));
-    let attr = Attribute_ {
-        id: id,
-        style: style,
-        value: P(spanned(lo, hi, MetaNameValue(InternedString::new("doc"),
-                                               lit))),
-        is_sugared_doc: true
-    };
-    spanned(lo, hi, attr)
-}
-
-/* Searching */
-/// Check if `needle` occurs in `haystack` by a structural
-/// comparison. This is slightly subtle, and relies on ignoring the
-/// span included in the `==` comparison a plain MetaItem.
-pub fn contains(haystack: &[P<MetaItem>], needle: &MetaItem) -> bool {
-    debug!("attr::contains (name={})", needle.name());
-    haystack.iter().any(|item| {
-        debug!("  testing: {}", item.name());
-        item.node == needle.node
-    })
-}
-
-pub fn contains_name<AM: AttrMetaMethods>(metas: &[AM], name: &str) -> bool {
-    debug!("attr::contains_name (name={})", name);
-    metas.iter().any(|item| {
-        debug!("  testing: {}", item.name());
-        item.check_name(name)
-    })
-}
-
-pub fn first_attr_value_str_by_name(attrs: &[Attribute], name: &str)
-                                 -> Option<InternedString> {
-    attrs.iter()
-        .find(|at| at.check_name(name))
-        .and_then(|at| at.value_str())
-}
-
-pub fn last_meta_item_value_str_by_name(items: &[P<MetaItem>], name: &str)
-                                     -> Option<InternedString> {
-    items.iter()
-         .rev()
-         .find(|mi| mi.check_name(name))
-         .and_then(|i| i.value_str())
-}
-
-/* Higher-level applications */
-
-pub fn sort_meta_items(items: Vec<P<MetaItem>>) -> Vec<P<MetaItem>> {
-    // This is sort of stupid here, but we need to sort by
-    // human-readable strings.
-    let mut v = items.into_iter()
-        .map(|mi| (mi.name(), mi))
-        .collect::<Vec<(InternedString, P<MetaItem>)>>();
-
-    v.sort_by(|&(ref a, _), &(ref b, _)| a.cmp(b));
-
-    // There doesn't seem to be a more optimal way to do this
-    v.into_iter().map(|(_, m)| m.map(|Spanned {node, span}| {
-        Spanned {
-            node: match node {
-                MetaList(n, mis) => MetaList(n, sort_meta_items(mis)),
-                _ => node
-            },
-            span: span
-        }
-    })).collect()
-}
-
-pub fn find_crate_name(attrs: &[Attribute]) -> Option<InternedString> {
-    first_attr_value_str_by_name(attrs, "crate_name")
-}
-
-/// Find the value of #[export_name=*] attribute and check its validity.
-pub fn find_export_name_attr(diag: &SpanHandler, attrs: &[Attribute]) -> Option<InternedString> {
-    attrs.iter().fold(None, |ia,attr| {
-        if attr.check_name("export_name") {
-            if let s@Some(_) = attr.value_str() {
-                s
-            } else {
-                diag.span_err(attr.span, "export_name attribute has invalid format");
-                diag.handler.help("use #[export_name=\"*\"]");
-                None
-            }
-        } else {
-            ia
-        }
-    })
-}
-
-#[derive(Copy, Clone, PartialEq)]
-pub enum InlineAttr {
-    None,
-    Hint,
-    Always,
-    Never,
-}
-
-/// Determine what `#[inline]` attribute is present in `attrs`, if any.
-pub fn find_inline_attr(diagnostic: Option<&SpanHandler>, attrs: &[Attribute]) -> InlineAttr {
-    attrs.iter().fold(InlineAttr::None, |ia,attr| {
-        match attr.node.value.node {
-            MetaWord(ref n) if *n == "inline" => {
-                syntax_attr::mark_used(&unlower_attribute(attr));
-                InlineAttr::Hint
-            }
-            MetaList(ref n, ref items) if *n == "inline" => {
-                syntax_attr::mark_used(&unlower_attribute(attr));
-                if items.len() != 1 {
-                    diagnostic.map(|d|{ d.span_err(attr.span, "expected one argument"); });
-                    InlineAttr::None
-                } else if contains_name(&items[..], "always") {
-                    InlineAttr::Always
-                } else if contains_name(&items[..], "never") {
-                    InlineAttr::Never
-                } else {
-                    diagnostic.map(|d|{ d.span_err((*items[0]).span, "invalid argument"); });
-                    InlineAttr::None
-                }
-            }
-            _ => ia
-        }
-    })
-}
-
-/// True if `#[inline]` or `#[inline(always)]` is present in `attrs`.
-pub fn requests_inline(attrs: &[Attribute]) -> bool {
-    match find_inline_attr(None, attrs) {
-        InlineAttr::Hint | InlineAttr::Always => true,
-        InlineAttr::None | InlineAttr::Never => false,
-    }
-}
-
-/// Represents the #[deprecated] and friends attributes.
-#[derive(RustcEncodable, RustcDecodable, Clone, Debug, PartialEq, Eq, Hash)]
-pub struct Stability {
-    pub level: StabilityLevel,
-    pub feature: InternedString,
-    pub since: Option<InternedString>,
-    pub deprecated_since: Option<InternedString>,
-    // The reason for the current stability level. If deprecated, the
-    // reason for deprecation.
-    pub reason: Option<InternedString>,
-    // The relevant rust-lang issue
-    pub issue: Option<u32>
-}
-
-/// The available stability levels.
-#[derive(RustcEncodable, RustcDecodable, PartialEq, PartialOrd, Clone, Debug, Copy, Eq, Hash)]
-pub enum StabilityLevel {
-    Unstable,
-    Stable,
-}
-
-impl fmt::Display for StabilityLevel {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        fmt::Debug::fmt(self, f)
-    }
-}
-
-fn find_stability_generic<'a,
-                              AM: AttrMetaMethods,
-                              I: Iterator<Item=&'a AM>>
-                             (diagnostic: &SpanHandler, attrs: I, item_sp: Span)
-                             -> (Option<Stability>, Vec<&'a AM>) {
-
-    let mut stab: Option<Stability> = None;
-    let mut deprecated: Option<(Option<InternedString>, Option<InternedString>)> = None;
-    let mut used_attrs: Vec<&'a AM> = vec![];
-
-    'outer: for attr in attrs {
-        let tag = attr.name();
-        let tag = &tag[..];
-        if tag != "deprecated" && tag != "unstable" && tag != "stable" {
-            continue // not a stability level
-        }
-
-        used_attrs.push(attr);
-
-        let (feature, since, reason, issue) = match attr.meta_item_list() {
-            Some(metas) => {
-                let mut feature = None;
-                let mut since = None;
-                let mut reason = None;
-                let mut issue = None;
-                for meta in metas {
-                    match &*meta.name() {
-                        "feature" => {
-                            match meta.value_str() {
-                                Some(v) => feature = Some(v),
-                                None => {
-                                    diagnostic.span_err(meta.span, "incorrect meta item");
-                                    continue 'outer;
-                                }
-                            }
-                        }
-                        "since" => {
-                            match meta.value_str() {
-                                Some(v) => since = Some(v),
-                                None => {
-                                    diagnostic.span_err(meta.span, "incorrect meta item");
-                                    continue 'outer;
-                                }
-                            }
-                        }
-                        "reason" => {
-                            match meta.value_str() {
-                                Some(v) => reason = Some(v),
-                                None => {
-                                    diagnostic.span_err(meta.span, "incorrect meta item");
-                                    continue 'outer;
-                                }
-                            }
-                        }
-                        "issue" => {
-                            match meta.value_str().and_then(|s| s.parse().ok()) {
-                                Some(v) => issue = Some(v),
-                                None => {
-                                    diagnostic.span_err(meta.span, "incorrect meta item");
-                                    continue 'outer;
-                                }
-                            }
-                        }
-                        _ => {}
-                    }
-                }
-                (feature, since, reason, issue)
-            }
-            None => {
-                diagnostic.span_err(attr.span(), "incorrect stability attribute type");
-                continue
-            }
-        };
-
-        // Deprecated tags don't require feature names
-        if feature == None && tag != "deprecated" {
-            diagnostic.span_err(attr.span(), "missing 'feature'");
-        }
-
-        // Unstable tags don't require a version
-        if since == None && tag != "unstable" {
-            diagnostic.span_err(attr.span(), "missing 'since'");
-        }
-
-        if tag == "unstable" || tag == "stable" {
-            if stab.is_some() {
-                diagnostic.span_err(item_sp, "multiple stability levels");
-            }
-
-            let level = match tag {
-                "unstable" => Unstable,
-                "stable" => Stable,
-                _ => unreachable!()
-            };
-
-            stab = Some(Stability {
-                level: level,
-                feature: feature.unwrap_or(intern_and_get_ident("bogus")),
-                since: since,
-                deprecated_since: None,
-                reason: reason,
-                issue: issue,
-            });
-        } else { // "deprecated"
-            if deprecated.is_some() {
-                diagnostic.span_err(item_sp, "multiple deprecated attributes");
-            }
-
-            deprecated = Some((since, reason));
-        }
-    }
-
-    // Merge the deprecation info into the stability info
-    if deprecated.is_some() {
-        match stab {
-            Some(ref mut s) => {
-                let (since, reason) = deprecated.unwrap();
-                s.deprecated_since = since;
-                s.reason = reason;
-            }
-            None => {
-                diagnostic.span_err(item_sp, "deprecated attribute must be paired with \
-                                              either stable or unstable attribute");
-            }
-        }
-    } else if stab.as_ref().map_or(false, |s| s.level == Unstable && s.issue.is_none()) {
-        // non-deprecated unstable items need to point to issues.
-        diagnostic.span_err(item_sp,
-                            "non-deprecated unstable items need to point \
-                             to an issue with `issue = \"NNN\"`");
-    }
-
-    (stab, used_attrs)
-}
-
-/// Find the first stability attribute. `None` if none exists.
-pub fn find_stability(diagnostic: &SpanHandler, attrs: &[Attribute],
-                      item_sp: Span) -> Option<Stability> {
-    let (s, used) = find_stability_generic(diagnostic, attrs.iter(), item_sp);
-    for used in used { syntax_attr::mark_used(&unlower_attribute(used)) }
-    return s;
-}
-
-pub fn require_unique_names(diagnostic: &SpanHandler, metas: &[P<MetaItem>]) {
-    let mut set = HashSet::new();
-    for meta in metas {
-        let name = meta.name();
-
-        if !set.insert(name.clone()) {
-            panic!(diagnostic.span_fatal(meta.span,
-                                  &format!("duplicate meta item `{}`", name)));
-        }
-    }
-}
-
-
-/// Parse #[repr(...)] forms.
-///
-/// Valid repr contents: any of the primitive integral type names (see
-/// `int_type_of_word`, below) to specify enum discriminant type; `C`, to use
-/// the same discriminant size that the corresponding C enum would or C
-/// structure layout, and `packed` to remove padding.
-pub fn find_repr_attrs(diagnostic: &SpanHandler, attr: &Attribute) -> Vec<ReprAttr> {
-    let mut acc = Vec::new();
-    match attr.node.value.node {
-        hir::MetaList(ref s, ref items) if *s == "repr" => {
-            syntax_attr::mark_used(&unlower_attribute(attr));
-            for item in items {
-                match item.node {
-                    hir::MetaWord(ref word) => {
-                        let hint = match &word[..] {
-                            // Can't use "extern" because it's not a lexical identifier.
-                            "C" => Some(ReprExtern),
-                            "packed" => Some(ReprPacked),
-                            "simd" => Some(ReprSimd),
-                            _ => match int_type_of_word(&word) {
-                                Some(ity) => Some(ReprInt(item.span, ity)),
-                                None => {
-                                    // Not a word we recognize
-                                    diagnostic.span_err(item.span,
-                                                        "unrecognized representation hint");
-                                    None
-                                }
-                            }
-                        };
-
-                        match hint {
-                            Some(h) => acc.push(h),
-                            None => { }
-                        }
-                    }
-                    // Not a word:
-                    _ => diagnostic.span_err(item.span, "unrecognized enum representation hint")
-                }
-            }
-        }
-        // Not a "repr" hint: ignore.
-        _ => { }
-    }
-    acc
-}
-
-fn int_type_of_word(s: &str) -> Option<IntType> {
-    match s {
-        "i8" => Some(SignedInt(hir::TyI8)),
-        "u8" => Some(UnsignedInt(hir::TyU8)),
-        "i16" => Some(SignedInt(hir::TyI16)),
-        "u16" => Some(UnsignedInt(hir::TyU16)),
-        "i32" => Some(SignedInt(hir::TyI32)),
-        "u32" => Some(UnsignedInt(hir::TyU32)),
-        "i64" => Some(SignedInt(hir::TyI64)),
-        "u64" => Some(UnsignedInt(hir::TyU64)),
-        "isize" => Some(SignedInt(hir::TyIs)),
-        "usize" => Some(UnsignedInt(hir::TyUs)),
-        _ => None
-    }
-}
-
-#[derive(PartialEq, Debug, RustcEncodable, RustcDecodable, Copy, Clone)]
-pub enum ReprAttr {
-    ReprAny,
-    ReprInt(Span, IntType),
-    ReprExtern,
-    ReprPacked,
-    ReprSimd,
-}
-
-impl ReprAttr {
-    pub fn is_ffi_safe(&self) -> bool {
-        match *self {
-            ReprAny => false,
-            ReprInt(_sp, ity) => ity.is_ffi_safe(),
-            ReprExtern => true,
-            ReprPacked => false,
-            ReprSimd => true,
-        }
-    }
-}
-
-#[derive(Eq, Hash, PartialEq, Debug, RustcEncodable, RustcDecodable, Copy, Clone)]
-pub enum IntType {
-    SignedInt(hir::IntTy),
-    UnsignedInt(hir::UintTy)
-}
-
-impl IntType {
-    #[inline]
-    pub fn is_signed(self) -> bool {
-        match self {
-            SignedInt(..) => true,
-            UnsignedInt(..) => false
-        }
-    }
-    fn is_ffi_safe(self) -> bool {
-        match self {
-            SignedInt(hir::TyI8) | UnsignedInt(hir::TyU8) |
-            SignedInt(hir::TyI16) | UnsignedInt(hir::TyU16) |
-            SignedInt(hir::TyI32) | UnsignedInt(hir::TyU32) |
-            SignedInt(hir::TyI64) | UnsignedInt(hir::TyU64) => true,
-            SignedInt(hir::TyIs) | UnsignedInt(hir::TyUs) => false
-        }
-    }
-}
diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs
index 1df3c1609b8..490f57e6607 100644
--- a/src/librustc_lint/builtin.rs
+++ b/src/librustc_lint/builtin.rs
@@ -674,7 +674,7 @@ impl Stability {
             span: Span, stability: &Option<&attr::Stability>) {
         // Deprecated attributes apply in-crate and cross-crate.
         let (lint, label) = match *stability {
-            Some(&attr::Stability { deprecated_since: Some(_), .. }) =>
+            Some(&attr::Stability { depr: Some(_), .. }) =>
                 (DEPRECATED, "deprecated"),
             _ => return
         };
@@ -684,8 +684,8 @@ impl Stability {
         fn output(cx: &LateContext, span: Span, stability: &Option<&attr::Stability>,
                   lint: &'static Lint, label: &'static str) {
             let msg = match *stability {
-                Some(&attr::Stability { reason: Some(ref s), .. }) => {
-                    format!("use of {} item: {}", label, *s)
+                Some(&attr::Stability {depr: Some(attr::Deprecation {ref reason, ..}), ..}) => {
+                    format!("use of {} item: {}", label, reason)
                 }
                 _ => format!("use of {} item", label)
             };
@@ -695,20 +695,6 @@ impl Stability {
     }
 }
 
-fn hir_to_ast_stability(stab: &attr::Stability) -> attr::Stability {
-    attr::Stability {
-        level: match stab.level {
-            attr::Unstable => attr::Unstable,
-            attr::Stable => attr::Stable,
-        },
-        feature: stab.feature.clone(),
-        since: stab.since.clone(),
-        deprecated_since: stab.deprecated_since.clone(),
-        reason: stab.reason.clone(),
-        issue: stab.issue,
-    }
-}
-
 impl LintPass for Stability {
     fn get_lints(&self) -> LintArray {
         lint_array!(DEPRECATED)
@@ -719,36 +705,31 @@ impl LateLintPass for Stability {
     fn check_item(&mut self, cx: &LateContext, item: &hir::Item) {
         stability::check_item(cx.tcx, item, false,
                               &mut |id, sp, stab|
-                                self.lint(cx, id, sp,
-                                          &stab.map(|s| hir_to_ast_stability(s)).as_ref()));
+                                self.lint(cx, id, sp, &stab));
     }
 
     fn check_expr(&mut self, cx: &LateContext, e: &hir::Expr) {
         stability::check_expr(cx.tcx, e,
                               &mut |id, sp, stab|
-                                self.lint(cx, id, sp,
-                                          &stab.map(|s| hir_to_ast_stability(s)).as_ref()));
+                                self.lint(cx, id, sp, &stab));
     }
 
     fn check_path(&mut self, cx: &LateContext, path: &hir::Path, id: ast::NodeId) {
         stability::check_path(cx.tcx, path, id,
                               &mut |id, sp, stab|
-                                self.lint(cx, id, sp,
-                                          &stab.map(|s| hir_to_ast_stability(s)).as_ref()));
+                                self.lint(cx, id, sp, &stab));
     }
 
     fn check_path_list_item(&mut self, cx: &LateContext, item: &hir::PathListItem) {
         stability::check_path_list_item(cx.tcx, item,
                                          &mut |id, sp, stab|
-                                           self.lint(cx, id, sp,
-                                                &stab.map(|s| hir_to_ast_stability(s)).as_ref()));
+                                           self.lint(cx, id, sp, &stab));
     }
 
     fn check_pat(&mut self, cx: &LateContext, pat: &hir::Pat) {
         stability::check_pat(cx.tcx, pat,
                              &mut |id, sp, stab|
-                                self.lint(cx, id, sp,
-                                          &stab.map(|s| hir_to_ast_stability(s)).as_ref()));
+                                self.lint(cx, id, sp, &stab));
     }
 }
 
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 160c7e7d754..88e254b86b2 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -324,8 +324,8 @@ impl Item {
         match self.stability {
             Some(ref s) => {
                 let mut base = match s.level {
-                    attr::Unstable => "unstable".to_string(),
-                    attr::Stable => String::new(),
+                    stability::Unstable => "unstable".to_string(),
+                    stability::Stable => String::new(),
                 };
                 if !s.deprecated_since.is_empty() {
                     base.push_str(" deprecated");
@@ -2679,7 +2679,7 @@ impl Clean<Item> for doctree::Macro {
 
 #[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
 pub struct Stability {
-    pub level: attr::StabilityLevel,
+    pub level: stability::StabilityLevel,
     pub feature: String,
     pub since: String,
     pub deprecated_since: String,
@@ -2690,32 +2690,31 @@ pub struct Stability {
 impl Clean<Stability> for attr::Stability {
     fn clean(&self, _: &DocContext) -> Stability {
         Stability {
-            level: self.level,
+            level: stability::StabilityLevel::from_attr_level(&self.level),
             feature: self.feature.to_string(),
-            since: self.since.as_ref().map_or("".to_string(),
-                                              |interned| interned.to_string()),
-            deprecated_since: self.deprecated_since.as_ref().map_or("".to_string(),
-                                                                    |istr| istr.to_string()),
-            reason: self.reason.as_ref().map_or("".to_string(),
-                                                |interned| interned.to_string()),
-            issue: self.issue,
+            since: match self.level {
+                attr::Stable {ref since} => since.to_string(),
+                _ => "".to_string(),
+            },
+            deprecated_since: match self.depr {
+                Some(attr::Deprecation {ref since, ..}) => since.to_string(),
+                _=> "".to_string(),
+            },
+            reason: match self.level {
+                attr::Unstable {reason: Some(ref reason), ..} => reason.to_string(),
+                _ => "".to_string(),
+            },
+            issue: match self.level {
+                attr::Unstable {issue, ..} => Some(issue),
+                _ => None,
+            }
         }
     }
 }
 
 impl<'a> Clean<Stability> for &'a attr::Stability {
-    fn clean(&self, _: &DocContext) -> Stability {
-        Stability {
-            level: self.level,
-            feature: self.feature.to_string(),
-            since: self.since.as_ref().map_or("".to_string(),
-                                              |interned| interned.to_string()),
-            deprecated_since: self.deprecated_since.as_ref().map_or("".to_string(),
-                                                                    |istr| istr.to_string()),
-            reason: self.reason.as_ref().map_or("".to_string(),
-                                                |interned| interned.to_string()),
-            issue: self.issue,
-        }
+    fn clean(&self, dc: &DocContext) -> Stability {
+        (**self).clean(dc)
     }
 }
 
diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs
index 05d88de812d..d6fee744da6 100644
--- a/src/librustdoc/html/render.rs
+++ b/src/librustdoc/html/render.rs
@@ -52,9 +52,10 @@ use std::sync::Arc;
 use externalfiles::ExternalHtml;
 
 use serialize::json::{self, ToJson};
-use syntax::{abi, ast, attr};
+use syntax::{abi, ast};
 use rustc::metadata::cstore::LOCAL_CRATE;
 use rustc::middle::def_id::{CRATE_DEF_INDEX, DefId};
+use rustc::middle::stability;
 use rustc::util::nodemap::DefIdSet;
 use rustc_front::hir;
 
@@ -1608,8 +1609,8 @@ fn item_module(w: &mut fmt::Formatter, cx: &Context,
         let s1 = i1.stability.as_ref().map(|s| s.level);
         let s2 = i2.stability.as_ref().map(|s| s.level);
         match (s1, s2) {
-            (Some(attr::Unstable), Some(attr::Stable)) => return Ordering::Greater,
-            (Some(attr::Stable), Some(attr::Unstable)) => return Ordering::Less,
+            (Some(stability::Unstable), Some(stability::Stable)) => return Ordering::Greater,
+            (Some(stability::Stable), Some(stability::Unstable)) => return Ordering::Less,
             _ => {}
         }
         i1.name.cmp(&i2.name)
@@ -1724,7 +1725,7 @@ fn short_stability(item: &clean::Item, cx: &Context, show_reason: bool) -> Optio
                 String::new()
             };
             format!("Deprecated{}{}", since, Markdown(&reason))
-        } else if stab.level == attr::Unstable {
+        } else if stab.level == stability::Unstable {
             let unstable_extra = if show_reason {
                 match (!stab.feature.is_empty(), &cx.issue_tracker_base_url, stab.issue) {
                     (true, &Some(ref tracker_url), Some(issue_no)) =>
diff --git a/src/libsyntax/attr.rs b/src/libsyntax/attr.rs
index bd99d33222d..eeb832d48b0 100644
--- a/src/libsyntax/attr.rs
+++ b/src/libsyntax/attr.rs
@@ -27,7 +27,6 @@ use ptr::P;
 
 use std::cell::{RefCell, Cell};
 use std::collections::HashSet;
-use std::fmt;
 
 thread_local! {
     static USED_ATTRS: RefCell<Vec<u64>> = RefCell::new(Vec::new())
@@ -382,174 +381,223 @@ pub fn cfg_matches(diagnostic: &SpanHandler, cfgs: &[P<MetaItem>], cfg: &ast::Me
     }
 }
 
-/// Represents the #[deprecated] and friends attributes.
+/// Represents the #[stable], #[unstable] and #[deprecated] attributes.
 #[derive(RustcEncodable, RustcDecodable, Clone, Debug, PartialEq, Eq, Hash)]
 pub struct Stability {
     pub level: StabilityLevel,
     pub feature: InternedString,
-    pub since: Option<InternedString>,
-    pub deprecated_since: Option<InternedString>,
-    // The reason for the current stability level. If deprecated, the
-    // reason for deprecation.
-    pub reason: Option<InternedString>,
-    // The relevant rust-lang issue
-    pub issue: Option<u32>
+    pub depr: Option<Deprecation>,
 }
 
 /// The available stability levels.
-#[derive(RustcEncodable, RustcDecodable, PartialEq, PartialOrd, Clone, Debug, Copy, Eq, Hash)]
+#[derive(RustcEncodable, RustcDecodable, PartialEq, PartialOrd, Clone, Debug, Eq, Hash)]
 pub enum StabilityLevel {
-    Unstable,
-    Stable,
+    // Reason for the current stability level and the relevant rust-lang issue
+    Unstable { reason: Option<InternedString>, issue: u32 },
+    Stable { since: InternedString },
 }
 
-impl fmt::Display for StabilityLevel {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        fmt::Debug::fmt(self, f)
-    }
+#[derive(RustcEncodable, RustcDecodable, PartialEq, PartialOrd, Clone, Debug, Eq, Hash)]
+pub struct Deprecation {
+    pub since: InternedString,
+    pub reason: InternedString,
 }
 
-fn find_stability_generic<'a,
-                              AM: AttrMetaMethods,
-                              I: Iterator<Item=&'a AM>>
-                             (diagnostic: &SpanHandler, attrs: I, item_sp: Span)
-                             -> (Option<Stability>, Vec<&'a AM>) {
+impl StabilityLevel {
+    pub fn is_unstable(&self) -> bool { if let Unstable {..} = *self { true } else { false }}
+    pub fn is_stable(&self) -> bool { if let Stable {..} = *self { true } else { false }}
+}
 
+fn find_stability_generic<'a, I>(diagnostic: &SpanHandler,
+                                 attrs_iter: I,
+                                 item_sp: Span)
+                                 -> Option<Stability>
+    where I: Iterator<Item = &'a Attribute>
+{
     let mut stab: Option<Stability> = None;
-    let mut deprecated: Option<(Option<InternedString>, Option<InternedString>)> = None;
-    let mut used_attrs: Vec<&'a AM> = vec![];
+    let mut depr: Option<Deprecation> = None;
 
-    'outer: for attr in attrs {
+    'outer: for attr in attrs_iter {
         let tag = attr.name();
-        let tag = &tag[..];
+        let tag = &*tag;
         if tag != "deprecated" && tag != "unstable" && tag != "stable" {
             continue // not a stability level
         }
 
-        used_attrs.push(attr);
-
-        let (feature, since, reason, issue) = match attr.meta_item_list() {
-            Some(metas) => {
-                let mut feature = None;
-                let mut since = None;
-                let mut reason = None;
-                let mut issue = None;
-                for meta in metas {
-                    match &*meta.name() {
-                        "feature" => {
-                            match meta.value_str() {
-                                Some(v) => feature = Some(v),
-                                None => {
-                                    diagnostic.span_err(meta.span, "incorrect meta item");
-                                    continue 'outer;
-                                }
+        mark_used(attr);
+
+        if let Some(metas) = attr.meta_item_list() {
+            let get = |meta: &MetaItem, item: &mut Option<InternedString>| {
+                if item.is_some() {
+                    diagnostic.span_err(meta.span, &format!("multiple '{}' items",
+                                                             meta.name()));
+                    return false
+                }
+                if let Some(v) = meta.value_str() {
+                    *item = Some(v);
+                    true
+                } else {
+                    diagnostic.span_err(meta.span, "incorrect meta item");
+                    false
+                }
+            };
+
+            match tag {
+                "deprecated" => {
+                    if depr.is_some() {
+                        diagnostic.span_err(item_sp, "multiple deprecated attributes");
+                        break
+                    }
+
+                    let mut since = None;
+                    let mut reason = None;
+                    for meta in metas {
+                        match &*meta.name() {
+                            "since" => if !get(meta, &mut since) { continue 'outer },
+                            "reason" => if !get(meta, &mut reason) { continue 'outer },
+                            _ => {
+                                diagnostic.span_err(meta.span, &format!("unknown meta item '{}'",
+                                                                        meta.name()));
+                                continue 'outer
                             }
                         }
-                        "since" => {
-                            match meta.value_str() {
-                                Some(v) => since = Some(v),
-                                None => {
-                                    diagnostic.span_err(meta.span, "incorrect meta item");
-                                    continue 'outer;
-                                }
-                            }
+                    }
+
+                    match (since, reason) {
+                        (Some(since), Some(reason)) => {
+                            depr = Some(Deprecation {
+                                since: since,
+                                reason: reason,
+                            })
                         }
-                        "reason" => {
-                            match meta.value_str() {
-                                Some(v) => reason = Some(v),
-                                None => {
-                                    diagnostic.span_err(meta.span, "incorrect meta item");
-                                    continue 'outer;
-                                }
-                            }
+                        (None, _) => {
+                            diagnostic.span_err(attr.span(), "missing 'since'");
+                            continue
                         }
-                        "issue" => {
-                            match meta.value_str().and_then(|s| s.parse().ok()) {
-                                Some(v) => issue = Some(v),
-                                None => {
-                                    diagnostic.span_err(meta.span, "incorrect meta item");
-                                    continue 'outer;
-                                }
-                            }
+                        _ => {
+                            diagnostic.span_err(attr.span(), "missing 'reason'");
+                            continue
                         }
-                        _ => {}
                     }
                 }
-                (feature, since, reason, issue)
-            }
-            None => {
-                diagnostic.span_err(attr.span(), "incorrect stability attribute type");
-                continue
-            }
-        };
+                "unstable" => {
+                    if stab.is_some() {
+                        diagnostic.span_err(item_sp, "multiple stability levels");
+                        break
+                    }
 
-        // Deprecated tags don't require feature names
-        if feature == None && tag != "deprecated" {
-            diagnostic.span_err(attr.span(), "missing 'feature'");
-        }
+                    let mut feature = None;
+                    let mut reason = None;
+                    let mut issue = None;
+                    for meta in metas {
+                        match &*meta.name() {
+                            "feature" => if !get(meta, &mut feature) { continue 'outer },
+                            "reason" => if !get(meta, &mut reason) { continue 'outer },
+                            "issue" => if !get(meta, &mut issue) { continue 'outer },
+                            _ => {
+                                diagnostic.span_err(meta.span, &format!("unknown meta item '{}'",
+                                                                        meta.name()));
+                                continue 'outer
+                            }
+                        }
+                    }
 
-        // Unstable tags don't require a version
-        if since == None && tag != "unstable" {
-            diagnostic.span_err(attr.span(), "missing 'since'");
-        }
+                    match (feature, reason, issue) {
+                        (Some(feature), reason, Some(issue)) => {
+                            stab = Some(Stability {
+                                level: Unstable {
+                                    reason: reason,
+                                    issue: {
+                                        if let Ok(issue) = issue.parse() {
+                                            issue
+                                        } else {
+                                            diagnostic.span_err(attr.span(), "incorrect 'issue'");
+                                            continue
+                                        }
+                                    }
+                                },
+                                feature: feature,
+                                depr: None,
+                            })
+                        }
+                        (None, _, _) => {
+                            diagnostic.span_err(attr.span(), "missing 'feature'");
+                            continue
+                        }
+                        _ => {
+                            diagnostic.span_err(attr.span(), "missing 'issue'");
+                            continue
+                        }
+                    }
+                }
+                "stable" => {
+                    if stab.is_some() {
+                        diagnostic.span_err(item_sp, "multiple stability levels");
+                        break
+                    }
 
-        if tag == "unstable" || tag == "stable" {
-            if stab.is_some() {
-                diagnostic.span_err(item_sp, "multiple stability levels");
-            }
+                    let mut feature = None;
+                    let mut since = None;
+                    for meta in metas {
+                        match &*meta.name() {
+                            "feature" => if !get(meta, &mut feature) { continue 'outer },
+                            "since" => if !get(meta, &mut since) { continue 'outer },
+                            _ => {
+                                diagnostic.span_err(meta.span, &format!("unknown meta item '{}'",
+                                                                        meta.name()));
+                                continue 'outer
+                            }
+                        }
+                    }
 
-            let level = match tag {
-                "unstable" => Unstable,
-                "stable" => Stable,
+                    match (feature, since) {
+                        (Some(feature), Some(since)) => {
+                            stab = Some(Stability {
+                                level: Stable {
+                                    since: since,
+                                },
+                                feature: feature,
+                                depr: None,
+                            })
+                        }
+                        (None, _) => {
+                            diagnostic.span_err(attr.span(), "missing 'feature'");
+                            continue
+                        }
+                        _ => {
+                            diagnostic.span_err(attr.span(), "missing 'since'");
+                            continue
+                        }
+                    }
+                }
                 _ => unreachable!()
-            };
-
-            stab = Some(Stability {
-                level: level,
-                feature: feature.unwrap_or(intern_and_get_ident("bogus")),
-                since: since,
-                deprecated_since: None,
-                reason: reason,
-                issue: issue,
-            });
-        } else { // "deprecated"
-            if deprecated.is_some() {
-                diagnostic.span_err(item_sp, "multiple deprecated attributes");
             }
-
-            deprecated = Some((since, reason));
+        } else {
+            diagnostic.span_err(attr.span(), "incorrect stability attribute type");
+            continue
         }
     }
 
     // Merge the deprecation info into the stability info
-    if deprecated.is_some() {
-        match stab {
-            Some(ref mut s) => {
-                let (since, reason) = deprecated.unwrap();
-                s.deprecated_since = since;
-                s.reason = reason;
-            }
-            None => {
-                diagnostic.span_err(item_sp, "deprecated attribute must be paired with \
-                                              either stable or unstable attribute");
+    if let Some(depr) = depr {
+        if let Some(ref mut stab) = stab {
+            if let Unstable {reason: ref mut reason @ None, ..} = stab.level {
+                *reason = Some(depr.reason.clone())
             }
+            stab.depr = Some(depr);
+        } else {
+            diagnostic.span_err(item_sp, "deprecated attribute must be paired with \
+                                          either stable or unstable attribute");
         }
-    } else if stab.as_ref().map_or(false, |s| s.level == Unstable && s.issue.is_none()) {
-        // non-deprecated unstable items need to point to issues.
-        diagnostic.span_err(item_sp,
-                            "non-deprecated unstable items need to point \
-                             to an issue with `issue = \"NNN\"`");
     }
 
-    (stab, used_attrs)
+    stab
 }
 
 /// Find the first stability attribute. `None` if none exists.
 pub fn find_stability(diagnostic: &SpanHandler, attrs: &[Attribute],
                       item_sp: Span) -> Option<Stability> {
-    let (s, used) = find_stability_generic(diagnostic, attrs.iter(), item_sp);
-    for used in used { mark_used(used) }
-    return s;
+    find_stability_generic(diagnostic, attrs.iter(), item_sp)
 }
 
 pub fn require_unique_names(diagnostic: &SpanHandler, metas: &[P<MetaItem>]) {
diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs
index 6196062b08a..3a06ea66d17 100644
--- a/src/libsyntax/ext/base.rs
+++ b/src/libsyntax/ext/base.rs
@@ -593,7 +593,7 @@ impl<'a> ExtCtxt<'a> {
         }
     }
 
-    #[unstable(feature = "rustc_private")]
+    #[unstable(feature = "rustc_private", issue = "0")]
     #[deprecated(since = "1.0.0",
                  reason = "Replaced with `expander().fold_expr()`")]
     pub fn expand_expr(&mut self, e: P<ast::Expr>) -> P<ast::Expr> {
diff --git a/src/libsyntax/util/small_vector.rs b/src/libsyntax/util/small_vector.rs
index 5353d12b678..9743da15a47 100644
--- a/src/libsyntax/util/small_vector.rs
+++ b/src/libsyntax/util/small_vector.rs
@@ -129,7 +129,7 @@ impl<T> SmallVector<T> {
     }
 
     /// Deprecated: use `into_iter`.
-    #[unstable(feature = "rustc_private")]
+    #[unstable(feature = "rustc_private", issue = "0")]
     #[deprecated(since = "1.0.0", reason = "use into_iter")]
     pub fn move_iter(self) -> IntoIter<T> {
         self.into_iter()
diff --git a/src/test/auxiliary/inherited_stability.rs b/src/test/auxiliary/inherited_stability.rs
index f4e6f6d7511..60477288f7c 100644
--- a/src/test/auxiliary/inherited_stability.rs
+++ b/src/test/auxiliary/inherited_stability.rs
@@ -29,7 +29,7 @@ pub mod stable_mod {
 #[unstable(feature = "test_feature", issue = "0")]
 pub mod unstable_mod {
     #[stable(feature = "test_feature", since = "1.0.0")]
-    #[deprecated(since = "1.0.0")]
+    #[deprecated(since = "1.0.0", reason = "text")]
     pub fn deprecated() {}
 
     pub fn unstable() {}
diff --git a/src/test/auxiliary/lint_output_format.rs b/src/test/auxiliary/lint_output_format.rs
index 51fad3ce3cd..8794119a869 100644
--- a/src/test/auxiliary/lint_output_format.rs
+++ b/src/test/auxiliary/lint_output_format.rs
@@ -15,7 +15,7 @@
 #![unstable(feature = "test_feature", issue = "0")]
 
 #[stable(feature = "test_feature", since = "1.0.0")]
-#[deprecated(since = "1.0.0")]
+#[deprecated(since = "1.0.0", reason = "text")]
 pub fn foo() -> usize {
     20
 }
diff --git a/src/test/auxiliary/lint_stability.rs b/src/test/auxiliary/lint_stability.rs
index 60097393a25..260361634ae 100644
--- a/src/test/auxiliary/lint_stability.rs
+++ b/src/test/auxiliary/lint_stability.rs
@@ -14,14 +14,14 @@
 #![stable(feature = "lint_stability", since = "1.0.0")]
 
 #[stable(feature = "test_feature", since = "1.0.0")]
-#[deprecated(since = "1.0.0")]
+#[deprecated(since = "1.0.0", reason = "text")]
 pub fn deprecated() {}
 #[stable(feature = "test_feature", since = "1.0.0")]
 #[deprecated(since = "1.0.0", reason = "text")]
 pub fn deprecated_text() {}
 
 #[unstable(feature = "test_feature", issue = "0")]
-#[deprecated(since = "1.0.0")]
+#[deprecated(since = "1.0.0", reason = "text")]
 pub fn deprecated_unstable() {}
 #[unstable(feature = "test_feature", issue = "0")]
 #[deprecated(since = "1.0.0", reason = "text")]
@@ -34,7 +34,7 @@ pub fn unstable_text() {}
 
 #[stable(feature = "rust1", since = "1.0.0")]
 pub fn stable() {}
-#[stable(feature = "rust1", since = "1.0.0", reason = "text")]
+#[stable(feature = "rust1", since = "1.0.0")]
 pub fn stable_text() {}
 
 #[stable(feature = "rust1", since = "1.0.0")]
@@ -42,14 +42,14 @@ pub struct MethodTester;
 
 impl MethodTester {
     #[stable(feature = "test_feature", since = "1.0.0")]
-    #[deprecated(since = "1.0.0")]
+    #[deprecated(since = "1.0.0", reason = "text")]
     pub fn method_deprecated(&self) {}
     #[stable(feature = "test_feature", since = "1.0.0")]
     #[deprecated(since = "1.0.0", reason = "text")]
     pub fn method_deprecated_text(&self) {}
 
     #[unstable(feature = "test_feature", issue = "0")]
-    #[deprecated(since = "1.0.0")]
+    #[deprecated(since = "1.0.0", reason = "text")]
     pub fn method_deprecated_unstable(&self) {}
     #[unstable(feature = "test_feature", issue = "0")]
     #[deprecated(since = "1.0.0", reason = "text")]
@@ -62,21 +62,21 @@ impl MethodTester {
 
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn method_stable(&self) {}
-    #[stable(feature = "rust1", since = "1.0.0", reason = "text")]
+    #[stable(feature = "rust1", since = "1.0.0")]
     pub fn method_stable_text(&self) {}
 }
 
 #[stable(feature = "test_feature", since = "1.0.0")]
 pub trait Trait {
     #[stable(feature = "test_feature", since = "1.0.0")]
-    #[deprecated(since = "1.0.0")]
+    #[deprecated(since = "1.0.0", reason = "text")]
     fn trait_deprecated(&self) {}
     #[stable(feature = "test_feature", since = "1.0.0")]
     #[deprecated(since = "1.0.0", reason = "text")]
     fn trait_deprecated_text(&self) {}
 
     #[unstable(feature = "test_feature", issue = "0")]
-    #[deprecated(since = "1.0.0")]
+    #[deprecated(since = "1.0.0", reason = "text")]
     fn trait_deprecated_unstable(&self) {}
     #[unstable(feature = "test_feature", issue = "0")]
     #[deprecated(since = "1.0.0", reason = "text")]
@@ -89,7 +89,7 @@ pub trait Trait {
 
     #[stable(feature = "rust1", since = "1.0.0")]
     fn trait_stable(&self) {}
-    #[stable(feature = "rust1", since = "1.0.0", reason = "text")]
+    #[stable(feature = "rust1", since = "1.0.0")]
     fn trait_stable_text(&self) {}
 }
 
@@ -99,12 +99,12 @@ impl Trait for MethodTester {}
 pub trait UnstableTrait { fn dummy(&self) { } }
 
 #[stable(feature = "test_feature", since = "1.0.0")]
-#[deprecated(since = "1.0.0")]
+#[deprecated(since = "1.0.0", reason = "text")]
 pub struct DeprecatedStruct {
     #[stable(feature = "test_feature", since = "1.0.0")] pub i: isize
 }
 #[unstable(feature = "test_feature", issue = "0")]
-#[deprecated(since = "1.0.0")]
+#[deprecated(since = "1.0.0", reason = "text")]
 pub struct DeprecatedUnstableStruct {
     #[stable(feature = "test_feature", since = "1.0.0")] pub i: isize
 }
@@ -118,10 +118,10 @@ pub struct StableStruct {
 }
 
 #[stable(feature = "test_feature", since = "1.0.0")]
-#[deprecated(since = "1.0.0")]
+#[deprecated(since = "1.0.0", reason = "text")]
 pub struct DeprecatedUnitStruct;
 #[unstable(feature = "test_feature", issue = "0")]
-#[deprecated(since = "1.0.0")]
+#[deprecated(since = "1.0.0", reason = "text")]
 pub struct DeprecatedUnstableUnitStruct;
 #[unstable(feature = "test_feature", issue = "0")]
 pub struct UnstableUnitStruct;
@@ -131,10 +131,10 @@ pub struct StableUnitStruct;
 #[stable(feature = "test_feature", since = "1.0.0")]
 pub enum Enum {
     #[stable(feature = "test_feature", since = "1.0.0")]
-    #[deprecated(since = "1.0.0")]
+    #[deprecated(since = "1.0.0", reason = "text")]
     DeprecatedVariant,
     #[unstable(feature = "test_feature", issue = "0")]
-    #[deprecated(since = "1.0.0")]
+    #[deprecated(since = "1.0.0", reason = "text")]
     DeprecatedUnstableVariant,
     #[unstable(feature = "test_feature", issue = "0")]
     UnstableVariant,
@@ -144,10 +144,10 @@ pub enum Enum {
 }
 
 #[stable(feature = "test_feature", since = "1.0.0")]
-#[deprecated(since = "1.0.0")]
+#[deprecated(since = "1.0.0", reason = "text")]
 pub struct DeprecatedTupleStruct(#[stable(feature = "rust1", since = "1.0.0")] pub isize);
 #[unstable(feature = "test_feature", issue = "0")]
-#[deprecated(since = "1.0.0")]
+#[deprecated(since = "1.0.0", reason = "text")]
 pub struct DeprecatedUnstableTupleStruct(#[stable(feature = "rust1", since = "1.0.0")] pub isize);
 #[unstable(feature = "test_feature", issue = "0")]
 pub struct UnstableTupleStruct(#[stable(feature = "rust1", since = "1.0.0")] pub isize);
diff --git a/src/test/auxiliary/lint_stability_fields.rs b/src/test/auxiliary/lint_stability_fields.rs
index b45af89dd3b..44b4abd38b8 100644
--- a/src/test/auxiliary/lint_stability_fields.rs
+++ b/src/test/auxiliary/lint_stability_fields.rs
@@ -18,7 +18,7 @@ pub struct Stable {
     pub inherit: u8, // it's a lie (stable doesn't inherit)
     #[unstable(feature = "test_feature", issue = "0")]
     pub override1: u8,
-    #[deprecated(since = "1.0.0")]
+    #[deprecated(since = "1.0.0", reason = "text")]
     #[unstable(feature = "test_feature", issue = "0")]
     pub override2: u8,
 }
@@ -27,14 +27,14 @@ pub struct Stable {
 pub struct Stable2(#[stable(feature = "rust1", since = "1.0.0")] pub u8,
                    #[unstable(feature = "test_feature", issue = "0")] pub u8,
                    #[unstable(feature = "test_feature", issue = "0")]
-                   #[deprecated(since = "1.0.0")] pub u8);
+                   #[deprecated(since = "1.0.0", reason = "text")] pub u8);
 
 #[unstable(feature = "test_feature", issue = "0")]
 pub struct Unstable {
     pub inherit: u8,
     #[stable(feature = "rust1", since = "1.0.0")]
     pub override1: u8,
-    #[deprecated(since = "1.0.0")]
+    #[deprecated(since = "1.0.0", reason = "text")]
     #[unstable(feature = "test_feature", issue = "0")]
     pub override2: u8,
 }
@@ -43,10 +43,10 @@ pub struct Unstable {
 pub struct Unstable2(pub u8,
                      #[stable(feature = "rust1", since = "1.0.0")] pub u8,
                      #[unstable(feature = "test_feature", issue = "0")]
-                     #[deprecated(since = "1.0.0")] pub u8);
+                     #[deprecated(since = "1.0.0", reason = "text")] pub u8);
 
 #[unstable(feature = "test_feature", issue = "0")]
-#[deprecated(feature = "rust1", since = "1.0.0")]
+#[deprecated(since = "1.0.0", reason = "text")]
 pub struct Deprecated {
     pub inherit: u8,
     #[stable(feature = "rust1", since = "1.0.0")]
@@ -56,7 +56,7 @@ pub struct Deprecated {
 }
 
 #[unstable(feature = "test_feature", issue = "0")]
-#[deprecated(feature = "rust1", since = "1.0.0")]
+#[deprecated(since = "1.0.0", reason = "text")]
 pub struct Deprecated2(pub u8,
                        #[stable(feature = "rust1", since = "1.0.0")] pub u8,
                        #[unstable(feature = "test_feature", issue = "0")] pub u8);
diff --git a/src/test/compile-fail/issue-17337.rs b/src/test/compile-fail/issue-17337.rs
index ff640793afe..501f6eb4dea 100644
--- a/src/test/compile-fail/issue-17337.rs
+++ b/src/test/compile-fail/issue-17337.rs
@@ -15,8 +15,8 @@
 struct Foo;
 
 impl Foo {
-    #[unstable(feature = "test_feature")]
-    #[deprecated(since = "1.0.0")]
+    #[unstable(feature = "test_feature", issue = "0")]
+    #[deprecated(since = "1.0.0", reason = "text")]
     fn foo(self) {}
 }
 
diff --git a/src/test/compile-fail/lint-output-format.rs b/src/test/compile-fail/lint-output-format.rs
index 3febacca73f..c22ad3182dd 100644
--- a/src/test/compile-fail/lint-output-format.rs
+++ b/src/test/compile-fail/lint-output-format.rs
@@ -15,7 +15,7 @@
 
 extern crate lint_output_format; //~ ERROR use of unstable library feature
 use lint_output_format::{foo, bar}; //~ ERROR use of unstable library feature
-//~^ WARNING use of deprecated item,
+//~^ WARNING use of deprecated item: text,
 
 fn main() {
     let _x = foo(); //~ WARNING #[warn(deprecated)] on by default
diff --git a/src/test/compile-fail/lint-stability-fields.rs b/src/test/compile-fail/lint-stability-fields.rs
index 7e675ca0bc4..11f64119680 100644
--- a/src/test/compile-fail/lint-stability-fields.rs
+++ b/src/test/compile-fail/lint-stability-fields.rs
@@ -189,7 +189,7 @@ mod this_crate {
         inherit: u8,
         #[unstable(feature = "test_feature", issue = "0")]
         override1: u8,
-        #[deprecated(since = "1.0.0")]
+        #[deprecated(since = "1.0.0", reason = "text")]
         #[unstable(feature = "test_feature", issue = "0")]
         override2: u8,
     }
@@ -198,14 +198,14 @@ mod this_crate {
     struct Stable2(u8,
                    #[stable(feature = "rust1", since = "1.0.0")] u8,
                    #[unstable(feature = "test_feature", issue = "0")]
-                   #[deprecated(since = "1.0.0")] u8);
+                   #[deprecated(since = "1.0.0", reason = "text")] u8);
 
     #[unstable(feature = "test_feature", issue = "0")]
     struct Unstable {
         inherit: u8,
         #[stable(feature = "rust1", since = "1.0.0")]
         override1: u8,
-        #[deprecated(since = "1.0.0")]
+        #[deprecated(since = "1.0.0", reason = "text")]
         #[unstable(feature = "test_feature", issue = "0")]
         override2: u8,
     }
@@ -214,10 +214,10 @@ mod this_crate {
     struct Unstable2(u8,
                      #[stable(feature = "rust1", since = "1.0.0")] u8,
                      #[unstable(feature = "test_feature", issue = "0")]
-                     #[deprecated(since = "1.0.0")] u8);
+                     #[deprecated(since = "1.0.0", reason = "text")] u8);
 
     #[unstable(feature = "test_feature", issue = "0")]
-    #[deprecated(feature = "rust1", since = "1.0.0")]
+    #[deprecated(since = "1.0.0", reason = "text")]
     struct Deprecated {
         inherit: u8,
         #[stable(feature = "rust1", since = "1.0.0")]
@@ -227,7 +227,7 @@ mod this_crate {
     }
 
     #[unstable(feature = "test_feature", issue = "0")]
-    #[deprecated(feature = "rust1", since = "1.0.0")]
+    #[deprecated(since = "1.0.0", reason = "text")]
     struct Deprecated2(u8,
                        #[stable(feature = "rust1", since = "1.0.0")] u8,
                        #[unstable(feature = "test_feature", issue = "0")] u8);
diff --git a/src/test/compile-fail/lint-stability.rs b/src/test/compile-fail/lint-stability.rs
index 6cc73ded975..864aafe5a6b 100644
--- a/src/test/compile-fail/lint-stability.rs
+++ b/src/test/compile-fail/lint-stability.rs
@@ -258,7 +258,7 @@ mod inheritance {
 
 mod this_crate {
     #[unstable(feature = "test_feature", issue = "0")]
-    #[deprecated(since = "1.0.0")]
+    #[deprecated(since = "1.0.0", reason = "text")]
     pub fn deprecated() {}
     #[unstable(feature = "test_feature", issue = "0")]
     #[deprecated(since = "1.0.0", reason = "text")]
@@ -271,7 +271,7 @@ mod this_crate {
 
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn stable() {}
-    #[stable(feature = "rust1", since = "1.0.0", reason = "text")]
+    #[stable(feature = "rust1", since = "1.0.0")]
     pub fn stable_text() {}
 
     #[stable(feature = "rust1", since = "1.0.0")]
@@ -279,7 +279,7 @@ mod this_crate {
 
     impl MethodTester {
         #[unstable(feature = "test_feature", issue = "0")]
-        #[deprecated(since = "1.0.0")]
+        #[deprecated(since = "1.0.0", reason = "text")]
         pub fn method_deprecated(&self) {}
         #[unstable(feature = "test_feature", issue = "0")]
         #[deprecated(since = "1.0.0", reason = "text")]
@@ -292,13 +292,13 @@ mod this_crate {
 
         #[stable(feature = "rust1", since = "1.0.0")]
         pub fn method_stable(&self) {}
-        #[stable(feature = "rust1", since = "1.0.0", reason = "text")]
+        #[stable(feature = "rust1", since = "1.0.0")]
         pub fn method_stable_text(&self) {}
     }
 
     pub trait Trait {
         #[unstable(feature = "test_feature", issue = "0")]
-        #[deprecated(since = "1.0.0")]
+        #[deprecated(since = "1.0.0", reason = "text")]
         fn trait_deprecated(&self) {}
         #[unstable(feature = "test_feature", issue = "0")]
         #[deprecated(since = "1.0.0", reason = "text")]
@@ -311,14 +311,14 @@ mod this_crate {
 
         #[stable(feature = "rust1", since = "1.0.0")]
         fn trait_stable(&self) {}
-        #[stable(feature = "rust1", since = "1.0.0", reason = "text")]
+        #[stable(feature = "rust1", since = "1.0.0")]
         fn trait_stable_text(&self) {}
     }
 
     impl Trait for MethodTester {}
 
     #[unstable(feature = "test_feature", issue = "0")]
-    #[deprecated(since = "1.0.0")]
+    #[deprecated(since = "1.0.0", reason = "text")]
     pub struct DeprecatedStruct {
         #[stable(feature = "test_feature", since = "1.0.0")] i: isize
     }
@@ -332,7 +332,7 @@ mod this_crate {
     }
 
     #[unstable(feature = "test_feature", issue = "0")]
-    #[deprecated(since = "1.0.0")]
+    #[deprecated(since = "1.0.0", reason = "text")]
     pub struct DeprecatedUnitStruct;
     #[unstable(feature = "test_feature", issue = "0")]
     pub struct UnstableUnitStruct;
@@ -341,7 +341,7 @@ mod this_crate {
 
     pub enum Enum {
         #[unstable(feature = "test_feature", issue = "0")]
-        #[deprecated(since = "1.0.0")]
+        #[deprecated(since = "1.0.0", reason = "text")]
         DeprecatedVariant,
         #[unstable(feature = "test_feature", issue = "0")]
         UnstableVariant,
@@ -351,7 +351,7 @@ mod this_crate {
     }
 
     #[unstable(feature = "test_feature", issue = "0")]
-    #[deprecated(since = "1.0.0")]
+    #[deprecated(since = "1.0.0", reason = "text")]
     pub struct DeprecatedTupleStruct(isize);
     #[unstable(feature = "test_feature", issue = "0")]
     pub struct UnstableTupleStruct(isize);
@@ -472,7 +472,7 @@ mod this_crate {
     }
 
     #[unstable(feature = "test_feature", issue = "0")]
-    #[deprecated(since = "1.0.0")]
+    #[deprecated(since = "1.0.0", reason = "text")]
     fn test_fn_body() {
         fn fn_in_body() {}
         fn_in_body();
@@ -480,7 +480,7 @@ mod this_crate {
 
     impl MethodTester {
         #[unstable(feature = "test_feature", issue = "0")]
-        #[deprecated(since = "1.0.0")]
+        #[deprecated(since = "1.0.0", reason = "text")]
         fn test_method_body(&self) {
             fn fn_in_body() {}
             fn_in_body();
@@ -488,7 +488,7 @@ mod this_crate {
     }
 
     #[unstable(feature = "test_feature", issue = "0")]
-    #[deprecated(since = "1.0.0")]
+    #[deprecated(since = "1.0.0", reason = "text")]
     pub trait DeprecatedTrait {
         fn dummy(&self) { }
     }
diff --git a/src/test/compile-fail/stability-attribute-sanity.rs b/src/test/compile-fail/stability-attribute-sanity.rs
index 00c5be0f211..f71e66ded7e 100644
--- a/src/test/compile-fail/stability-attribute-sanity.rs
+++ b/src/test/compile-fail/stability-attribute-sanity.rs
@@ -14,22 +14,19 @@
 #![staged_api]
 
 mod bogus_attribute_types_1 {
-    #[stable(feature = "a", since = "a", reason)] //~ ERROR incorrect meta item
+    #[stable(feature = "a", since = "a", reason)] //~ ERROR unknown meta item 'reason'
     fn f1() { }
 
-    #[stable(feature = "a", since, reason = "a")] //~ ERROR incorrect meta item
+    #[stable(feature = "a", since)] //~ ERROR incorrect meta item
     fn f2() { }
 
-    #[stable(feature, since = "a", reason = "a")] //~ ERROR incorrect meta item
+    #[stable(feature, since = "a")] //~ ERROR incorrect meta item
     fn f3() { }
 
-    #[stable(feature = "a", since = "a", reason(b))] //~ ERROR incorrect meta item
-    fn f4() { }
-
-    #[stable(feature = "a", since(b), reason = "a")] //~ ERROR incorrect meta item
+    #[stable(feature = "a", since(b))] //~ ERROR incorrect meta item
     fn f5() { }
 
-    #[stable(feature(b), since = "a", reason = "a")] //~ ERROR incorrect meta item
+    #[stable(feature(b), since = "a")] //~ ERROR incorrect meta item
     fn f6() { }
 }
 
@@ -56,11 +53,11 @@ mod bogus_attribute_types_2 {
 }
 
 mod missing_feature_names {
-    #[unstable(since = "a", issue = "0")] //~ ERROR missing 'feature'
+    #[unstable(issue = "0")] //~ ERROR missing 'feature'
     fn f1() { }
 
-    #[unstable(feature = "a")]
-    fn f2() { } //~ ERROR need to point to an issue
+    #[unstable(feature = "a")] //~ ERROR missing 'issue'
+    fn f2() { }
 
     #[stable(since = "a")] //~ ERROR missing 'feature'
     fn f3() { }
@@ -75,12 +72,12 @@ mod missing_version {
     fn f2() { }
 }
 
-#[unstable(feature = "a", since = "b", issue = "0")]
+#[unstable(feature = "a", issue = "0")]
 #[stable(feature = "a", since = "b")]
 fn multiple1() { } //~ ERROR multiple stability levels
 
-#[unstable(feature = "a", since = "b", issue = "0")]
-#[unstable(feature = "a", since = "b", issue = "0")]
+#[unstable(feature = "a", issue = "0")]
+#[unstable(feature = "a", issue = "0")]
 fn multiple2() { } //~ ERROR multiple stability levels
 
 #[stable(feature = "a", since = "b")]
@@ -88,12 +85,12 @@ fn multiple2() { } //~ ERROR multiple stability levels
 fn multiple3() { } //~ ERROR multiple stability levels
 
 #[stable(feature = "a", since = "b")]
-#[deprecated(since = "b")]
-#[deprecated(since = "b")]
+#[deprecated(since = "b", reason = "text")]
+#[deprecated(since = "b", reason = "text")]
 fn multiple4() { } //~ ERROR multiple deprecated attributes
 //~^ ERROR Invalid stability or deprecation version found
 
-#[deprecated(since = "a")]
+#[deprecated(since = "a", reason = "text")]
 fn deprecated_without_unstable_or_stable() { } //~ ERROR deprecated attribute must be paired
 
 fn main() { }