Skip to content

Commit 19cb8f3

Browse files
committed
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.
1 parent 4db0b32 commit 19cb8f3

File tree

8 files changed

+561
-11
lines changed

8 files changed

+561
-11
lines changed

src/librustc/lint/builtin.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1771,6 +1771,11 @@ impl LintPass for Stability {
17711771
stability::check_path(cx.tcx, path, id,
17721772
&mut |id, sp, stab| self.lint(cx, id, sp, stab));
17731773
}
1774+
1775+
fn check_pat(&mut self, cx: &Context, pat: &ast::Pat) {
1776+
stability::check_pat(cx.tcx, pat,
1777+
&mut |id, sp, stab| self.lint(cx, id, sp, stab))
1778+
}
17741779
}
17751780

17761781
declare_lint! {

src/librustc/middle/stability.rs

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,10 @@ impl<'a> Annotator<'a> {
5858
attrs: &Vec<Attribute>, item_sp: Span, f: F, required: bool) where
5959
F: FnOnce(&mut Annotator),
6060
{
61+
debug!("annotate(id = {:?}, attrs = {:?})", id, attrs);
6162
match attr::find_stability(self.sess.diagnostic(), attrs, item_sp) {
6263
Some(stab) => {
64+
debug!("annotate: found {:?}", stab);
6365
self.index.local.insert(id, stab.clone());
6466

6567
// Don't inherit #[stable(feature = "rust1", since = "1.0.0")]
@@ -72,6 +74,8 @@ impl<'a> Annotator<'a> {
7274
}
7375
}
7476
None => {
77+
debug!("annotate: not found, use_parent = {:?}, parent = {:?}",
78+
use_parent, self.parent);
7579
if use_parent {
7680
if let Some(stab) = self.parent.clone() {
7781
self.index.local.insert(id, stab);
@@ -299,6 +303,12 @@ impl<'a, 'v, 'tcx> Visitor<'v> for Checker<'a, 'tcx> {
299303
&mut |id, sp, stab| self.check(id, sp, stab));
300304
visit::walk_path(self, path)
301305
}
306+
307+
fn visit_pat(&mut self, pat: &ast::Pat) {
308+
check_pat(self.tcx, pat,
309+
&mut |id, sp, stab| self.check(id, sp, stab));
310+
visit::walk_pat(self, pat)
311+
}
302312
}
303313

304314
/// Helper for discovering nodes to check for stability
@@ -385,6 +395,76 @@ pub fn check_expr(tcx: &ty::ctxt, e: &ast::Expr,
385395
None => return
386396
}
387397
}
398+
ast::ExprField(ref base_e, ref field) => {
399+
span = field.span;
400+
match ty::expr_ty_adjusted(tcx, base_e).sty {
401+
ty::ty_struct(did, _) => {
402+
ty::lookup_struct_fields(tcx, did)
403+
.iter()
404+
.find(|f| f.name == field.node.name)
405+
.unwrap_or_else(|| {
406+
tcx.sess.span_bug(field.span,
407+
"stability::check_expr: unknown named field access")
408+
})
409+
.id
410+
}
411+
_ => tcx.sess.span_bug(e.span,
412+
"stability::check_expr: named field access on non-struct")
413+
}
414+
}
415+
ast::ExprTupField(ref base_e, ref field) => {
416+
span = field.span;
417+
match ty::expr_ty_adjusted(tcx, base_e).sty {
418+
ty::ty_struct(did, _) => {
419+
ty::lookup_struct_fields(tcx, did)
420+
.get(field.node)
421+
.unwrap_or_else(|| {
422+
tcx.sess.span_bug(field.span,
423+
"stability::check_expr: unknown unnamed field access")
424+
})
425+
.id
426+
}
427+
ty::ty_tup(..) => return,
428+
_ => tcx.sess.span_bug(e.span,
429+
"stability::check_expr: unnamed field access on \
430+
something other than a tuple or struct")
431+
}
432+
}
433+
ast::ExprStruct(_, ref expr_fields, _) => {
434+
let type_ = ty::expr_ty(tcx, e);
435+
match type_.sty {
436+
ty::ty_struct(did, _) => {
437+
let struct_fields = ty::lookup_struct_fields(tcx, did);
438+
// check the stability of each field that appears
439+
// in the construction expression.
440+
for field in expr_fields {
441+
let did = struct_fields
442+
.iter()
443+
.find(|f| f.name == field.ident.node.name)
444+
.unwrap_or_else(|| {
445+
tcx.sess.span_bug(field.span,
446+
"stability::check_expr: unknown named \
447+
field access")
448+
})
449+
.id;
450+
maybe_do_stability_check(tcx, did, field.span, cb);
451+
}
452+
453+
// we're done.
454+
return
455+
}
456+
// we don't look at stability attributes on
457+
// struct-like enums (yet...), but it's definitely not
458+
// a bug to have construct one.
459+
ty::ty_enum(..) => return,
460+
_ => {
461+
tcx.sess.span_bug(e.span,
462+
&format!("stability::check_expr: struct construction \
463+
of non-struct, type {:?}",
464+
type_.repr(tcx)));
465+
}
466+
}
467+
}
388468
_ => return
389469
};
390470

@@ -403,6 +483,47 @@ pub fn check_path(tcx: &ty::ctxt, path: &ast::Path, id: ast::NodeId,
403483

404484
}
405485

486+
pub fn check_pat(tcx: &ty::ctxt, pat: &ast::Pat,
487+
cb: &mut FnMut(ast::DefId, Span, &Option<Stability>)) {
488+
debug!("check_pat(pat = {:?})", pat);
489+
if is_internal(tcx, pat.span) { return; }
490+
491+
let did = match ty::pat_ty_opt(tcx, pat) {
492+
Some(&ty::TyS { sty: ty::ty_struct(did, _), .. }) => did,
493+
Some(_) | None => return,
494+
};
495+
let struct_fields = ty::lookup_struct_fields(tcx, did);
496+
match pat.node {
497+
// Foo(a, b, c)
498+
ast::PatEnum(_, Some(ref pat_fields)) => {
499+
for (field, struct_field) in pat_fields.iter().zip(struct_fields.iter()) {
500+
// a .. pattern is fine, but anything positional is
501+
// not.
502+
if let ast::PatWild(ast::PatWildMulti) = field.node {
503+
continue
504+
}
505+
maybe_do_stability_check(tcx, struct_field.id, field.span, cb)
506+
}
507+
}
508+
// Foo { a, b, c }
509+
ast::PatStruct(_, ref pat_fields, _) => {
510+
for field in pat_fields {
511+
let did = struct_fields
512+
.iter()
513+
.find(|f| f.name == field.node.ident.name)
514+
.unwrap_or_else(|| {
515+
tcx.sess.span_bug(field.span,
516+
"stability::check_pat: unknown named field access")
517+
})
518+
.id;
519+
maybe_do_stability_check(tcx, did, field.span, cb);
520+
}
521+
}
522+
// everything else is fine.
523+
_ => {}
524+
}
525+
}
526+
406527
fn maybe_do_stability_check(tcx: &ty::ctxt, id: ast::DefId, span: Span,
407528
cb: &mut FnMut(ast::DefId, Span, &Option<Stability>)) {
408529
if !is_staged_api(tcx, id) { return }

src/librustc/middle/ty.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4298,6 +4298,9 @@ pub fn free_region_from_def(outlives_extent: region::DestructionScopeData,
42984298
pub fn pat_ty<'tcx>(cx: &ctxt<'tcx>, pat: &ast::Pat) -> Ty<'tcx> {
42994299
return node_id_to_type(cx, pat.id);
43004300
}
4301+
pub fn pat_ty_opt<'tcx>(cx: &ctxt<'tcx>, pat: &ast::Pat) -> Option<Ty<'tcx>> {
4302+
return node_id_to_type_opt(cx, pat.id);
4303+
}
43014304

43024305

43034306
// Returns the type of an expression as a monotype.

src/libstd/old_io/mem.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ impl MemWriter {
102102

103103
impl Writer for MemWriter {
104104
#[inline]
105+
#[allow(deprecated)]
105106
fn write_all(&mut self, buf: &[u8]) -> IoResult<()> {
106107
self.buf.push_all(buf);
107108
Ok(())

src/test/auxiliary/lint_stability.rs

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -100,14 +100,22 @@ pub trait UnstableTrait { fn dummy(&self) { } }
100100

101101
#[stable(feature = "test_feature", since = "1.0.0")]
102102
#[deprecated(since = "1.0.0")]
103-
pub struct DeprecatedStruct { pub i: int }
103+
pub struct DeprecatedStruct {
104+
#[stable(feature = "test_feature", since = "1.0.0")] pub i: int
105+
}
104106
#[unstable(feature = "test_feature")]
105107
#[deprecated(since = "1.0.0")]
106-
pub struct DeprecatedUnstableStruct { pub i: int }
108+
pub struct DeprecatedUnstableStruct {
109+
#[stable(feature = "test_feature", since = "1.0.0")] pub i: int
110+
}
107111
#[unstable(feature = "test_feature")]
108-
pub struct UnstableStruct { pub i: int }
112+
pub struct UnstableStruct {
113+
#[stable(feature = "test_feature", since = "1.0.0")] pub i: int
114+
}
109115
#[stable(feature = "rust1", since = "1.0.0")]
110-
pub struct StableStruct { pub i: int }
116+
pub struct StableStruct {
117+
#[stable(feature = "test_feature", since = "1.0.0")] pub i: int
118+
}
111119

112120
#[stable(feature = "test_feature", since = "1.0.0")]
113121
#[deprecated(since = "1.0.0")]
@@ -137,14 +145,14 @@ pub enum Enum {
137145

138146
#[stable(feature = "test_feature", since = "1.0.0")]
139147
#[deprecated(since = "1.0.0")]
140-
pub struct DeprecatedTupleStruct(pub int);
148+
pub struct DeprecatedTupleStruct(#[stable(feature = "rust1", since = "1.0.0")] pub int);
141149
#[unstable(feature = "test_feature")]
142150
#[deprecated(since = "1.0.0")]
143-
pub struct DeprecatedUnstableTupleStruct(pub int);
151+
pub struct DeprecatedUnstableTupleStruct(#[stable(feature = "rust1", since = "1.0.0")] pub int);
144152
#[unstable(feature = "test_feature")]
145-
pub struct UnstableTupleStruct(pub int);
153+
pub struct UnstableTupleStruct(#[stable(feature = "rust1", since = "1.0.0")] pub int);
146154
#[stable(feature = "rust1", since = "1.0.0")]
147-
pub struct StableTupleStruct(pub int);
155+
pub struct StableTupleStruct(#[stable(feature = "rust1", since = "1.0.0")] pub int);
148156

149157
#[macro_export]
150158
macro_rules! macro_test {
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![feature(staged_api)]
12+
#![staged_api]
13+
#![stable(feature = "rust1", since = "1.0.0")]
14+
15+
#[stable(feature = "rust1", since = "1.0.0")]
16+
pub struct Stable {
17+
#[stable(feature = "rust1", since = "1.0.0")]
18+
pub inherit: u8, // it's a lie (stable doesn't inherit)
19+
#[unstable(feature = "test_feature")]
20+
pub override1: u8,
21+
#[deprecated(since = "1.0.0")]
22+
#[unstable(feature = "test_feature")]
23+
pub override2: u8,
24+
}
25+
26+
#[stable(feature = "rust1", since = "1.0.0")]
27+
pub struct Stable2(#[stable(feature = "rust1", since = "1.0.0")] pub u8,
28+
#[unstable(feature = "test_feature")] pub u8,
29+
#[unstable(feature = "test_feature")] #[deprecated(since = "1.0.0")] pub u8);
30+
31+
#[unstable(feature = "test_feature")]
32+
pub struct Unstable {
33+
pub inherit: u8,
34+
#[stable(feature = "rust1", since = "1.0.0")]
35+
pub override1: u8,
36+
#[deprecated(since = "1.0.0")]
37+
#[unstable(feature = "test_feature")]
38+
pub override2: u8,
39+
}
40+
41+
#[unstable(feature = "test_feature")]
42+
pub struct Unstable2(pub u8,
43+
#[stable(feature = "rust1", since = "1.0.0")] pub u8,
44+
#[unstable(feature = "test_feature")] #[deprecated(since = "1.0.0")] pub u8);
45+
46+
#[unstable(feature = "test_feature")]
47+
#[deprecated(feature = "rust1", since = "1.0.0")]
48+
pub struct Deprecated {
49+
pub inherit: u8,
50+
#[stable(feature = "rust1", since = "1.0.0")]
51+
pub override1: u8,
52+
#[unstable(feature = "test_feature")]
53+
pub override2: u8,
54+
}
55+
56+
#[unstable(feature = "test_feature")]
57+
#[deprecated(feature = "rust1", since = "1.0.0")]
58+
pub struct Deprecated2(pub u8,
59+
#[stable(feature = "rust1", since = "1.0.0")] pub u8,
60+
#[unstable(feature = "test_feature")] pub u8);

0 commit comments

Comments
 (0)