Skip to content

Commit b4ff0bc

Browse files
committed
auto merge of #8921 : huonw/rust/stability, r=brson
Significant progress on #6875, enough that I'll open new bugs and turn that into a metabug when this lands. Description & example in the commit message.
2 parents 7ee90a0 + 506f69a commit b4ff0bc

File tree

8 files changed

+745
-3
lines changed

8 files changed

+745
-3
lines changed

doc/rust.md

+57-1
Original file line numberDiff line numberDiff line change
@@ -1038,7 +1038,7 @@ code_. They are defined in the same way as any other Rust function,
10381038
except that they have the `extern` modifier.
10391039

10401040
~~~
1041-
// Declares an extern fn, the ABI defaults to "C"
1041+
// Declares an extern fn, the ABI defaults to "C"
10421042
extern fn new_vec() -> ~[int] { ~[] }
10431043
10441044
// Declares an extern fn with "stdcall" ABI
@@ -1723,6 +1723,62 @@ Supported traits for `deriving` are:
17231723
each constituent field of the type must also implement `ToStr` and will have
17241724
`field.to_str()` invoked to build up the result.
17251725

1726+
### Stability
1727+
One can indicate the stability of an API using the following attributes:
1728+
1729+
* `deprecated`: This item should no longer be used, e.g. it has been
1730+
replaced. No guarantee of backwards-compatibility.
1731+
* `experimental`: This item was only recently introduced or is
1732+
otherwise in a state of flux. It may change significantly, or even
1733+
be removed. No guarantee of backwards-compatibility.
1734+
* `unstable`: This item is still under development, but requires more
1735+
testing to be considered stable. No guarantee of backwards-compatibility.
1736+
* `stable`: This item is considered stable, and will not change
1737+
significantly. Guarantee of backwards-compatibility.
1738+
* `frozen`: This item is very stable, and is unlikely to
1739+
change. Guarantee of backwards-compatibility.
1740+
* `locked`: This item will never change unless a serious bug is
1741+
found. Guarantee of backwards-compatibility.
1742+
1743+
These levels are directly inspired by
1744+
[Node.js' "stability index"](http://nodejs.org/api/documentation.html).
1745+
1746+
There are lints for disallowing items marked with certain levels:
1747+
`deprecated`, `experimental` and `unstable`; the first two will warn
1748+
by default. Items with not marked with a stability are considered to
1749+
be unstable for the purposes of the lint. One can give an optional
1750+
string that will be displayed when the lint flags the use of an item.
1751+
1752+
~~~ {.xfail-test}
1753+
#[warn(unstable)];
1754+
1755+
#[deprecated="replaced by `best`"]
1756+
fn bad() {
1757+
// delete everything
1758+
}
1759+
1760+
fn better() {
1761+
// delete fewer things
1762+
}
1763+
1764+
#[stable]
1765+
fn best() {
1766+
// delete nothing
1767+
}
1768+
1769+
fn main() {
1770+
bad(); // "warning: use of deprecated item: replaced by `best`"
1771+
1772+
better(); // "warning: use of unmarked item"
1773+
1774+
best(); // no warning
1775+
}
1776+
~~~
1777+
1778+
> **Note:** Currently these are only checked when applied to
1779+
> individual functions, structs, methods and enum variants, *not* to
1780+
> entire modules, traits, impls or enums themselves.
1781+
17261782
# Statements and expressions
17271783

17281784
Rust is _primarily_ an expression language. This means that most forms of

src/librustc/metadata/encoder.rs

+1
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,7 @@ fn encode_enum_variant_info(ecx: &EncodeContext,
330330
encode_name(ecx, ebml_w, variant.node.name);
331331
encode_parent_item(ebml_w, local_def(id));
332332
encode_visibility(ebml_w, variant.node.vis);
333+
encode_attributes(ebml_w, variant.node.attrs);
333334
match variant.node.kind {
334335
ast::tuple_variant_kind(ref args)
335336
if args.len() > 0 && generics.ty_params.len() == 0 => {

src/librustc/middle/lint.rs

+129-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use driver::session;
1313
use middle::ty;
1414
use middle::pat_util;
15+
use metadata::csearch;
1516
use util::ppaux::{ty_to_str};
1617

1718
use std::cmp;
@@ -27,7 +28,7 @@ use std::u8;
2728
use extra::smallintmap::SmallIntMap;
2829
use syntax::ast_map;
2930
use syntax::attr;
30-
use syntax::attr::AttrMetaMethods;
31+
use syntax::attr::{AttrMetaMethods, AttributeMethods};
3132
use syntax::codemap::Span;
3233
use syntax::codemap;
3334
use syntax::parse::token;
@@ -97,6 +98,10 @@ pub enum lint {
9798
missing_doc,
9899
unreachable_code,
99100

101+
deprecated,
102+
experimental,
103+
unstable,
104+
100105
warnings,
101106
}
102107

@@ -281,6 +286,27 @@ static lint_table: &'static [(&'static str, LintSpec)] = &[
281286
default: warn
282287
}),
283288

289+
("deprecated",
290+
LintSpec {
291+
lint: deprecated,
292+
desc: "detects use of #[deprecated] items",
293+
default: warn
294+
}),
295+
296+
("experimental",
297+
LintSpec {
298+
lint: experimental,
299+
desc: "detects use of #[experimental] items",
300+
default: warn
301+
}),
302+
303+
("unstable",
304+
LintSpec {
305+
lint: unstable,
306+
desc: "detects use of #[unstable] items (incl. items with no stability attribute)",
307+
default: allow
308+
}),
309+
284310
("warnings",
285311
LintSpec {
286312
lint: warnings,
@@ -1375,6 +1401,107 @@ fn lint_missing_doc() -> @mut OuterLint {
13751401
@mut MissingDocLintVisitor { stopping_on_items: false } as @mut OuterLint
13761402
}
13771403

1404+
/// Checks for use of items with #[deprecated], #[experimental] and
1405+
/// #[unstable] (or none of them) attributes.
1406+
struct StabilityLintVisitor { stopping_on_items: bool }
1407+
1408+
impl StabilityLintVisitor {
1409+
fn handle_def(&mut self, sp: Span, def: &ast::Def, cx: @mut Context) {
1410+
let id = ast_util::def_id_of_def(*def);
1411+
1412+
let stability = if ast_util::is_local(id) {
1413+
// this crate
1414+
match cx.tcx.items.find(&id.node) {
1415+
Some(ast_node) => {
1416+
let s = do ast_node.with_attrs |attrs| {
1417+
do attrs.map_move |a| {
1418+
attr::find_stability(a.iter().map(|a| a.meta()))
1419+
}
1420+
};
1421+
match s {
1422+
Some(s) => s,
1423+
1424+
// no possibility of having attributes
1425+
// (e.g. it's a local variable), so just
1426+
// ignore it.
1427+
None => return
1428+
}
1429+
}
1430+
_ => cx.tcx.sess.bug(fmt!("handle_def: %? not found", id))
1431+
}
1432+
} else {
1433+
// cross-crate
1434+
1435+
let mut s = None;
1436+
// run through all the attributes and take the first
1437+
// stability one.
1438+
do csearch::get_item_attrs(cx.tcx.cstore, id) |meta_items| {
1439+
if s.is_none() {
1440+
s = attr::find_stability(meta_items.move_iter())
1441+
}
1442+
}
1443+
s
1444+
};
1445+
1446+
let (lint, label) = match stability {
1447+
// no stability attributes == Unstable
1448+
None => (unstable, "unmarked"),
1449+
Some(attr::Stability { level: attr::Unstable, _ }) => (unstable, "unstable"),
1450+
Some(attr::Stability { level: attr::Experimental, _ }) => {
1451+
(experimental, "experimental")
1452+
}
1453+
Some(attr::Stability { level: attr::Deprecated, _ }) => (deprecated, "deprecated"),
1454+
_ => return
1455+
};
1456+
1457+
let msg = match stability {
1458+
Some(attr::Stability { text: Some(ref s), _ }) => {
1459+
fmt!("use of %s item: %s", label, *s)
1460+
}
1461+
_ => fmt!("use of %s item", label)
1462+
};
1463+
1464+
cx.span_lint(lint, sp, msg);
1465+
}
1466+
}
1467+
1468+
impl SubitemStoppableVisitor for StabilityLintVisitor {
1469+
fn is_running_on_items(&mut self) -> bool { !self.stopping_on_items }
1470+
}
1471+
1472+
impl Visitor<@mut Context> for StabilityLintVisitor {
1473+
fn visit_item(&mut self, i:@ast::item, e:@mut Context) {
1474+
self.OVERRIDE_visit_item(i, e);
1475+
}
1476+
1477+
fn visit_fn(&mut self, fk:&visit::fn_kind, fd:&ast::fn_decl,
1478+
b:&ast::Block, s:Span, n:ast::NodeId, e:@mut Context) {
1479+
self.OVERRIDE_visit_fn(fk, fd, b, s, n, e);
1480+
}
1481+
1482+
fn visit_expr(&mut self, ex: @ast::Expr, cx: @mut Context) {
1483+
match ex.node {
1484+
ast::ExprMethodCall(*) |
1485+
ast::ExprPath(*) |
1486+
ast::ExprStruct(*) => {
1487+
match cx.tcx.def_map.find(&ex.id) {
1488+
Some(def) => self.handle_def(ex.span, def, cx),
1489+
None => {}
1490+
}
1491+
}
1492+
_ => {}
1493+
}
1494+
1495+
visit::walk_expr(self, ex, cx)
1496+
}
1497+
}
1498+
1499+
outer_lint_boilerplate_impl!(StabilityLintVisitor)
1500+
1501+
fn lint_stability() -> @mut OuterLint {
1502+
@mut StabilityLintVisitor { stopping_on_items: false } as @mut OuterLint
1503+
}
1504+
13781505
struct LintCheckVisitor;
13791506

13801507
impl Visitor<@mut Context> for LintCheckVisitor {
@@ -1458,6 +1585,7 @@ pub fn check_crate(tcx: ty::ctxt, crate: @ast::Crate) {
14581585
cx.add_old_lint(lint_unused_mut());
14591586
cx.add_old_lint(lint_unnecessary_allocations());
14601587
cx.add_old_lint(lint_missing_doc());
1588+
cx.add_old_lint(lint_stability());
14611589
cx.add_lint(lint_session(cx));
14621590

14631591
// Actually perform the lint checks (iterating the ast)

src/librustc/middle/ty.rs

-1
Original file line numberDiff line numberDiff line change
@@ -4706,4 +4706,3 @@ pub fn trait_of_method(tcx: ctxt, def_id: ast::DefId)
47064706

47074707
result
47084708
}
4709-

src/libsyntax/ast_map.rs

+20
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,26 @@ pub enum ast_node {
8080
node_callee_scope(@Expr)
8181
}
8282

83+
impl ast_node {
84+
pub fn with_attrs<T>(&self, f: &fn(Option<&[Attribute]>) -> T) -> T {
85+
let attrs = match *self {
86+
node_item(i, _) => Some(i.attrs.as_slice()),
87+
node_foreign_item(fi, _, _, _) => Some(fi.attrs.as_slice()),
88+
node_trait_method(tm, _, _) => match *tm {
89+
required(ref type_m) => Some(type_m.attrs.as_slice()),
90+
provided(m) => Some(m.attrs.as_slice())
91+
},
92+
node_method(m, _, _) => Some(m.attrs.as_slice()),
93+
node_variant(ref v, _, _) => Some(v.node.attrs.as_slice()),
94+
// unit/tuple structs take the attributes straight from
95+
// the struct definition.
96+
node_struct_ctor(_, strct, _) => Some(strct.attrs.as_slice()),
97+
_ => None
98+
};
99+
f(attrs)
100+
}
101+
}
102+
83103
pub type map = @mut HashMap<NodeId, ast_node>;
84104

85105
pub struct Ctx {

src/libsyntax/attr.rs

+38
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,44 @@ pub fn test_cfg<AM: AttrMetaMethods, It: Iterator<AM>>
313313
no_cfgs || some_cfg_matches
314314
}
315315

316+
/// Represents the #[deprecated="foo"] (etc) attributes.
317+
pub struct Stability {
318+
level: StabilityLevel,
319+
text: Option<@str>
320+
}
321+
322+
/// The available stability levels.
323+
#[deriving(Eq,Ord,Clone)]
324+
pub enum StabilityLevel {
325+
Deprecated,
326+
Experimental,
327+
Unstable,
328+
Stable,
329+
Frozen,
330+
Locked
331+
}
332+
333+
/// Find the first stability attribute. `None` if none exists.
334+
pub fn find_stability<AM: AttrMetaMethods, It: Iterator<AM>>(mut metas: It) -> Option<Stability> {
335+
for m in metas {
336+
let level = match m.name().as_slice() {
337+
"deprecated" => Deprecated,
338+
"experimental" => Experimental,
339+
"unstable" => Unstable,
340+
"stable" => Stable,
341+
"frozen" => Frozen,
342+
"locked" => Locked,
343+
_ => loop // not a stability level
344+
};
345+
346+
return Some(Stability {
347+
level: level,
348+
text: m.value_str()
349+
});
350+
}
351+
None
352+
}
353+
316354
pub fn require_unique_names(diagnostic: @mut span_handler,
317355
metas: &[@MetaItem]) {
318356
let mut set = HashSet::new();

0 commit comments

Comments
 (0)