Skip to content

Commit 6372915

Browse files
committed
auto merge of #15561 : huonw/rust/must-use-iterators, r=alexcrichton
Similar to the stability attributes, a type annotated with `#[must_use = "informative snippet"]` will print the normal warning message along with "informative snippet". This allows the type author to provide some guidance about why the type should be used. --- It can be a little unintuitive that something like `v.iter().map(|x| println!("{}", x));` does nothing: the majority of the iterator adaptors are lazy and do not execute anything until something calls `next`, e.g. a `for` loop, `collect`, `fold`, etc. The majority of such errors can be seen by someone writing something like the above, i.e. just calling an iterator adaptor and doing nothing with it (and doing this is certainly useless), so we can co-opt the `must_use` lint, using the message functionality to give a hint to the reason why. Fixes #14666.
2 parents 898701c + 27d18fb commit 6372915

File tree

3 files changed

+47
-12
lines changed

3 files changed

+47
-12
lines changed

src/libcore/iter.rs

+18-1
Original file line numberDiff line numberDiff line change
@@ -751,6 +751,7 @@ impl<A, B, T: ExactSize<A>, U: ExactSize<B>> ExactSize<(A, B)> for Zip<T, U> {}
751751

752752
/// An double-ended iterator with the direction inverted
753753
#[deriving(Clone)]
754+
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
754755
pub struct Rev<T> {
755756
iter: T
756757
}
@@ -779,6 +780,7 @@ impl<A, T: DoubleEndedIterator<A> + RandomAccessIterator<A>> RandomAccessIterato
779780
}
780781

