Check stability of struct fields.

We were recording stability attributes applied to fields in the
compiler, and even annotating it in the libs, but the compiler didn't
actually do the checks to give errors/warnings in user crates.
This commit is contained in:
Huon Wilson 2015-02-25 22:34:21 +11:00
parent 4db0b32467
commit 19cb8f32d8
8 changed files with 561 additions and 11 deletions

View File

@ -1771,6 +1771,11 @@ impl LintPass for Stability {
stability::check_path(cx.tcx, path, id,
&mut |id, sp, stab| self.lint(cx, id, sp, stab));
}
fn check_pat(&mut self, cx: &Context, pat: &ast::Pat) {
stability::check_pat(cx.tcx, pat,
&mut |id, sp, stab| self.lint(cx, id, sp, stab))
}
}
declare_lint! {

View File

@ -58,8 +58,10 @@ impl<'a> Annotator<'a> {
attrs: &Vec<Attribute>, item_sp: Span, f: F, required: bool) where
F: FnOnce(&mut Annotator),
{
debug!("annotate(id = {:?}, attrs = {:?})", id, attrs);
match attr::find_stability(self.sess.diagnostic(), attrs, item_sp) {
Some(stab) => {
debug!("annotate: found {:?}", stab);
self.index.local.insert(id, stab.clone());
// Don't inherit #[stable(feature = "rust1", since = "1.0.0")]
@ -72,6 +74,8 @@ impl<'a> Annotator<'a> {
}
}
None => {
debug!("annotate: not found, use_parent = {:?}, parent = {:?}",
use_parent, self.parent);
if use_parent {
if let Some(stab) = self.parent.clone() {
self.index.local.insert(id, stab);
@ -299,6 +303,12 @@ impl<'a, 'v, 'tcx> Visitor<'v> for Checker<'a, 'tcx> {
&mut |id, sp, stab| self.check(id, sp, stab));
visit::walk_path(self, path)
}
fn visit_pat(&mut self, pat: &ast::Pat) {
check_pat(self.tcx, pat,
&mut |id, sp, stab| self.check(id, sp, stab));
visit::walk_pat(self, pat)
}
}
/// Helper for discovering nodes to check for stability
@ -385,6 +395,76 @@ pub fn check_expr(tcx: &ty::ctxt, e: &ast::Expr,
None => return
}
}
ast::ExprField(ref base_e, ref field) => {
span = field.span;
match ty::expr_ty_adjusted(tcx, base_e).sty {
ty::ty_struct(did, _) => {
ty::lookup_struct_fields(tcx, did)
.iter()
.find(|f| f.name == field.node.name)
.unwrap_or_else(|| {
tcx.sess.span_bug(field.span,
"stability::check_expr: unknown named field access")
})
.id
}
_ => tcx.sess.span_bug(e.span,
"stability::check_expr: named field access on non-struct")
}
}
ast::ExprTupField(ref base_e, ref field) => {
span = field.span;
match ty::expr_ty_adjusted(tcx, base_e).sty {
ty::ty_struct(did, _) => {
ty::lookup_struct_fields(tcx, did)
.get(field.node)
.unwrap_or_else(|| {
tcx.sess.span_bug(field.span,
"stability::check_expr: unknown unnamed field access")
})
.id
}
ty::ty_tup(..) => return,
_ => tcx.sess.span_bug(e.span,
"stability::check_expr: unnamed field access on \
something other than a tuple or struct")
}
}
ast::ExprStruct(_, ref expr_fields, _) => {
let type_ = ty::expr_ty(tcx, e);
match type_.sty {
ty::ty_struct(did, _) => {
let struct_fields = ty::lookup_struct_fields(tcx, did);
// check the stability of each field that appears
// in the construction expression.
for field in expr_fields {
let did = struct_fields
.iter()
.find(|f| f.name == field.ident.node.name)
.unwrap_or_else(|| {
tcx.sess.span_bug(field.span,
"stability::check_expr: unknown named \
field access")
})
.id;
maybe_do_stability_check(tcx, did, field.span, cb);
}
// we're done.
return
}
// we don't look at stability attributes on
// struct-like enums (yet...), but it's definitely not
// a bug to have construct one.
ty::ty_enum(..) => return,
_ => {
tcx.sess.span_bug(e.span,
&format!("stability::check_expr: struct construction \
of non-struct, type {:?}",
type_.repr(tcx)));
}
}
}
_ => return
};
@ -403,6 +483,47 @@ pub fn check_path(tcx: &ty::ctxt, path: &ast::Path, id: ast::NodeId,
}
pub fn check_pat(tcx: &ty::ctxt, pat: &ast::Pat,
cb: &mut FnMut(ast::DefId, Span, &Option<Stability>)) {
debug!("check_pat(pat = {:?})", pat);
if is_internal(tcx, pat.span) { return; }
let did = match ty::pat_ty_opt(tcx, pat) {
Some(&ty::TyS { sty: ty::ty_struct(did, _), .. }) => did,
Some(_) | None => return,
};
let struct_fields = ty::lookup_struct_fields(tcx, did);
match pat.node {
// Foo(a, b, c)
ast::PatEnum(_, Some(ref pat_fields)) => {
for (field, struct_field) in pat_fields.iter().zip(struct_fields.iter()) {
// a .. pattern is fine, but anything positional is
// not.
if let ast::PatWild(ast::PatWildMulti) = field.node {
continue
}
maybe_do_stability_check(tcx, struct_field.id, field.span, cb)
}
}
// Foo { a, b, c }
ast::PatStruct(_, ref pat_fields, _) => {
for field in pat_fields {
let did = struct_fields
.iter()
.find(|f| f.name == field.node.ident.name)
.unwrap_or_else(|| {
tcx.sess.span_bug(field.span,
"stability::check_pat: unknown named field access")
})
.id;
maybe_do_stability_check(tcx, did, field.span, cb);
}
}
// everything else is fine.
_ => {}
}
}
fn maybe_do_stability_check(tcx: &ty::ctxt, id: ast::DefId, span: Span,
cb: &mut FnMut(ast::DefId, Span, &Option<Stability>)) {
if !is_staged_api(tcx, id) { return }

View File

@ -4298,6 +4298,9 @@ pub fn free_region_from_def(outlives_extent: region::DestructionScopeData,
pub fn pat_ty<'tcx>(cx: &ctxt<'tcx>, pat: &ast::Pat) -> Ty<'tcx> {
return node_id_to_type(cx, pat.id);
}
pub fn pat_ty_opt<'tcx>(cx: &ctxt<'tcx>, pat: &ast::Pat) -> Option<Ty<'tcx>> {
return node_id_to_type_opt(cx, pat.id);
}
// Returns the type of an expression as a monotype.

