Skip to content

Commit 52acc05

Browse files
committed
Rework stability annotation pass
1 parent b2f5393 commit 52acc05

File tree

3 files changed

+163
-137
lines changed

3 files changed

+163
-137
lines changed

src/librustc/middle/stability.rs

+147-130
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
//! propagating default levels lexically from parent to children ast nodes.
1313
1414
pub use self::StabilityLevel::*;
15+
use self::AnnotationKind::*;
1516

1617
use session::Session;
1718
use lint;
@@ -30,8 +31,8 @@ use syntax::attr::{self, Stability, AttrMetaMethods};
3031
use util::nodemap::{DefIdMap, FnvHashSet, FnvHashMap};
3132

3233
use rustc_front::hir;
33-
use rustc_front::hir::{FnDecl, Block, Crate, Item, Generics, StructField, Variant};
34-
use rustc_front::visit::{self, FnKind, Visitor};
34+
use rustc_front::hir::{Block, Crate, Item, Generics, StructField, Variant};
35+
use rustc_front::visit::{self, Visitor};
3536

3637
use std::mem::replace;
3738
use std::cmp::Ordering;
@@ -48,6 +49,16 @@ impl StabilityLevel {
4849
}
4950
}
5051

52+
#[derive(PartialEq)]
53+
enum AnnotationKind {
54+
// Annotation is required if not inherited from unstable parents
55+
AnnRequired,
56+
// Annotation is useless, reject it
57+
AnnProhibited,
58+
// Annotation itself is useless, but it can be propagated to children
59+
AnnContainer,
60+
}
61+
5162
/// A stability index, giving the stability level for items and methods.
5263
pub struct Index<'tcx> {
5364
/// This is mostly a cache, except the stabilities of local items
@@ -64,174 +75,180 @@ struct Annotator<'a, 'tcx: 'a> {
6475
index: &'a mut Index<'tcx>,
6576
parent: Option<&'tcx Stability>,
6677
export_map: &'a PublicItems,
78+
in_trait_impl: bool,
79+
in_enum: bool,
6780
}
6881

