auto merge of : vberger/rust/deprecated_in_macros, r=aturon

Closes .

The stability lint will now check code generated by macro expansion. It will allow to detect :
- arguments passed to macros using deprecated (and others) items
- macro expansion generating code using deprecated items due to its arguments (hence the second commit, fixing such issue found in libcollections)

Checking is still done at expansion, but it will also detect a macro explicitly using a deprecated item in its definition.
This commit is contained in:
bors 2014-09-22 23:50:30 +00:00
commit 43fd619819
18 changed files with 94 additions and 42 deletions
src
liballoc
libcollections
libcore
libcoretest
libgetopts
libglob
libnum
librustc/lint
librustrt
libtest
test

@ -311,13 +311,13 @@ mod tests {
task::spawn(proc() {
let arc_v: Arc<Vec<int>> = rx.recv();
assert_eq!(*arc_v.get(3), 4);
assert_eq!((*arc_v)[3], 4);
});
tx.send(arc_v.clone());
assert_eq!(*arc_v.get(2), 3);
assert_eq!(*arc_v.get(4), 5);
assert_eq!((*arc_v)[2], 3);
assert_eq!((*arc_v)[4], 5);
info!("{:?}", arc_v);
}

@ -131,7 +131,7 @@ pub trait MutableMap<K, V>: Map<K, V> + Mutable {
/// let mut map = HashMap::new();
/// assert_eq!(map.insert("key", 2i), true);
/// assert_eq!(map.insert("key", 9i), false);
/// assert_eq!(map.get(&"key"), &9i);
/// assert_eq!(map["key"], 9i);
/// ```
#[inline]
fn insert(&mut self, key: K, value: V) -> bool {
@ -171,7 +171,7 @@ pub trait MutableMap<K, V>: Map<K, V> + Mutable {
///
/// map.insert("a", 1i);
/// assert_eq!(map.swap("a", 37i), Some(1i));
/// assert_eq!(map.get(&"a"), &37i);
/// assert_eq!(map["a"], 37i);
/// ```
fn swap(&mut self, k: K, v: V) -> Option<V>;
@ -203,7 +203,7 @@ pub trait MutableMap<K, V>: Map<K, V> + Mutable {
/// Some(x) => *x = 7i,
/// None => (),
/// }
/// assert_eq!(map.get(&"a"), &7i);
/// assert_eq!(map["a"], 7i);
/// ```
fn find_mut<'a>(&'a mut self, key: &K) -> Option<&'a mut V>;
}

@ -542,6 +542,7 @@ mod tests {
use vec::Vec;
#[test]
#[allow(deprecated)]
fn test_simple() {
let mut d = RingBuf::new();
assert_eq!(d.len(), 0u);
@ -587,6 +588,7 @@ mod tests {
}
#[test]
#[allow(deprecated)]
fn test_boxes() {
let a: Gc<int> = box(GC) 5;
let b: Gc<int> = box(GC) 72;

@ -904,6 +904,7 @@ mod tests {
}
#[test]
#[allow(deprecated)]
fn test_initn() {
let mut a = vec![11i, 12, 13];
let b: &[int] = &[11, 12, 13];
@ -1303,6 +1304,7 @@ mod tests {
}
#[test]
#[allow(deprecated)]
fn test_bsearch_elem() {
assert_eq!([1i,2,3,4,5].bsearch_elem(&5), Some(4));
assert_eq!([1i,2,3,4,5].bsearch_elem(&4), Some(3));
@ -1350,11 +1352,11 @@ mod tests {
#[test]
fn test_reverse() {
let mut v: Vec<int> = vec![10i, 20];
assert_eq!(*v.get(0), 10);
assert_eq!(*v.get(1), 20);
assert_eq!(v[0], 10);
assert_eq!(v[1], 20);
v.reverse();
assert_eq!(*v.get(0), 20);
assert_eq!(*v.get(1), 10);
assert_eq!(v[0], 20);
assert_eq!(v[1], 10);
let mut v3: Vec<int> = vec![];
v3.reverse();
@ -1462,6 +1464,7 @@ mod tests {
}
#[test]
#[allow(deprecated)]
fn test_shift() {
let mut x = vec![1i, 2, 3];
assert_eq!(x.shift(), Some(1));
@ -1901,6 +1904,7 @@ mod tests {
}
#[test]
#[allow(deprecated)]
fn test_copy_from() {
let mut a = [1i,2,3,4,5];
let b = [6i,7,8];

@ -348,11 +348,11 @@ impl<V:Clone> SmallIntMap<V> {
/// let mut map = SmallIntMap::new();
///
/// // Key does not exist, will do a simple insert
/// assert!(map.update(1, vec![1i, 2], |old, new| old.append(new.as_slice())));
/// assert!(map.update(1, vec![1i, 2], |mut old, new| { old.extend(new.into_iter()); old }));
/// assert_eq!(map[1], vec![1i, 2]);
///
/// // Key exists, update the value
/// assert!(!map.update(1, vec![3i, 4], |old, new| old.append(new.as_slice())));
/// assert!(!map.update(1, vec![3i, 4], |mut old, new| { old.extend(new.into_iter()); old }));
/// assert_eq!(map[1], vec![1i, 2, 3, 4]);
/// ```
pub fn update(&mut self, key: uint, newval: V, ff: |V, V| -> V) -> bool {
@ -452,7 +452,7 @@ impl<V> Index<uint, V> for SmallIntMap<V> {
}*/
macro_rules! iterator {
(impl $name:ident -> $elem:ty, $getter:ident) => {
(impl $name:ident -> $elem:ty, $($getter:ident),+) => {
impl<'a, T> Iterator<$elem> for $name<'a, T> {
#[inline]
fn next(&mut self) -> Option<$elem> {
@ -462,7 +462,7 @@ macro_rules! iterator {
if elem.is_some() {
let index = self.front;
self.front += 1;
return Some((index, elem. $getter ()));
return Some((index, elem $(. $getter ())+));
}
}
_ => ()
@ -481,7 +481,7 @@ macro_rules! iterator {
}
macro_rules! double_ended_iterator {
(impl $name:ident -> $elem:ty, $getter:ident) => {
(impl $name:ident -> $elem:ty, $($getter:ident),+) => {
impl<'a, T> DoubleEndedIterator<$elem> for $name<'a, T> {
#[inline]
fn next_back(&mut self) -> Option<$elem> {
@ -490,7 +490,7 @@ macro_rules! double_ended_iterator {
Some(elem) => {
if elem.is_some() {
self.back -= 1;
return Some((self.back, elem. $getter ()));
return Some((self.back, elem$(. $getter ())+));
}
}
_ => ()
@ -510,8 +510,8 @@ pub struct Entries<'a, T:'a> {
iter: slice::Items<'a, Option<T>>
}
iterator!(impl Entries -> (uint, &'a T), get_ref)
double_ended_iterator!(impl Entries -> (uint, &'a T), get_ref)
iterator!(impl Entries -> (uint, &'a T), as_ref, unwrap)
double_ended_iterator!(impl Entries -> (uint, &'a T), as_ref, unwrap)
/// Forward iterator over the key-value pairs of a map, with the
/// values being mutable.
@ -521,8 +521,8 @@ pub struct MutEntries<'a, T:'a> {
iter: slice::MutItems<'a, Option<T>>
}
iterator!(impl MutEntries -> (uint, &'a mut T), get_mut_ref)
double_ended_iterator!(impl MutEntries -> (uint, &'a mut T), get_mut_ref)
iterator!(impl MutEntries -> (uint, &'a mut T), as_mut, unwrap)
double_ended_iterator!(impl MutEntries -> (uint, &'a mut T), as_mut, unwrap)
/// Forward iterator over the keys of a map
pub type Keys<'a, T> =

@ -434,7 +434,7 @@ impl<T> TrieMap<T> {
fn bound_mut<'a>(&'a mut self, key: uint, upper: bool) -> MutEntries<'a, T> {
bound!(MutEntries, self = self,
key = key, is_upper = upper,
slice_from = mut_slice_from, iter = mut_iter,
slice_from = slice_from_mut, iter = iter_mut,
mutability = mut)
}
@ -1020,7 +1020,7 @@ macro_rules! iterator_impl {
}
iterator_impl! { Entries, iter = iter, mutability = }
iterator_impl! { MutEntries, iter = mut_iter, mutability = mut }
iterator_impl! { MutEntries, iter = iter_mut, mutability = mut }
/// A forward iterator over a set.
pub struct SetItems<'a> {

@ -960,6 +960,7 @@ impl<T> Vec<T> {
/// # Example
///
/// ```
/// #![allow(deprecated)]
/// let vec = vec![1i, 2, 3, 4];
/// assert!(vec.tailn(2) == [3, 4]);
/// ```
@ -1065,6 +1066,7 @@ impl<T> Vec<T> {
/// # Example
///
/// ```
/// #![allow(deprecated)]
/// let mut vec = vec![1i, 2, 3];
/// assert!(vec.shift() == Some(1));
/// assert_eq!(vec, vec![2, 3]);

@ -1062,11 +1062,11 @@ pub trait MutableCloneableSlice<T> {
/// let mut dst = [0i, 0, 0];
/// let src = [1i, 2];
///
/// assert!(dst.copy_from(src) == 2);
/// assert!(dst.clone_from_slice(src) == 2);
/// assert!(dst == [1, 2, 0]);
///
/// let src2 = [3i, 4, 5, 6];
/// assert!(dst.copy_from(src2) == 3);
/// assert!(dst.clone_from_slice(src2) == 3);
/// assert!(dst == [3i, 4, 5]);
/// ```
fn clone_from_slice(self, &[T]) -> uint;

@ -42,6 +42,7 @@ fn test_ordering_order() {
}
#[test]
#[allow(deprecated)]
fn test_lexical_ordering() {
fn t(o1: Ordering, o2: Ordering, e: Ordering) {
assert_eq!(lexical_ordering(o1, o2), e);

@ -244,6 +244,7 @@ fn test_ord() {
}
#[test]
#[allow(deprecated)]
fn test_mutate() {
let mut x = Some(3i);
assert!(x.mutate(|i| i+1));

@ -1142,7 +1142,7 @@ mod tests {
Ok(ref m) => {
// The next variable after the flag is just a free argument
assert!(*m.free.get(0) == "20".to_string());
assert!(m.free[0] == "20".to_string());
}
_ => fail!()
}
@ -1298,8 +1298,8 @@ mod tests {
assert!(m.opt_present("t"));
assert_eq!(m.opt_str("t").unwrap(), "20".to_string());
let pair = m.opt_strs("test");
assert!(*pair.get(0) == "20".to_string());
assert!(*pair.get(1) == "30".to_string());
assert!(pair[0] == "20".to_string());
assert!(pair[1] == "30".to_string());
}
_ => fail!()
}
@ -1351,19 +1351,19 @@ mod tests {
let rs = getopts(args.as_slice(), opts.as_slice());
match rs {
Ok(ref m) => {
assert!(*m.free.get(0) == "prog".to_string());
assert!(*m.free.get(1) == "free1".to_string());
assert!(m.free[0] == "prog".to_string());
assert!(m.free[1] == "free1".to_string());
assert_eq!(m.opt_str("s").unwrap(), "20".to_string());
assert!(*m.free.get(2) == "free2".to_string());
assert!(m.free[2] == "free2".to_string());
assert!((m.opt_present("flag")));
assert_eq!(m.opt_str("long").unwrap(), "30".to_string());
assert!((m.opt_present("f")));
let pair = m.opt_strs("m");
assert!(*pair.get(0) == "40".to_string());
assert!(*pair.get(1) == "50".to_string());
assert!(pair[0] == "40".to_string());
assert!(pair[1] == "50".to_string());
let pair = m.opt_strs("n");
assert!(*pair.get(0) == "-A B".to_string());
assert!(*pair.get(1) == "-60 70".to_string());
assert!(pair[0] == "-A B".to_string());
assert!(pair[1] == "-60 70".to_string());
assert!((!m.opt_present("notpresent")));
}
_ => fail!()

@ -336,6 +336,7 @@ impl Pattern {
* # Example
*
* ```rust
* #![allow(deprecated)]
* use glob::Pattern;
*
* assert!(Pattern::new("c?t").matches("cat"));

@ -18,6 +18,7 @@ pub trait Integer: Num + PartialOrd
/// # Examples
///
/// ```
/// # #![allow(deprecated)]
/// # use num::Integer;
/// assert!(( 8i).div_floor(& 3) == 2);
/// assert!(( 8i).div_floor(&-3) == -3);
@ -34,6 +35,7 @@ pub trait Integer: Num + PartialOrd
/// Floored integer modulo, satisfying:
///
/// ```
/// # #![allow(deprecated)]
/// # use num::Integer;
/// # let n = 1i; let d = 1i;
/// assert!(n.div_floor(&d) * d + n.mod_floor(&d) == n)
@ -42,6 +44,7 @@ pub trait Integer: Num + PartialOrd
/// # Examples
///
/// ```
/// # #![allow(deprecated)]
/// # use num::Integer;
/// assert!(( 8i).mod_floor(& 3) == 2);
/// assert!(( 8i).mod_floor(&-3) == -1);
@ -60,6 +63,7 @@ pub trait Integer: Num + PartialOrd
/// # Examples
///
/// ```
/// # #![allow(deprecated)]
/// # use num::Integer;
/// assert_eq!(6i.gcd(&8), 2);
/// assert_eq!(7i.gcd(&3), 1);
@ -71,6 +75,7 @@ pub trait Integer: Num + PartialOrd
/// # Examples
///
/// ```
/// # #![allow(deprecated)]
/// # use num::Integer;
/// assert_eq!(7i.lcm(&3), 21);
/// assert_eq!(2i.lcm(&4), 4);
@ -86,6 +91,7 @@ pub trait Integer: Num + PartialOrd
/// # Examples
///
/// ```
/// # #![allow(deprecated)]
/// # use num::Integer;
/// assert_eq!(9i.is_multiple_of(&3), true);
/// assert_eq!(3i.is_multiple_of(&9), false);
@ -97,6 +103,7 @@ pub trait Integer: Num + PartialOrd
/// # Examples
///
/// ```
/// # #![allow(deprecated)]
/// # use num::Integer;
/// assert_eq!(3i.is_even(), false);
/// assert_eq!(4i.is_even(), true);
@ -108,6 +115,7 @@ pub trait Integer: Num + PartialOrd
/// # Examples
///
/// ```
/// # #![allow(deprecated)]
/// # use num::Integer;
/// assert_eq!(3i.is_odd(), true);
/// assert_eq!(4i.is_odd(), false);
@ -120,6 +128,7 @@ pub trait Integer: Num + PartialOrd
/// # Examples
///
/// ```
/// # #![allow(deprecated)]
/// # use num::Integer;
/// assert_eq!(( 8i).div_rem( &3), ( 2, 2));
/// assert_eq!(( 8i).div_rem(&-3), (-2, 2));
@ -142,6 +151,7 @@ pub trait Integer: Num + PartialOrd
/// # Examples
///
/// ```
/// # #![allow(deprecated)]
/// # use num::Integer;
/// assert_eq!(( 8i).div_mod_floor( &3), ( 2, 2));
/// assert_eq!(( 8i).div_mod_floor(&-3), (-3, -1));

@ -1490,8 +1490,27 @@ impl LintPass for Stability {
}
fn check_expr(&mut self, cx: &Context, e: &ast::Expr) {
// if the expression was produced by a macro expansion,
if e.span.expn_id != NO_EXPANSION { return }
// skip if `e` is not from macro arguments
let skip = cx.tcx.sess.codemap().with_expn_info(e.span.expn_id, |expninfo| {
match expninfo {
Some(ref info) => {
if info.call_site.expn_id != NO_EXPANSION ||
!( e.span.lo > info.call_site.lo && e.span.hi < info.call_site.hi ) {
// This code is not from the arguments,
// or this macro call was generated by an other macro
// We can't handle it.
true
} else if info.callee.span.is_none() {
// We don't want to mess with compiler builtins.
true
} else {
false
}
},
_ => { false }
}
});
if skip { return; }
let id = match e.node {
ast::ExprPath(..) | ast::ExprStruct(..) => {

@ -423,7 +423,7 @@ mod tests {
// TLD shouldn't carry over.
assert!(my_key.get().is_none());
my_key.replace(Some("child data".to_string()));
assert!(my_key.get().get_ref().as_slice() == "child data");
assert!(my_key.get().as_ref().unwrap().as_slice() == "child data");
// should be cleaned up for us
});

@ -1519,9 +1519,9 @@ mod tests {
let filtered = filter_tests(&opts, tests);
assert_eq!(filtered.len(), 1);
assert_eq!(filtered.get(0).desc.name.to_string(),
assert_eq!(filtered[0].desc.name.to_string(),
"1".to_string());
assert!(filtered.get(0).desc.ignore == false);
assert!(filtered[0].desc.ignore == false);
}
#[test]

@ -181,3 +181,13 @@ pub struct LockedTupleStruct(pub int);
macro_rules! macro_test(
() => (deprecated());
)
#[macro_export]
macro_rules! macro_test_arg(
($func:expr) => ($func);
)
#[macro_export]
macro_rules! macro_test_arg_nested(
($func:ident) => (macro_test_arg!($func()));
)

@ -109,12 +109,14 @@ mod cross_crate {
let _ = FrozenTupleStruct (1);
let _ = LockedTupleStruct (1);
// At the moment, the following just checks that the stability
// level of expanded code does not trigger the
// lint. Eventually, we will want to lint the contents of the
// At the moment, the lint checker only checks stability in
// in the arguments of macros.
// Eventually, we will want to lint the contents of the
// macro in the module *defining* it. Also, stability levels
// on macros themselves are not yet linted.
macro_test!();
macro_test_arg!(deprecated_text()); //~ ERROR use of deprecated item: text
macro_test_arg_nested!(deprecated_text);
}
fn test_method_param<F: Trait>(foo: F) {