781782
/// A mutable reference to an iterator
783+
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
782784
pub struct ByRef<'a, T> {
783785
iter: &'a mut T
784786
}
@@ -1039,6 +1041,7 @@ impl<A, T: Clone + Iterator<A>> CloneableIterator for T {
10391041

10401042
/// An iterator that repeats endlessly
10411043
#[deriving(Clone)]
1044+
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
10421045
pub struct Cycle<T> {
10431046
orig: T,
10441047
iter: T,
@@ -1090,6 +1093,7 @@ impl<A, T: Clone + RandomAccessIterator<A>> RandomAccessIterator<A> for Cycle<T>
10901093

10911094
/// An iterator which strings two iterators together
10921095
#[deriving(Clone)]
1096+
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
10931097
pub struct Chain<T, U> {
10941098
a: T,
10951099
b: U,
@@ -1159,6 +1163,7 @@ for Chain<T, U> {
11591163

11601164
/// An iterator which iterates two other iterators simultaneously
11611165
#[deriving(Clone)]
1166+
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
11621167
pub struct Zip<T, U> {
11631168
a: T,
11641169
b: U
@@ -1237,6 +1242,7 @@ RandomAccessIterator<(A, B)> for Zip<T, U> {
12371242
}
12381243

12391244
/// An iterator which maps the values of `iter` with `f`
1245+
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
12401246
pub struct Map<'a, A, B, T> {
12411247
iter: T,
12421248
f: |A|: 'a -> B
@@ -1287,6 +1293,7 @@ impl<'a, A, B, T: RandomAccessIterator<A>> RandomAccessIterator<B> for Map<'a, A
12871293
}
12881294

12891295
/// An iterator which filters the elements of `iter` with `predicate`
1296+
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
12901297
pub struct Filter<'a, A, T> {
12911298
iter: T,
12921299
predicate: |&A|: 'a -> bool
@@ -1331,6 +1338,7 @@ impl<'a, A, T: DoubleEndedIterator<A>> DoubleEndedIterator<A> for Filter<'a, A,
13311338
}
13321339

13331340
/// An iterator which uses `f` to both filter and map elements from `iter`
1341+
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
13341342
pub struct FilterMap<'a, A, B, T> {
13351343
iter: T,
13361344
f: |A|: 'a -> Option<B>
@@ -1375,6 +1383,7 @@ for FilterMap<'a, A, B, T> {
13751383

13761384
/// An iterator which yields the current count and the element during iteration
13771385
#[deriving(Clone)]
1386+
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
13781387
pub struct Enumerate<T> {
13791388
iter: T,
13801389
count: uint
@@ -1429,6 +1438,7 @@ impl<A, T: RandomAccessIterator<A>> RandomAccessIterator<(uint, A)> for Enumerat
14291438
}
14301439

14311440
/// An iterator with a `peek()` that returns an optional reference to the next element.
1441+
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
14321442
pub struct Peekable<A, T> {
14331443
iter: T,
14341444
peeked: Option<A>,
@@ -1479,6 +1489,7 @@ impl<'a, A, T: Iterator<A>> Peekable<A, T> {
14791489
}
14801490

14811491
/// An iterator which rejects elements while `predicate` is true
1492+
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
14821493
pub struct SkipWhile<'a, A, T> {
14831494
iter: T,
14841495
flag: bool,
@@ -1517,6 +1528,7 @@ impl<'a, A, T: Iterator<A>> Iterator<A> for SkipWhile<'a, A, T> {
15171528
}
15181529

15191530
/// An iterator which only accepts elements while `predicate` is true
1531+
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
15201532
pub struct TakeWhile<'a, A, T> {
15211533
iter: T,
15221534
flag: bool,
@@ -1552,6 +1564,7 @@ impl<'a, A, T: Iterator<A>> Iterator<A> for TakeWhile<'a, A, T> {
15521564

15531565
/// An iterator which skips over `n` elements of `iter`.
15541566
#[deriving(Clone)]
1567+
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
15551568
pub struct Skip<T> {
15561569
iter: T,
15571570
n: uint
@@ -1616,6 +1629,7 @@ impl<A, T: RandomAccessIterator<A>> RandomAccessIterator<A> for Skip<T> {
16161629

16171630
/// An iterator which only iterates over the first `n` iterations of `iter`.
16181631
#[deriving(Clone)]
1632+
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
16191633
pub struct Take<T> {
16201634
iter: T,
16211635
n: uint
@@ -1665,6 +1679,7 @@ impl<A, T: RandomAccessIterator<A>> RandomAccessIterator<A> for Take<T> {
16651679

16661680

16671681
/// An iterator to maintain state while iterating another iterator
1682+
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
16681683
pub struct Scan<'a, A, B, T, St> {
16691684
iter: T,
16701685
f: |&mut St, A|: 'a -> Option<B>,
@@ -1689,6 +1704,7 @@ impl<'a, A, B, T: Iterator<A>, St> Iterator<B> for Scan<'a, A, B, T, St> {
16891704
/// An iterator that maps each element to an iterator,
16901705
/// and yields the elements of the produced iterators
16911706
///
1707+
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
16921708
pub struct FlatMap<'a, A, T, U> {
16931709
iter: T,
16941710
f: |A|: 'a -> U,
@@ -1748,6 +1764,7 @@ impl<'a,
17481764
/// An iterator that yields `None` forever after the underlying iterator
17491765
/// yields `None` once.
17501766
#[deriving(Clone)]
1767+
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
17511768
pub struct Fuse<T> {
17521769
iter: T,
17531770
done: bool
@@ -1820,6 +1837,7 @@ impl<T> Fuse<T> {
18201837

18211838
/// An iterator that calls a function with a reference to each
18221839
/// element before yielding it.
1840+
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
18231841
pub struct Inspect<'a, A, T> {
18241842
iter: T,
18251843
f: |&A|: 'a
@@ -2299,4 +2317,3 @@ pub mod order {
22992317
}
23002318
}
23012319
}
2302-

src/librustc/lint/builtin.rs

+21-11
Original file line numberDiff line numberDiff line change
@@ -669,22 +669,13 @@ impl LintPass for UnusedResult {
669669
if ast_util::is_local(did) {
670670
match cx.tcx.map.get(did.node) {
671671
ast_map::NodeItem(it) => {
672-
if attr::contains_name(it.attrs.as_slice(),
673-
"must_use") {
674-
cx.span_lint(UNUSED_MUST_USE, s.span,
675-
"unused result which must be used");
676-
warned = true;
677-
}
672+
warned |= check_must_use(cx, it.attrs.as_slice(), s.span);
678673
}
679674
_ => {}
680675
}
681676
} else {
682677
csearch::get_item_attrs(&cx.sess().cstore, did, |attrs| {
683-
if attr::contains_name(attrs.as_slice(), "must_use") {
684-
cx.span_lint(UNUSED_MUST_USE, s.span,
685-
"unused result which must be used");
686-
warned = true;
687-
}
678+
warned |= check_must_use(cx, attrs.as_slice(), s.span);
688679
});
689680
}
690681
}
@@ -693,6 +684,25 @@ impl LintPass for UnusedResult {
693684
if !warned {
694685
cx.span_lint(UNUSED_RESULT, s.span, "unused result");
695686
}
687+
688+
fn check_must_use(cx: &Context, attrs: &[ast::Attribute], sp: Span) -> bool {
689+
for attr in attrs.iter() {
690+
if attr.check_name("must_use") {
691+
let mut msg = "unused result which must be used".to_string();
692+
// check for #[must_use="..."]
693+
match attr.value_str() {
694+
None => {}
695+
Some(s) => {
696+
msg.push_str(": ");
697+
msg.push_str(s.get());
698+
}
699+
}
700+
cx.span_lint(UNUSED_MUST_USE, sp, msg.as_slice());
701+
return true;
702+
}
703+
}
704+
false
705+
}
696706
}
697707
}
698708

src/test/compile-fail/unused-result.rs

+8
Original file line numberDiff line numberDiff line change
@@ -14,27 +14,35 @@
1414
#[must_use]
1515
enum MustUse { Test }
1616

17+
#[must_use = "some message"]
18+
enum MustUseMsg { Test2 }
19+
1720
fn foo<T>() -> T { fail!() }
1821

1922
fn bar() -> int { return foo::<int>(); }
2023
fn baz() -> MustUse { return foo::<MustUse>(); }
24+
fn qux() -> MustUseMsg { return foo::<MustUseMsg>(); }
2125

2226
#[allow(unused_result)]
2327
fn test() {
2428
foo::<int>();
2529
foo::<MustUse>(); //~ ERROR: unused result which must be used
30+
foo::<MustUseMsg>(); //~ ERROR: unused result which must be used: some message
2631
}
2732

2833
#[allow(unused_result, unused_must_use)]
2934
fn test2() {
3035
foo::<int>();
3136
foo::<MustUse>();
37+
foo::<MustUseMsg>();
3238
}
3339

3440
fn main() {
3541
foo::<int>(); //~ ERROR: unused result
3642
foo::<MustUse>(); //~ ERROR: unused result which must be used
43+
foo::<MustUseMsg>(); //~ ERROR: unused result which must be used: some message
3744

3845
let _ = foo::<int>();
3946
let _ = foo::<MustUse>();
47+
let _ = foo::<MustUseMsg>();
4048
}

0 commit comments

Comments
 (0)