Skip to content

Commit 2e0fc86

Browse files
aturonnrc
authored andcommitted
Add stability inheritance
This commit makes several changes to the stability index infrastructure: * Stability levels are now inherited lexically, i.e., each item's stability level becomes the default for any nested items. * The computed stability level for an item is stored as part of the metadata. When using an item from an external crate, this data is looked up and cached. * The stability lint works from the computed stability level, rather than manual stability attribute annotations. However, the lint still checks only a limited set of item uses (e.g., it does not check every component of a path on import). This will be addressed in a later PR, as part of issue rust-lang#8962. * The stability lint only applies to items originating from external crates, since the stability index is intended as a promise to downstream crates. * The "experimental" lint is now _allow_ by default. This is because almost all existing crates have been marked "experimental", pending library stabilization. With inheritance in place, this would generate a massive explosion of warnings for every Rust program. The lint should be changed back to deny-by-default after library stabilization is complete. * The "deprecated" lint still warns by default. The net result: we can begin tracking stability index for the standard libraries as we stabilize, without impacting most clients. Closes rust-lang#13540.
1 parent 1288bbe commit 2e0fc86

15 files changed

+385
-109
lines changed

src/doc/rust.md

+24-9
Original file line numberDiff line numberDiff line change
@@ -2301,28 +2301,43 @@ One can indicate the stability of an API using the following attributes:
23012301
These levels are directly inspired by
23022302
[Node.js' "stability index"](http://nodejs.org/api/documentation.html).
23032303

2304-
There are lints for disallowing items marked with certain levels:
2305-
`deprecated`, `experimental` and `unstable`; the first two will warn
2306-
by default. Items with not marked with a stability are considered to
2307-
be unstable for the purposes of the lint. One can give an optional
2304+
Stability levels are inherited, so an items's stability attribute is the
2305+
default stability for everything nested underneath it.
2306+
2307+
There are lints for disallowing items marked with certain levels: `deprecated`,
2308+
`experimental` and `unstable`. For now, only `deprecated` warns by default, but
2309+
this will change once the standard library has been stabilized.
2310+
Stability levels are meant to be promises at the crate
2311+
level, so these lints only apply when referencing
2312+
items from an _external_ crate, not to items defined within the
2313+
current crate. Items with no stability level are considered
2314+
to be unstable for the purposes of the lint. One can give an optional
23082315
string that will be displayed when the lint flags the use of an item.
23092316

2310-
~~~~ {.ignore}
2311-
#![warn(unstable)]
2317+
For example, if we define one crate called `stability_levels`:
23122318

2319+
~~~~ {.ignore}
23132320
#[deprecated="replaced by `best`"]
2314-
fn bad() {
2321+
pub fn bad() {
23152322
// delete everything
23162323
}
23172324
2318-
fn better() {
2325+
pub fn better() {
23192326
// delete fewer things
23202327
}
23212328
23222329
#[stable]
2323-
fn best() {
2330+
pub fn best() {
23242331
// delete nothing
23252332
}
2333+
~~~~
2334+
2335+
then the lints will work as follows for a client crate:
2336+
2337+
~~~~ {.ignore}
2338+
#![warn(unstable)]
2339+
extern crate stability_levels;
2340+
use stability_levels::{bad, better, best};
23262341
23272342
fn main() {
23282343
bad(); // "warning: use of deprecated item: replaced by `best`"

src/librustc/driver/driver.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use metadata::common::LinkMeta;
2020
use metadata::creader;
2121
use middle::cfg;
2222
use middle::cfg::graphviz::LabelledCFG;
23-
use middle::{trans, freevars, kind, ty, typeck, lint, reachable};
23+
use middle::{trans, freevars, stability, kind, ty, typeck, lint, reachable};
2424
use middle::dependency_format;
2525
use middle;
2626
use plugin::load::Plugins;
@@ -312,8 +312,11 @@ pub fn phase_3_run_analysis_passes(sess: Session,
312312
time(time_passes, "loop checking", (), |_|
313313
middle::check_loop::check_crate(&sess, krate));
314314

315+
let stability_index = time(time_passes, "stability index", (), |_|
316+
stability::Index::build(krate));
317+
315318
let ty_cx = ty::mk_ctxt(sess, def_map, named_region_map, ast_map,
316-
freevars, region_map, lang_items);
319+
freevars, region_map, lang_items, stability_index);
317320

318321
// passes are timed inside typeck
319322
typeck::check_crate(&ty_cx, trait_map, krate);

src/librustc/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ pub mod middle {
8181
pub mod weak_lang_items;
8282
pub mod save;
8383
pub mod intrinsicck;
84+
pub mod stability;
8485
}
8586

8687
pub mod front {

src/librustc/metadata/common.rs

+3
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,9 @@ pub static tag_method_argument_name: uint = 0x8f;
210210
pub static tag_reachable_extern_fns: uint = 0x90;
211211
pub static tag_reachable_extern_fn_id: uint = 0x91;
212212

213+
pub static tag_items_data_item_stability: uint = 0x92;
214+
215+
213216
#[deriving(Clone, Show)]
214217
pub struct LinkMeta {
215218
pub crateid: CrateId,

src/librustc/metadata/csearch.rs

+8
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ use serialize::ebml::reader;
2525
use std::rc::Rc;
2626
use syntax::ast;
2727
use syntax::ast_map;
28+
use syntax::attr;
2829
use syntax::diagnostic::expect;
2930
use syntax::parse::token;
3031

@@ -328,3 +329,10 @@ pub fn is_typedef(cstore: &cstore::CStore, did: ast::DefId) -> bool {
328329
let cdata = cstore.get_crate_data(did.krate);
329330
decoder::is_typedef(&*cdata, did.node)
330331
}
332+
333+
pub fn get_stability(cstore: &cstore::CStore,
334+
def: ast::DefId)
335+
-> Option<attr::Stability> {
336+
let cdata = cstore.get_crate_data(def.krate);
337+
decoder::get_stability(&*cdata, def.node)
338+
}

src/librustc/metadata/decoder.rs

+8
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,14 @@ pub fn get_type(cdata: Cmd, id: ast::NodeId, tcx: &ty::ctxt)
439439
}
440440
}
441441

442+
pub fn get_stability(cdata: Cmd, id: ast::NodeId) -> Option<attr::Stability> {
443+
let item = lookup_item(id, cdata.data());
444+
reader::maybe_get_doc(item, tag_items_data_item_stability).map(|doc| {
445+
let mut decoder = reader::Decoder::new(doc);
446+
Decodable::decode(&mut decoder).unwrap()
447+
})
448+
}
449+
442450
pub fn get_impl_trait(cdata: Cmd,
443451
id: ast::NodeId,
444452
tcx: &ty::ctxt) -> Option<Rc<ty::TraitRef>>

src/librustc/metadata/encoder.rs

+31-1
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,10 @@ fn encode_enum_variant_info(ecx: &EncodeContext,
327327
encode_parent_item(ebml_w, local_def(id));
328328
encode_visibility(ebml_w, variant.node.vis);
329329
encode_attributes(ebml_w, variant.node.attrs.as_slice());
330+
331+
let stab = ecx.tcx.stability.borrow().lookup_local(variant.node.id);
332+
encode_stability(ebml_w, stab);
333+
330334
match variant.node.kind {
331335
ast::TupleVariantKind(ref args)
332336
if args.len() > 0 && generics.ty_params.len() == 0 => {
@@ -588,6 +592,7 @@ fn encode_info_for_mod(ecx: &EncodeContext,
588592

589593
encode_path(ebml_w, path.clone());
590594
encode_visibility(ebml_w, vis);
595+
encode_stability(ebml_w, ecx.tcx.stability.borrow().lookup_local(id));
591596

592597
// Encode the reexports of this module, if this module is public.
593598
if vis == Public {
@@ -717,6 +722,8 @@ fn encode_info_for_struct_ctor(ecx: &EncodeContext,
717722
encode_symbol(ecx, ebml_w, ctor_id);
718723
}
719724

725+
encode_stability(ebml_w, ecx.tcx.stability.borrow().lookup_local(ctor_id));
726+
720727
// indicate that this is a tuple struct ctor, because downstream users will normally want
721728
// the tuple struct definition, but without this there is no way for them to tell that
722729
// they actually have a ctor rather than a normal function
@@ -761,6 +768,9 @@ fn encode_info_for_method(ecx: &EncodeContext,
761768
encode_method_ty_fields(ecx, ebml_w, m);
762769
encode_parent_item(ebml_w, local_def(parent_id));
763770

771+
let stab = ecx.tcx.stability.borrow().lookup_local(m.def_id.node);
772+
encode_stability(ebml_w, stab);
773+
764774
// The type for methods gets encoded twice, which is unfortunate.
765775
let tpt = lookup_item_type(ecx.tcx, m.def_id);
766776
encode_bounds_and_type(ebml_w, ecx, &tpt);
@@ -880,6 +890,14 @@ fn encode_sized(ebml_w: &mut Encoder, sized: Sized) {
880890
ebml_w.end_tag();
881891
}
882892

893+
fn encode_stability(ebml_w: &mut Encoder, stab_opt: Option<attr::Stability>) {
894+
stab_opt.map(|stab| {
895+
ebml_w.start_tag(tag_items_data_item_stability);
896+
stab.encode(ebml_w).unwrap();
897+
ebml_w.end_tag();
898+
});
899+
}
900+
883901
fn encode_info_for_item(ecx: &EncodeContext,
884902
ebml_w: &mut Encoder,
885903
item: &Item,
@@ -900,6 +918,8 @@ fn encode_info_for_item(ecx: &EncodeContext,
900918
ecx.tcx.sess.codemap().span_to_str(item.span));
901919

902920
let def_id = local_def(item.id);
921+
let stab = tcx.stability.borrow().lookup_local(item.id);
922+
903923
match item.node {
904924
ItemStatic(_, m, _) => {
905925
add_to_index(item, ebml_w, index);
@@ -921,6 +941,7 @@ fn encode_info_for_item(ecx: &EncodeContext,
921941
encode_inlined_item(ecx, ebml_w, IIItemRef(item));
922942
}
923943
encode_visibility(ebml_w, vis);
944+
encode_stability(ebml_w, stab);
924945
ebml_w.end_tag();
925946
}
926947
ItemFn(ref decl, fn_style, _, ref generics, _) => {
@@ -939,6 +960,7 @@ fn encode_info_for_item(ecx: &EncodeContext,
939960
encode_symbol(ecx, ebml_w, item.id);
940961
}
941962
encode_visibility(ebml_w, vis);
963+
encode_stability(ebml_w, stab);
942964
encode_method_argument_names(ebml_w, &**decl);
943965
ebml_w.end_tag();
944966
}
@@ -968,6 +990,7 @@ fn encode_info_for_item(ecx: &EncodeContext,
968990
ebml_w.end_tag();
969991
}
970992
encode_visibility(ebml_w, vis);
993+
encode_stability(ebml_w, stab);
971994
ebml_w.end_tag();
972995
}
973996
ItemTy(..) => {
@@ -979,6 +1002,7 @@ fn encode_info_for_item(ecx: &EncodeContext,
9791002
encode_name(ebml_w, item.ident.name);
9801003
encode_path(ebml_w, path);
9811004
encode_visibility(ebml_w, vis);
1005+
encode_stability(ebml_w, stab);
9821006
ebml_w.end_tag();
9831007
}
9841008
ItemEnum(ref enum_definition, ref generics) => {
@@ -1001,6 +1025,7 @@ fn encode_info_for_item(ecx: &EncodeContext,
10011025
encode_inherent_implementations(ecx, ebml_w, def_id);
10021026

10031027
encode_visibility(ebml_w, vis);
1028+
encode_stability(ebml_w, stab);
10041029
ebml_w.end_tag();
10051030

10061031
encode_enum_variant_info(ecx,
@@ -1035,6 +1060,7 @@ fn encode_info_for_item(ecx: &EncodeContext,
10351060
encode_name(ebml_w, item.ident.name);
10361061
encode_attributes(ebml_w, item.attrs.as_slice());
10371062
encode_path(ebml_w, path.clone());
1063+
encode_stability(ebml_w, stab);
10381064
encode_visibility(ebml_w, vis);
10391065

10401066
/* Encode def_ids for each field and method
@@ -1095,6 +1121,7 @@ fn encode_info_for_item(ecx: &EncodeContext,
10951121
encode_impl_vtables(ebml_w, ecx, &impl_vtables);
10961122
}
10971123
encode_path(ebml_w, path.clone());
1124+
encode_stability(ebml_w, stab);
10981125
ebml_w.end_tag();
10991126

11001127
// Iterate down the methods, emitting them. We rely on the
@@ -1138,6 +1165,7 @@ fn encode_info_for_item(ecx: &EncodeContext,
11381165
// should no longer need this ugly little hack either.
11391166
encode_sized(ebml_w, sized);
11401167
encode_visibility(ebml_w, vis);
1168+
encode_stability(ebml_w, stab);
11411169
for &method_def_id in ty::trait_method_def_ids(tcx, def_id).iter() {
11421170
ebml_w.start_tag(tag_item_trait_method);
11431171
encode_def_id(ebml_w, method_def_id);
@@ -1176,9 +1204,11 @@ fn encode_info_for_item(ecx: &EncodeContext,
11761204
ebml_w.start_tag(tag_items_data_item);
11771205

11781206
encode_method_ty_fields(ecx, ebml_w, &*method_ty);
1179-
11801207
encode_parent_item(ebml_w, def_id);
11811208

1209+
let stab = tcx.stability.borrow().lookup_local(method_def_id.node);
1210+
encode_stability(ebml_w, stab);
1211+
11821212
let elem = ast_map::PathName(method_ty.ident.name);
11831213
encode_path(ebml_w, path.clone().chain(Some(elem).move_iter()));
11841214

src/librustc/middle/lint.rs

+11-29
Original file line numberDiff line numberDiff line change
@@ -372,7 +372,8 @@ static lint_table: &'static [(&'static str, LintSpec)] = &[
372372
LintSpec {
373373
lint: Experimental,
374374
desc: "detects use of #[experimental] items",
375-
default: Warn
375+
// FIXME #6875: Change to Warn after std library stabilization is complete
376+
default: Allow
376377
}),
377378

378379
("unstable",
@@ -1661,6 +1662,8 @@ fn check_missing_doc_variant(cx: &Context, v: &ast::Variant) {
16611662
/// Checks for use of items with #[deprecated], #[experimental] and
16621663
/// #[unstable] (or none of them) attributes.
16631664
fn check_stability(cx: &Context, e: &ast::Expr) {
1665+
let tcx = cx.tcx;
1666+
16641667
let id = match e.node {
16651668
ast::ExprPath(..) | ast::ExprStruct(..) => {
16661669
match cx.tcx.def_map.borrow().find(&e.id) {
@@ -1670,16 +1673,16 @@ fn check_stability(cx: &Context, e: &ast::Expr) {
16701673
}
16711674
ast::ExprMethodCall(..) => {
16721675
let method_call = typeck::MethodCall::expr(e.id);
1673-
match cx.tcx.method_map.borrow().find(&method_call) {
1676+
match tcx.method_map.borrow().find(&method_call) {
16741677
Some(method) => {
16751678
match method.origin {
16761679
typeck::MethodStatic(def_id) => {
16771680
// If this implements a trait method, get def_id
16781681
// of the method inside trait definition.
16791682
// Otherwise, use the current def_id (which refers
16801683
// to the method inside impl).
1681-
ty::trait_method_of_method(
1682-
cx.tcx, def_id).unwrap_or(def_id)
1684+
ty::trait_method_of_method(cx.tcx, def_id)
1685+
.unwrap_or(def_id)
16831686
}
16841687
typeck::MethodParam(typeck::MethodParam {
16851688
trait_id: trait_id,
@@ -1699,32 +1702,11 @@ fn check_stability(cx: &Context, e: &ast::Expr) {
16991702
_ => return
17001703
};
17011704

1702-
let stability = if ast_util::is_local(id) {
1703-
// this crate
1704-
let s = cx.tcx.map.with_attrs(id.node, |attrs| {
1705-
attrs.map(|a| attr::find_stability(a.as_slice()))
1706-
});
1707-
match s {
1708-
Some(s) => s,
1705+
// stability attributes are promises made across crates; do not
1706+
// check anything for crate-local usage.
1707+
if ast_util::is_local(id) { return }
17091708

1710-
// no possibility of having attributes
1711-
// (e.g. it's a local variable), so just
1712-
// ignore it.
1713-
None => return
1714-
}
1715-
} else {
1716-
// cross-crate
1717-
1718-
let mut s = None;
1719-
// run through all the attributes and take the first
1720-
// stability one.
1721-
csearch::get_item_attrs(&cx.tcx.sess.cstore, id, |attrs| {
1722-
if s.is_none() {
1723-
s = attr::find_stability(attrs.as_slice())
1724-
}
1725-
});
1726-
s
1727-
};
1709+
let stability = tcx.stability.borrow_mut().lookup(&tcx.sess.cstore, id);
17281710

17291711
let (lint, label) = match stability {
17301712
// no stability attributes == Unstable

0 commit comments

Comments
 (0)