View File

@ -102,6 +102,7 @@ impl MemWriter {
impl Writer for MemWriter {
#[inline]
#[allow(deprecated)]
fn write_all(&mut self, buf: &[u8]) -> IoResult<()> {
self.buf.push_all(buf);
Ok(())

View File

@ -100,14 +100,22 @@ pub trait UnstableTrait { fn dummy(&self) { } }
#[stable(feature = "test_feature", since = "1.0.0")]
#[deprecated(since = "1.0.0")]
pub struct DeprecatedStruct { pub i: int }
pub struct DeprecatedStruct {
#[stable(feature = "test_feature", since = "1.0.0")] pub i: int
}
#[unstable(feature = "test_feature")]
#[deprecated(since = "1.0.0")]
pub struct DeprecatedUnstableStruct { pub i: int }
pub struct DeprecatedUnstableStruct {
#[stable(feature = "test_feature", since = "1.0.0")] pub i: int
}
#[unstable(feature = "test_feature")]
pub struct UnstableStruct { pub i: int }
pub struct UnstableStruct {
#[stable(feature = "test_feature", since = "1.0.0")] pub i: int
}
#[stable(feature = "rust1", since = "1.0.0")]
pub struct StableStruct { pub i: int }
pub struct StableStruct {
#[stable(feature = "test_feature", since = "1.0.0")] pub i: int
}
#[stable(feature = "test_feature", since = "1.0.0")]
#[deprecated(since = "1.0.0")]
@ -137,14 +145,14 @@ pub enum Enum {
#[stable(feature = "test_feature", since = "1.0.0")]
#[deprecated(since = "1.0.0")]
pub struct DeprecatedTupleStruct(pub int);
pub struct DeprecatedTupleStruct(#[stable(feature = "rust1", since = "1.0.0")] pub int);
#[unstable(feature = "test_feature")]
#[deprecated(since = "1.0.0")]
pub struct DeprecatedUnstableTupleStruct(pub int);
pub struct DeprecatedUnstableTupleStruct(#[stable(feature = "rust1", since = "1.0.0")] pub int);
#[unstable(feature = "test_feature")]
pub struct UnstableTupleStruct(pub int);
pub struct UnstableTupleStruct(#[stable(feature = "rust1", since = "1.0.0")] pub int);
#[stable(feature = "rust1", since = "1.0.0")]
pub struct StableTupleStruct(pub int);
pub struct StableTupleStruct(#[stable(feature = "rust1", since = "1.0.0")] pub int);
#[macro_export]
macro_rules! macro_test {

View File

@ -0,0 +1,60 @@
// Copyright 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.
#![feature(staged_api)]
#![staged_api]
#![stable(feature = "rust1", since = "1.0.0")]
#[stable(feature = "rust1", since = "1.0.0")]
pub struct Stable {
#[stable(feature = "rust1", since = "1.0.0")]
pub inherit: u8, // it's a lie (stable doesn't inherit)
#[unstable(feature = "test_feature")]
pub override1: u8,
#[deprecated(since = "1.0.0")]
#[unstable(feature = "test_feature")]
pub override2: u8,
}
#[stable(feature = "rust1", since = "1.0.0")]
pub struct Stable2(#[stable(feature = "rust1", since = "1.0.0")] pub u8,
#[unstable(feature = "test_feature")] pub u8,
#[unstable(feature = "test_feature")] #[deprecated(since = "1.0.0")] pub u8);
#[unstable(feature = "test_feature")]
pub struct Unstable {
pub inherit: u8,
#[stable(feature = "rust1", since = "1.0.0")]
pub override1: u8,
#[deprecated(since = "1.0.0")]
#[unstable(feature = "test_feature")]
pub override2: u8,
}
#[unstable(feature = "test_feature")]
pub struct Unstable2(pub u8,
#[stable(feature = "rust1", since = "1.0.0")] pub u8,
#[unstable(feature = "test_feature")] #[deprecated(since = "1.0.0")] pub u8);
#[unstable(feature = "test_feature")]
#[deprecated(feature = "rust1", since = "1.0.0")]
pub struct Deprecated {
pub inherit: u8,
#[stable(feature = "rust1", since = "1.0.0")]
pub override1: u8,
#[unstable(feature = "test_feature")]
pub override2: u8,
}
#[unstable(feature = "test_feature")]
#[deprecated(feature = "rust1", since = "1.0.0")]
pub struct Deprecated2(pub u8,
#[stable(feature = "rust1", since = "1.0.0")] pub u8,
#[unstable(feature = "test_feature")] pub u8);

View File

@ -0,0 +1,346 @@
// Copyright 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.
// aux-build:lint_stability_fields.rs
#![deny(deprecated)]
#![allow(dead_code)]
#![feature(staged_api)]
#![staged_api]
mod cross_crate {
extern crate lint_stability_fields;
use self::lint_stability_fields::*;
pub fn foo() {
let x = Stable {
inherit: 1,
override1: 2, //~ WARN use of unstable
override2: 3,
//~^ ERROR use of deprecated item
//~^^ WARN use of unstable
};
let _ = x.inherit;
let _ = x.override1; //~ WARN use of unstable
let _ = x.override2;
//~^ ERROR use of deprecated item
//~^^ WARN use of unstable
let Stable {
inherit: _,
override1: _, //~ WARN use of unstable
override2: _
//~^ ERROR use of deprecated item
//~^^ WARN use of unstable
} = x;
// all fine
let Stable { .. } = x;
let x = Stable2(1, 2, 3);
let _ = x.0;
let _ = x.1; //~ WARN use of unstable
let _ = x.2;
//~^ ERROR use of deprecated item
//~^^ WARN use of unstable
let Stable2(_,
_, //~ WARN use of unstable
_)
//~^ ERROR use of deprecated item
//~^^ WARN use of unstable
= x;
// all fine
let Stable2(..) = x;
let x = Unstable { //~ WARN use of unstable
inherit: 1, //~ WARN use of unstable
override1: 2,
override2: 3,
//~^ ERROR use of deprecated item
//~^^ WARN use of unstable
};
let _ = x.inherit; //~ WARN use of unstable
let _ = x.override1;
let _ = x.override2;
//~^ ERROR use of deprecated item
//~^^ WARN use of unstable
let Unstable { //~ WARN use of unstable
inherit: _, //~ WARN use of unstable
override1: _,
override2: _
//~^ ERROR use of deprecated item
//~^^ WARN use of unstable
} = x;
let Unstable //~ WARN use of unstable
// the patterns are all fine:
{ .. } = x;
let x = Unstable2(1, 2, 3); //~ WARN use of unstable
let _ = x.0; //~ WARN use of unstable
let _ = x.1;
let _ = x.2;
//~^ ERROR use of deprecated item
//~^^ WARN use of unstable
let Unstable2 //~ WARN use of unstable
(_, //~ WARN use of unstable
_,
_)
//~^ ERROR use of deprecated item
//~^^ WARN use of unstable
= x;
let Unstable2 //~ WARN use of unstable
// the patterns are all fine:
(..) = x;
let x = Deprecated {
//~^ ERROR use of deprecated item
//~^^ WARN use of unstable
inherit: 1,
//~^ ERROR use of deprecated item
//~^^ WARN use of unstable
override1: 2,
override2: 3, //~ WARN use of unstable
};
let _ = x.inherit;
//~^ ERROR use of deprecated item
//~^^ WARN use of unstable
let _ = x.override1;
let _ = x.override2; //~ WARN use of unstable
let Deprecated {
//~^ ERROR use of deprecated item
//~^^ WARN use of unstable
inherit: _,
//~^ ERROR use of deprecated item
//~^^ WARN use of unstable
override1: _,
override2: _ //~ WARN use of unstable
} = x;
let Deprecated
//~^ ERROR use of deprecated item
//~^^ WARN use of unstable
// the patterns are all fine:
{ .. } = x;
let x = Deprecated2(1, 2, 3);
//~^ ERROR use of deprecated item
//~^^ WARN use of unstable
let _ = x.0;
//~^ ERROR use of deprecated item
//~^^ WARN use of unstable
let _ = x.1;
let _ = x.2; //~ WARN use of unstable
let Deprecated2
//~^ ERROR use of deprecated item
//~^^ WARN use of unstable
(_,
//~^ ERROR use of deprecated item
//~^^ WARN use of unstable
_,
_) //~ WARN use of unstable
= x;
let Deprecated2
//~^ ERROR use of deprecated item
//~^^ WARN use of unstable
// the patterns are all fine:
(..) = x;
}
}
mod this_crate {
#[stable(feature = "rust1", since = "1.0.0")]
struct Stable {
inherit: u8,
#[unstable(feature = "test_feature")]
override1: u8,
#[deprecated(since = "1.0.0")]
#[unstable(feature = "test_feature")]
override2: u8,
}
#[stable(feature = "rust1", since = "1.0.0")]
struct Stable2(u8,
#[stable(feature = "rust1", since = "1.0.0")] u8,
#[unstable(feature = "test_feature")] #[deprecated(since = "1.0.0")] u8);
#[unstable(feature = "test_feature")]
struct Unstable {
inherit: u8,
#[stable(feature = "rust1", since = "1.0.0")]
override1: u8,
#[deprecated(since = "1.0.0")]
#[unstable(feature = "test_feature")]
override2: u8,
}
#[unstable(feature = "test_feature")]
struct Unstable2(u8,
#[stable(feature = "rust1", since = "1.0.0")] u8,
#[unstable(feature = "test_feature")] #[deprecated(since = "1.0.0")] u8);
#[unstable(feature = "test_feature")]
#[deprecated(feature = "rust1", since = "1.0.0")]
struct Deprecated {
inherit: u8,
#[stable(feature = "rust1", since = "1.0.0")]
override1: u8,
#[unstable(feature = "test_feature")]
override2: u8,
}
#[unstable(feature = "test_feature")]
#[deprecated(feature = "rust1", since = "1.0.0")]
struct Deprecated2(u8,
#[stable(feature = "rust1", since = "1.0.0")] u8,
#[unstable(feature = "test_feature")] u8);
pub fn foo() {
let x = Stable {
inherit: 1,
override1: 2,
override2: 3,
//~^ ERROR use of deprecated item
};
let _ = x.inherit;
let _ = x.override1;
let _ = x.override2;
//~^ ERROR use of deprecated item
let Stable {
inherit: _,
override1: _,
override2: _
//~^ ERROR use of deprecated item
} = x;
// all fine
let Stable { .. } = x;
let x = Stable2(1, 2, 3);
let _ = x.0;
let _ = x.1;
let _ = x.2;
//~^ ERROR use of deprecated item
let Stable2(_,
_,
_)
//~^ ERROR use of deprecated item
= x;
// all fine
let Stable2(..) = x;
let x = Unstable {
inherit: 1,
override1: 2,
override2: 3,
//~^ ERROR use of deprecated item
};
let _ = x.inherit;
let _ = x.override1;
let _ = x.override2;
//~^ ERROR use of deprecated item
let Unstable {
inherit: _,
override1: _,
override2: _
//~^ ERROR use of deprecated item
} = x;
let Unstable
// the patterns are all fine:
{ .. } = x;
let x = Unstable2(1, 2, 3);
let _ = x.0;
let _ = x.1;
let _ = x.2;
//~^ ERROR use of deprecated item
let Unstable2
(_,
_,
_)
//~^ ERROR use of deprecated item
= x;
let Unstable2
// the patterns are all fine:
(..) = x;
let x = Deprecated {
//~^ ERROR use of deprecated item
inherit: 1,
//~^ ERROR use of deprecated item
override1: 2,
override2: 3,
};
let _ = x.inherit;
//~^ ERROR use of deprecated item
let _ = x.override1;
let _ = x.override2;
let Deprecated {
//~^ ERROR use of deprecated item
inherit: _,
//~^ ERROR use of deprecated item
override1: _,
override2: _
} = x;
let Deprecated
//~^ ERROR use of deprecated item
// the patterns are all fine:
{ .. } = x;
let x = Deprecated2(1, 2, 3);
//~^ ERROR use of deprecated item
let _ = x.0;
//~^ ERROR use of deprecated item
let _ = x.1;
let _ = x.2;
let Deprecated2
//~^ ERROR use of deprecated item
(_,
//~^ ERROR use of deprecated item
_,
_)
= x;
let Deprecated2
//~^ ERROR use of deprecated item
// the patterns are all fine:
(..) = x;
}
}
fn main() {}

View File

@ -317,11 +317,17 @@ mod this_crate {
#[unstable(feature = "test_feature")]
#[deprecated(since = "1.0.0")]
pub struct DeprecatedStruct { i: isize }
pub struct DeprecatedStruct {
#[stable(feature = "test_feature", since = "1.0.0")] i: isize
}
#[unstable(feature = "test_feature")]
pub struct UnstableStruct { i: isize }
pub struct UnstableStruct {
#[stable(feature = "test_feature", since = "1.0.0")] i: isize
}
#[stable(feature = "rust1", since = "1.0.0")]
pub struct StableStruct { i: isize }
pub struct StableStruct {
#[stable(feature = "test_feature", since = "1.0.0")] i: isize
}
#[unstable(feature = "test_feature")]
#[deprecated(since = "1.0.0")]