Refactor attr::Stability

Stricter checking + enforcement of invariants at compile time
This commit is contained in:
Vadim Petrochenkov 2015-10-13 06:01:31 +03:00
parent 843e528fd0
commit ab671552c3
17 changed files with 307 additions and 894 deletions

View File

@ -11,6 +11,8 @@
//! A pass that annotates every item and method with its stability level, //! A pass that annotates every item and method with its stability level,
//! propagating default levels lexically from parent to children ast nodes. //! propagating default levels lexically from parent to children ast nodes.
pub use self::StabilityLevel::*;
use session::Session; use session::Session;
use lint; use lint;
use metadata::cstore::LOCAL_CRATE; use metadata::cstore::LOCAL_CRATE;
@ -34,6 +36,18 @@ use rustc_front::visit::{self, FnKind, Visitor};
use std::mem::replace; use std::mem::replace;
use std::cmp::Ordering; 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. /// A stability index, giving the stability level for items and methods.
pub struct Index<'tcx> { pub struct Index<'tcx> {
/// This is mostly a cache, except the stabilities of local items /// 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 // if parent is deprecated and we're not, inherit this by merging
// deprecated_since and its reason. // deprecated_since and its reason.
if let Some(parent_stab) = self.parent { if let Some(parent_stab) = self.parent {
if parent_stab.deprecated_since.is_some() if parent_stab.depr.is_some()
&& stab.deprecated_since.is_none() { && stab.depr.is_none() {
stab.deprecated_since = parent_stab.deprecated_since.clone(); stab.depr = parent_stab.depr.clone()
stab.reason = parent_stab.reason.clone();
} }
} }
@ -78,9 +91,9 @@ impl<'a, 'tcx: 'a> Annotator<'a, 'tcx> {
// Check if deprecated_since < stable_since. If it is, // Check if deprecated_since < stable_since. If it is,
// this is *almost surely* an accident. // this is *almost surely* an accident.
let deprecated_predates_stable = match (stab.deprecated_since.as_ref(), let deprecated_predates_stable = match (&stab.depr, &stab.level) {
stab.since.as_ref()) { (&Some(attr::Deprecation {since: ref dep_since, ..}),
(Some(dep_since), Some(stab_since)) => { &attr::Stable {since: ref stab_since}) => {
// explicit version of iter::order::lt to handle parse errors properly // explicit version of iter::order::lt to handle parse errors properly
let mut is_less = false; let mut is_less = false;
for (dep_v, stab_v) in dep_since.split(".").zip(stab_since.split(".")) { 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)); self.index.map.insert(def_id, Some(stab));
// Don't inherit #[stable(feature = "rust1", since = "1.0.0")] // 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)); let parent = replace(&mut self.parent, Some(stab));
f(self); f(self);
self.parent = parent; self.parent = parent;
@ -261,7 +274,7 @@ impl<'tcx> Index<'tcx> {
/// features and possibly prints errors. Returns a list of all /// features and possibly prints errors. Returns a list of all
/// features used. /// features used.
pub fn check_unstable_api_usage(tcx: &ty::ctxt) 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; let ref active_lib_features = tcx.sess.features.borrow().declared_lib_features;
// Put the active features into a map for quick lookup // 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> { struct Checker<'a, 'tcx: 'a> {
tcx: &'a ty::ctxt<'tcx>, tcx: &'a ty::ctxt<'tcx>,
active_features: FnvHashSet<InternedString>, 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. // Within a block where feature gate checking can be skipped.
in_skip_block: u32, in_skip_block: u32,
} }
@ -303,8 +316,8 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
} }
match *stab { match *stab {
Some(&Stability { level: attr::Unstable, ref feature, ref reason, issue, .. }) => { Some(&Stability { level: attr::Unstable {ref reason, issue}, ref feature, .. }) => {
self.used_features.insert(feature.clone(), attr::Unstable); self.used_features.insert(feature.clone(), Unstable);
if !self.active_features.contains(feature) { if !self.active_features.contains(feature) {
let msg = match *reason { let msg = match *reason {
@ -312,13 +325,12 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
&feature, &r), &feature, &r),
None => format!("use of unstable library feature '{}'", &feature) None => format!("use of unstable library feature '{}'", &feature)
}; };
emit_feature_err(&self.tcx.sess.parse_sess.span_diagnostic, 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, .. }) => { Some(&Stability { ref level, ref feature, .. }) => {
self.used_features.insert(feature.clone(), level); self.used_features.insert(feature.clone(), StabilityLevel::from_attr_level(level));
// Stable APIs are always ok to call and deprecated APIs are // Stable APIs are always ok to call and deprecated APIs are
// handled by a lint. // 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. /// libraries, identify activated features that don't exist and error about them.
pub fn check_unused_or_stable_features(sess: &Session, pub fn check_unused_or_stable_features(sess: &Session,
lib_features_used: &FnvHashMap<InternedString, lib_features_used: &FnvHashMap<InternedString,
attr::StabilityLevel>) { StabilityLevel>) {
let ref declared_lib_features = sess.features.borrow().declared_lib_features; let ref declared_lib_features = sess.features.borrow().declared_lib_features;
let mut remaining_lib_features: FnvHashMap<InternedString, Span> let mut remaining_lib_features: FnvHashMap<InternedString, Span>
= declared_lib_features.clone().into_iter().collect(); = 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 { for (used_lib_feature, level) in lib_features_used {
match remaining_lib_features.remove(used_lib_feature) { match remaining_lib_features.remove(used_lib_feature) {
Some(span) => { Some(span) => {
if *level == attr::Stable { if *level == Stable {
sess.add_lint(lint::builtin::STABLE_FEATURES, sess.add_lint(lint::builtin::STABLE_FEATURES,
ast::CRATE_NODE_ID, ast::CRATE_NODE_ID,
span, span,

View File

@ -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
}
}
}

View File

@ -674,7 +674,7 @@ impl Stability {
span: Span, stability: &Option<&attr::Stability>) { span: Span, stability: &Option<&attr::Stability>) {
// Deprecated attributes apply in-crate and cross-crate. // Deprecated attributes apply in-crate and cross-crate.
let (lint, label) = match *stability { let (lint, label) = match *stability {
Some(&attr::Stability { deprecated_since: Some(_), .. }) => Some(&attr::Stability { depr: Some(_), .. }) =>
(DEPRECATED, "deprecated"), (DEPRECATED, "deprecated"),
_ => return _ => return
}; };
@ -684,8 +684,8 @@ impl Stability {
fn output(cx: &LateContext, span: Span, stability: &Option<&attr::Stability>, fn output(cx: &LateContext, span: Span, stability: &Option<&attr::Stability>,
lint: &'static Lint, label: &'static str) { lint: &'static Lint, label: &'static str) {
let msg = match *stability { let msg = match *stability {
Some(&attr::Stability { reason: Some(ref s), .. }) => { Some(&attr::Stability {depr: Some(attr::Deprecation {ref reason, ..}), ..}) => {
format!("use of {} item: {}", label, *s) format!("use of {} item: {}", label, reason)
} }
_ => format!("use of {} item", label) _ => 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 { impl LintPass for Stability {
fn get_lints(&self) -> LintArray { fn get_lints(&self) -> LintArray {
lint_array!(DEPRECATED) lint_array!(DEPRECATED)
@ -719,36 +705,31 @@ impl LateLintPass for Stability {
fn check_item(&mut self, cx: &LateContext, item: &hir::Item) { fn check_item(&mut self, cx: &LateContext, item: &hir::Item) {
stability::check_item(cx.tcx, item, false, stability::check_item(cx.tcx, item, false,
&mut |id, sp, stab| &mut |id, sp, stab|
self.lint(cx, id, sp, self.lint(cx, id, sp, &stab));
&stab.map(|s| hir_to_ast_stability(s)).as_ref()));
} }
fn check_expr(&mut self, cx: &LateContext, e: &hir::Expr) { fn check_expr(&mut self, cx: &LateContext, e: &hir::Expr) {
stability::check_expr(cx.tcx, e, stability::check_expr(cx.tcx, e,
&mut |id, sp, stab| &mut |id, sp, stab|
self.lint(cx, id, sp, self.lint(cx, id, sp, &stab));
&stab.map(|s| hir_to_ast_stability(s)).as_ref()));
} }
fn check_path(&mut self, cx: &LateContext, path: &hir::Path, id: ast::NodeId) { fn check_path(&mut self, cx: &LateContext, path: &hir::Path, id: ast::NodeId) {
stability::check_path(cx.tcx, path, id, stability::check_path(cx.tcx, path, id,
&mut |id, sp, stab| &mut |id, sp, stab|
self.lint(cx, id, sp, self.lint(cx, id, sp, &stab));
&stab.map(|s| hir_to_ast_stability(s)).as_ref()));
} }
fn check_path_list_item(&mut self, cx: &LateContext, item: &hir::PathListItem) { fn check_path_list_item(&mut self, cx: &LateContext, item: &hir::PathListItem) {
stability::check_path_list_item(cx.tcx, item, stability::check_path_list_item(cx.tcx, item,
&mut |id, sp, stab| &mut |id, sp, stab|
self.lint(cx, id, sp, self.lint(cx, id, sp, &stab));
&stab.map(|s| hir_to_ast_stability(s)).as_ref()));
} }
fn check_pat(&mut self, cx: &LateContext, pat: &hir::Pat) { fn check_pat(&mut self, cx: &LateContext, pat: &hir::Pat) {
stability::check_pat(cx.tcx, pat, stability::check_pat(cx.tcx, pat,
&mut |id, sp, stab| &mut |id, sp, stab|
self.lint(cx, id, sp, self.lint(cx, id, sp, &stab));
&stab.map(|s| hir_to_ast_stability(s)).as_ref()));
} }
} }

View File

@ -324,8 +324,8 @@ impl Item {
match self.stability { match self.stability {
Some(ref s) => { Some(ref s) => {
let mut base = match s.level { let mut base = match s.level {
attr::Unstable => "unstable".to_string(), stability::Unstable => "unstable".to_string(),
attr::Stable => String::new(), stability::Stable => String::new(),
}; };
if !s.deprecated_since.is_empty() { if !s.deprecated_since.is_empty() {
base.push_str(" deprecated"); base.push_str(" deprecated");
@ -2679,7 +2679,7 @@ impl Clean<Item> for doctree::Macro {
#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] #[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
pub struct Stability { pub struct Stability {
pub level: attr::StabilityLevel, pub level: stability::StabilityLevel,
pub feature: String, pub feature: String,
pub since: String, pub since: String,
pub deprecated_since: String, pub deprecated_since: String,
@ -2690,32 +2690,31 @@ pub struct Stability {
impl Clean<Stability> for attr::Stability { impl Clean<Stability> for attr::Stability {
fn clean(&self, _: &DocContext) -> Stability { fn clean(&self, _: &DocContext) -> Stability {
Stability { Stability {
level: self.level, level: stability::StabilityLevel::from_attr_level(&self.level),
feature: self.feature.to_string(), feature: self.feature.to_string(),
since: self.since.as_ref().map_or("".to_string(), since: match self.level {
|interned| interned.to_string()), attr::Stable {ref since} => since.to_string(),
deprecated_since: self.deprecated_since.as_ref().map_or("".to_string(), _ => "".to_string(),
|istr| istr.to_string()), },
reason: self.reason.as_ref().map_or("".to_string(), deprecated_since: match self.depr {
|interned| interned.to_string()), Some(attr::Deprecation {ref since, ..}) => since.to_string(),
issue: self.issue, _=> "".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 { impl<'a> Clean<Stability> for &'a attr::Stability {
fn clean(&self, _: &DocContext) -> Stability { fn clean(&self, dc: &DocContext) -> Stability {
Stability { (**self).clean(dc)
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,
}
} }
} }

View File

@ -52,9 +52,10 @@ use std::sync::Arc;
use externalfiles::ExternalHtml; use externalfiles::ExternalHtml;
use serialize::json::{self, ToJson}; use serialize::json::{self, ToJson};
use syntax::{abi, ast, attr}; use syntax::{abi, ast};
use rustc::metadata::cstore::LOCAL_CRATE; use rustc::metadata::cstore::LOCAL_CRATE;
use rustc::middle::def_id::{CRATE_DEF_INDEX, DefId}; use rustc::middle::def_id::{CRATE_DEF_INDEX, DefId};
use rustc::middle::stability;
use rustc::util::nodemap::DefIdSet; use rustc::util::nodemap::DefIdSet;
use rustc_front::hir; 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 s1 = i1.stability.as_ref().map(|s| s.level);
let s2 = i2.stability.as_ref().map(|s| s.level); let s2 = i2.stability.as_ref().map(|s| s.level);
match (s1, s2) { match (s1, s2) {
(Some(attr::Unstable), Some(attr::Stable)) => return Ordering::Greater, (Some(stability::Unstable), Some(stability::Stable)) => return Ordering::Greater,
(Some(attr::Stable), Some(attr::Unstable)) => return Ordering::Less, (Some(stability::Stable), Some(stability::Unstable)) => return Ordering::Less,
_ => {} _ => {}
} }
i1.name.cmp(&i2.name) i1.name.cmp(&i2.name)
@ -1724,7 +1725,7 @@ fn short_stability(item: &clean::Item, cx: &Context, show_reason: bool) -> Optio
String::new() String::new()
}; };
format!("Deprecated{}{}", since, Markdown(&reason)) format!("Deprecated{}{}", since, Markdown(&reason))
} else if stab.level == attr::Unstable { } else if stab.level == stability::Unstable {
let unstable_extra = if show_reason { let unstable_extra = if show_reason {
match (!stab.feature.is_empty(), &cx.issue_tracker_base_url, stab.issue) { match (!stab.feature.is_empty(), &cx.issue_tracker_base_url, stab.issue) {
(true, &Some(ref tracker_url), Some(issue_no)) => (true, &Some(ref tracker_url), Some(issue_no)) =>

View File

@ -27,7 +27,6 @@ use ptr::P;
use std::cell::{RefCell, Cell}; use std::cell::{RefCell, Cell};
use std::collections::HashSet; use std::collections::HashSet;
use std::fmt;
thread_local! { thread_local! {
static USED_ATTRS: RefCell<Vec<u64>> = RefCell::new(Vec::new()) 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)] #[derive(RustcEncodable, RustcDecodable, Clone, Debug, PartialEq, Eq, Hash)]
pub struct Stability { pub struct Stability {
pub level: StabilityLevel, pub level: StabilityLevel,
pub feature: InternedString, pub feature: InternedString,
pub since: Option<InternedString>, pub depr: Option<Deprecation>,
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. /// 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 { pub enum StabilityLevel {
Unstable, // Reason for the current stability level and the relevant rust-lang issue
Stable, Unstable { reason: Option<InternedString>, issue: u32 },
Stable { since: InternedString },
} }
impl fmt::Display for StabilityLevel { #[derive(RustcEncodable, RustcDecodable, PartialEq, PartialOrd, Clone, Debug, Eq, Hash)]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { pub struct Deprecation {
fmt::Debug::fmt(self, f) pub since: InternedString,
} pub reason: InternedString,
} }
fn find_stability_generic<'a, impl StabilityLevel {
AM: AttrMetaMethods, pub fn is_unstable(&self) -> bool { if let Unstable {..} = *self { true } else { false }}
I: Iterator<Item=&'a AM>> pub fn is_stable(&self) -> bool { if let Stable {..} = *self { true } else { false }}
(diagnostic: &SpanHandler, attrs: I, item_sp: Span) }
-> (Option<Stability>, Vec<&'a AM>) {
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 stab: Option<Stability> = None;
let mut deprecated: Option<(Option<InternedString>, Option<InternedString>)> = None; let mut depr: Option<Deprecation> = None;
let mut used_attrs: Vec<&'a AM> = vec![];
'outer: for attr in attrs { 'outer: for attr in attrs_iter {
let tag = attr.name(); let tag = attr.name();
let tag = &tag[..]; let tag = &*tag;
if tag != "deprecated" && tag != "unstable" && tag != "stable" { if tag != "deprecated" && tag != "unstable" && tag != "stable" {
continue // not a stability level continue // not a stability level
} }
used_attrs.push(attr); mark_used(attr);
let (feature, since, reason, issue) = match attr.meta_item_list() { if let Some(metas) = attr.meta_item_list() {
Some(metas) => { let get = |meta: &MetaItem, item: &mut Option<InternedString>| {
let mut feature = None; if item.is_some() {
let mut since = None; diagnostic.span_err(meta.span, &format!("multiple '{}' items",
let mut reason = None; meta.name()));
let mut issue = None; return false
for meta in metas { }
match &*meta.name() { if let Some(v) = meta.value_str() {
"feature" => { *item = Some(v);
match meta.value_str() { true
Some(v) => feature = Some(v), } else {
None => { diagnostic.span_err(meta.span, "incorrect meta item");
diagnostic.span_err(meta.span, "incorrect meta item"); false
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 { match tag {
level: level, "deprecated" => {
feature: feature.unwrap_or(intern_and_get_ident("bogus")), if depr.is_some() {
since: since, diagnostic.span_err(item_sp, "multiple deprecated attributes");
deprecated_since: None, break
reason: reason, }
issue: issue,
});
} else { // "deprecated"
if deprecated.is_some() {
diagnostic.span_err(item_sp, "multiple deprecated attributes");
}
deprecated = Some((since, reason)); 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
}
}
}
match (since, reason) {
(Some(since), Some(reason)) => {
depr = Some(Deprecation {
since: since,
reason: reason,
})
}
(None, _) => {
diagnostic.span_err(attr.span(), "missing 'since'");
continue
}
_ => {
diagnostic.span_err(attr.span(), "missing 'reason'");
continue
}
}
}
"unstable" => {
if stab.is_some() {
diagnostic.span_err(item_sp, "multiple stability levels");
break
}
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
}
}
}
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
}
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
}
}
}
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!()
}
} else {
diagnostic.span_err(attr.span(), "incorrect stability attribute type");
continue
} }
} }
// Merge the deprecation info into the stability info // Merge the deprecation info into the stability info
if deprecated.is_some() { if let Some(depr) = depr {
match stab { if let Some(ref mut stab) = stab {
Some(ref mut s) => { if let Unstable {reason: ref mut reason @ None, ..} = stab.level {
let (since, reason) = deprecated.unwrap(); *reason = Some(depr.reason.clone())
s.deprecated_since = since;
s.reason = reason;
}
None => {
diagnostic.span_err(item_sp, "deprecated attribute must be paired with \
either stable or unstable attribute");
} }
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. /// Find the first stability attribute. `None` if none exists.
pub fn find_stability(diagnostic: &SpanHandler, attrs: &[Attribute], pub fn find_stability(diagnostic: &SpanHandler, attrs: &[Attribute],
item_sp: Span) -> Option<Stability> { item_sp: Span) -> Option<Stability> {
let (s, used) = find_stability_generic(diagnostic, attrs.iter(), item_sp); find_stability_generic(diagnostic, attrs.iter(), item_sp)
for used in used { mark_used(used) }
return s;
} }
pub fn require_unique_names(diagnostic: &SpanHandler, metas: &[P<MetaItem>]) { pub fn require_unique_names(diagnostic: &SpanHandler, metas: &[P<MetaItem>]) {

View File

@ -593,7 +593,7 @@ impl<'a> ExtCtxt<'a> {
} }
} }
#[unstable(feature = "rustc_private")] #[unstable(feature = "rustc_private", issue = "0")]
#[deprecated(since = "1.0.0", #[deprecated(since = "1.0.0",
reason = "Replaced with `expander().fold_expr()`")] reason = "Replaced with `expander().fold_expr()`")]
pub fn expand_expr(&mut self, e: P<ast::Expr>) -> P<ast::Expr> { pub fn expand_expr(&mut self, e: P<ast::Expr>) -> P<ast::Expr> {

View File

@ -129,7 +129,7 @@ impl<T> SmallVector<T> {
} }
/// Deprecated: use `into_iter`. /// Deprecated: use `into_iter`.
#[unstable(feature = "rustc_private")] #[unstable(feature = "rustc_private", issue = "0")]
#[deprecated(since = "1.0.0", reason = "use into_iter")] #[deprecated(since = "1.0.0", reason = "use into_iter")]
pub fn move_iter(self) -> IntoIter<T> { pub fn move_iter(self) -> IntoIter<T> {
self.into_iter() self.into_iter()

View File

@ -29,7 +29,7 @@ pub mod stable_mod {
#[unstable(feature = "test_feature", issue = "0")] #[unstable(feature = "test_feature", issue = "0")]
pub mod unstable_mod { pub mod unstable_mod {
#[stable(feature = "test_feature", 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() {} pub fn deprecated() {}
pub fn unstable() {} pub fn unstable() {}

View File

@ -15,7 +15,7 @@
#![unstable(feature = "test_feature", issue = "0")] #![unstable(feature = "test_feature", issue = "0")]
#[stable(feature = "test_feature", 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 foo() -> usize { pub fn foo() -> usize {
20 20
} }

View File

@ -14,14 +14,14 @@
#![stable(feature = "lint_stability", since = "1.0.0")] #![stable(feature = "lint_stability", since = "1.0.0")]
#[stable(feature = "test_feature", 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() {} pub fn deprecated() {}
#[stable(feature = "test_feature", since = "1.0.0")] #[stable(feature = "test_feature", since = "1.0.0")]
#[deprecated(since = "1.0.0", reason = "text")] #[deprecated(since = "1.0.0", reason = "text")]
pub fn deprecated_text() {} pub fn deprecated_text() {}
#[unstable(feature = "test_feature", issue = "0")] #[unstable(feature = "test_feature", issue = "0")]
#[deprecated(since = "1.0.0")] #[deprecated(since = "1.0.0", reason = "text")]
pub fn deprecated_unstable() {} pub fn deprecated_unstable() {}
#[unstable(feature = "test_feature", issue = "0")] #[unstable(feature = "test_feature", issue = "0")]
#[deprecated(since = "1.0.0", reason = "text")] #[deprecated(since = "1.0.0", reason = "text")]
@ -34,7 +34,7 @@ pub fn unstable_text() {}
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
pub fn stable() {} pub fn stable() {}
#[stable(feature = "rust1", since = "1.0.0", reason = "text")] #[stable(feature = "rust1", since = "1.0.0")]
pub fn stable_text() {} pub fn stable_text() {}
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
@ -42,14 +42,14 @@ pub struct MethodTester;
impl MethodTester { impl MethodTester {
#[stable(feature = "test_feature", 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 method_deprecated(&self) {} pub fn method_deprecated(&self) {}
#[stable(feature = "test_feature", since = "1.0.0")] #[stable(feature = "test_feature", since = "1.0.0")]
#[deprecated(since = "1.0.0", reason = "text")] #[deprecated(since = "1.0.0", reason = "text")]
pub fn method_deprecated_text(&self) {} pub fn method_deprecated_text(&self) {}
#[unstable(feature = "test_feature", issue = "0")] #[unstable(feature = "test_feature", issue = "0")]
#[deprecated(since = "1.0.0")] #[deprecated(since = "1.0.0", reason = "text")]
pub fn method_deprecated_unstable(&self) {} pub fn method_deprecated_unstable(&self) {}
#[unstable(feature = "test_feature", issue = "0")] #[unstable(feature = "test_feature", issue = "0")]
#[deprecated(since = "1.0.0", reason = "text")] #[deprecated(since = "1.0.0", reason = "text")]
@ -62,21 +62,21 @@ impl MethodTester {
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
pub fn method_stable(&self) {} 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 fn method_stable_text(&self) {}
} }
#[stable(feature = "test_feature", since = "1.0.0")] #[stable(feature = "test_feature", since = "1.0.0")]
pub trait Trait { pub trait Trait {
#[stable(feature = "test_feature", since = "1.0.0")] #[stable(feature = "test_feature", since = "1.0.0")]
#[deprecated(since = "1.0.0")] #[deprecated(since = "1.0.0", reason = "text")]
fn trait_deprecated(&self) {} fn trait_deprecated(&self) {}
#[stable(feature = "test_feature", since = "1.0.0")] #[stable(feature = "test_feature", since = "1.0.0")]
#[deprecated(since = "1.0.0", reason = "text")] #[deprecated(since = "1.0.0", reason = "text")]
fn trait_deprecated_text(&self) {} fn trait_deprecated_text(&self) {}
#[unstable(feature = "test_feature", issue = "0")] #[unstable(feature = "test_feature", issue = "0")]
#[deprecated(since = "1.0.0")] #[deprecated(since = "1.0.0", reason = "text")]
fn trait_deprecated_unstable(&self) {} fn trait_deprecated_unstable(&self) {}
#[unstable(feature = "test_feature", issue = "0")] #[unstable(feature = "test_feature", issue = "0")]
#[deprecated(since = "1.0.0", reason = "text")] #[deprecated(since = "1.0.0", reason = "text")]
@ -89,7 +89,7 @@ pub trait Trait {
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
fn trait_stable(&self) {} 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) {} fn trait_stable_text(&self) {}
} }
@ -99,12 +99,12 @@ impl Trait for MethodTester {}
pub trait UnstableTrait { fn dummy(&self) { } } pub trait UnstableTrait { fn dummy(&self) { } }
#[stable(feature = "test_feature", 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 struct DeprecatedStruct { pub struct DeprecatedStruct {
#[stable(feature = "test_feature", since = "1.0.0")] pub i: isize #[stable(feature = "test_feature", since = "1.0.0")] pub i: isize
} }
#[unstable(feature = "test_feature", issue = "0")] #[unstable(feature = "test_feature", issue = "0")]
#[deprecated(since = "1.0.0")] #[deprecated(since = "1.0.0", reason = "text")]
pub struct DeprecatedUnstableStruct { pub struct DeprecatedUnstableStruct {
#[stable(feature = "test_feature", since = "1.0.0")] pub i: isize #[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")] #[stable(feature = "test_feature", since = "1.0.0")]
#[deprecated(since = "1.0.0")] #[deprecated(since = "1.0.0", reason = "text")]
pub struct DeprecatedUnitStruct; pub struct DeprecatedUnitStruct;
#[unstable(feature = "test_feature", issue = "0")] #[unstable(feature = "test_feature", issue = "0")]
#[deprecated(since = "1.0.0")] #[deprecated(since = "1.0.0", reason = "text")]
pub struct DeprecatedUnstableUnitStruct; pub struct DeprecatedUnstableUnitStruct;
#[unstable(feature = "test_feature", issue = "0")] #[unstable(feature = "test_feature", issue = "0")]
pub struct UnstableUnitStruct; pub struct UnstableUnitStruct;
@ -131,10 +131,10 @@ pub struct StableUnitStruct;
#[stable(feature = "test_feature", since = "1.0.0")] #[stable(feature = "test_feature", since = "1.0.0")]
pub enum Enum { pub enum Enum {
#[stable(feature = "test_feature", since = "1.0.0")] #[stable(feature = "test_feature", since = "1.0.0")]
#[deprecated(since = "1.0.0")] #[deprecated(since = "1.0.0", reason = "text")]
DeprecatedVariant, DeprecatedVariant,
#[unstable(feature = "test_feature", issue = "0")] #[unstable(feature = "test_feature", issue = "0")]
#[deprecated(since = "1.0.0")] #[deprecated(since = "1.0.0", reason = "text")]
DeprecatedUnstableVariant, DeprecatedUnstableVariant,
#[unstable(feature = "test_feature", issue = "0")] #[unstable(feature = "test_feature", issue = "0")]
UnstableVariant, UnstableVariant,
@ -144,10 +144,10 @@ pub enum Enum {
} }
#[stable(feature = "test_feature", 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 struct DeprecatedTupleStruct(#[stable(feature = "rust1", since = "1.0.0")] pub isize); pub struct DeprecatedTupleStruct(#[stable(feature = "rust1", since = "1.0.0")] pub isize);
#[unstable(feature = "test_feature", issue = "0")] #[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); pub struct DeprecatedUnstableTupleStruct(#[stable(feature = "rust1", since = "1.0.0")] pub isize);
#[unstable(feature = "test_feature", issue = "0")] #[unstable(feature = "test_feature", issue = "0")]
pub struct UnstableTupleStruct(#[stable(feature = "rust1", since = "1.0.0")] pub isize); pub struct UnstableTupleStruct(#[stable(feature = "rust1", since = "1.0.0")] pub isize);

View File

@ -18,7 +18,7 @@ pub struct Stable {
pub inherit: u8, // it's a lie (stable doesn't inherit) pub inherit: u8, // it's a lie (stable doesn't inherit)
#[unstable(feature = "test_feature", issue = "0")] #[unstable(feature = "test_feature", issue = "0")]
pub override1: u8, pub override1: u8,
#[deprecated(since = "1.0.0")] #[deprecated(since = "1.0.0", reason = "text")]
#[unstable(feature = "test_feature", issue = "0")] #[unstable(feature = "test_feature", issue = "0")]
pub override2: u8, pub override2: u8,
} }
@ -27,14 +27,14 @@ pub struct Stable {
pub struct Stable2(#[stable(feature = "rust1", since = "1.0.0")] pub u8, 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")] pub u8,
#[unstable(feature = "test_feature", issue = "0")] #[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")] #[unstable(feature = "test_feature", issue = "0")]
pub struct Unstable { pub struct Unstable {
pub inherit: u8, pub inherit: u8,
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
pub override1: u8, pub override1: u8,
#[deprecated(since = "1.0.0")] #[deprecated(since = "1.0.0", reason = "text")]
#[unstable(feature = "test_feature", issue = "0")] #[unstable(feature = "test_feature", issue = "0")]
pub override2: u8, pub override2: u8,
} }
@ -43,10 +43,10 @@ pub struct Unstable {
pub struct Unstable2(pub u8, pub struct Unstable2(pub u8,
#[stable(feature = "rust1", since = "1.0.0")] pub u8, #[stable(feature = "rust1", since = "1.0.0")] pub u8,
#[unstable(feature = "test_feature", issue = "0")] #[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")] #[unstable(feature = "test_feature", issue = "0")]
#[deprecated(feature = "rust1", since = "1.0.0")] #[deprecated(since = "1.0.0", reason = "text")]
pub struct Deprecated { pub struct Deprecated {
pub inherit: u8, pub inherit: u8,
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
@ -56,7 +56,7 @@ pub struct Deprecated {
} }
#[unstable(feature = "test_feature", issue = "0")] #[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, pub struct Deprecated2(pub u8,
#[stable(feature = "rust1", since = "1.0.0")] pub u8, #[stable(feature = "rust1", since = "1.0.0")] pub u8,
#[unstable(feature = "test_feature", issue = "0")] pub u8); #[unstable(feature = "test_feature", issue = "0")] pub u8);

View File

@ -15,8 +15,8 @@
struct Foo; struct Foo;
impl Foo { impl Foo {
#[unstable(feature = "test_feature")] #[unstable(feature = "test_feature", issue = "0")]
#[deprecated(since = "1.0.0")] #[deprecated(since = "1.0.0", reason = "text")]
fn foo(self) {} fn foo(self) {}
} }

View File

@ -15,7 +15,7 @@
extern crate lint_output_format; //~ ERROR use of unstable library feature extern crate lint_output_format; //~ ERROR use of unstable library feature
use lint_output_format::{foo, bar}; //~ 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() { fn main() {
let _x = foo(); //~ WARNING #[warn(deprecated)] on by default let _x = foo(); //~ WARNING #[warn(deprecated)] on by default

View File

@ -189,7 +189,7 @@ mod this_crate {
inherit: u8, inherit: u8,
#[unstable(feature = "test_feature", issue = "0")] #[unstable(feature = "test_feature", issue = "0")]
override1: u8, override1: u8,
#[deprecated(since = "1.0.0")] #[deprecated(since = "1.0.0", reason = "text")]
#[unstable(feature = "test_feature", issue = "0")] #[unstable(feature = "test_feature", issue = "0")]
override2: u8, override2: u8,
} }
@ -198,14 +198,14 @@ mod this_crate {
struct Stable2(u8, struct Stable2(u8,
#[stable(feature = "rust1", since = "1.0.0")] u8, #[stable(feature = "rust1", since = "1.0.0")] u8,
#[unstable(feature = "test_feature", issue = "0")] #[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")] #[unstable(feature = "test_feature", issue = "0")]
struct Unstable { struct Unstable {
inherit: u8, inherit: u8,
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
override1: u8, override1: u8,
#[deprecated(since = "1.0.0")] #[deprecated(since = "1.0.0", reason = "text")]
#[unstable(feature = "test_feature", issue = "0")] #[unstable(feature = "test_feature", issue = "0")]
override2: u8, override2: u8,
} }
@ -214,10 +214,10 @@ mod this_crate {
struct Unstable2(u8, struct Unstable2(u8,
#[stable(feature = "rust1", since = "1.0.0")] u8, #[stable(feature = "rust1", since = "1.0.0")] u8,
#[unstable(feature = "test_feature", issue = "0")] #[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")] #[unstable(feature = "test_feature", issue = "0")]
#[deprecated(feature = "rust1", since = "1.0.0")] #[deprecated(since = "1.0.0", reason = "text")]
struct Deprecated { struct Deprecated {
inherit: u8, inherit: u8,
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
@ -227,7 +227,7 @@ mod this_crate {
} }
#[unstable(feature = "test_feature", issue = "0")] #[unstable(feature = "test_feature", issue = "0")]
#[deprecated(feature = "rust1", since = "1.0.0")] #[deprecated(since = "1.0.0", reason = "text")]
struct Deprecated2(u8, struct Deprecated2(u8,
#[stable(feature = "rust1", since = "1.0.0")] u8, #[stable(feature = "rust1", since = "1.0.0")] u8,
#[unstable(feature = "test_feature", issue = "0")] u8); #[unstable(feature = "test_feature", issue = "0")] u8);

View File

@ -258,7 +258,7 @@ mod inheritance {
mod this_crate { mod this_crate {
#[unstable(feature = "test_feature", issue = "0")] #[unstable(feature = "test_feature", issue = "0")]
#[deprecated(since = "1.0.0")] #[deprecated(since = "1.0.0", reason = "text")]
pub fn deprecated() {} pub fn deprecated() {}
#[unstable(feature = "test_feature", issue = "0")] #[unstable(feature = "test_feature", issue = "0")]
#[deprecated(since = "1.0.0", reason = "text")] #[deprecated(since = "1.0.0", reason = "text")]
@ -271,7 +271,7 @@ mod this_crate {
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
pub fn stable() {} pub fn stable() {}
#[stable(feature = "rust1", since = "1.0.0", reason = "text")] #[stable(feature = "rust1", since = "1.0.0")]
pub fn stable_text() {} pub fn stable_text() {}
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
@ -279,7 +279,7 @@ mod this_crate {
impl MethodTester { impl MethodTester {
#[unstable(feature = "test_feature", issue = "0")] #[unstable(feature = "test_feature", issue = "0")]
#[deprecated(since = "1.0.0")] #[deprecated(since = "1.0.0", reason = "text")]
pub fn method_deprecated(&self) {} pub fn method_deprecated(&self) {}
#[unstable(feature = "test_feature", issue = "0")] #[unstable(feature = "test_feature", issue = "0")]
#[deprecated(since = "1.0.0", reason = "text")] #[deprecated(since = "1.0.0", reason = "text")]
@ -292,13 +292,13 @@ mod this_crate {
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
pub fn method_stable(&self) {} 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 fn method_stable_text(&self) {}
} }
pub trait Trait { pub trait Trait {
#[unstable(feature = "test_feature", issue = "0")] #[unstable(feature = "test_feature", issue = "0")]
#[deprecated(since = "1.0.0")] #[deprecated(since = "1.0.0", reason = "text")]
fn trait_deprecated(&self) {} fn trait_deprecated(&self) {}
#[unstable(feature = "test_feature", issue = "0")] #[unstable(feature = "test_feature", issue = "0")]
#[deprecated(since = "1.0.0", reason = "text")] #[deprecated(since = "1.0.0", reason = "text")]
@ -311,14 +311,14 @@ mod this_crate {
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
fn trait_stable(&self) {} 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) {} fn trait_stable_text(&self) {}
} }
impl Trait for MethodTester {} impl Trait for MethodTester {}
#[unstable(feature = "test_feature", issue = "0")] #[unstable(feature = "test_feature", issue = "0")]
#[deprecated(since = "1.0.0")] #[deprecated(since = "1.0.0", reason = "text")]
pub struct DeprecatedStruct { pub struct DeprecatedStruct {
#[stable(feature = "test_feature", since = "1.0.0")] i: isize #[stable(feature = "test_feature", since = "1.0.0")] i: isize
} }
@ -332,7 +332,7 @@ mod this_crate {
} }
#[unstable(feature = "test_feature", issue = "0")] #[unstable(feature = "test_feature", issue = "0")]
#[deprecated(since = "1.0.0")] #[deprecated(since = "1.0.0", reason = "text")]
pub struct DeprecatedUnitStruct; pub struct DeprecatedUnitStruct;
#[unstable(feature = "test_feature", issue = "0")] #[unstable(feature = "test_feature", issue = "0")]
pub struct UnstableUnitStruct; pub struct UnstableUnitStruct;
@ -341,7 +341,7 @@ mod this_crate {
pub enum Enum { pub enum Enum {
#[unstable(feature = "test_feature", issue = "0")] #[unstable(feature = "test_feature", issue = "0")]
#[deprecated(since = "1.0.0")] #[deprecated(since = "1.0.0", reason = "text")]
DeprecatedVariant, DeprecatedVariant,
#[unstable(feature = "test_feature", issue = "0")] #[unstable(feature = "test_feature", issue = "0")]
UnstableVariant, UnstableVariant,
@ -351,7 +351,7 @@ mod this_crate {
} }
#[unstable(feature = "test_feature", issue = "0")] #[unstable(feature = "test_feature", issue = "0")]
#[deprecated(since = "1.0.0")] #[deprecated(since = "1.0.0", reason = "text")]
pub struct DeprecatedTupleStruct(isize); pub struct DeprecatedTupleStruct(isize);
#[unstable(feature = "test_feature", issue = "0")] #[unstable(feature = "test_feature", issue = "0")]
pub struct UnstableTupleStruct(isize); pub struct UnstableTupleStruct(isize);
@ -472,7 +472,7 @@ mod this_crate {
} }
#[unstable(feature = "test_feature", issue = "0")] #[unstable(feature = "test_feature", issue = "0")]
#[deprecated(since = "1.0.0")] #[deprecated(since = "1.0.0", reason = "text")]
fn test_fn_body() { fn test_fn_body() {
fn fn_in_body() {} fn fn_in_body() {}
fn_in_body(); fn_in_body();
@ -480,7 +480,7 @@ mod this_crate {
impl MethodTester { impl MethodTester {
#[unstable(feature = "test_feature", issue = "0")] #[unstable(feature = "test_feature", issue = "0")]
#[deprecated(since = "1.0.0")] #[deprecated(since = "1.0.0", reason = "text")]
fn test_method_body(&self) { fn test_method_body(&self) {
fn fn_in_body() {} fn fn_in_body() {}
fn_in_body(); fn_in_body();
@ -488,7 +488,7 @@ mod this_crate {
} }
#[unstable(feature = "test_feature", issue = "0")] #[unstable(feature = "test_feature", issue = "0")]
#[deprecated(since = "1.0.0")] #[deprecated(since = "1.0.0", reason = "text")]
pub trait DeprecatedTrait { pub trait DeprecatedTrait {
fn dummy(&self) { } fn dummy(&self) { }
} }

View File

@ -14,22 +14,19 @@
#![staged_api] #![staged_api]
mod bogus_attribute_types_1 { 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() { } fn f1() { }
#[stable(feature = "a", since, reason = "a")] //~ ERROR incorrect meta item #[stable(feature = "a", since)] //~ ERROR incorrect meta item
fn f2() { } fn f2() { }
#[stable(feature, since = "a", reason = "a")] //~ ERROR incorrect meta item #[stable(feature, since = "a")] //~ ERROR incorrect meta item
fn f3() { } fn f3() { }
#[stable(feature = "a", since = "a", reason(b))] //~ ERROR incorrect meta item #[stable(feature = "a", since(b))] //~ ERROR incorrect meta item
fn f4() { }
#[stable(feature = "a", since(b), reason = "a")] //~ ERROR incorrect meta item
fn f5() { } fn f5() { }
#[stable(feature(b), since = "a", reason = "a")] //~ ERROR incorrect meta item #[stable(feature(b), since = "a")] //~ ERROR incorrect meta item
fn f6() { } fn f6() { }
} }
@ -56,11 +53,11 @@ mod bogus_attribute_types_2 {
} }
mod missing_feature_names { mod missing_feature_names {
#[unstable(since = "a", issue = "0")] //~ ERROR missing 'feature' #[unstable(issue = "0")] //~ ERROR missing 'feature'
fn f1() { } fn f1() { }
#[unstable(feature = "a")] #[unstable(feature = "a")] //~ ERROR missing 'issue'
fn f2() { } //~ ERROR need to point to an issue fn f2() { }
#[stable(since = "a")] //~ ERROR missing 'feature' #[stable(since = "a")] //~ ERROR missing 'feature'
fn f3() { } fn f3() { }
@ -75,12 +72,12 @@ mod missing_version {
fn f2() { } fn f2() { }
} }
#[unstable(feature = "a", since = "b", issue = "0")] #[unstable(feature = "a", issue = "0")]
#[stable(feature = "a", since = "b")] #[stable(feature = "a", since = "b")]
fn multiple1() { } //~ ERROR multiple stability levels fn multiple1() { } //~ ERROR multiple stability levels
#[unstable(feature = "a", since = "b", issue = "0")] #[unstable(feature = "a", issue = "0")]
#[unstable(feature = "a", since = "b", issue = "0")] #[unstable(feature = "a", issue = "0")]
fn multiple2() { } //~ ERROR multiple stability levels fn multiple2() { } //~ ERROR multiple stability levels
#[stable(feature = "a", since = "b")] #[stable(feature = "a", since = "b")]
@ -88,12 +85,12 @@ fn multiple2() { } //~ ERROR multiple stability levels
fn multiple3() { } //~ ERROR multiple stability levels fn multiple3() { } //~ ERROR multiple stability levels
#[stable(feature = "a", since = "b")] #[stable(feature = "a", since = "b")]
#[deprecated(since = "b")] #[deprecated(since = "b", reason = "text")]
#[deprecated(since = "b")] #[deprecated(since = "b", reason = "text")]
fn multiple4() { } //~ ERROR multiple deprecated attributes fn multiple4() { } //~ ERROR multiple deprecated attributes
//~^ ERROR Invalid stability or deprecation version found //~^ 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 deprecated_without_unstable_or_stable() { } //~ ERROR deprecated attribute must be paired
fn main() { } fn main() { }