6982
impl<'a, 'tcx: 'a> Annotator<'a, 'tcx> {
7083
// Determine the stability for a node based on its attributes and inherited
7184
// stability. The stability is recorded in the index and used as the parent.
72-
fn annotate<F>(&mut self, id: NodeId, use_parent: bool,
73-
attrs: &Vec<Attribute>, item_sp: Span, f: F, required: bool) where
74-
F: FnOnce(&mut Annotator),
85+
fn annotate<F>(&mut self, id: NodeId, attrs: &Vec<Attribute>,
86+
item_sp: Span, kind: AnnotationKind, visit_children: F)
87+
where F: FnOnce(&mut Annotator)
7588
{
7689
if self.index.staged_api[&LOCAL_CRATE] {
7790
debug!("annotate(id = {:?}, attrs = {:?})", id, attrs);
78-
match attr::find_stability(self.tcx.sess.diagnostic(), attrs, item_sp) {
79-
Some(mut stab) => {
80-
debug!("annotate: found {:?}", stab);
81-
// if parent is deprecated and we're not, inherit this by merging
82-
// deprecated_since and its reason.
83-
if let Some(parent_stab) = self.parent {
84-
if parent_stab.depr.is_some()
85-
&& stab.depr.is_none() {
86-
stab.depr = parent_stab.depr.clone()
87-
}
91+
if let Some(mut stab) = attr::find_stability(self.tcx.sess.diagnostic(),
92+
attrs, item_sp) {
93+
// Error if prohibited, or can't inherit anything from a container
94+
if kind == AnnProhibited ||
95+
kind == AnnContainer && stab.level.is_stable() && stab.depr.is_none() {
96+
self.tcx.sess.span_err(item_sp, "This stability annotation is useless");
97+
}
98+
99+
debug!("annotate: found {:?}", stab);
100+
// If parent is deprecated and we're not, inherit this by merging
101+
// deprecated_since and its reason.
102+
if let Some(parent_stab) = self.parent {
103+
if parent_stab.depr.is_some() && stab.depr.is_none() {
104+
stab.depr = parent_stab.depr.clone()
88105
}
106+
}
89107

90-
let stab = self.tcx.intern_stability(stab);
91-
92-
// Check if deprecated_since < stable_since. If it is,
93-
// this is *almost surely* an accident.
94-
let deprecated_predates_stable = match (&stab.depr, &stab.level) {
95-
(&Some(attr::Deprecation {since: ref dep_since, ..}),
96-
&attr::Stable {since: ref stab_since}) => {
97-
// explicit version of iter::order::lt to handle parse errors properly
98-
let mut is_less = false;
99-
for (dep_v, stab_v) in dep_since.split(".").zip(stab_since.split(".")) {
100-
match (dep_v.parse::<u64>(), stab_v.parse::<u64>()) {
101-
(Ok(dep_v), Ok(stab_v)) => match dep_v.cmp(&stab_v) {
102-
Ordering::Less => {
103-
is_less = true;
104-
break;
105-
}
106-
Ordering::Equal => { continue; }
107-
Ordering::Greater => { break; }
108-
},
109-
_ => {
110-
self.tcx.sess.span_err(item_sp,
111-
"Invalid stability or deprecation version found");
112-
// act like it isn't less because the question is now
113-
// nonsensical, and this makes us not do anything else
114-
// interesting.
115-
break;
116-
}
108+
let stab = self.tcx.intern_stability(stab);
109+
110+
// Check if deprecated_since < stable_since. If it is,
111+
// this is *almost surely* an accident.
112+
if let (&Some(attr::Deprecation {since: ref dep_since, ..}),
113+
&attr::Stable {since: ref stab_since}) = (&stab.depr, &stab.level) {
114+
// Explicit version of iter::order::lt to handle parse errors properly
115+
for (dep_v, stab_v) in dep_since.split(".").zip(stab_since.split(".")) {
116+
if let (Ok(dep_v), Ok(stab_v)) = (dep_v.parse::<u64>(), stab_v.parse()) {
117+
match dep_v.cmp(&stab_v) {
118+
Ordering::Less => {
119+
self.tcx.sess.span_err(item_sp, "An API can't be stabilized \
120+
after it is deprecated");
121+
break
117122
}
123+
Ordering::Equal => continue,
124+
Ordering::Greater => break,
118125
}
119-
is_less
120-
},
121-
_ => false,
122-
};
123-
124-
if deprecated_predates_stable {
125-
self.tcx.sess.span_err(item_sp,
126-
"An API can't be stabilized after it is deprecated");
126+
} else {
127+
// Act like it isn't less because the question is now nonsensical,
128+
// and this makes us not do anything else interesting.
129+
self.tcx.sess.span_err(item_sp, "Invalid stability or deprecation \
130+
version found");
131+
break
132+
}
127133
}
134+
}
128135

129-
let def_id = self.tcx.map.local_def_id(id);
130-
self.index.map.insert(def_id, Some(stab));
131-
132-
// Don't inherit #[stable(feature = "rust1", since = "1.0.0")]
133-
if !stab.level.is_stable() {
134-
let parent = replace(&mut self.parent, Some(stab));
135-
f(self);
136-
self.parent = parent;
137-
} else {
138-
f(self);
136+
let def_id = self.tcx.map.local_def_id(id);
137+
self.index.map.insert(def_id, Some(stab));
138+
139+
let parent = replace(&mut self.parent, Some(stab));
140+
visit_children(self);
141+
self.parent = parent;
142+
} else {
143+
debug!("annotate: not found, parent = {:?}", self.parent);
144+
let mut is_error = kind == AnnRequired &&
145+
self.export_map.contains(&id) &&
146+
!self.tcx.sess.opts.test;
147+
if let Some(stab) = self.parent {
148+
if stab.level.is_unstable() {
149+
let def_id = self.tcx.map.local_def_id(id);
150+
self.index.map.insert(def_id, Some(stab));
151+
is_error = false;
139152
}
140153
}
141-
None => {
142-
debug!("annotate: not found, use_parent = {:?}, parent = {:?}",
143-
use_parent, self.parent);
144-
if use_parent {
145-
if let Some(stab) = self.parent {
146-
let def_id = self.tcx.map.local_def_id(id);
147-
self.index.map.insert(def_id, Some(stab));
148-
} else if self.index.staged_api[&LOCAL_CRATE] && required
149-
&& self.export_map.contains(&id)
150-
&& !self.tcx.sess.opts.test {
151-
self.tcx.sess.span_err(item_sp,
152-
"This node does not \
153-
have a stability attribute");
154-
}
155-
}
156-
f(self);
154+
if is_error {
155+
self.tcx.sess.span_err(item_sp, "This node does not have \
156+
a stability attribute");
157157
}
158+
visit_children(self);
158159
}
159160
} else {
160-
// Emit warnings for non-staged-api crates. These should be errors.
161+
// Emit errors for non-staged-api crates.
161162
for attr in attrs {
162163
let tag = attr.name();
163164
if tag == "unstable" || tag == "stable" || tag == "deprecated" {
164165
attr::mark_used(attr);
165-
self.tcx.sess.span_err(attr.span(),
166-
"stability attributes may not be used outside \
167-
of the standard library");
166+
self.tcx.sess.span_err(attr.span(), "stability attributes may not be used \
167+
outside of the standard library");
168168
}
169169
}
170-
f(self);
170+
visit_children(self);
171171
}
172172
}
173173
}
174174

175175
impl<'a, 'tcx, 'v> Visitor<'v> for Annotator<'a, 'tcx> {
176176
fn visit_item(&mut self, i: &Item) {
177-
// FIXME (#18969): the following is a hack around the fact
178-
// that we cannot currently annotate the stability of
179-
// `deriving`. Basically, we do *not* allow stability
180-
// inheritance on trait implementations, so that derived
181-
// implementations appear to be unannotated. This then allows
182-
// derived implementations to be automatically tagged with the
183-
// stability of the trait. This is WRONG, but expedient to get
184-
// libstd stabilized for the 1.0 release.
185-
let use_parent = match i.node {
186-
hir::ItemImpl(_, _, _, Some(_), _, _) => false,
187-
_ => true,
188-
};
189-
190-
// In case of a `pub use <mod>;`, we should not error since the stability
191-
// is inherited from the module itself
192-
let required = match i.node {
193-
hir::ItemUse(_) => i.vis != hir::Public,
194-
_ => true
195-
};
196-
197-
self.annotate(i.id, use_parent, &i.attrs, i.span,
198-
|v| visit::walk_item(v, i), required);
199-
200-
if let hir::ItemStruct(ref sd, _) = i.node {
201-
if !sd.is_struct() {
202-
self.annotate(sd.id(), true, &i.attrs, i.span, |_| {}, true)
177+
let orig_in_trait_impl = self.in_trait_impl;
178+
let orig_in_enum = self.in_enum;
179+
let mut kind = AnnRequired;
180+
match i.node {
181+
// Inherent impls and foreign modules serve only as containers for other items,
182+
// they don't have their own stability. They still can be annotated as unstable
183+
// and propagate this unstability to children, but this annotation is completely
184+
// optional. They inherit stability from their parents when unannotated.
185+
hir::ItemImpl(_, _, _, None, _, _) | hir::ItemForeignMod(..) => {
186+
self.in_trait_impl = false;
187+
kind = AnnContainer;
188+
}
189+
hir::ItemImpl(_, _, _, Some(_), _, _) => {
190+
self.in_trait_impl = true;
191+
}
192+
hir::ItemStruct(ref sd, _) => {
193+
self.in_enum = false;
194+
if !sd.is_struct() {
195+
self.annotate(sd.id(), &i.attrs, i.span, AnnRequired, |_| {})
196+
}
197+
}
198+
hir::ItemEnum(..) => {
199+
self.in_enum = true;
203200
}
201+
_ => {}
204202
}
205-
}
206203

207-
fn visit_fn(&mut self, _: FnKind<'v>, _: &'v FnDecl,
208-
_: &'v Block, _: Span, _: NodeId) {
209-
// Items defined in a function body have no reason to have
210-
// a stability attribute, so we don't recurse.
204+
self.annotate(i.id, &i.attrs, i.span, kind, |v| {
205+
visit::walk_item(v, i)
206+
});
207+
self.in_trait_impl = orig_in_trait_impl;
208+
self.in_enum = orig_in_enum;
211209
}
212210

213211
fn visit_trait_item(&mut self, ti: &hir::TraitItem) {
214-
self.annotate(ti.id, true, &ti.attrs, ti.span,
215-
|v| visit::walk_trait_item(v, ti), true);
212+
self.annotate(ti.id, &ti.attrs, ti.span, AnnRequired, |v| {
213+
visit::walk_trait_item(v, ti);
214+
});
216215
}
217216

218217
fn visit_impl_item(&mut self, ii: &hir::ImplItem) {
219-
self.annotate(ii.id, true, &ii.attrs, ii.span,
220-
|v| visit::walk_impl_item(v, ii), false);
218+
let kind = if self.in_trait_impl { AnnProhibited } else { AnnRequired };
219+
self.annotate(ii.id, &ii.attrs, ii.span, kind, |v| {
220+
visit::walk_impl_item(v, ii);
221+
});
221222
}
222223

223224
fn visit_variant(&mut self, var: &Variant, g: &'v Generics, item_id: NodeId) {
224-
self.annotate(var.node.data.id(), true, &var.node.attrs, var.span,
225-
|v| visit::walk_variant(v, var, g, item_id), true)
225+
self.annotate(var.node.data.id(), &var.node.attrs, var.span, AnnRequired, |v| {
226+
visit::walk_variant(v, var, g, item_id);
227+
})
226228
}
227229

228230
fn visit_struct_field(&mut self, s: &StructField) {
229-
self.annotate(s.node.id, true, &s.node.attrs, s.span,
230-
|v| visit::walk_struct_field(v, s), !s.node.kind.is_unnamed());
231+
// FIXME: This is temporary, can't use attributes with tuple variant fields until snapshot
232+
let kind = if self.in_enum && s.node.kind.is_unnamed() {
233+
AnnProhibited
234+
} else {
235+
AnnRequired
236+
};
237+
self.annotate(s.node.id, &s.node.attrs, s.span, kind, |v| {
238+
visit::walk_struct_field(v, s);
239+
});
231240
}
232241

233242
fn visit_foreign_item(&mut self, i: &hir::ForeignItem) {
234-
self.annotate(i.id, true, &i.attrs, i.span, |_| {}, true);
243+
self.annotate(i.id, &i.attrs, i.span, AnnRequired, |v| {
244+
visit::walk_foreign_item(v, i);
245+
});
246+
}
247+
248+
fn visit_macro_def(&mut self, md: &'v hir::MacroDef) {
249+
if md.imported_from.is_none() {
250+
self.annotate(md.id, &md.attrs, md.span, AnnRequired, |_| {});
251+
}
235252
}
236253
}
237254

@@ -243,21 +260,21 @@ impl<'tcx> Index<'tcx> {
243260
index: self,
244261
parent: None,
245262
export_map: export_map,
263+
in_trait_impl: false,
264+
in_enum: false,
246265
};
247-
annotator.annotate(ast::CRATE_NODE_ID, true, &krate.attrs, krate.span,
248-
|v| visit::walk_crate(v, krate), true);
266+
annotator.annotate(ast::CRATE_NODE_ID, &krate.attrs, krate.span, AnnRequired,
267+
|v| visit::walk_crate(v, krate));
249268
}
250269

251270
pub fn new(krate: &Crate) -> Index {
252271
let mut is_staged_api = false;
253272
for attr in &krate.attrs {
254-
if &attr.name()[..] == "staged_api" {
255-
match attr.node.value.node {
256-
ast::MetaWord(_) => {
257-
attr::mark_used(attr);
258-
is_staged_api = true;
259-
}
260-
_ => (/*pass*/)
273+
if attr.name() == "staged_api" {
274+
if let ast::MetaWord(_) = attr.node.value.node {
275+
attr::mark_used(attr);
276+
is_staged_api = true;
277+
break
261278
}
262279
}
263280
}

src/librustc_driver/driver.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -739,7 +739,6 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
739739
lang_items,
740740
stability::Index::new(krate),
741741
|tcx| {
742-
743742
// passes are timed inside typeck
744743
typeck::check_crate(tcx, trait_map);
745744

@@ -756,7 +755,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
756755

757756
// Do not move this check past lint
758757
time(time_passes, "stability index", || {
759-
tcx.stability.borrow_mut().build(tcx, krate, &public_items)
758+
tcx.stability.borrow_mut().build(tcx, krate, &exported_items)
760759
});
761760

762761
time(time_passes,

0 commit comments

Comments
 (0)