blob: 5dbe45e6a4401edbf69651f273bebdd29eab7996 [file] [edit]
#![warn(clippy::unnecessary_fold)]
fn is_any(acc: bool, x: usize) -> bool {
acc || x > 2
}
/// Calls which should trigger the `UNNECESSARY_FOLD` lint
fn unnecessary_fold() {
use std::ops::{Add, Mul};
// Can be replaced by .any
let _ = (0..3).any(|x| x > 2);
//~^ unnecessary_fold
// Can be replaced by .any (checking suggestion)
let _ = (0..3).fold(false, is_any);
//~^ redundant_closure
// Can be replaced by .all
let _ = (0..3).all(|x| x > 2);
//~^ unnecessary_fold
// Can be replaced by .sum
let _: i32 = (0..3).sum();
//~^ unnecessary_fold
let _: i32 = (0..3).sum();
//~^ unnecessary_fold
let _: i32 = (0..3).sum();
//~^ unnecessary_fold
// Can be replaced by .product
let _: i32 = (0..3).product();
//~^ unnecessary_fold
let _: i32 = (0..3).product();
//~^ unnecessary_fold
let _: i32 = (0..3).product();
//~^ unnecessary_fold
}
/// Should trigger the `UNNECESSARY_FOLD` lint, with an error span including exactly `.fold(...)`
fn unnecessary_fold_span_for_multi_element_chain() {
let _: bool = (0..3).map(|x| 2 * x).any(|x| x > 2);
//~^ unnecessary_fold
}
/// Calls which should not trigger the `UNNECESSARY_FOLD` lint
fn unnecessary_fold_should_ignore() {
let _ = (0..3).fold(true, |acc, x| acc || x > 2);
let _ = (0..3).fold(false, |acc, x| acc && x > 2);
let _ = (0..3).fold(1, |acc, x| acc + x);
let _ = (0..3).fold(0, |acc, x| acc * x);
let _ = (0..3).fold(0, |acc, x| 1 + acc + x);
struct Adder;
impl Adder {
fn add(lhs: i32, rhs: i32) -> i32 {
unimplemented!()
}
fn mul(lhs: i32, rhs: i32) -> i32 {
unimplemented!()
}
}
// `add`/`mul` are inherent methods
let _: i32 = (0..3).fold(0, Adder::add);
let _: i32 = (0..3).fold(1, Adder::mul);
trait FakeAdd<Rhs = Self> {
type Output;
fn add(self, other: Rhs) -> Self::Output;
}
impl FakeAdd for i32 {
type Output = Self;
fn add(self, other: i32) -> Self::Output {
self + other
}
}
trait FakeMul<Rhs = Self> {
type Output;
fn mul(self, other: Rhs) -> Self::Output;
}
impl FakeMul for i32 {
type Output = Self;
fn mul(self, other: i32) -> Self::Output {
self * other
}
}
// `add`/`mul` come from an unrelated trait
let _: i32 = (0..3).fold(0, FakeAdd::add);
let _: i32 = (0..3).fold(1, FakeMul::mul);
let _ = [(0..2), (0..3)].iter().fold(0, |a, b| a + b.len());
let _ = [(0..2), (0..3)].iter().fold(1, |a, b| a * b.len());
}
/// Should lint only the line containing the fold
fn unnecessary_fold_over_multiple_lines() {
let _ = (0..3)
.map(|x| x + 1)
.filter(|x| x % 2 == 0)
.any(|x| x > 2);
//~^ unnecessary_fold
}
fn issue10000() {
use std::collections::HashMap;
use std::hash::BuildHasher;
use std::ops::{Add, Mul};
fn anything<T>(_: T) {}
fn num(_: i32) {}
fn smoketest_map<S: BuildHasher>(mut map: HashMap<i32, i32, S>) {
map.insert(0, 0);
assert_eq!(map.values().sum::<i32>(), 0);
//~^ unnecessary_fold
// more cases:
let _ = map.values().sum::<i32>();
//~^ unnecessary_fold
let _ = map.values().sum::<i32>();
//~^ unnecessary_fold
let _ = map.values().product::<i32>();
//~^ unnecessary_fold
let _ = map.values().product::<i32>();
//~^ unnecessary_fold
let _: i32 = map.values().sum();
//~^ unnecessary_fold
let _: i32 = map.values().sum();
//~^ unnecessary_fold
let _: i32 = map.values().product();
//~^ unnecessary_fold
let _: i32 = map.values().product();
//~^ unnecessary_fold
anything(map.values().sum::<i32>());
//~^ unnecessary_fold
anything(map.values().sum::<i32>());
//~^ unnecessary_fold
anything(map.values().product::<i32>());
//~^ unnecessary_fold
anything(map.values().product::<i32>());
//~^ unnecessary_fold
num(map.values().sum());
//~^ unnecessary_fold
num(map.values().sum());
//~^ unnecessary_fold
num(map.values().product());
//~^ unnecessary_fold
num(map.values().product());
//~^ unnecessary_fold
}
smoketest_map(HashMap::new());
fn add_turbofish_not_necessary() -> i32 {
(0..3).sum()
//~^ unnecessary_fold
}
fn mul_turbofish_not_necessary() -> i32 {
(0..3).product()
//~^ unnecessary_fold
}
fn add_turbofish_necessary() -> impl Add {
(0..3).sum::<i32>()
//~^ unnecessary_fold
}
fn mul_turbofish_necessary() -> impl Mul {
(0..3).product::<i32>()
//~^ unnecessary_fold
}
}
fn issue16581() {
let _ = (2..=3).product::<i32>();
//~^ unnecessary_fold
let _ = (1..=3).sum::<i32>();
//~^ unnecessary_fold
let _ = (2..=3).product::<i32>();
//~^ unnecessary_fold
let _ = (1..=3).sum::<i32>();
//~^ unnecessary_fold
let _ = (0..3).any(|x| x > 2);
//~^ unnecessary_fold
let _ = (0..3).all(|x| x > 2);
//~^ unnecessary_fold
let _ = (0..3).sum::<i32>();
//~^ unnecessary_fold
let _ = (0..3).product::<i32>();
//~^ unnecessary_fold
}
fn wrongly_unmangled_macros() {
macro_rules! test_expr {
($e:expr) => {
($e + 1) > 2
};
}
let _ = (0..3).any(|x| test_expr!(x));
//~^ unnecessary_fold
}
/// Folding over an `Option`'s iterator is `map_or` in disguise (issue #1658)
fn option_fold() {
let opt: Option<i32> = Some(2);
// `.iter()`: suggest `opt.as_ref().map_or(...)`
let _ = opt.as_ref().map_or(10, |x| 10 + x);
//~^ unnecessary_fold
// `.into_iter()`: `Option` is consumed, suggest plain `map_or`
let _ = opt.map_or(10, |x| 10 * x);
//~^ unnecessary_fold
// `.iter_mut()`: suggest `opt.as_mut().map_or(...)`
let mut opt_mut: Option<i32> = Some(3);
let _ = opt_mut.as_mut().map_or(10, |x| 10 + *x);
//~^ unnecessary_fold
// accumulator unused in the closure body
let _ = opt.as_ref().map_or(10, |x| *x);
//~^ unnecessary_fold
// accumulator used more than once: a literal can be duplicated freely
let _ = opt.as_ref().map_or(2, |x| 2 * 2 + x);
//~^ unnecessary_fold
// a binding of a `Copy` type can also be duplicated freely
let init = 10;
let _ = opt.as_ref().map_or(init, |x| init + x);
//~^ unnecessary_fold
// `Option` expression receiver (not a binding)
let _ = Some(1).map_or(5, |x| 5 - x);
//~^ unnecessary_fold
// should NOT lint: `acc` is bound by the enclosing fold's closure, and
// substituting a closure parameter is not safe when folds are nested
let _ = (0..3).fold(0, |acc, x| opt.iter().fold(acc, |a, b| a + b) + x);
// an option fold nested in a standard fold is still linted when its init
// is a literal
let _ = (0..3).fold(0, |acc, x| opt.as_ref().map_or(1, |b| 1 + b) + x);
//~^ unnecessary_fold
// should NOT lint: a `mut` accumulator is likely reassigned in the body,
// and substituting into the assignment would not compile
let _ = opt.iter().fold(0, |mut acc, x| {
acc += x;
acc
});
// should NOT lint: substituting a call would re-evaluate it
fn compute() -> i32 {
42
}
let _ = opt.iter().fold(compute(), |acc, x| acc + x);
// should NOT lint: substituting a non-`Copy` binding would move it twice
let owned = String::from("a");
let _ = opt.iter().fold(owned, |acc, x| acc + &x.to_string());
// should NOT lint: fold over a general iterator with non-literal init
let _ = (0..3).fold(init, |acc, x| acc + x);
// should NOT lint: `Result` iterators are out of scope here
let res: Result<i32, ()> = Ok(1);
let _ = res.iter().fold(init, |acc, x| acc + x);
}
fn main() {}