diff options
Diffstat (limited to 'src/tools/clippy/tests/ui')
991 files changed, 55470 insertions, 0 deletions
diff --git a/src/tools/clippy/tests/ui/absurd-extreme-comparisons.rs b/src/tools/clippy/tests/ui/absurd-extreme-comparisons.rs new file mode 100644 index 00000000000..d205b383d1f --- /dev/null +++ b/src/tools/clippy/tests/ui/absurd-extreme-comparisons.rs @@ -0,0 +1,61 @@ +#![warn(clippy::absurd_extreme_comparisons)] +#![allow( + unused, + clippy::eq_op, + clippy::no_effect, + clippy::unnecessary_operation, + clippy::needless_pass_by_value +)] + +#[rustfmt::skip] +fn main() { + const Z: u32 = 0; + let u: u32 = 42; + u <= 0; + u <= Z; + u < Z; + Z >= u; + Z > u; + u > u32::MAX; + u >= u32::MAX; + u32::MAX < u; + u32::MAX <= u; + 1-1 > u; + u >= !0; + u <= 12 - 2*6; + let i: i8 = 0; + i < -127 - 1; + i8::MAX >= i; + 3-7 < i32::MIN; + let b = false; + b >= true; + false > b; + u > 0; // ok + // this is handled by clippy::unit_cmp + () < {}; +} + +use std::cmp::{Ordering, PartialEq, PartialOrd}; + +#[derive(PartialEq, PartialOrd)] +pub struct U(u64); + +impl PartialEq<u32> for U { + fn eq(&self, other: &u32) -> bool { + self.eq(&U(u64::from(*other))) + } +} +impl PartialOrd<u32> for U { + fn partial_cmp(&self, other: &u32) -> Option<Ordering> { + self.partial_cmp(&U(u64::from(*other))) + } +} + +pub fn foo(val: U) -> bool { + val > u32::MAX +} + +pub fn bar(len: u64) -> bool { + // This is OK as we are casting from target sized to fixed size + len >= usize::MAX as u64 +} diff --git a/src/tools/clippy/tests/ui/absurd-extreme-comparisons.stderr b/src/tools/clippy/tests/ui/absurd-extreme-comparisons.stderr new file mode 100644 index 00000000000..6de554378aa --- /dev/null +++ b/src/tools/clippy/tests/ui/absurd-extreme-comparisons.stderr @@ -0,0 +1,147 @@ +error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false + --> $DIR/absurd-extreme-comparisons.rs:14:5 + | +LL | u <= 0; + | ^^^^^^ + | + = note: `-D clippy::absurd-extreme-comparisons` implied by `-D warnings` + = help: because `0` is the minimum value for this type, the case where the two sides are not equal never occurs, consider using `u == 0` instead + +error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false + --> $DIR/absurd-extreme-comparisons.rs:15:5 + | +LL | u <= Z; + | ^^^^^^ + | + = help: because `Z` is the minimum value for this type, the case where the two sides are not equal never occurs, consider using `u == Z` instead + +error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false + --> $DIR/absurd-extreme-comparisons.rs:16:5 + | +LL | u < Z; + | ^^^^^ + | + = help: because `Z` is the minimum value for this type, this comparison is always false + +error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false + --> $DIR/absurd-extreme-comparisons.rs:17:5 + | +LL | Z >= u; + | ^^^^^^ + | + = help: because `Z` is the minimum value for this type, the case where the two sides are not equal never occurs, consider using `Z == u` instead + +error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false + --> $DIR/absurd-extreme-comparisons.rs:18:5 + | +LL | Z > u; + | ^^^^^ + | + = help: because `Z` is the minimum value for this type, this comparison is always false + +error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false + --> $DIR/absurd-extreme-comparisons.rs:19:5 + | +LL | u > u32::MAX; + | ^^^^^^^^^^^^ + | + = help: because `u32::MAX` is the maximum value for this type, this comparison is always false + +error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false + --> $DIR/absurd-extreme-comparisons.rs:20:5 + | +LL | u >= u32::MAX; + | ^^^^^^^^^^^^^ + | + = help: because `u32::MAX` is the maximum value for this type, the case where the two sides are not equal never occurs, consider using `u == u32::MAX` instead + +error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false + --> $DIR/absurd-extreme-comparisons.rs:21:5 + | +LL | u32::MAX < u; + | ^^^^^^^^^^^^ + | + = help: because `u32::MAX` is the maximum value for this type, this comparison is always false + +error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false + --> $DIR/absurd-extreme-comparisons.rs:22:5 + | +LL | u32::MAX <= u; + | ^^^^^^^^^^^^^ + | + = help: because `u32::MAX` is the maximum value for this type, the case where the two sides are not equal never occurs, consider using `u32::MAX == u` instead + +error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false + --> $DIR/absurd-extreme-comparisons.rs:23:5 + | +LL | 1-1 > u; + | ^^^^^^^ + | + = help: because `1-1` is the minimum value for this type, this comparison is always false + +error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false + --> $DIR/absurd-extreme-comparisons.rs:24:5 + | +LL | u >= !0; + | ^^^^^^^ + | + = help: because `!0` is the maximum value for this type, the case where the two sides are not equal never occurs, consider using `u == !0` instead + +error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false + --> $DIR/absurd-extreme-comparisons.rs:25:5 + | +LL | u <= 12 - 2*6; + | ^^^^^^^^^^^^^ + | + = help: because `12 - 2*6` is the minimum value for this type, the case where the two sides are not equal never occurs, consider using `u == 12 - 2*6` instead + +error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false + --> $DIR/absurd-extreme-comparisons.rs:27:5 + | +LL | i < -127 - 1; + | ^^^^^^^^^^^^ + | + = help: because `-127 - 1` is the minimum value for this type, this comparison is always false + +error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false + --> $DIR/absurd-extreme-comparisons.rs:28:5 + | +LL | i8::MAX >= i; + | ^^^^^^^^^^^^ + | + = help: because `i8::MAX` is the maximum value for this type, this comparison is always true + +error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false + --> $DIR/absurd-extreme-comparisons.rs:29:5 + | +LL | 3-7 < i32::MIN; + | ^^^^^^^^^^^^^^ + | + = help: because `i32::MIN` is the minimum value for this type, this comparison is always false + +error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false + --> $DIR/absurd-extreme-comparisons.rs:31:5 + | +LL | b >= true; + | ^^^^^^^^^ + | + = help: because `true` is the maximum value for this type, the case where the two sides are not equal never occurs, consider using `b == true` instead + +error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false + --> $DIR/absurd-extreme-comparisons.rs:32:5 + | +LL | false > b; + | ^^^^^^^^^ + | + = help: because `false` is the minimum value for this type, this comparison is always false + +error: <-comparison of unit values detected. This will always be false + --> $DIR/absurd-extreme-comparisons.rs:35:5 + | +LL | () < {}; + | ^^^^^^^ + | + = note: `#[deny(clippy::unit_cmp)]` on by default + +error: aborting due to 18 previous errors + diff --git a/src/tools/clippy/tests/ui/approx_const.rs b/src/tools/clippy/tests/ui/approx_const.rs new file mode 100644 index 00000000000..fb57a0becbb --- /dev/null +++ b/src/tools/clippy/tests/ui/approx_const.rs @@ -0,0 +1,60 @@ +#[warn(clippy::approx_constant)] +#[allow(unused, clippy::shadow_unrelated, clippy::similar_names)] +fn main() { + let my_e = 2.7182; + let almost_e = 2.718; + let no_e = 2.71; + + let my_1_frac_pi = 0.3183; + let no_1_frac_pi = 0.31; + + let my_frac_1_sqrt_2 = 0.70710678; + let almost_frac_1_sqrt_2 = 0.70711; + let my_frac_1_sqrt_2 = 0.707; + + let my_frac_2_pi = 0.63661977; + let no_frac_2_pi = 0.636; + + let my_frac_2_sq_pi = 1.128379; + let no_frac_2_sq_pi = 1.128; + + let my_frac_pi_2 = 1.57079632679; + let no_frac_pi_2 = 1.5705; + + let my_frac_pi_3 = 1.04719755119; + let no_frac_pi_3 = 1.047; + + let my_frac_pi_4 = 0.785398163397; + let no_frac_pi_4 = 0.785; + + let my_frac_pi_6 = 0.523598775598; + let no_frac_pi_6 = 0.523; + + let my_frac_pi_8 = 0.3926990816987; + let no_frac_pi_8 = 0.392; + + let my_ln_10 = 2.302585092994046; + let no_ln_10 = 2.303; + + let my_ln_2 = 0.6931471805599453; + let no_ln_2 = 0.693; + + let my_log10_e = 0.4342944819032518; + let no_log10_e = 0.434; + + let my_log2_e = 1.4426950408889634; + let no_log2_e = 1.442; + + let log2_10 = 3.321928094887362; + let no_log2_10 = 3.321; + + let log10_2 = 0.301029995663981; + let no_log10_2 = 0.301; + + let my_pi = 3.1415; + let almost_pi = 3.14; + let no_pi = 3.15; + + let my_sq2 = 1.4142; + let no_sq2 = 1.414; +} diff --git a/src/tools/clippy/tests/ui/approx_const.stderr b/src/tools/clippy/tests/ui/approx_const.stderr new file mode 100644 index 00000000000..98b85443f0b --- /dev/null +++ b/src/tools/clippy/tests/ui/approx_const.stderr @@ -0,0 +1,130 @@ +error: approximate value of `f{32, 64}::consts::E` found. Consider using it directly + --> $DIR/approx_const.rs:4:16 + | +LL | let my_e = 2.7182; + | ^^^^^^ + | + = note: `-D clippy::approx-constant` implied by `-D warnings` + +error: approximate value of `f{32, 64}::consts::E` found. Consider using it directly + --> $DIR/approx_const.rs:5:20 + | +LL | let almost_e = 2.718; + | ^^^^^ + +error: approximate value of `f{32, 64}::consts::FRAC_1_PI` found. Consider using it directly + --> $DIR/approx_const.rs:8:24 + | +LL | let my_1_frac_pi = 0.3183; + | ^^^^^^ + +error: approximate value of `f{32, 64}::consts::FRAC_1_SQRT_2` found. Consider using it directly + --> $DIR/approx_const.rs:11:28 + | +LL | let my_frac_1_sqrt_2 = 0.70710678; + | ^^^^^^^^^^ + +error: approximate value of `f{32, 64}::consts::FRAC_1_SQRT_2` found. Consider using it directly + --> $DIR/approx_const.rs:12:32 + | +LL | let almost_frac_1_sqrt_2 = 0.70711; + | ^^^^^^^ + +error: approximate value of `f{32, 64}::consts::FRAC_2_PI` found. Consider using it directly + --> $DIR/approx_const.rs:15:24 + | +LL | let my_frac_2_pi = 0.63661977; + | ^^^^^^^^^^ + +error: approximate value of `f{32, 64}::consts::FRAC_2_SQRT_PI` found. Consider using it directly + --> $DIR/approx_const.rs:18:27 + | +LL | let my_frac_2_sq_pi = 1.128379; + | ^^^^^^^^ + +error: approximate value of `f{32, 64}::consts::FRAC_PI_2` found. Consider using it directly + --> $DIR/approx_const.rs:21:24 + | +LL | let my_frac_pi_2 = 1.57079632679; + | ^^^^^^^^^^^^^ + +error: approximate value of `f{32, 64}::consts::FRAC_PI_3` found. Consider using it directly + --> $DIR/approx_const.rs:24:24 + | +LL | let my_frac_pi_3 = 1.04719755119; + | ^^^^^^^^^^^^^ + +error: approximate value of `f{32, 64}::consts::FRAC_PI_4` found. Consider using it directly + --> $DIR/approx_const.rs:27:24 + | +LL | let my_frac_pi_4 = 0.785398163397; + | ^^^^^^^^^^^^^^ + +error: approximate value of `f{32, 64}::consts::FRAC_PI_6` found. Consider using it directly + --> $DIR/approx_const.rs:30:24 + | +LL | let my_frac_pi_6 = 0.523598775598; + | ^^^^^^^^^^^^^^ + +error: approximate value of `f{32, 64}::consts::FRAC_PI_8` found. Consider using it directly + --> $DIR/approx_const.rs:33:24 + | +LL | let my_frac_pi_8 = 0.3926990816987; + | ^^^^^^^^^^^^^^^ + +error: approximate value of `f{32, 64}::consts::LN_10` found. Consider using it directly + --> $DIR/approx_const.rs:36:20 + | +LL | let my_ln_10 = 2.302585092994046; + | ^^^^^^^^^^^^^^^^^ + +error: approximate value of `f{32, 64}::consts::LN_2` found. Consider using it directly + --> $DIR/approx_const.rs:39:19 + | +LL | let my_ln_2 = 0.6931471805599453; + | ^^^^^^^^^^^^^^^^^^ + +error: approximate value of `f{32, 64}::consts::LOG10_E` found. Consider using it directly + --> $DIR/approx_const.rs:42:22 + | +LL | let my_log10_e = 0.4342944819032518; + | ^^^^^^^^^^^^^^^^^^ + +error: approximate value of `f{32, 64}::consts::LOG2_E` found. Consider using it directly + --> $DIR/approx_const.rs:45:21 + | +LL | let my_log2_e = 1.4426950408889634; + | ^^^^^^^^^^^^^^^^^^ + +error: approximate value of `f{32, 64}::consts::LOG2_10` found. Consider using it directly + --> $DIR/approx_const.rs:48:19 + | +LL | let log2_10 = 3.321928094887362; + | ^^^^^^^^^^^^^^^^^ + +error: approximate value of `f{32, 64}::consts::LOG10_2` found. Consider using it directly + --> $DIR/approx_const.rs:51:19 + | +LL | let log10_2 = 0.301029995663981; + | ^^^^^^^^^^^^^^^^^ + +error: approximate value of `f{32, 64}::consts::PI` found. Consider using it directly + --> $DIR/approx_const.rs:54:17 + | +LL | let my_pi = 3.1415; + | ^^^^^^ + +error: approximate value of `f{32, 64}::consts::PI` found. Consider using it directly + --> $DIR/approx_const.rs:55:21 + | +LL | let almost_pi = 3.14; + | ^^^^ + +error: approximate value of `f{32, 64}::consts::SQRT_2` found. Consider using it directly + --> $DIR/approx_const.rs:58:18 + | +LL | let my_sq2 = 1.4142; + | ^^^^^^ + +error: aborting due to 21 previous errors + diff --git a/src/tools/clippy/tests/ui/as_conversions.rs b/src/tools/clippy/tests/ui/as_conversions.rs new file mode 100644 index 00000000000..e01ba0c64df --- /dev/null +++ b/src/tools/clippy/tests/ui/as_conversions.rs @@ -0,0 +1,7 @@ +#[warn(clippy::as_conversions)] + +fn main() { + let i = 0u32 as u64; + + let j = &i as *const u64 as *mut u64; +} diff --git a/src/tools/clippy/tests/ui/as_conversions.stderr b/src/tools/clippy/tests/ui/as_conversions.stderr new file mode 100644 index 00000000000..312d3a7460e --- /dev/null +++ b/src/tools/clippy/tests/ui/as_conversions.stderr @@ -0,0 +1,27 @@ +error: using a potentially dangerous silent `as` conversion + --> $DIR/as_conversions.rs:4:13 + | +LL | let i = 0u32 as u64; + | ^^^^^^^^^^^ + | + = note: `-D clippy::as-conversions` implied by `-D warnings` + = help: consider using a safe wrapper for this conversion + +error: using a potentially dangerous silent `as` conversion + --> $DIR/as_conversions.rs:6:13 + | +LL | let j = &i as *const u64 as *mut u64; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a safe wrapper for this conversion + +error: using a potentially dangerous silent `as` conversion + --> $DIR/as_conversions.rs:6:13 + | +LL | let j = &i as *const u64 as *mut u64; + | ^^^^^^^^^^^^^^^^ + | + = help: consider using a safe wrapper for this conversion + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/assertions_on_constants.rs b/src/tools/clippy/tests/ui/assertions_on_constants.rs new file mode 100644 index 00000000000..60d721c2f20 --- /dev/null +++ b/src/tools/clippy/tests/ui/assertions_on_constants.rs @@ -0,0 +1,29 @@ +macro_rules! assert_const { + ($len:expr) => { + assert!($len > 0); + debug_assert!($len < 0); + }; +} + +fn main() { + assert!(true); + assert!(false); + assert!(true, "true message"); + assert!(false, "false message"); + + let msg = "panic message"; + assert!(false, msg.to_uppercase()); + + const B: bool = true; + assert!(B); + + const C: bool = false; + assert!(C); + assert!(C, "C message"); + + debug_assert!(true); + // Don't lint this, since there is no better way for expressing "Only panic in debug mode". + debug_assert!(false); // #3948 + assert_const!(3); + assert_const!(-1); +} diff --git a/src/tools/clippy/tests/ui/assertions_on_constants.stderr b/src/tools/clippy/tests/ui/assertions_on_constants.stderr new file mode 100644 index 00000000000..8f09c8ce9d5 --- /dev/null +++ b/src/tools/clippy/tests/ui/assertions_on_constants.stderr @@ -0,0 +1,84 @@ +error: `assert!(true)` will be optimized out by the compiler + --> $DIR/assertions_on_constants.rs:9:5 + | +LL | assert!(true); + | ^^^^^^^^^^^^^^ + | + = note: `-D clippy::assertions-on-constants` implied by `-D warnings` + = help: remove it + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: `assert!(false)` should probably be replaced + --> $DIR/assertions_on_constants.rs:10:5 + | +LL | assert!(false); + | ^^^^^^^^^^^^^^^ + | + = help: use `panic!()` or `unreachable!()` + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: `assert!(true)` will be optimized out by the compiler + --> $DIR/assertions_on_constants.rs:11:5 + | +LL | assert!(true, "true message"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: remove it + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: `assert!(false, "false message")` should probably be replaced + --> $DIR/assertions_on_constants.rs:12:5 + | +LL | assert!(false, "false message"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `panic!("false message")` or `unreachable!("false message")` + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: `assert!(false, msg.to_uppercase())` should probably be replaced + --> $DIR/assertions_on_constants.rs:15:5 + | +LL | assert!(false, msg.to_uppercase()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `panic!(msg.to_uppercase())` or `unreachable!(msg.to_uppercase())` + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: `assert!(true)` will be optimized out by the compiler + --> $DIR/assertions_on_constants.rs:18:5 + | +LL | assert!(B); + | ^^^^^^^^^^^ + | + = help: remove it + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: `assert!(false)` should probably be replaced + --> $DIR/assertions_on_constants.rs:21:5 + | +LL | assert!(C); + | ^^^^^^^^^^^ + | + = help: use `panic!()` or `unreachable!()` + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: `assert!(false, "C message")` should probably be replaced + --> $DIR/assertions_on_constants.rs:22:5 + | +LL | assert!(C, "C message"); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `panic!("C message")` or `unreachable!("C message")` + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: `debug_assert!(true)` will be optimized out by the compiler + --> $DIR/assertions_on_constants.rs:24:5 + | +LL | debug_assert!(true); + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: remove it + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 9 previous errors + diff --git a/src/tools/clippy/tests/ui/assign_ops.fixed b/src/tools/clippy/tests/ui/assign_ops.fixed new file mode 100644 index 00000000000..52b1b3afe16 --- /dev/null +++ b/src/tools/clippy/tests/ui/assign_ops.fixed @@ -0,0 +1,21 @@ +// run-rustfix + +#[allow(dead_code, unused_assignments)] +#[warn(clippy::assign_op_pattern)] +fn main() { + let mut a = 5; + a += 1; + a += 1; + a -= 1; + a *= 99; + a *= 42; + a /= 2; + a %= 5; + a &= 1; + a = 1 - a; + a = 5 / a; + a = 42 % a; + a = 6 << a; + let mut s = String::new(); + s += "bla"; +} diff --git a/src/tools/clippy/tests/ui/assign_ops.rs b/src/tools/clippy/tests/ui/assign_ops.rs new file mode 100644 index 00000000000..527a46b2c2b --- /dev/null +++ b/src/tools/clippy/tests/ui/assign_ops.rs @@ -0,0 +1,21 @@ +// run-rustfix + +#[allow(dead_code, unused_assignments)] +#[warn(clippy::assign_op_pattern)] +fn main() { + let mut a = 5; + a = a + 1; + a = 1 + a; + a = a - 1; + a = a * 99; + a = 42 * a; + a = a / 2; + a = a % 5; + a = a & 1; + a = 1 - a; + a = 5 / a; + a = 42 % a; + a = 6 << a; + let mut s = String::new(); + s = s + "bla"; +} diff --git a/src/tools/clippy/tests/ui/assign_ops.stderr b/src/tools/clippy/tests/ui/assign_ops.stderr new file mode 100644 index 00000000000..3486bd8da4d --- /dev/null +++ b/src/tools/clippy/tests/ui/assign_ops.stderr @@ -0,0 +1,58 @@ +error: manual implementation of an assign operation + --> $DIR/assign_ops.rs:7:5 + | +LL | a = a + 1; + | ^^^^^^^^^ help: replace it with: `a += 1` + | + = note: `-D clippy::assign-op-pattern` implied by `-D warnings` + +error: manual implementation of an assign operation + --> $DIR/assign_ops.rs:8:5 + | +LL | a = 1 + a; + | ^^^^^^^^^ help: replace it with: `a += 1` + +error: manual implementation of an assign operation + --> $DIR/assign_ops.rs:9:5 + | +LL | a = a - 1; + | ^^^^^^^^^ help: replace it with: `a -= 1` + +error: manual implementation of an assign operation + --> $DIR/assign_ops.rs:10:5 + | +LL | a = a * 99; + | ^^^^^^^^^^ help: replace it with: `a *= 99` + +error: manual implementation of an assign operation + --> $DIR/assign_ops.rs:11:5 + | +LL | a = 42 * a; + | ^^^^^^^^^^ help: replace it with: `a *= 42` + +error: manual implementation of an assign operation + --> $DIR/assign_ops.rs:12:5 + | +LL | a = a / 2; + | ^^^^^^^^^ help: replace it with: `a /= 2` + +error: manual implementation of an assign operation + --> $DIR/assign_ops.rs:13:5 + | +LL | a = a % 5; + | ^^^^^^^^^ help: replace it with: `a %= 5` + +error: manual implementation of an assign operation + --> $DIR/assign_ops.rs:14:5 + | +LL | a = a & 1; + | ^^^^^^^^^ help: replace it with: `a &= 1` + +error: manual implementation of an assign operation + --> $DIR/assign_ops.rs:20:5 + | +LL | s = s + "bla"; + | ^^^^^^^^^^^^^ help: replace it with: `s += "bla"` + +error: aborting due to 9 previous errors + diff --git a/src/tools/clippy/tests/ui/assign_ops2.rs b/src/tools/clippy/tests/ui/assign_ops2.rs new file mode 100644 index 00000000000..4703a8c7777 --- /dev/null +++ b/src/tools/clippy/tests/ui/assign_ops2.rs @@ -0,0 +1,55 @@ +#[allow(unused_assignments)] +#[warn(clippy::misrefactored_assign_op, clippy::assign_op_pattern)] +fn main() { + let mut a = 5; + a += a + 1; + a += 1 + a; + a -= a - 1; + a *= a * 99; + a *= 42 * a; + a /= a / 2; + a %= a % 5; + a &= a & 1; + a *= a * a; + a = a * a * a; + a = a * 42 * a; + a = a * 2 + a; + a -= 1 - a; + a /= 5 / a; + a %= 42 % a; + a <<= 6 << a; +} + +// check that we don't lint on op assign impls, because that's just the way to impl them + +use std::ops::{Mul, MulAssign}; + +#[derive(Copy, Clone, Debug, PartialEq)] +pub struct Wrap(i64); + +impl Mul<i64> for Wrap { + type Output = Self; + + fn mul(self, rhs: i64) -> Self { + Wrap(self.0 * rhs) + } +} + +impl MulAssign<i64> for Wrap { + fn mul_assign(&mut self, rhs: i64) { + *self = *self * rhs + } +} + +fn cow_add_assign() { + use std::borrow::Cow; + let mut buf = Cow::Owned(String::from("bar")); + let cows = Cow::Borrowed("foo"); + + // this can be linted + buf = buf + cows.clone(); + + // this should not as cow<str> Add is not commutative + buf = cows + buf; + println!("{}", buf); +} diff --git a/src/tools/clippy/tests/ui/assign_ops2.stderr b/src/tools/clippy/tests/ui/assign_ops2.stderr new file mode 100644 index 00000000000..70b15d18a56 --- /dev/null +++ b/src/tools/clippy/tests/ui/assign_ops2.stderr @@ -0,0 +1,146 @@ +error: variable appears on both sides of an assignment operation + --> $DIR/assign_ops2.rs:5:5 + | +LL | a += a + 1; + | ^^^^^^^^^^ + | + = note: `-D clippy::misrefactored-assign-op` implied by `-D warnings` +help: Did you mean `a = a + 1` or `a = a + a + 1`? Consider replacing it with + | +LL | a += 1; + | ^^^^^^ +help: or + | +LL | a = a + a + 1; + | ^^^^^^^^^^^^^ + +error: variable appears on both sides of an assignment operation + --> $DIR/assign_ops2.rs:6:5 + | +LL | a += 1 + a; + | ^^^^^^^^^^ + | +help: Did you mean `a = a + 1` or `a = a + 1 + a`? Consider replacing it with + | +LL | a += 1; + | ^^^^^^ +help: or + | +LL | a = a + 1 + a; + | ^^^^^^^^^^^^^ + +error: variable appears on both sides of an assignment operation + --> $DIR/assign_ops2.rs:7:5 + | +LL | a -= a - 1; + | ^^^^^^^^^^ + | +help: Did you mean `a = a - 1` or `a = a - (a - 1)`? Consider replacing it with + | +LL | a -= 1; + | ^^^^^^ +help: or + | +LL | a = a - (a - 1); + | ^^^^^^^^^^^^^^^ + +error: variable appears on both sides of an assignment operation + --> $DIR/assign_ops2.rs:8:5 + | +LL | a *= a * 99; + | ^^^^^^^^^^^ + | +help: Did you mean `a = a * 99` or `a = a * a * 99`? Consider replacing it with + | +LL | a *= 99; + | ^^^^^^^ +help: or + | +LL | a = a * a * 99; + | ^^^^^^^^^^^^^^ + +error: variable appears on both sides of an assignment operation + --> $DIR/assign_ops2.rs:9:5 + | +LL | a *= 42 * a; + | ^^^^^^^^^^^ + | +help: Did you mean `a = a * 42` or `a = a * 42 * a`? Consider replacing it with + | +LL | a *= 42; + | ^^^^^^^ +help: or + | +LL | a = a * 42 * a; + | ^^^^^^^^^^^^^^ + +error: variable appears on both sides of an assignment operation + --> $DIR/assign_ops2.rs:10:5 + | +LL | a /= a / 2; + | ^^^^^^^^^^ + | +help: Did you mean `a = a / 2` or `a = a / (a / 2)`? Consider replacing it with + | +LL | a /= 2; + | ^^^^^^ +help: or + | +LL | a = a / (a / 2); + | ^^^^^^^^^^^^^^^ + +error: variable appears on both sides of an assignment operation + --> $DIR/assign_ops2.rs:11:5 + | +LL | a %= a % 5; + | ^^^^^^^^^^ + | +help: Did you mean `a = a % 5` or `a = a % (a % 5)`? Consider replacing it with + | +LL | a %= 5; + | ^^^^^^ +help: or + | +LL | a = a % (a % 5); + | ^^^^^^^^^^^^^^^ + +error: variable appears on both sides of an assignment operation + --> $DIR/assign_ops2.rs:12:5 + | +LL | a &= a & 1; + | ^^^^^^^^^^ + | +help: Did you mean `a = a & 1` or `a = a & a & 1`? Consider replacing it with + | +LL | a &= 1; + | ^^^^^^ +help: or + | +LL | a = a & a & 1; + | ^^^^^^^^^^^^^ + +error: variable appears on both sides of an assignment operation + --> $DIR/assign_ops2.rs:13:5 + | +LL | a *= a * a; + | ^^^^^^^^^^ + | +help: Did you mean `a = a * a` or `a = a * a * a`? Consider replacing it with + | +LL | a *= a; + | ^^^^^^ +help: or + | +LL | a = a * a * a; + | ^^^^^^^^^^^^^ + +error: manual implementation of an assign operation + --> $DIR/assign_ops2.rs:50:5 + | +LL | buf = buf + cows.clone(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `buf += cows.clone()` + | + = note: `-D clippy::assign-op-pattern` implied by `-D warnings` + +error: aborting due to 10 previous errors + diff --git a/src/tools/clippy/tests/ui/atomic_ordering_bool.rs b/src/tools/clippy/tests/ui/atomic_ordering_bool.rs new file mode 100644 index 00000000000..cdbde79b19e --- /dev/null +++ b/src/tools/clippy/tests/ui/atomic_ordering_bool.rs @@ -0,0 +1,25 @@ +#![warn(clippy::invalid_atomic_ordering)] + +use std::sync::atomic::{AtomicBool, Ordering}; + +fn main() { + let x = AtomicBool::new(true); + + // Allowed load ordering modes + let _ = x.load(Ordering::Acquire); + let _ = x.load(Ordering::SeqCst); + let _ = x.load(Ordering::Relaxed); + + // Disallowed load ordering modes + let _ = x.load(Ordering::Release); + let _ = x.load(Ordering::AcqRel); + + // Allowed store ordering modes + x.store(false, Ordering::Release); + x.store(false, Ordering::SeqCst); + x.store(false, Ordering::Relaxed); + + // Disallowed store ordering modes + x.store(false, Ordering::Acquire); + x.store(false, Ordering::AcqRel); +} diff --git a/src/tools/clippy/tests/ui/atomic_ordering_bool.stderr b/src/tools/clippy/tests/ui/atomic_ordering_bool.stderr new file mode 100644 index 00000000000..397b893aed9 --- /dev/null +++ b/src/tools/clippy/tests/ui/atomic_ordering_bool.stderr @@ -0,0 +1,35 @@ +error: atomic loads cannot have `Release` and `AcqRel` ordering + --> $DIR/atomic_ordering_bool.rs:14:20 + | +LL | let _ = x.load(Ordering::Release); + | ^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::invalid-atomic-ordering` implied by `-D warnings` + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` + +error: atomic loads cannot have `Release` and `AcqRel` ordering + --> $DIR/atomic_ordering_bool.rs:15:20 + | +LL | let _ = x.load(Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` + +error: atomic stores cannot have `Acquire` and `AcqRel` ordering + --> $DIR/atomic_ordering_bool.rs:23:20 + | +LL | x.store(false, Ordering::Acquire); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` + +error: atomic stores cannot have `Acquire` and `AcqRel` ordering + --> $DIR/atomic_ordering_bool.rs:24:20 + | +LL | x.store(false, Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/atomic_ordering_fence.rs b/src/tools/clippy/tests/ui/atomic_ordering_fence.rs new file mode 100644 index 00000000000..5ee5182ca05 --- /dev/null +++ b/src/tools/clippy/tests/ui/atomic_ordering_fence.rs @@ -0,0 +1,20 @@ +#![warn(clippy::invalid_atomic_ordering)] + +use std::sync::atomic::{compiler_fence, fence, Ordering}; + +fn main() { + // Allowed fence ordering modes + fence(Ordering::Acquire); + fence(Ordering::Release); + fence(Ordering::AcqRel); + fence(Ordering::SeqCst); + + // Disallowed fence ordering modes + fence(Ordering::Relaxed); + + compiler_fence(Ordering::Acquire); + compiler_fence(Ordering::Release); + compiler_fence(Ordering::AcqRel); + compiler_fence(Ordering::SeqCst); + compiler_fence(Ordering::Relaxed); +} diff --git a/src/tools/clippy/tests/ui/atomic_ordering_fence.stderr b/src/tools/clippy/tests/ui/atomic_ordering_fence.stderr new file mode 100644 index 00000000000..3ceff27d9ad --- /dev/null +++ b/src/tools/clippy/tests/ui/atomic_ordering_fence.stderr @@ -0,0 +1,19 @@ +error: memory fences cannot have `Relaxed` ordering + --> $DIR/atomic_ordering_fence.rs:13:11 + | +LL | fence(Ordering::Relaxed); + | ^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::invalid-atomic-ordering` implied by `-D warnings` + = help: consider using ordering modes `Acquire`, `Release`, `AcqRel` or `SeqCst` + +error: memory fences cannot have `Relaxed` ordering + --> $DIR/atomic_ordering_fence.rs:19:20 + | +LL | compiler_fence(Ordering::Relaxed); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire`, `Release`, `AcqRel` or `SeqCst` + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/atomic_ordering_int.rs b/src/tools/clippy/tests/ui/atomic_ordering_int.rs new file mode 100644 index 00000000000..40a00ba3de3 --- /dev/null +++ b/src/tools/clippy/tests/ui/atomic_ordering_int.rs @@ -0,0 +1,86 @@ +#![warn(clippy::invalid_atomic_ordering)] + +use std::sync::atomic::{AtomicI16, AtomicI32, AtomicI64, AtomicI8, AtomicIsize, Ordering}; + +fn main() { + // `AtomicI8` test cases + let x = AtomicI8::new(0); + + // Allowed load ordering modes + let _ = x.load(Ordering::Acquire); + let _ = x.load(Ordering::SeqCst); + let _ = x.load(Ordering::Relaxed); + + // Disallowed load ordering modes + let _ = x.load(Ordering::Release); + let _ = x.load(Ordering::AcqRel); + + // Allowed store ordering modes + x.store(1, Ordering::Release); + x.store(1, Ordering::SeqCst); + x.store(1, Ordering::Relaxed); + + // Disallowed store ordering modes + x.store(1, Ordering::Acquire); + x.store(1, Ordering::AcqRel); + + // `AtomicI16` test cases + let x = AtomicI16::new(0); + + let _ = x.load(Ordering::Acquire); + let _ = x.load(Ordering::SeqCst); + let _ = x.load(Ordering::Relaxed); + let _ = x.load(Ordering::Release); + let _ = x.load(Ordering::AcqRel); + + x.store(1, Ordering::Release); + x.store(1, Ordering::SeqCst); + x.store(1, Ordering::Relaxed); + x.store(1, Ordering::Acquire); + x.store(1, Ordering::AcqRel); + + // `AtomicI32` test cases + let x = AtomicI32::new(0); + + let _ = x.load(Ordering::Acquire); + let _ = x.load(Ordering::SeqCst); + let _ = x.load(Ordering::Relaxed); + let _ = x.load(Ordering::Release); + let _ = x.load(Ordering::AcqRel); + + x.store(1, Ordering::Release); + x.store(1, Ordering::SeqCst); + x.store(1, Ordering::Relaxed); + x.store(1, Ordering::Acquire); + x.store(1, Ordering::AcqRel); + + // `AtomicI64` test cases + let x = AtomicI64::new(0); + + let _ = x.load(Ordering::Acquire); + let _ = x.load(Ordering::SeqCst); + let _ = x.load(Ordering::Relaxed); + let _ = x.load(Ordering::Release); + let _ = x.load(Ordering::AcqRel); + + x.store(1, Ordering::Release); + x.store(1, Ordering::SeqCst); + x.store(1, Ordering::Relaxed); + x.store(1, Ordering::Acquire); + x.store(1, Ordering::AcqRel); + + // `AtomicIsize` test cases + let x = AtomicIsize::new(0); + + let _ = x.load(Ordering::Acquire); + let _ = x.load(Ordering::SeqCst); + let _ = x.load(Ordering::Relaxed); + let _ = x.load(Ordering::Release); + let _ = x.load(Ordering::AcqRel); + + x.store(1, Ordering::Release); + x.store(1, Ordering::SeqCst); + x.store(1, Ordering::Relaxed); + x.store(1, Ordering::Acquire); + x.store(1, Ordering::AcqRel); +} diff --git a/src/tools/clippy/tests/ui/atomic_ordering_int.stderr b/src/tools/clippy/tests/ui/atomic_ordering_int.stderr new file mode 100644 index 00000000000..bbaf234d3c9 --- /dev/null +++ b/src/tools/clippy/tests/ui/atomic_ordering_int.stderr @@ -0,0 +1,163 @@ +error: atomic loads cannot have `Release` and `AcqRel` ordering + --> $DIR/atomic_ordering_int.rs:15:20 + | +LL | let _ = x.load(Ordering::Release); + | ^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::invalid-atomic-ordering` implied by `-D warnings` + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` + +error: atomic loads cannot have `Release` and `AcqRel` ordering + --> $DIR/atomic_ordering_int.rs:16:20 + | +LL | let _ = x.load(Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` + +error: atomic stores cannot have `Acquire` and `AcqRel` ordering + --> $DIR/atomic_ordering_int.rs:24:16 + | +LL | x.store(1, Ordering::Acquire); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` + +error: atomic stores cannot have `Acquire` and `AcqRel` ordering + --> $DIR/atomic_ordering_int.rs:25:16 + | +LL | x.store(1, Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` + +error: atomic loads cannot have `Release` and `AcqRel` ordering + --> $DIR/atomic_ordering_int.rs:33:20 + | +LL | let _ = x.load(Ordering::Release); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` + +error: atomic loads cannot have `Release` and `AcqRel` ordering + --> $DIR/atomic_ordering_int.rs:34:20 + | +LL | let _ = x.load(Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` + +error: atomic stores cannot have `Acquire` and `AcqRel` ordering + --> $DIR/atomic_ordering_int.rs:39:16 + | +LL | x.store(1, Ordering::Acquire); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` + +error: atomic stores cannot have `Acquire` and `AcqRel` ordering + --> $DIR/atomic_ordering_int.rs:40:16 + | +LL | x.store(1, Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` + +error: atomic loads cannot have `Release` and `AcqRel` ordering + --> $DIR/atomic_ordering_int.rs:48:20 + | +LL | let _ = x.load(Ordering::Release); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` + +error: atomic loads cannot have `Release` and `AcqRel` ordering + --> $DIR/atomic_ordering_int.rs:49:20 + | +LL | let _ = x.load(Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` + +error: atomic stores cannot have `Acquire` and `AcqRel` ordering + --> $DIR/atomic_ordering_int.rs:54:16 + | +LL | x.store(1, Ordering::Acquire); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` + +error: atomic stores cannot have `Acquire` and `AcqRel` ordering + --> $DIR/atomic_ordering_int.rs:55:16 + | +LL | x.store(1, Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` + +error: atomic loads cannot have `Release` and `AcqRel` ordering + --> $DIR/atomic_ordering_int.rs:63:20 + | +LL | let _ = x.load(Ordering::Release); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` + +error: atomic loads cannot have `Release` and `AcqRel` ordering + --> $DIR/atomic_ordering_int.rs:64:20 + | +LL | let _ = x.load(Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` + +error: atomic stores cannot have `Acquire` and `AcqRel` ordering + --> $DIR/atomic_ordering_int.rs:69:16 + | +LL | x.store(1, Ordering::Acquire); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` + +error: atomic stores cannot have `Acquire` and `AcqRel` ordering + --> $DIR/atomic_ordering_int.rs:70:16 + | +LL | x.store(1, Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` + +error: atomic loads cannot have `Release` and `AcqRel` ordering + --> $DIR/atomic_ordering_int.rs:78:20 + | +LL | let _ = x.load(Ordering::Release); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` + +error: atomic loads cannot have `Release` and `AcqRel` ordering + --> $DIR/atomic_ordering_int.rs:79:20 + | +LL | let _ = x.load(Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` + +error: atomic stores cannot have `Acquire` and `AcqRel` ordering + --> $DIR/atomic_ordering_int.rs:84:16 + | +LL | x.store(1, Ordering::Acquire); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` + +error: atomic stores cannot have `Acquire` and `AcqRel` ordering + --> $DIR/atomic_ordering_int.rs:85:16 + | +LL | x.store(1, Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` + +error: aborting due to 20 previous errors + diff --git a/src/tools/clippy/tests/ui/atomic_ordering_ptr.rs b/src/tools/clippy/tests/ui/atomic_ordering_ptr.rs new file mode 100644 index 00000000000..ecbb05c7fbc --- /dev/null +++ b/src/tools/clippy/tests/ui/atomic_ordering_ptr.rs @@ -0,0 +1,27 @@ +#![warn(clippy::invalid_atomic_ordering)] + +use std::sync::atomic::{AtomicPtr, Ordering}; + +fn main() { + let ptr = &mut 5; + let other_ptr = &mut 10; + let x = AtomicPtr::new(ptr); + + // Allowed load ordering modes + let _ = x.load(Ordering::Acquire); + let _ = x.load(Ordering::SeqCst); + let _ = x.load(Ordering::Relaxed); + + // Disallowed load ordering modes + let _ = x.load(Ordering::Release); + let _ = x.load(Ordering::AcqRel); + + // Allowed store ordering modes + x.store(other_ptr, Ordering::Release); + x.store(other_ptr, Ordering::SeqCst); + x.store(other_ptr, Ordering::Relaxed); + + // Disallowed store ordering modes + x.store(other_ptr, Ordering::Acquire); + x.store(other_ptr, Ordering::AcqRel); +} diff --git a/src/tools/clippy/tests/ui/atomic_ordering_ptr.stderr b/src/tools/clippy/tests/ui/atomic_ordering_ptr.stderr new file mode 100644 index 00000000000..558ae55518d --- /dev/null +++ b/src/tools/clippy/tests/ui/atomic_ordering_ptr.stderr @@ -0,0 +1,35 @@ +error: atomic loads cannot have `Release` and `AcqRel` ordering + --> $DIR/atomic_ordering_ptr.rs:16:20 + | +LL | let _ = x.load(Ordering::Release); + | ^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::invalid-atomic-ordering` implied by `-D warnings` + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` + +error: atomic loads cannot have `Release` and `AcqRel` ordering + --> $DIR/atomic_ordering_ptr.rs:17:20 + | +LL | let _ = x.load(Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` + +error: atomic stores cannot have `Acquire` and `AcqRel` ordering + --> $DIR/atomic_ordering_ptr.rs:25:24 + | +LL | x.store(other_ptr, Ordering::Acquire); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` + +error: atomic stores cannot have `Acquire` and `AcqRel` ordering + --> $DIR/atomic_ordering_ptr.rs:26:24 + | +LL | x.store(other_ptr, Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/atomic_ordering_uint.rs b/src/tools/clippy/tests/ui/atomic_ordering_uint.rs new file mode 100644 index 00000000000..a0d5d7c4010 --- /dev/null +++ b/src/tools/clippy/tests/ui/atomic_ordering_uint.rs @@ -0,0 +1,86 @@ +#![warn(clippy::invalid_atomic_ordering)] + +use std::sync::atomic::{AtomicU16, AtomicU32, AtomicU64, AtomicU8, AtomicUsize, Ordering}; + +fn main() { + // `AtomicU8` test cases + let x = AtomicU8::new(0); + + // Allowed load ordering modes + let _ = x.load(Ordering::Acquire); + let _ = x.load(Ordering::SeqCst); + let _ = x.load(Ordering::Relaxed); + + // Disallowed load ordering modes + let _ = x.load(Ordering::Release); + let _ = x.load(Ordering::AcqRel); + + // Allowed store ordering modes + x.store(1, Ordering::Release); + x.store(1, Ordering::SeqCst); + x.store(1, Ordering::Relaxed); + + // Disallowed store ordering modes + x.store(1, Ordering::Acquire); + x.store(1, Ordering::AcqRel); + + // `AtomicU16` test cases + let x = AtomicU16::new(0); + + let _ = x.load(Ordering::Acquire); + let _ = x.load(Ordering::SeqCst); + let _ = x.load(Ordering::Relaxed); + let _ = x.load(Ordering::Release); + let _ = x.load(Ordering::AcqRel); + + x.store(1, Ordering::Release); + x.store(1, Ordering::SeqCst); + x.store(1, Ordering::Relaxed); + x.store(1, Ordering::Acquire); + x.store(1, Ordering::AcqRel); + + // `AtomicU32` test cases + let x = AtomicU32::new(0); + + let _ = x.load(Ordering::Acquire); + let _ = x.load(Ordering::SeqCst); + let _ = x.load(Ordering::Relaxed); + let _ = x.load(Ordering::Release); + let _ = x.load(Ordering::AcqRel); + + x.store(1, Ordering::Release); + x.store(1, Ordering::SeqCst); + x.store(1, Ordering::Relaxed); + x.store(1, Ordering::Acquire); + x.store(1, Ordering::AcqRel); + + // `AtomicU64` test cases + let x = AtomicU64::new(0); + + let _ = x.load(Ordering::Acquire); + let _ = x.load(Ordering::SeqCst); + let _ = x.load(Ordering::Relaxed); + let _ = x.load(Ordering::Release); + let _ = x.load(Ordering::AcqRel); + + x.store(1, Ordering::Release); + x.store(1, Ordering::SeqCst); + x.store(1, Ordering::Relaxed); + x.store(1, Ordering::Acquire); + x.store(1, Ordering::AcqRel); + + // `AtomicUsize` test cases + let x = AtomicUsize::new(0); + + let _ = x.load(Ordering::Acquire); + let _ = x.load(Ordering::SeqCst); + let _ = x.load(Ordering::Relaxed); + let _ = x.load(Ordering::Release); + let _ = x.load(Ordering::AcqRel); + + x.store(1, Ordering::Release); + x.store(1, Ordering::SeqCst); + x.store(1, Ordering::Relaxed); + x.store(1, Ordering::Acquire); + x.store(1, Ordering::AcqRel); +} diff --git a/src/tools/clippy/tests/ui/atomic_ordering_uint.stderr b/src/tools/clippy/tests/ui/atomic_ordering_uint.stderr new file mode 100644 index 00000000000..5703135bcf1 --- /dev/null +++ b/src/tools/clippy/tests/ui/atomic_ordering_uint.stderr @@ -0,0 +1,163 @@ +error: atomic loads cannot have `Release` and `AcqRel` ordering + --> $DIR/atomic_ordering_uint.rs:15:20 + | +LL | let _ = x.load(Ordering::Release); + | ^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::invalid-atomic-ordering` implied by `-D warnings` + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` + +error: atomic loads cannot have `Release` and `AcqRel` ordering + --> $DIR/atomic_ordering_uint.rs:16:20 + | +LL | let _ = x.load(Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` + +error: atomic stores cannot have `Acquire` and `AcqRel` ordering + --> $DIR/atomic_ordering_uint.rs:24:16 + | +LL | x.store(1, Ordering::Acquire); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` + +error: atomic stores cannot have `Acquire` and `AcqRel` ordering + --> $DIR/atomic_ordering_uint.rs:25:16 + | +LL | x.store(1, Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` + +error: atomic loads cannot have `Release` and `AcqRel` ordering + --> $DIR/atomic_ordering_uint.rs:33:20 + | +LL | let _ = x.load(Ordering::Release); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` + +error: atomic loads cannot have `Release` and `AcqRel` ordering + --> $DIR/atomic_ordering_uint.rs:34:20 + | +LL | let _ = x.load(Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` + +error: atomic stores cannot have `Acquire` and `AcqRel` ordering + --> $DIR/atomic_ordering_uint.rs:39:16 + | +LL | x.store(1, Ordering::Acquire); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` + +error: atomic stores cannot have `Acquire` and `AcqRel` ordering + --> $DIR/atomic_ordering_uint.rs:40:16 + | +LL | x.store(1, Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` + +error: atomic loads cannot have `Release` and `AcqRel` ordering + --> $DIR/atomic_ordering_uint.rs:48:20 + | +LL | let _ = x.load(Ordering::Release); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` + +error: atomic loads cannot have `Release` and `AcqRel` ordering + --> $DIR/atomic_ordering_uint.rs:49:20 + | +LL | let _ = x.load(Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` + +error: atomic stores cannot have `Acquire` and `AcqRel` ordering + --> $DIR/atomic_ordering_uint.rs:54:16 + | +LL | x.store(1, Ordering::Acquire); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` + +error: atomic stores cannot have `Acquire` and `AcqRel` ordering + --> $DIR/atomic_ordering_uint.rs:55:16 + | +LL | x.store(1, Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` + +error: atomic loads cannot have `Release` and `AcqRel` ordering + --> $DIR/atomic_ordering_uint.rs:63:20 + | +LL | let _ = x.load(Ordering::Release); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` + +error: atomic loads cannot have `Release` and `AcqRel` ordering + --> $DIR/atomic_ordering_uint.rs:64:20 + | +LL | let _ = x.load(Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` + +error: atomic stores cannot have `Acquire` and `AcqRel` ordering + --> $DIR/atomic_ordering_uint.rs:69:16 + | +LL | x.store(1, Ordering::Acquire); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` + +error: atomic stores cannot have `Acquire` and `AcqRel` ordering + --> $DIR/atomic_ordering_uint.rs:70:16 + | +LL | x.store(1, Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` + +error: atomic loads cannot have `Release` and `AcqRel` ordering + --> $DIR/atomic_ordering_uint.rs:78:20 + | +LL | let _ = x.load(Ordering::Release); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` + +error: atomic loads cannot have `Release` and `AcqRel` ordering + --> $DIR/atomic_ordering_uint.rs:79:20 + | +LL | let _ = x.load(Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` + +error: atomic stores cannot have `Acquire` and `AcqRel` ordering + --> $DIR/atomic_ordering_uint.rs:84:16 + | +LL | x.store(1, Ordering::Acquire); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` + +error: atomic stores cannot have `Acquire` and `AcqRel` ordering + --> $DIR/atomic_ordering_uint.rs:85:16 + | +LL | x.store(1, Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Release`, `SeqCst` or `Relaxed` + +error: aborting due to 20 previous errors + diff --git a/src/tools/clippy/tests/ui/attrs.rs b/src/tools/clippy/tests/ui/attrs.rs new file mode 100644 index 00000000000..91b65a43be7 --- /dev/null +++ b/src/tools/clippy/tests/ui/attrs.rs @@ -0,0 +1,43 @@ +#![warn(clippy::inline_always, clippy::deprecated_semver)] +#![allow(clippy::assertions_on_constants)] +#[inline(always)] +fn test_attr_lint() { + assert!(true) +} + +#[inline(always)] +fn false_positive_expr() { + unreachable!() +} + +#[inline(always)] +fn false_positive_stmt() { + unreachable!(); +} + +#[inline(always)] +fn empty_and_false_positive_stmt() { + unreachable!(); +} + +#[deprecated(since = "forever")] +pub const SOME_CONST: u8 = 42; + +#[deprecated(since = "1")] +pub const ANOTHER_CONST: u8 = 23; + +#[deprecated(since = "0.1.1")] +pub const YET_ANOTHER_CONST: u8 = 0; + +fn main() { + test_attr_lint(); + if false { + false_positive_expr() + } + if false { + false_positive_stmt() + } + if false { + empty_and_false_positive_stmt() + } +} diff --git a/src/tools/clippy/tests/ui/attrs.stderr b/src/tools/clippy/tests/ui/attrs.stderr new file mode 100644 index 00000000000..39ddf6f226d --- /dev/null +++ b/src/tools/clippy/tests/ui/attrs.stderr @@ -0,0 +1,24 @@ +error: you have declared `#[inline(always)]` on `test_attr_lint`. This is usually a bad idea + --> $DIR/attrs.rs:3:1 + | +LL | #[inline(always)] + | ^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::inline-always` implied by `-D warnings` + +error: the since field must contain a semver-compliant version + --> $DIR/attrs.rs:23:14 + | +LL | #[deprecated(since = "forever")] + | ^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::deprecated-semver` implied by `-D warnings` + +error: the since field must contain a semver-compliant version + --> $DIR/attrs.rs:26:14 + | +LL | #[deprecated(since = "1")] + | ^^^^^^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/author.rs b/src/tools/clippy/tests/ui/author.rs new file mode 100644 index 00000000000..0a1be356896 --- /dev/null +++ b/src/tools/clippy/tests/ui/author.rs @@ -0,0 +1,4 @@ +fn main() { + #[clippy::author] + let x: char = 0x45 as char; +} diff --git a/src/tools/clippy/tests/ui/author.stdout b/src/tools/clippy/tests/ui/author.stdout new file mode 100644 index 00000000000..211d11c7c1a --- /dev/null +++ b/src/tools/clippy/tests/ui/author.stdout @@ -0,0 +1,14 @@ +if_chain! { + if let StmtKind::Local(ref local) = stmt.kind; + if let Some(ref init) = local.init; + if let ExprKind::Cast(ref expr, ref cast_ty) = init.kind; + if let TyKind::Path(ref qp) = cast_ty.kind; + if match_qpath(qp, &["char"]); + if let ExprKind::Lit(ref lit) = expr.kind; + if let LitKind::Int(69, _) = lit.node; + if let PatKind::Binding(BindingAnnotation::Unannotated, _, name, None) = local.pat.kind; + if name.as_str() == "x"; + then { + // report your lint here + } +} diff --git a/src/tools/clippy/tests/ui/author/blocks.rs b/src/tools/clippy/tests/ui/author/blocks.rs new file mode 100644 index 00000000000..a8068436b70 --- /dev/null +++ b/src/tools/clippy/tests/ui/author/blocks.rs @@ -0,0 +1,16 @@ +#![feature(stmt_expr_attributes)] +#![allow(redundant_semicolons, clippy::no_effect)] + +#[rustfmt::skip] +fn main() { + #[clippy::author] + { + ;;;; + } +} + +#[clippy::author] +fn foo() { + let x = 42i32; + -x; +} diff --git a/src/tools/clippy/tests/ui/author/blocks.stdout b/src/tools/clippy/tests/ui/author/blocks.stdout new file mode 100644 index 00000000000..c8ef75e48fc --- /dev/null +++ b/src/tools/clippy/tests/ui/author/blocks.stdout @@ -0,0 +1,13 @@ +if_chain! { + if let ExprKind::Block(ref block) = expr.kind; + if let Some(trailing_expr) = &block.expr; + if block.stmts.len() == 0; + then { + // report your lint here + } +} +if_chain! { + then { + // report your lint here + } +} diff --git a/src/tools/clippy/tests/ui/author/call.rs b/src/tools/clippy/tests/ui/author/call.rs new file mode 100644 index 00000000000..e99c3c41dc4 --- /dev/null +++ b/src/tools/clippy/tests/ui/author/call.rs @@ -0,0 +1,4 @@ +fn main() { + #[clippy::author] + let _ = ::std::cmp::min(3, 4); +} diff --git a/src/tools/clippy/tests/ui/author/call.stdout b/src/tools/clippy/tests/ui/author/call.stdout new file mode 100644 index 00000000000..4dccf666631 --- /dev/null +++ b/src/tools/clippy/tests/ui/author/call.stdout @@ -0,0 +1,16 @@ +if_chain! { + if let StmtKind::Local(ref local) = stmt.kind; + if let Some(ref init) = local.init; + if let ExprKind::Call(ref func, ref args) = init.kind; + if let ExprKind::Path(ref path) = func.kind; + if match_qpath(path, &["{{root}}", "std", "cmp", "min"]); + if args.len() == 2; + if let ExprKind::Lit(ref lit) = args[0].kind; + if let LitKind::Int(3, _) = lit.node; + if let ExprKind::Lit(ref lit1) = args[1].kind; + if let LitKind::Int(4, _) = lit1.node; + if let PatKind::Wild = local.pat.kind; + then { + // report your lint here + } +} diff --git a/src/tools/clippy/tests/ui/author/for_loop.rs b/src/tools/clippy/tests/ui/author/for_loop.rs new file mode 100644 index 00000000000..b3dec876535 --- /dev/null +++ b/src/tools/clippy/tests/ui/author/for_loop.rs @@ -0,0 +1,8 @@ +#![feature(stmt_expr_attributes)] + +fn main() { + #[clippy::author] + for y in 0..10 { + let z = y; + } +} diff --git a/src/tools/clippy/tests/ui/author/for_loop.stdout b/src/tools/clippy/tests/ui/author/for_loop.stdout new file mode 100644 index 00000000000..81ede955347 --- /dev/null +++ b/src/tools/clippy/tests/ui/author/for_loop.stdout @@ -0,0 +1,62 @@ +if_chain! { + if let ExprKind::DropTemps(ref expr) = expr.kind; + if let ExprKind::Match(ref expr1, ref arms, MatchSource::ForLoopDesugar) = expr.kind; + if let ExprKind::Call(ref func, ref args) = expr1.kind; + if let ExprKind::Path(ref path) = func.kind; + if match_qpath(path, &["{{root}}", "std", "iter", "IntoIterator", "into_iter"]); + if args.len() == 1; + if let ExprKind::Struct(ref path1, ref fields, None) = args[0].kind; + if match_qpath(path1, &["{{root}}", "std", "ops", "Range"]); + if fields.len() == 2; + // unimplemented: field checks + if arms.len() == 1; + if let ExprKind::Loop(ref body, ref label, LoopSource::ForLoop) = arms[0].body.kind; + if let Some(trailing_expr) = &body.expr; + if body.stmts.len() == 4; + if let StmtKind::Local(ref local) = body.stmts[0].kind; + if let PatKind::Binding(BindingAnnotation::Mutable, _, name, None) = local.pat.kind; + if name.as_str() == "__next"; + if let StmtKind::Expr(ref e, _) = body.stmts[1].kind + if let ExprKind::Match(ref expr2, ref arms1, MatchSource::ForLoopDesugar) = e.kind; + if let ExprKind::Call(ref func1, ref args1) = expr2.kind; + if let ExprKind::Path(ref path2) = func1.kind; + if match_qpath(path2, &["{{root}}", "std", "iter", "Iterator", "next"]); + if args1.len() == 1; + if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, ref inner) = args1[0].kind; + if let ExprKind::Path(ref path3) = inner.kind; + if match_qpath(path3, &["iter"]); + if arms1.len() == 2; + if let ExprKind::Assign(ref target, ref value, ref _span) = arms1[0].body.kind; + if let ExprKind::Path(ref path4) = target.kind; + if match_qpath(path4, &["__next"]); + if let ExprKind::Path(ref path5) = value.kind; + if match_qpath(path5, &["val"]); + if let PatKind::TupleStruct(ref path6, ref fields1, None) = arms1[0].pat.kind; + if match_qpath(path6, &["{{root}}", "std", "option", "Option", "Some"]); + if fields1.len() == 1; + // unimplemented: field checks + if let ExprKind::Break(ref destination, None) = arms1[1].body.kind; + if let PatKind::Path(ref path7) = arms1[1].pat.kind; + if match_qpath(path7, &["{{root}}", "std", "option", "Option", "None"]); + if let StmtKind::Local(ref local1) = body.stmts[2].kind; + if let Some(ref init) = local1.init; + if let ExprKind::Path(ref path8) = init.kind; + if match_qpath(path8, &["__next"]); + if let PatKind::Binding(BindingAnnotation::Unannotated, _, name1, None) = local1.pat.kind; + if name1.as_str() == "y"; + if let StmtKind::Expr(ref e1, _) = body.stmts[3].kind + if let ExprKind::Block(ref block) = e1.kind; + if let Some(trailing_expr1) = &block.expr; + if block.stmts.len() == 1; + if let StmtKind::Local(ref local2) = block.stmts[0].kind; + if let Some(ref init1) = local2.init; + if let ExprKind::Path(ref path9) = init1.kind; + if match_qpath(path9, &["y"]); + if let PatKind::Binding(BindingAnnotation::Unannotated, _, name2, None) = local2.pat.kind; + if name2.as_str() == "z"; + if let PatKind::Binding(BindingAnnotation::Mutable, _, name3, None) = arms[0].pat.kind; + if name3.as_str() == "iter"; + then { + // report your lint here + } +} diff --git a/src/tools/clippy/tests/ui/author/if.rs b/src/tools/clippy/tests/ui/author/if.rs new file mode 100644 index 00000000000..2e9cb1466d0 --- /dev/null +++ b/src/tools/clippy/tests/ui/author/if.rs @@ -0,0 +1,10 @@ +#[allow(clippy::all)] + +fn main() { + #[clippy::author] + let _ = if true { + 1 == 1; + } else { + 2 == 2; + }; +} diff --git a/src/tools/clippy/tests/ui/author/if.stdout b/src/tools/clippy/tests/ui/author/if.stdout new file mode 100644 index 00000000000..c18d035953e --- /dev/null +++ b/src/tools/clippy/tests/ui/author/if.stdout @@ -0,0 +1,31 @@ +if_chain! { + if let StmtKind::Local(ref local) = stmt.kind; + if let Some(ref init) = local.init; + if let Some((ref cond, ref then, Some(else_))) = higher::if_block(&init); + if let ExprKind::Block(ref block) = else_.kind; + if let Some(trailing_expr) = &block.expr; + if block.stmts.len() == 1; + if let StmtKind::Semi(ref e, _) = block.stmts[0].kind + if let ExprKind::Binary(ref op, ref left, ref right) = e.kind; + if BinOpKind::Eq == op.node; + if let ExprKind::Lit(ref lit) = left.kind; + if let LitKind::Int(2, _) = lit.node; + if let ExprKind::Lit(ref lit1) = right.kind; + if let LitKind::Int(2, _) = lit1.node; + if let ExprKind::Lit(ref lit2) = cond.kind; + if let LitKind::Bool(true) = lit2.node; + if let ExprKind::Block(ref block1) = then.kind; + if let Some(trailing_expr1) = &block1.expr; + if block1.stmts.len() == 1; + if let StmtKind::Semi(ref e1, _) = block1.stmts[0].kind + if let ExprKind::Binary(ref op1, ref left1, ref right1) = e1.kind; + if BinOpKind::Eq == op1.node; + if let ExprKind::Lit(ref lit3) = left1.kind; + if let LitKind::Int(1, _) = lit3.node; + if let ExprKind::Lit(ref lit4) = right1.kind; + if let LitKind::Int(1, _) = lit4.node; + if let PatKind::Wild = local.pat.kind; + then { + // report your lint here + } +} diff --git a/src/tools/clippy/tests/ui/author/issue_3849.rs b/src/tools/clippy/tests/ui/author/issue_3849.rs new file mode 100644 index 00000000000..bae4570e539 --- /dev/null +++ b/src/tools/clippy/tests/ui/author/issue_3849.rs @@ -0,0 +1,14 @@ +#![allow(dead_code)] +#![allow(clippy::zero_ptr)] +#![allow(clippy::transmute_ptr_to_ref)] +#![allow(clippy::transmuting_null)] + +pub const ZPTR: *const usize = 0 as *const _; + +fn main() { + unsafe { + #[clippy::author] + let _: &i32 = std::mem::transmute(ZPTR); + let _: &i32 = std::mem::transmute(0 as *const i32); + } +} diff --git a/src/tools/clippy/tests/ui/author/issue_3849.stdout b/src/tools/clippy/tests/ui/author/issue_3849.stdout new file mode 100644 index 00000000000..65f93f3cdc0 --- /dev/null +++ b/src/tools/clippy/tests/ui/author/issue_3849.stdout @@ -0,0 +1,14 @@ +if_chain! { + if let StmtKind::Local(ref local) = stmt.kind; + if let Some(ref init) = local.init; + if let ExprKind::Call(ref func, ref args) = init.kind; + if let ExprKind::Path(ref path) = func.kind; + if match_qpath(path, &["std", "mem", "transmute"]); + if args.len() == 1; + if let ExprKind::Path(ref path1) = args[0].kind; + if match_qpath(path1, &["ZPTR"]); + if let PatKind::Wild = local.pat.kind; + then { + // report your lint here + } +} diff --git a/src/tools/clippy/tests/ui/author/matches.rs b/src/tools/clippy/tests/ui/author/matches.rs new file mode 100644 index 00000000000..674e07ec2d3 --- /dev/null +++ b/src/tools/clippy/tests/ui/author/matches.rs @@ -0,0 +1,13 @@ +#![allow(clippy::let_and_return)] + +fn main() { + #[clippy::author] + let a = match 42 { + 16 => 5, + 17 => { + let x = 3; + x + }, + _ => 1, + }; +} diff --git a/src/tools/clippy/tests/ui/author/matches.stdout b/src/tools/clippy/tests/ui/author/matches.stdout new file mode 100644 index 00000000000..2e8f8227dca --- /dev/null +++ b/src/tools/clippy/tests/ui/author/matches.stdout @@ -0,0 +1,33 @@ +if_chain! { + if let StmtKind::Local(ref local) = stmt.kind; + if let Some(ref init) = local.init; + if let ExprKind::Match(ref expr, ref arms, MatchSource::Normal) = init.kind; + if let ExprKind::Lit(ref lit) = expr.kind; + if let LitKind::Int(42, _) = lit.node; + if arms.len() == 3; + if let ExprKind::Lit(ref lit1) = arms[0].body.kind; + if let LitKind::Int(5, _) = lit1.node; + if let PatKind::Lit(ref lit_expr) = arms[0].pat.kind + if let ExprKind::Lit(ref lit2) = lit_expr.kind; + if let LitKind::Int(16, _) = lit2.node; + if let ExprKind::Block(ref block) = arms[1].body.kind; + if let Some(trailing_expr) = &block.expr; + if block.stmts.len() == 1; + if let StmtKind::Local(ref local1) = block.stmts[0].kind; + if let Some(ref init1) = local1.init; + if let ExprKind::Lit(ref lit3) = init1.kind; + if let LitKind::Int(3, _) = lit3.node; + if let PatKind::Binding(BindingAnnotation::Unannotated, _, name, None) = local1.pat.kind; + if name.as_str() == "x"; + if let PatKind::Lit(ref lit_expr1) = arms[1].pat.kind + if let ExprKind::Lit(ref lit4) = lit_expr1.kind; + if let LitKind::Int(17, _) = lit4.node; + if let ExprKind::Lit(ref lit5) = arms[2].body.kind; + if let LitKind::Int(1, _) = lit5.node; + if let PatKind::Wild = arms[2].pat.kind; + if let PatKind::Binding(BindingAnnotation::Unannotated, _, name1, None) = local.pat.kind; + if name1.as_str() == "a"; + then { + // report your lint here + } +} diff --git a/src/tools/clippy/tests/ui/auxiliary/doc_unsafe_macros.rs b/src/tools/clippy/tests/ui/auxiliary/doc_unsafe_macros.rs new file mode 100644 index 00000000000..869672d1eda --- /dev/null +++ b/src/tools/clippy/tests/ui/auxiliary/doc_unsafe_macros.rs @@ -0,0 +1,8 @@ +#[macro_export] +macro_rules! undocd_unsafe { + () => { + pub unsafe fn oy_vey() { + unimplemented!(); + } + }; +} diff --git a/src/tools/clippy/tests/ui/auxiliary/implicit_hasher_macros.rs b/src/tools/clippy/tests/ui/auxiliary/implicit_hasher_macros.rs new file mode 100644 index 00000000000..1eb77c53183 --- /dev/null +++ b/src/tools/clippy/tests/ui/auxiliary/implicit_hasher_macros.rs @@ -0,0 +1,6 @@ +#[macro_export] +macro_rules! implicit_hasher_fn { + () => { + pub fn f(input: &HashMap<u32, u32>) {} + }; +} diff --git a/src/tools/clippy/tests/ui/auxiliary/macro_rules.rs b/src/tools/clippy/tests/ui/auxiliary/macro_rules.rs new file mode 100644 index 00000000000..0bbb9534928 --- /dev/null +++ b/src/tools/clippy/tests/ui/auxiliary/macro_rules.rs @@ -0,0 +1,58 @@ +#![allow(dead_code)] + +//! Used to test that certain lints don't trigger in imported external macros + +#[macro_export] +macro_rules! foofoo { + () => { + loop {} + }; +} + +#[macro_export] +macro_rules! must_use_unit { + () => { + #[must_use] + fn foo() {} + }; +} + +#[macro_export] +macro_rules! try_err { + () => { + pub fn try_err_fn() -> Result<i32, i32> { + let err: i32 = 1; + // To avoid warnings during rustfix + if true { + Err(err)? + } else { + Ok(2) + } + } + }; +} + +#[macro_export] +macro_rules! string_add { + () => { + let y = "".to_owned(); + let z = y + "..."; + }; +} + +#[macro_export] +macro_rules! take_external { + ($s:expr) => { + std::mem::replace($s, Default::default()) + }; +} + +#[macro_export] +macro_rules! option_env_unwrap_external { + ($env: expr) => { + option_env!($env).unwrap() + }; + ($env: expr, $message: expr) => { + option_env!($env).expect($message) + }; +} diff --git a/src/tools/clippy/tests/ui/auxiliary/option_helpers.rs b/src/tools/clippy/tests/ui/auxiliary/option_helpers.rs new file mode 100644 index 00000000000..ed11c41e21c --- /dev/null +++ b/src/tools/clippy/tests/ui/auxiliary/option_helpers.rs @@ -0,0 +1,51 @@ +#![allow(dead_code, unused_variables)] + +/// Utility macro to test linting behavior in `option_methods()` +/// The lints included in `option_methods()` should not lint if the call to map is partially +/// within a macro +#[macro_export] +macro_rules! opt_map { + ($opt:expr, $map:expr) => { + ($opt).map($map) + }; +} + +/// Struct to generate false positive for Iterator-based lints +#[derive(Copy, Clone)] +pub struct IteratorFalsePositives { + pub foo: u32, +} + +impl IteratorFalsePositives { + pub fn filter(self) -> IteratorFalsePositives { + self + } + + pub fn next(self) -> IteratorFalsePositives { + self + } + + pub fn find(self) -> Option<u32> { + Some(self.foo) + } + + pub fn position(self) -> Option<u32> { + Some(self.foo) + } + + pub fn rposition(self) -> Option<u32> { + Some(self.foo) + } + + pub fn nth(self, n: usize) -> Option<u32> { + Some(self.foo) + } + + pub fn skip(self, _: usize) -> IteratorFalsePositives { + self + } + + pub fn skip_while(self) -> IteratorFalsePositives { + self + } +} diff --git a/src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs b/src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs new file mode 100644 index 00000000000..21bb5b01e02 --- /dev/null +++ b/src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs @@ -0,0 +1,22 @@ +// no-prefer-dynamic + +#![crate_type = "proc-macro"] +#![feature(repr128, proc_macro_hygiene, proc_macro_quote)] + +extern crate proc_macro; + +use proc_macro::{quote, TokenStream}; + +#[proc_macro_derive(DeriveSomething)] +pub fn derive(_: TokenStream) -> TokenStream { + // Shound not trigger `used_underscore_binding` + let _inside_derive = 1; + assert_eq!(_inside_derive, _inside_derive); + + let output = quote! { + // Should not trigger `useless_attribute` + #[allow(dead_code)] + extern crate rustc_middle; + }; + output +} diff --git a/src/tools/clippy/tests/ui/auxiliary/use_self_macro.rs b/src/tools/clippy/tests/ui/auxiliary/use_self_macro.rs new file mode 100644 index 00000000000..a8a85b4baef --- /dev/null +++ b/src/tools/clippy/tests/ui/auxiliary/use_self_macro.rs @@ -0,0 +1,15 @@ +macro_rules! use_self { + ( + impl $ty:ident { + fn func(&$this:ident) { + [fields($($field:ident)*)] + } + } + ) => ( + impl $ty { + fn func(&$this) { + let $ty { $($field),* } = $this; + } + } + ) +} diff --git a/src/tools/clippy/tests/ui/auxiliary/wildcard_imports_helper.rs b/src/tools/clippy/tests/ui/auxiliary/wildcard_imports_helper.rs new file mode 100644 index 00000000000..414477aedd7 --- /dev/null +++ b/src/tools/clippy/tests/ui/auxiliary/wildcard_imports_helper.rs @@ -0,0 +1,21 @@ +pub use crate::extern_exports::*; + +pub fn extern_foo() {} +pub fn extern_bar() {} + +pub struct ExternA; + +pub mod inner { + pub mod inner_for_self_import { + pub fn inner_extern_foo() {} + pub fn inner_extern_bar() {} + } +} + +mod extern_exports { + pub fn extern_exported() {} + pub struct ExternExportedStruct; + pub enum ExternExportedEnum { + A, + } +} diff --git a/src/tools/clippy/tests/ui/await_holding_lock.rs b/src/tools/clippy/tests/ui/await_holding_lock.rs new file mode 100644 index 00000000000..5c1fdd83efb --- /dev/null +++ b/src/tools/clippy/tests/ui/await_holding_lock.rs @@ -0,0 +1,64 @@ +// edition:2018 +#![warn(clippy::await_holding_lock)] + +use std::sync::Mutex; + +async fn bad(x: &Mutex<u32>) -> u32 { + let guard = x.lock().unwrap(); + baz().await +} + +async fn good(x: &Mutex<u32>) -> u32 { + { + let guard = x.lock().unwrap(); + let y = *guard + 1; + } + baz().await; + let guard = x.lock().unwrap(); + 47 +} + +async fn baz() -> u32 { + 42 +} + +async fn also_bad(x: &Mutex<u32>) -> u32 { + let first = baz().await; + + let guard = x.lock().unwrap(); + + let second = baz().await; + + let third = baz().await; + + first + second + third +} + +async fn not_good(x: &Mutex<u32>) -> u32 { + let first = baz().await; + + let second = { + let guard = x.lock().unwrap(); + baz().await + }; + + let third = baz().await; + + first + second + third +} + +fn block_bad(x: &Mutex<u32>) -> impl std::future::Future<Output = u32> + '_ { + async move { + let guard = x.lock().unwrap(); + baz().await + } +} + +fn main() { + let m = Mutex::new(100); + good(&m); + bad(&m); + also_bad(&m); + not_good(&m); + block_bad(&m); +} diff --git a/src/tools/clippy/tests/ui/await_holding_lock.stderr b/src/tools/clippy/tests/ui/await_holding_lock.stderr new file mode 100644 index 00000000000..8c47cb37d8c --- /dev/null +++ b/src/tools/clippy/tests/ui/await_holding_lock.stderr @@ -0,0 +1,63 @@ +error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. + --> $DIR/await_holding_lock.rs:7:9 + | +LL | let guard = x.lock().unwrap(); + | ^^^^^ + | + = note: `-D clippy::await-holding-lock` implied by `-D warnings` +note: these are all the await points this lock is held through + --> $DIR/await_holding_lock.rs:7:5 + | +LL | / let guard = x.lock().unwrap(); +LL | | baz().await +LL | | } + | |_^ + +error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. + --> $DIR/await_holding_lock.rs:28:9 + | +LL | let guard = x.lock().unwrap(); + | ^^^^^ + | +note: these are all the await points this lock is held through + --> $DIR/await_holding_lock.rs:28:5 + | +LL | / let guard = x.lock().unwrap(); +LL | | +LL | | let second = baz().await; +LL | | +... | +LL | | first + second + third +LL | | } + | |_^ + +error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. + --> $DIR/await_holding_lock.rs:41:13 + | +LL | let guard = x.lock().unwrap(); + | ^^^^^ + | +note: these are all the await points this lock is held through + --> $DIR/await_holding_lock.rs:41:9 + | +LL | / let guard = x.lock().unwrap(); +LL | | baz().await +LL | | }; + | |_____^ + +error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. + --> $DIR/await_holding_lock.rs:52:13 + | +LL | let guard = x.lock().unwrap(); + | ^^^^^ + | +note: these are all the await points this lock is held through + --> $DIR/await_holding_lock.rs:52:9 + | +LL | / let guard = x.lock().unwrap(); +LL | | baz().await +LL | | } + | |_____^ + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/bit_masks.rs b/src/tools/clippy/tests/ui/bit_masks.rs new file mode 100644 index 00000000000..cfb493fb52a --- /dev/null +++ b/src/tools/clippy/tests/ui/bit_masks.rs @@ -0,0 +1,63 @@ +const THREE_BITS: i64 = 7; +const EVEN_MORE_REDIRECTION: i64 = THREE_BITS; + +#[warn(clippy::bad_bit_mask)] +#[allow( + clippy::ineffective_bit_mask, + clippy::identity_op, + clippy::no_effect, + clippy::unnecessary_operation +)] +fn main() { + let x = 5; + + x & 0 == 0; + x & 1 == 1; //ok, distinguishes bit 0 + x & 1 == 0; //ok, compared with zero + x & 2 == 1; + x | 0 == 0; //ok, equals x == 0 (maybe warn?) + x | 1 == 3; //ok, equals x == 2 || x == 3 + x | 3 == 3; //ok, equals x <= 3 + x | 3 == 2; + + x & 1 > 1; + x & 2 > 1; // ok, distinguishes x & 2 == 2 from x & 2 == 0 + x & 2 < 1; // ok, distinguishes x & 2 == 2 from x & 2 == 0 + x | 1 > 1; // ok (if a bit silly), equals x > 1 + x | 2 > 1; + x | 2 <= 2; // ok (if a bit silly), equals x <= 2 + + x & 192 == 128; // ok, tests for bit 7 and not bit 6 + x & 0xffc0 == 0xfe80; // ok + + // this also now works with constants + x & THREE_BITS == 8; + x | EVEN_MORE_REDIRECTION < 7; + + 0 & x == 0; + 1 | x > 1; + + // and should now also match uncommon usage + 1 < 2 | x; + 2 == 3 | x; + 1 == x & 2; + + x | 1 > 2; // no error, because we allowed ineffective bit masks + ineffective(); +} + +#[warn(clippy::ineffective_bit_mask)] +#[allow(clippy::bad_bit_mask, clippy::no_effect, clippy::unnecessary_operation)] +fn ineffective() { + let x = 5; + + x | 1 > 3; + x | 1 < 4; + x | 1 <= 3; + x | 1 >= 8; + + x | 1 > 2; // not an error (yet), better written as x >= 2 + x | 1 >= 7; // not an error (yet), better written as x >= 6 + x | 3 > 4; // not an error (yet), better written as x >= 4 + x | 4 <= 19; +} diff --git a/src/tools/clippy/tests/ui/bit_masks.stderr b/src/tools/clippy/tests/ui/bit_masks.stderr new file mode 100644 index 00000000000..dc5ad6dfbdf --- /dev/null +++ b/src/tools/clippy/tests/ui/bit_masks.stderr @@ -0,0 +1,110 @@ +error: &-masking with zero + --> $DIR/bit_masks.rs:14:5 + | +LL | x & 0 == 0; + | ^^^^^^^^^^ + | + = note: `-D clippy::bad-bit-mask` implied by `-D warnings` + +error: this operation will always return zero. This is likely not the intended outcome + --> $DIR/bit_masks.rs:14:5 + | +LL | x & 0 == 0; + | ^^^^^ + | + = note: `#[deny(clippy::erasing_op)]` on by default + +error: incompatible bit mask: `_ & 2` can never be equal to `1` + --> $DIR/bit_masks.rs:17:5 + | +LL | x & 2 == 1; + | ^^^^^^^^^^ + +error: incompatible bit mask: `_ | 3` can never be equal to `2` + --> $DIR/bit_masks.rs:21:5 + | +LL | x | 3 == 2; + | ^^^^^^^^^^ + +error: incompatible bit mask: `_ & 1` will never be higher than `1` + --> $DIR/bit_masks.rs:23:5 + | +LL | x & 1 > 1; + | ^^^^^^^^^ + +error: incompatible bit mask: `_ | 2` will always be higher than `1` + --> $DIR/bit_masks.rs:27:5 + | +LL | x | 2 > 1; + | ^^^^^^^^^ + +error: incompatible bit mask: `_ & 7` can never be equal to `8` + --> $DIR/bit_masks.rs:34:5 + | +LL | x & THREE_BITS == 8; + | ^^^^^^^^^^^^^^^^^^^ + +error: incompatible bit mask: `_ | 7` will never be lower than `7` + --> $DIR/bit_masks.rs:35:5 + | +LL | x | EVEN_MORE_REDIRECTION < 7; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: &-masking with zero + --> $DIR/bit_masks.rs:37:5 + | +LL | 0 & x == 0; + | ^^^^^^^^^^ + +error: this operation will always return zero. This is likely not the intended outcome + --> $DIR/bit_masks.rs:37:5 + | +LL | 0 & x == 0; + | ^^^^^ + +error: incompatible bit mask: `_ | 2` will always be higher than `1` + --> $DIR/bit_masks.rs:41:5 + | +LL | 1 < 2 | x; + | ^^^^^^^^^ + +error: incompatible bit mask: `_ | 3` can never be equal to `2` + --> $DIR/bit_masks.rs:42:5 + | +LL | 2 == 3 | x; + | ^^^^^^^^^^ + +error: incompatible bit mask: `_ & 2` can never be equal to `1` + --> $DIR/bit_masks.rs:43:5 + | +LL | 1 == x & 2; + | ^^^^^^^^^^ + +error: ineffective bit mask: `x | 1` compared to `3`, is the same as x compared directly + --> $DIR/bit_masks.rs:54:5 + | +LL | x | 1 > 3; + | ^^^^^^^^^ + | + = note: `-D clippy::ineffective-bit-mask` implied by `-D warnings` + +error: ineffective bit mask: `x | 1` compared to `4`, is the same as x compared directly + --> $DIR/bit_masks.rs:55:5 + | +LL | x | 1 < 4; + | ^^^^^^^^^ + +error: ineffective bit mask: `x | 1` compared to `3`, is the same as x compared directly + --> $DIR/bit_masks.rs:56:5 + | +LL | x | 1 <= 3; + | ^^^^^^^^^^ + +error: ineffective bit mask: `x | 1` compared to `8`, is the same as x compared directly + --> $DIR/bit_masks.rs:57:5 + | +LL | x | 1 >= 8; + | ^^^^^^^^^^ + +error: aborting due to 17 previous errors + diff --git a/src/tools/clippy/tests/ui/blacklisted_name.rs b/src/tools/clippy/tests/ui/blacklisted_name.rs new file mode 100644 index 00000000000..ca9d8d16b78 --- /dev/null +++ b/src/tools/clippy/tests/ui/blacklisted_name.rs @@ -0,0 +1,40 @@ +#![allow( + dead_code, + clippy::similar_names, + clippy::single_match, + clippy::toplevel_ref_arg, + unused_mut, + unused_variables +)] +#![warn(clippy::blacklisted_name)] + +fn test(foo: ()) {} + +fn main() { + let foo = 42; + let bar = 42; + let baz = 42; + + let barb = 42; + let barbaric = 42; + + match (42, Some(1337), Some(0)) { + (foo, Some(bar), baz @ Some(_)) => (), + _ => (), + } +} + +fn issue_1647(mut foo: u8) { + let mut bar = 0; + if let Some(mut baz) = Some(42) {} +} + +fn issue_1647_ref() { + let ref bar = 0; + if let Some(ref baz) = Some(42) {} +} + +fn issue_1647_ref_mut() { + let ref mut bar = 0; + if let Some(ref mut baz) = Some(42) {} +} diff --git a/src/tools/clippy/tests/ui/blacklisted_name.stderr b/src/tools/clippy/tests/ui/blacklisted_name.stderr new file mode 100644 index 00000000000..44123829fb0 --- /dev/null +++ b/src/tools/clippy/tests/ui/blacklisted_name.stderr @@ -0,0 +1,88 @@ +error: use of a blacklisted/placeholder name `foo` + --> $DIR/blacklisted_name.rs:11:9 + | +LL | fn test(foo: ()) {} + | ^^^ + | + = note: `-D clippy::blacklisted-name` implied by `-D warnings` + +error: use of a blacklisted/placeholder name `foo` + --> $DIR/blacklisted_name.rs:14:9 + | +LL | let foo = 42; + | ^^^ + +error: use of a blacklisted/placeholder name `bar` + --> $DIR/blacklisted_name.rs:15:9 + | +LL | let bar = 42; + | ^^^ + +error: use of a blacklisted/placeholder name `baz` + --> $DIR/blacklisted_name.rs:16:9 + | +LL | let baz = 42; + | ^^^ + +error: use of a blacklisted/placeholder name `foo` + --> $DIR/blacklisted_name.rs:22:10 + | +LL | (foo, Some(bar), baz @ Some(_)) => (), + | ^^^ + +error: use of a blacklisted/placeholder name `bar` + --> $DIR/blacklisted_name.rs:22:20 + | +LL | (foo, Some(bar), baz @ Some(_)) => (), + | ^^^ + +error: use of a blacklisted/placeholder name `baz` + --> $DIR/blacklisted_name.rs:22:26 + | +LL | (foo, Some(bar), baz @ Some(_)) => (), + | ^^^ + +error: use of a blacklisted/placeholder name `foo` + --> $DIR/blacklisted_name.rs:27:19 + | +LL | fn issue_1647(mut foo: u8) { + | ^^^ + +error: use of a blacklisted/placeholder name `bar` + --> $DIR/blacklisted_name.rs:28:13 + | +LL | let mut bar = 0; + | ^^^ + +error: use of a blacklisted/placeholder name `baz` + --> $DIR/blacklisted_name.rs:29:21 + | +LL | if let Some(mut baz) = Some(42) {} + | ^^^ + +error: use of a blacklisted/placeholder name `bar` + --> $DIR/blacklisted_name.rs:33:13 + | +LL | let ref bar = 0; + | ^^^ + +error: use of a blacklisted/placeholder name `baz` + --> $DIR/blacklisted_name.rs:34:21 + | +LL | if let Some(ref baz) = Some(42) {} + | ^^^ + +error: use of a blacklisted/placeholder name `bar` + --> $DIR/blacklisted_name.rs:38:17 + | +LL | let ref mut bar = 0; + | ^^^ + +error: use of a blacklisted/placeholder name `baz` + --> $DIR/blacklisted_name.rs:39:25 + | +LL | if let Some(ref mut baz) = Some(42) {} + | ^^^ + +error: aborting due to 14 previous errors + diff --git a/src/tools/clippy/tests/ui/block_in_if_condition.fixed b/src/tools/clippy/tests/ui/block_in_if_condition.fixed new file mode 100644 index 00000000000..955801e40f9 --- /dev/null +++ b/src/tools/clippy/tests/ui/block_in_if_condition.fixed @@ -0,0 +1,75 @@ +// run-rustfix +#![warn(clippy::block_in_if_condition_expr)] +#![warn(clippy::block_in_if_condition_stmt)] +#![allow(unused, clippy::let_and_return)] +#![warn(clippy::nonminimal_bool)] + +macro_rules! blocky { + () => {{ + true + }}; +} + +macro_rules! blocky_too { + () => {{ + let r = true; + r + }}; +} + +fn macro_if() { + if blocky!() {} + + if blocky_too!() {} +} + +fn condition_has_block() -> i32 { + let res = { + let x = 3; + x == 3 + }; if res { + 6 + } else { + 10 + } +} + +fn condition_has_block_with_single_expression() -> i32 { + if true { + 6 + } else { + 10 + } +} + +fn condition_is_normal() -> i32 { + let x = 3; + if x == 3 { + 6 + } else { + 10 + } +} + +fn condition_is_unsafe_block() { + let a: i32 = 1; + + // this should not warn because the condition is an unsafe block + if unsafe { 1u32 == std::mem::transmute(a) } { + println!("1u32 == a"); + } +} + +fn block_in_assert() { + let opt = Some(42); + assert!(opt + .as_ref() + .and_then(|val| { + let mut v = val * 2; + v -= 1; + Some(v * 3) + }) + .is_some()); +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/block_in_if_condition.rs b/src/tools/clippy/tests/ui/block_in_if_condition.rs new file mode 100644 index 00000000000..a6ea01d5fc5 --- /dev/null +++ b/src/tools/clippy/tests/ui/block_in_if_condition.rs @@ -0,0 +1,75 @@ +// run-rustfix +#![warn(clippy::block_in_if_condition_expr)] +#![warn(clippy::block_in_if_condition_stmt)] +#![allow(unused, clippy::let_and_return)] +#![warn(clippy::nonminimal_bool)] + +macro_rules! blocky { + () => {{ + true + }}; +} + +macro_rules! blocky_too { + () => {{ + let r = true; + r + }}; +} + +fn macro_if() { + if blocky!() {} + + if blocky_too!() {} +} + +fn condition_has_block() -> i32 { + if { + let x = 3; + x == 3 + } { + 6 + } else { + 10 + } +} + +fn condition_has_block_with_single_expression() -> i32 { + if { true } { + 6 + } else { + 10 + } +} + +fn condition_is_normal() -> i32 { + let x = 3; + if true && x == 3 { + 6 + } else { + 10 + } +} + +fn condition_is_unsafe_block() { + let a: i32 = 1; + + // this should not warn because the condition is an unsafe block + if unsafe { 1u32 == std::mem::transmute(a) } { + println!("1u32 == a"); + } +} + +fn block_in_assert() { + let opt = Some(42); + assert!(opt + .as_ref() + .and_then(|val| { + let mut v = val * 2; + v -= 1; + Some(v * 3) + }) + .is_some()); +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/block_in_if_condition.stderr b/src/tools/clippy/tests/ui/block_in_if_condition.stderr new file mode 100644 index 00000000000..b0a0a276c89 --- /dev/null +++ b/src/tools/clippy/tests/ui/block_in_if_condition.stderr @@ -0,0 +1,36 @@ +error: in an `if` condition, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let` + --> $DIR/block_in_if_condition.rs:27:5 + | +LL | / if { +LL | | let x = 3; +LL | | x == 3 +LL | | } { + | |_____^ + | + = note: `-D clippy::block-in-if-condition-stmt` implied by `-D warnings` +help: try + | +LL | let res = { +LL | let x = 3; +LL | x == 3 +LL | }; if res { + | + +error: omit braces around single expression condition + --> $DIR/block_in_if_condition.rs:38:8 + | +LL | if { true } { + | ^^^^^^^^ help: try: `true` + | + = note: `-D clippy::block-in-if-condition-expr` implied by `-D warnings` + +error: this boolean expression can be simplified + --> $DIR/block_in_if_condition.rs:47:8 + | +LL | if true && x == 3 { + | ^^^^^^^^^^^^^^ help: try: `x == 3` + | + = note: `-D clippy::nonminimal-bool` implied by `-D warnings` + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/block_in_if_condition_closure.rs b/src/tools/clippy/tests/ui/block_in_if_condition_closure.rs new file mode 100644 index 00000000000..bac3eda5e7f --- /dev/null +++ b/src/tools/clippy/tests/ui/block_in_if_condition_closure.rs @@ -0,0 +1,48 @@ +#![warn(clippy::block_in_if_condition_expr)] +#![warn(clippy::block_in_if_condition_stmt)] +#![allow(unused, clippy::let_and_return)] + +fn predicate<F: FnOnce(T) -> bool, T>(pfn: F, val: T) -> bool { + pfn(val) +} + +fn pred_test() { + let v = 3; + let sky = "blue"; + // This is a sneaky case, where the block isn't directly in the condition, + // but is actually nside a closure that the condition is using. + // The same principle applies -- add some extra expressions to make sure + // linter isn't confused by them. + if v == 3 + && sky == "blue" + && predicate( + |x| { + let target = 3; + x == target + }, + v, + ) + {} + + if predicate( + |x| { + let target = 3; + x == target + }, + v, + ) {} +} + +fn closure_without_block() { + if predicate(|x| x == 3, 6) {} +} + +fn macro_in_closure() { + let option = Some(true); + + if option.unwrap_or_else(|| unimplemented!()) { + unimplemented!() + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/block_in_if_condition_closure.stderr b/src/tools/clippy/tests/ui/block_in_if_condition_closure.stderr new file mode 100644 index 00000000000..86cd24fe763 --- /dev/null +++ b/src/tools/clippy/tests/ui/block_in_if_condition_closure.stderr @@ -0,0 +1,24 @@ +error: in an `if` condition, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let` + --> $DIR/block_in_if_condition_closure.rs:19:17 + | +LL | |x| { + | _________________^ +LL | | let target = 3; +LL | | x == target +LL | | }, + | |_____________^ + | + = note: `-D clippy::block-in-if-condition-stmt` implied by `-D warnings` + +error: in an `if` condition, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let` + --> $DIR/block_in_if_condition_closure.rs:28:13 + | +LL | |x| { + | _____________^ +LL | | let target = 3; +LL | | x == target +LL | | }, + | |_________^ + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/bool_comparison.fixed b/src/tools/clippy/tests/ui/bool_comparison.fixed new file mode 100644 index 00000000000..91211764759 --- /dev/null +++ b/src/tools/clippy/tests/ui/bool_comparison.fixed @@ -0,0 +1,129 @@ +// run-rustfix + +#[warn(clippy::bool_comparison)] +fn main() { + let x = true; + if x { + "yes" + } else { + "no" + }; + if !x { + "yes" + } else { + "no" + }; + if x { + "yes" + } else { + "no" + }; + if !x { + "yes" + } else { + "no" + }; + if !x { + "yes" + } else { + "no" + }; + if x { + "yes" + } else { + "no" + }; + if !x { + "yes" + } else { + "no" + }; + if x { + "yes" + } else { + "no" + }; + if !x { + "yes" + } else { + "no" + }; + if x { + "yes" + } else { + "no" + }; + if x { + "yes" + } else { + "no" + }; + if !x { + "yes" + } else { + "no" + }; + let y = true; + if !x & y { + "yes" + } else { + "no" + }; + if x & !y { + "yes" + } else { + "no" + }; +} + +#[allow(dead_code)] +fn issue3703() { + struct Foo; + impl PartialEq<bool> for Foo { + fn eq(&self, _: &bool) -> bool { + true + } + } + impl PartialEq<Foo> for bool { + fn eq(&self, _: &Foo) -> bool { + true + } + } + impl PartialOrd<bool> for Foo { + fn partial_cmp(&self, _: &bool) -> Option<std::cmp::Ordering> { + None + } + } + impl PartialOrd<Foo> for bool { + fn partial_cmp(&self, _: &Foo) -> Option<std::cmp::Ordering> { + None + } + } + + if Foo == true {} + if true == Foo {} + if Foo != true {} + if true != Foo {} + if Foo == false {} + if false == Foo {} + if Foo != false {} + if false != Foo {} + if Foo < false {} + if false < Foo {} +} + +#[allow(dead_code)] +fn issue4983() { + let a = true; + let b = false; + + if a != b {}; + if a != b {}; + if a == b {}; + if !a == !b {}; + + if b != a {}; + if b != a {}; + if b == a {}; + if !b == !a {}; +} diff --git a/src/tools/clippy/tests/ui/bool_comparison.rs b/src/tools/clippy/tests/ui/bool_comparison.rs new file mode 100644 index 00000000000..01ee35859f0 --- /dev/null +++ b/src/tools/clippy/tests/ui/bool_comparison.rs @@ -0,0 +1,129 @@ +// run-rustfix + +#[warn(clippy::bool_comparison)] +fn main() { + let x = true; + if x == true { + "yes" + } else { + "no" + }; + if x == false { + "yes" + } else { + "no" + }; + if true == x { + "yes" + } else { + "no" + }; + if false == x { + "yes" + } else { + "no" + }; + if x != true { + "yes" + } else { + "no" + }; + if x != false { + "yes" + } else { + "no" + }; + if true != x { + "yes" + } else { + "no" + }; + if false != x { + "yes" + } else { + "no" + }; + if x < true { + "yes" + } else { + "no" + }; + if false < x { + "yes" + } else { + "no" + }; + if x > false { + "yes" + } else { + "no" + }; + if true > x { + "yes" + } else { + "no" + }; + let y = true; + if x < y { + "yes" + } else { + "no" + }; + if x > y { + "yes" + } else { + "no" + }; +} + +#[allow(dead_code)] +fn issue3703() { + struct Foo; + impl PartialEq<bool> for Foo { + fn eq(&self, _: &bool) -> bool { + true + } + } + impl PartialEq<Foo> for bool { + fn eq(&self, _: &Foo) -> bool { + true + } + } + impl PartialOrd<bool> for Foo { + fn partial_cmp(&self, _: &bool) -> Option<std::cmp::Ordering> { + None + } + } + impl PartialOrd<Foo> for bool { + fn partial_cmp(&self, _: &Foo) -> Option<std::cmp::Ordering> { + None + } + } + + if Foo == true {} + if true == Foo {} + if Foo != true {} + if true != Foo {} + if Foo == false {} + if false == Foo {} + if Foo != false {} + if false != Foo {} + if Foo < false {} + if false < Foo {} +} + +#[allow(dead_code)] +fn issue4983() { + let a = true; + let b = false; + + if a == !b {}; + if !a == b {}; + if a == b {}; + if !a == !b {}; + + if b == !a {}; + if !b == a {}; + if b == a {}; + if !b == !a {}; +} diff --git a/src/tools/clippy/tests/ui/bool_comparison.stderr b/src/tools/clippy/tests/ui/bool_comparison.stderr new file mode 100644 index 00000000000..eeb1f20ee89 --- /dev/null +++ b/src/tools/clippy/tests/ui/bool_comparison.stderr @@ -0,0 +1,112 @@ +error: equality checks against true are unnecessary + --> $DIR/bool_comparison.rs:6:8 + | +LL | if x == true { + | ^^^^^^^^^ help: try simplifying it as shown: `x` + | + = note: `-D clippy::bool-comparison` implied by `-D warnings` + +error: equality checks against false can be replaced by a negation + --> $DIR/bool_comparison.rs:11:8 + | +LL | if x == false { + | ^^^^^^^^^^ help: try simplifying it as shown: `!x` + +error: equality checks against true are unnecessary + --> $DIR/bool_comparison.rs:16:8 + | +LL | if true == x { + | ^^^^^^^^^ help: try simplifying it as shown: `x` + +error: equality checks against false can be replaced by a negation + --> $DIR/bool_comparison.rs:21:8 + | +LL | if false == x { + | ^^^^^^^^^^ help: try simplifying it as shown: `!x` + +error: inequality checks against true can be replaced by a negation + --> $DIR/bool_comparison.rs:26:8 + | +LL | if x != true { + | ^^^^^^^^^ help: try simplifying it as shown: `!x` + +error: inequality checks against false are unnecessary + --> $DIR/bool_comparison.rs:31:8 + | +LL | if x != false { + | ^^^^^^^^^^ help: try simplifying it as shown: `x` + +error: inequality checks against true can be replaced by a negation + --> $DIR/bool_comparison.rs:36:8 + | +LL | if true != x { + | ^^^^^^^^^ help: try simplifying it as shown: `!x` + +error: inequality checks against false are unnecessary + --> $DIR/bool_comparison.rs:41:8 + | +LL | if false != x { + | ^^^^^^^^^^ help: try simplifying it as shown: `x` + +error: less than comparison against true can be replaced by a negation + --> $DIR/bool_comparison.rs:46:8 + | +LL | if x < true { + | ^^^^^^^^ help: try simplifying it as shown: `!x` + +error: greater than checks against false are unnecessary + --> $DIR/bool_comparison.rs:51:8 + | +LL | if false < x { + | ^^^^^^^^^ help: try simplifying it as shown: `x` + +error: greater than checks against false are unnecessary + --> $DIR/bool_comparison.rs:56:8 + | +LL | if x > false { + | ^^^^^^^^^ help: try simplifying it as shown: `x` + +error: less than comparison against true can be replaced by a negation + --> $DIR/bool_comparison.rs:61:8 + | +LL | if true > x { + | ^^^^^^^^ help: try simplifying it as shown: `!x` + +error: order comparisons between booleans can be simplified + --> $DIR/bool_comparison.rs:67:8 + | +LL | if x < y { + | ^^^^^ help: try simplifying it as shown: `!x & y` + +error: order comparisons between booleans can be simplified + --> $DIR/bool_comparison.rs:72:8 + | +LL | if x > y { + | ^^^^^ help: try simplifying it as shown: `x & !y` + +error: This comparison might be written more concisely + --> $DIR/bool_comparison.rs:120:8 + | +LL | if a == !b {}; + | ^^^^^^^ help: try simplifying it as shown: `a != b` + +error: This comparison might be written more concisely + --> $DIR/bool_comparison.rs:121:8 + | +LL | if !a == b {}; + | ^^^^^^^ help: try simplifying it as shown: `a != b` + +error: This comparison might be written more concisely + --> $DIR/bool_comparison.rs:125:8 + | +LL | if b == !a {}; + | ^^^^^^^ help: try simplifying it as shown: `b != a` + +error: This comparison might be written more concisely + --> $DIR/bool_comparison.rs:126:8 + | +LL | if !b == a {}; + | ^^^^^^^ help: try simplifying it as shown: `b != a` + +error: aborting due to 18 previous errors + diff --git a/src/tools/clippy/tests/ui/borrow_box.rs b/src/tools/clippy/tests/ui/borrow_box.rs new file mode 100644 index 00000000000..1901de46ca8 --- /dev/null +++ b/src/tools/clippy/tests/ui/borrow_box.rs @@ -0,0 +1,99 @@ +#![deny(clippy::borrowed_box)] +#![allow(clippy::blacklisted_name)] +#![allow(unused_variables)] +#![allow(dead_code)] + +pub fn test1(foo: &mut Box<bool>) { + // Although this function could be changed to "&mut bool", + // avoiding the Box, mutable references to boxes are not + // flagged by this lint. + // + // This omission is intentional: By passing a mutable Box, + // the memory location of the pointed-to object could be + // modified. By passing a mutable reference, the contents + // could change, but not the location. + println!("{:?}", foo) +} + +pub fn test2() { + let foo: &Box<bool>; +} + +struct Test3<'a> { + foo: &'a Box<bool>, +} + +trait Test4 { + fn test4(a: &Box<bool>); +} + +impl<'a> Test4 for Test3<'a> { + fn test4(a: &Box<bool>) { + unimplemented!(); + } +} + +use std::any::Any; + +pub fn test5(foo: &mut Box<dyn Any>) { + println!("{:?}", foo) +} + +pub fn test6() { + let foo: &Box<dyn Any>; +} + +struct Test7<'a> { + foo: &'a Box<dyn Any>, +} + +trait Test8 { + fn test8(a: &Box<dyn Any>); +} + +impl<'a> Test8 for Test7<'a> { + fn test8(a: &Box<dyn Any>) { + unimplemented!(); + } +} + +pub fn test9(foo: &mut Box<dyn Any + Send + Sync>) { + let _ = foo; +} + +pub fn test10() { + let foo: &Box<dyn Any + Send + 'static>; +} + +struct Test11<'a> { + foo: &'a Box<dyn Any + Send>, +} + +trait Test12 { + fn test4(a: &Box<dyn Any + 'static>); +} + +impl<'a> Test12 for Test11<'a> { + fn test4(a: &Box<dyn Any + 'static>) { + unimplemented!(); + } +} + +pub fn test13(boxed_slice: &mut Box<[i32]>) { + // Unconditionally replaces the box pointer. + // + // This cannot be accomplished if "&mut [i32]" is passed, + // and provides a test case where passing a reference to + // a Box is valid. + let mut data = vec![12]; + *boxed_slice = data.into_boxed_slice(); +} + +fn main() { + test1(&mut Box::new(false)); + test2(); + test5(&mut (Box::new(false) as Box<dyn Any>)); + test6(); + test9(&mut (Box::new(false) as Box<dyn Any + Send + Sync>)); + test10(); +} diff --git a/src/tools/clippy/tests/ui/borrow_box.stderr b/src/tools/clippy/tests/ui/borrow_box.stderr new file mode 100644 index 00000000000..b5db691f89f --- /dev/null +++ b/src/tools/clippy/tests/ui/borrow_box.stderr @@ -0,0 +1,26 @@ +error: you seem to be trying to use `&Box<T>`. Consider using just `&T` + --> $DIR/borrow_box.rs:19:14 + | +LL | let foo: &Box<bool>; + | ^^^^^^^^^^ help: try: `&bool` + | +note: the lint level is defined here + --> $DIR/borrow_box.rs:1:9 + | +LL | #![deny(clippy::borrowed_box)] + | ^^^^^^^^^^^^^^^^^^^^ + +error: you seem to be trying to use `&Box<T>`. Consider using just `&T` + --> $DIR/borrow_box.rs:23:10 + | +LL | foo: &'a Box<bool>, + | ^^^^^^^^^^^^^ help: try: `&'a bool` + +error: you seem to be trying to use `&Box<T>`. Consider using just `&T` + --> $DIR/borrow_box.rs:27:17 + | +LL | fn test4(a: &Box<bool>); + | ^^^^^^^^^^ help: try: `&bool` + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/borrow_interior_mutable_const.rs b/src/tools/clippy/tests/ui/borrow_interior_mutable_const.rs new file mode 100644 index 00000000000..fef9f4f39f8 --- /dev/null +++ b/src/tools/clippy/tests/ui/borrow_interior_mutable_const.rs @@ -0,0 +1,85 @@ +#![warn(clippy::borrow_interior_mutable_const)] +#![allow(clippy::declare_interior_mutable_const, clippy::ref_in_deref)] + +use std::borrow::Cow; +use std::cell::Cell; +use std::fmt::Display; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::Once; + +const ATOMIC: AtomicUsize = AtomicUsize::new(5); +const CELL: Cell<usize> = Cell::new(6); +const ATOMIC_TUPLE: ([AtomicUsize; 1], Vec<AtomicUsize>, u8) = ([ATOMIC], Vec::new(), 7); +const INTEGER: u8 = 8; +const STRING: String = String::new(); +const STR: &str = "012345"; +const COW: Cow<str> = Cow::Borrowed("abcdef"); +const NO_ANN: &dyn Display = &70; +static STATIC_TUPLE: (AtomicUsize, String) = (ATOMIC, STRING); +const ONCE_INIT: Once = Once::new(); + +trait Trait<T>: Copy { + type NonCopyType; + + const ATOMIC: AtomicUsize; +} + +impl Trait<u32> for u64 { + type NonCopyType = u16; + + const ATOMIC: AtomicUsize = AtomicUsize::new(9); +} + +fn main() { + ATOMIC.store(1, Ordering::SeqCst); //~ ERROR interior mutability + assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); //~ ERROR interior mutability + + let _once = ONCE_INIT; + let _once_ref = &ONCE_INIT; //~ ERROR interior mutability + let _once_ref_2 = &&ONCE_INIT; //~ ERROR interior mutability + let _once_ref_4 = &&&&ONCE_INIT; //~ ERROR interior mutability + let _once_mut = &mut ONCE_INIT; //~ ERROR interior mutability + let _atomic_into_inner = ATOMIC.into_inner(); + // these should be all fine. + let _twice = (ONCE_INIT, ONCE_INIT); + let _ref_twice = &(ONCE_INIT, ONCE_INIT); + let _ref_once = &(ONCE_INIT, ONCE_INIT).0; + let _array_twice = [ONCE_INIT, ONCE_INIT]; + let _ref_array_twice = &[ONCE_INIT, ONCE_INIT]; + let _ref_array_once = &[ONCE_INIT, ONCE_INIT][0]; + + // referencing projection is still bad. + let _ = &ATOMIC_TUPLE; //~ ERROR interior mutability + let _ = &ATOMIC_TUPLE.0; //~ ERROR interior mutability + let _ = &(&&&&ATOMIC_TUPLE).0; //~ ERROR interior mutability + let _ = &ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability + let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); //~ ERROR interior mutability + let _ = &*ATOMIC_TUPLE.1; //~ ERROR interior mutability + let _ = &ATOMIC_TUPLE.2; + let _ = (&&&&ATOMIC_TUPLE).0; + let _ = (&&&&ATOMIC_TUPLE).2; + let _ = ATOMIC_TUPLE.0; + let _ = ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability + let _ = ATOMIC_TUPLE.1.into_iter(); + let _ = ATOMIC_TUPLE.2; + let _ = &{ ATOMIC_TUPLE }; + + CELL.set(2); //~ ERROR interior mutability + assert_eq!(CELL.get(), 6); //~ ERROR interior mutability + + assert_eq!(INTEGER, 8); + assert!(STRING.is_empty()); + + let a = ATOMIC; + a.store(4, Ordering::SeqCst); + assert_eq!(a.load(Ordering::SeqCst), 4); + + STATIC_TUPLE.0.store(3, Ordering::SeqCst); + assert_eq!(STATIC_TUPLE.0.load(Ordering::SeqCst), 3); + assert!(STATIC_TUPLE.1.is_empty()); + + u64::ATOMIC.store(5, Ordering::SeqCst); //~ ERROR interior mutability + assert_eq!(u64::ATOMIC.load(Ordering::SeqCst), 9); //~ ERROR interior mutability + + assert_eq!(NO_ANN.to_string(), "70"); // should never lint this. +} diff --git a/src/tools/clippy/tests/ui/borrow_interior_mutable_const.stderr b/src/tools/clippy/tests/ui/borrow_interior_mutable_const.stderr new file mode 100644 index 00000000000..dc738064a17 --- /dev/null +++ b/src/tools/clippy/tests/ui/borrow_interior_mutable_const.stderr @@ -0,0 +1,131 @@ +error: a `const` item with interior mutability should not be borrowed + --> $DIR/borrow_interior_mutable_const.rs:34:5 + | +LL | ATOMIC.store(1, Ordering::SeqCst); //~ ERROR interior mutability + | ^^^^^^ + | + = note: `-D clippy::borrow-interior-mutable-const` implied by `-D warnings` + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/borrow_interior_mutable_const.rs:35:16 + | +LL | assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); //~ ERROR interior mutability + | ^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/borrow_interior_mutable_const.rs:38:22 + | +LL | let _once_ref = &ONCE_INIT; //~ ERROR interior mutability + | ^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/borrow_interior_mutable_const.rs:39:25 + | +LL | let _once_ref_2 = &&ONCE_INIT; //~ ERROR interior mutability + | ^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/borrow_interior_mutable_const.rs:40:27 + | +LL | let _once_ref_4 = &&&&ONCE_INIT; //~ ERROR interior mutability + | ^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/borrow_interior_mutable_const.rs:41:26 + | +LL | let _once_mut = &mut ONCE_INIT; //~ ERROR interior mutability + | ^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/borrow_interior_mutable_const.rs:52:14 + | +LL | let _ = &ATOMIC_TUPLE; //~ ERROR interior mutability + | ^^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/borrow_interior_mutable_const.rs:53:14 + | +LL | let _ = &ATOMIC_TUPLE.0; //~ ERROR interior mutability + | ^^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/borrow_interior_mutable_const.rs:54:19 + | +LL | let _ = &(&&&&ATOMIC_TUPLE).0; //~ ERROR interior mutability + | ^^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/borrow_interior_mutable_const.rs:55:14 + | +LL | let _ = &ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability + | ^^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/borrow_interior_mutable_const.rs:56:13 + | +LL | let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); //~ ERROR interior mutability + | ^^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/borrow_interior_mutable_const.rs:62:13 + | +LL | let _ = ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability + | ^^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/borrow_interior_mutable_const.rs:67:5 + | +LL | CELL.set(2); //~ ERROR interior mutability + | ^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/borrow_interior_mutable_const.rs:68:16 + | +LL | assert_eq!(CELL.get(), 6); //~ ERROR interior mutability + | ^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/borrow_interior_mutable_const.rs:81:5 + | +LL | u64::ATOMIC.store(5, Ordering::SeqCst); //~ ERROR interior mutability + | ^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/borrow_interior_mutable_const.rs:82:16 + | +LL | assert_eq!(u64::ATOMIC.load(Ordering::SeqCst), 9); //~ ERROR interior mutability + | ^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: aborting due to 16 previous errors + diff --git a/src/tools/clippy/tests/ui/box_vec.rs b/src/tools/clippy/tests/ui/box_vec.rs new file mode 100644 index 00000000000..87b67c23704 --- /dev/null +++ b/src/tools/clippy/tests/ui/box_vec.rs @@ -0,0 +1,32 @@ +#![warn(clippy::all)] +#![allow(clippy::boxed_local, clippy::needless_pass_by_value)] +#![allow(clippy::blacklisted_name)] + +macro_rules! boxit { + ($init:expr, $x:ty) => { + let _: Box<$x> = Box::new($init); + }; +} + +fn test_macro() { + boxit!(Vec::new(), Vec<u8>); +} +pub fn test(foo: Box<Vec<bool>>) { + println!("{:?}", foo.get(0)) +} + +pub fn test2(foo: Box<dyn Fn(Vec<u32>)>) { + // pass if #31 is fixed + foo(vec![1, 2, 3]) +} + +pub fn test_local_not_linted() { + let _: Box<Vec<bool>>; +} + +fn main() { + test(Box::new(Vec::new())); + test2(Box::new(|v| println!("{:?}", v))); + test_macro(); + test_local_not_linted(); +} diff --git a/src/tools/clippy/tests/ui/box_vec.stderr b/src/tools/clippy/tests/ui/box_vec.stderr new file mode 100644 index 00000000000..fca12eddd57 --- /dev/null +++ b/src/tools/clippy/tests/ui/box_vec.stderr @@ -0,0 +1,11 @@ +error: you seem to be trying to use `Box<Vec<T>>`. Consider using just `Vec<T>` + --> $DIR/box_vec.rs:14:18 + | +LL | pub fn test(foo: Box<Vec<bool>>) { + | ^^^^^^^^^^^^^^ + | + = note: `-D clippy::box-vec` implied by `-D warnings` + = help: `Vec<T>` is already on the heap, `Box<Vec<T>>` makes an extra allocation. + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/builtin-type-shadow.rs b/src/tools/clippy/tests/ui/builtin-type-shadow.rs new file mode 100644 index 00000000000..69b8b6a0e68 --- /dev/null +++ b/src/tools/clippy/tests/ui/builtin-type-shadow.rs @@ -0,0 +1,9 @@ +#![warn(clippy::builtin_type_shadow)] +#![allow(non_camel_case_types)] + +fn foo<u32>(a: u32) -> u32 { + 42 + // ^ rustc's type error +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/builtin-type-shadow.stderr b/src/tools/clippy/tests/ui/builtin-type-shadow.stderr new file mode 100644 index 00000000000..b6a4adde848 --- /dev/null +++ b/src/tools/clippy/tests/ui/builtin-type-shadow.stderr @@ -0,0 +1,26 @@ +error: This generic shadows the built-in type `u32` + --> $DIR/builtin-type-shadow.rs:4:8 + | +LL | fn foo<u32>(a: u32) -> u32 { + | ^^^ + | + = note: `-D clippy::builtin-type-shadow` implied by `-D warnings` + +error[E0308]: mismatched types + --> $DIR/builtin-type-shadow.rs:5:5 + | +LL | fn foo<u32>(a: u32) -> u32 { + | --- --- expected `u32` because of return type + | | + | this type parameter +LL | 42 + | ^^ expected type parameter `u32`, found integer + | + = note: expected type parameter `u32` + found type `{integer}` + = help: type parameters must be constrained to match other types + = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/tools/clippy/tests/ui/bytecount.rs b/src/tools/clippy/tests/ui/bytecount.rs new file mode 100644 index 00000000000..c724ee21be3 --- /dev/null +++ b/src/tools/clippy/tests/ui/bytecount.rs @@ -0,0 +1,24 @@ +#[deny(clippy::naive_bytecount)] +fn main() { + let x = vec![0_u8; 16]; + + let _ = x.iter().filter(|&&a| a == 0).count(); // naive byte count + + let _ = (&x[..]).iter().filter(|&a| *a == 0).count(); // naive byte count + + let _ = x.iter().filter(|a| **a > 0).count(); // not an equality count, OK. + + let _ = x.iter().map(|a| a + 1).filter(|&a| a < 15).count(); // not a slice + + let b = 0; + + let _ = x.iter().filter(|_| b > 0).count(); // woah there + + let _ = x.iter().filter(|_a| b == b + 1).count(); // nothing to see here, move along + + let _ = x.iter().filter(|a| b + 1 == **a).count(); // naive byte count + + let y = vec![0_u16; 3]; + + let _ = y.iter().filter(|&&a| a == 0).count(); // naive count, but not bytes +} diff --git a/src/tools/clippy/tests/ui/bytecount.stderr b/src/tools/clippy/tests/ui/bytecount.stderr new file mode 100644 index 00000000000..436f5d86a06 --- /dev/null +++ b/src/tools/clippy/tests/ui/bytecount.stderr @@ -0,0 +1,26 @@ +error: You appear to be counting bytes the naive way + --> $DIR/bytecount.rs:5:13 + | +LL | let _ = x.iter().filter(|&&a| a == 0).count(); // naive byte count + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider using the bytecount crate: `bytecount::count(x, 0)` + | +note: the lint level is defined here + --> $DIR/bytecount.rs:1:8 + | +LL | #[deny(clippy::naive_bytecount)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: You appear to be counting bytes the naive way + --> $DIR/bytecount.rs:7:13 + | +LL | let _ = (&x[..]).iter().filter(|&a| *a == 0).count(); // naive byte count + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider using the bytecount crate: `bytecount::count((&x[..]), 0)` + +error: You appear to be counting bytes the naive way + --> $DIR/bytecount.rs:19:13 + | +LL | let _ = x.iter().filter(|a| b + 1 == **a).count(); // naive byte count + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider using the bytecount crate: `bytecount::count(x, b + 1)` + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/cast.rs b/src/tools/clippy/tests/ui/cast.rs new file mode 100644 index 00000000000..7e0b211d862 --- /dev/null +++ b/src/tools/clippy/tests/ui/cast.rs @@ -0,0 +1,95 @@ +#[warn( + clippy::cast_precision_loss, + clippy::cast_possible_truncation, + clippy::cast_sign_loss, + clippy::cast_possible_wrap +)] +#[allow(clippy::no_effect, clippy::unnecessary_operation)] +fn main() { + // Test clippy::cast_precision_loss + let x0 = 1i32; + x0 as f32; + let x1 = 1i64; + x1 as f32; + x1 as f64; + let x2 = 1u32; + x2 as f32; + let x3 = 1u64; + x3 as f32; + x3 as f64; + // Test clippy::cast_possible_truncation + 1f32 as i32; + 1f32 as u32; + 1f64 as f32; + 1i32 as i8; + 1i32 as u8; + 1f64 as isize; + 1f64 as usize; + // Test clippy::cast_possible_wrap + 1u8 as i8; + 1u16 as i16; + 1u32 as i32; + 1u64 as i64; + 1usize as isize; + // Test clippy::cast_sign_loss + 1i32 as u32; + -1i32 as u32; + 1isize as usize; + -1isize as usize; + 0i8 as u8; + i8::max_value() as u8; + i16::max_value() as u16; + i32::max_value() as u32; + i64::max_value() as u64; + i128::max_value() as u128; + + (-1i8).abs() as u8; + (-1i16).abs() as u16; + (-1i32).abs() as u32; + (-1i64).abs() as u64; + (-1isize).abs() as usize; + + (-1i8).checked_abs().unwrap() as u8; + (-1i16).checked_abs().unwrap() as u16; + (-1i32).checked_abs().unwrap() as u32; + (-1i64).checked_abs().unwrap() as u64; + (-1isize).checked_abs().unwrap() as usize; + + (-1i8).rem_euclid(1i8) as u8; + (-1i8).rem_euclid(1i8) as u16; + (-1i16).rem_euclid(1i16) as u16; + (-1i16).rem_euclid(1i16) as u32; + (-1i32).rem_euclid(1i32) as u32; + (-1i32).rem_euclid(1i32) as u64; + (-1i64).rem_euclid(1i64) as u64; + (-1i64).rem_euclid(1i64) as u128; + (-1isize).rem_euclid(1isize) as usize; + (1i8).rem_euclid(-1i8) as u8; + (1i8).rem_euclid(-1i8) as u16; + (1i16).rem_euclid(-1i16) as u16; + (1i16).rem_euclid(-1i16) as u32; + (1i32).rem_euclid(-1i32) as u32; + (1i32).rem_euclid(-1i32) as u64; + (1i64).rem_euclid(-1i64) as u64; + (1i64).rem_euclid(-1i64) as u128; + (1isize).rem_euclid(-1isize) as usize; + + (-1i8).checked_rem_euclid(1i8).unwrap() as u8; + (-1i8).checked_rem_euclid(1i8).unwrap() as u16; + (-1i16).checked_rem_euclid(1i16).unwrap() as u16; + (-1i16).checked_rem_euclid(1i16).unwrap() as u32; + (-1i32).checked_rem_euclid(1i32).unwrap() as u32; + (-1i32).checked_rem_euclid(1i32).unwrap() as u64; + (-1i64).checked_rem_euclid(1i64).unwrap() as u64; + (-1i64).checked_rem_euclid(1i64).unwrap() as u128; + (-1isize).checked_rem_euclid(1isize).unwrap() as usize; + (1i8).checked_rem_euclid(-1i8).unwrap() as u8; + (1i8).checked_rem_euclid(-1i8).unwrap() as u16; + (1i16).checked_rem_euclid(-1i16).unwrap() as u16; + (1i16).checked_rem_euclid(-1i16).unwrap() as u32; + (1i32).checked_rem_euclid(-1i32).unwrap() as u32; + (1i32).checked_rem_euclid(-1i32).unwrap() as u64; + (1i64).checked_rem_euclid(-1i64).unwrap() as u64; + (1i64).checked_rem_euclid(-1i64).unwrap() as u128; + (1isize).checked_rem_euclid(-1isize).unwrap() as usize; +} diff --git a/src/tools/clippy/tests/ui/cast.stderr b/src/tools/clippy/tests/ui/cast.stderr new file mode 100644 index 00000000000..4c66d736494 --- /dev/null +++ b/src/tools/clippy/tests/ui/cast.stderr @@ -0,0 +1,142 @@ +error: casting `i32` to `f32` causes a loss of precision (`i32` is 32 bits wide, but `f32`'s mantissa is only 23 bits wide) + --> $DIR/cast.rs:11:5 + | +LL | x0 as f32; + | ^^^^^^^^^ + | + = note: `-D clippy::cast-precision-loss` implied by `-D warnings` + +error: casting `i64` to `f32` causes a loss of precision (`i64` is 64 bits wide, but `f32`'s mantissa is only 23 bits wide) + --> $DIR/cast.rs:13:5 + | +LL | x1 as f32; + | ^^^^^^^^^ + +error: casting `i64` to `f64` causes a loss of precision (`i64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide) + --> $DIR/cast.rs:14:5 + | +LL | x1 as f64; + | ^^^^^^^^^ + +error: casting `u32` to `f32` causes a loss of precision (`u32` is 32 bits wide, but `f32`'s mantissa is only 23 bits wide) + --> $DIR/cast.rs:16:5 + | +LL | x2 as f32; + | ^^^^^^^^^ + +error: casting `u64` to `f32` causes a loss of precision (`u64` is 64 bits wide, but `f32`'s mantissa is only 23 bits wide) + --> $DIR/cast.rs:18:5 + | +LL | x3 as f32; + | ^^^^^^^^^ + +error: casting `u64` to `f64` causes a loss of precision (`u64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide) + --> $DIR/cast.rs:19:5 + | +LL | x3 as f64; + | ^^^^^^^^^ + +error: casting `f32` to `i32` may truncate the value + --> $DIR/cast.rs:21:5 + | +LL | 1f32 as i32; + | ^^^^^^^^^^^ + | + = note: `-D clippy::cast-possible-truncation` implied by `-D warnings` + +error: casting `f32` to `u32` may truncate the value + --> $DIR/cast.rs:22:5 + | +LL | 1f32 as u32; + | ^^^^^^^^^^^ + +error: casting `f32` to `u32` may lose the sign of the value + --> $DIR/cast.rs:22:5 + | +LL | 1f32 as u32; + | ^^^^^^^^^^^ + | + = note: `-D clippy::cast-sign-loss` implied by `-D warnings` + +error: casting `f64` to `f32` may truncate the value + --> $DIR/cast.rs:23:5 + | +LL | 1f64 as f32; + | ^^^^^^^^^^^ + +error: casting `i32` to `i8` may truncate the value + --> $DIR/cast.rs:24:5 + | +LL | 1i32 as i8; + | ^^^^^^^^^^ + +error: casting `i32` to `u8` may truncate the value + --> $DIR/cast.rs:25:5 + | +LL | 1i32 as u8; + | ^^^^^^^^^^ + +error: casting `f64` to `isize` may truncate the value + --> $DIR/cast.rs:26:5 + | +LL | 1f64 as isize; + | ^^^^^^^^^^^^^ + +error: casting `f64` to `usize` may truncate the value + --> $DIR/cast.rs:27:5 + | +LL | 1f64 as usize; + | ^^^^^^^^^^^^^ + +error: casting `f64` to `usize` may lose the sign of the value + --> $DIR/cast.rs:27:5 + | +LL | 1f64 as usize; + | ^^^^^^^^^^^^^ + +error: casting `u8` to `i8` may wrap around the value + --> $DIR/cast.rs:29:5 + | +LL | 1u8 as i8; + | ^^^^^^^^^ + | + = note: `-D clippy::cast-possible-wrap` implied by `-D warnings` + +error: casting `u16` to `i16` may wrap around the value + --> $DIR/cast.rs:30:5 + | +LL | 1u16 as i16; + | ^^^^^^^^^^^ + +error: casting `u32` to `i32` may wrap around the value + --> $DIR/cast.rs:31:5 + | +LL | 1u32 as i32; + | ^^^^^^^^^^^ + +error: casting `u64` to `i64` may wrap around the value + --> $DIR/cast.rs:32:5 + | +LL | 1u64 as i64; + | ^^^^^^^^^^^ + +error: casting `usize` to `isize` may wrap around the value + --> $DIR/cast.rs:33:5 + | +LL | 1usize as isize; + | ^^^^^^^^^^^^^^^ + +error: casting `i32` to `u32` may lose the sign of the value + --> $DIR/cast.rs:36:5 + | +LL | -1i32 as u32; + | ^^^^^^^^^^^^ + +error: casting `isize` to `usize` may lose the sign of the value + --> $DIR/cast.rs:38:5 + | +LL | -1isize as usize; + | ^^^^^^^^^^^^^^^^ + +error: aborting due to 22 previous errors + diff --git a/src/tools/clippy/tests/ui/cast_alignment.rs b/src/tools/clippy/tests/ui/cast_alignment.rs new file mode 100644 index 00000000000..4c08935639f --- /dev/null +++ b/src/tools/clippy/tests/ui/cast_alignment.rs @@ -0,0 +1,27 @@ +//! Test casts for alignment issues + +#![feature(rustc_private)] +extern crate libc; + +#[warn(clippy::cast_ptr_alignment)] +#[allow(clippy::no_effect, clippy::unnecessary_operation, clippy::cast_lossless)] +fn main() { + /* These should be warned against */ + + // cast to more-strictly-aligned type + (&1u8 as *const u8) as *const u16; + (&mut 1u8 as *mut u8) as *mut u16; + + /* These should be ok */ + + // not a pointer type + 1u8 as u16; + // cast to less-strictly-aligned type + (&1u16 as *const u16) as *const u8; + (&mut 1u16 as *mut u16) as *mut u8; + // For c_void, we should trust the user. See #2677 + (&1u32 as *const u32 as *const std::os::raw::c_void) as *const u32; + (&1u32 as *const u32 as *const libc::c_void) as *const u32; + // For ZST, we should trust the user. See #4256 + (&1u32 as *const u32 as *const ()) as *const u32; +} diff --git a/src/tools/clippy/tests/ui/cast_alignment.stderr b/src/tools/clippy/tests/ui/cast_alignment.stderr new file mode 100644 index 00000000000..79219f86155 --- /dev/null +++ b/src/tools/clippy/tests/ui/cast_alignment.stderr @@ -0,0 +1,16 @@ +error: casting from `*const u8` to a more-strictly-aligned pointer (`*const u16`) (1 < 2 bytes) + --> $DIR/cast_alignment.rs:12:5 + | +LL | (&1u8 as *const u8) as *const u16; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::cast-ptr-alignment` implied by `-D warnings` + +error: casting from `*mut u8` to a more-strictly-aligned pointer (`*mut u16`) (1 < 2 bytes) + --> $DIR/cast_alignment.rs:13:5 + | +LL | (&mut 1u8 as *mut u8) as *mut u16; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/cast_lossless_float.fixed b/src/tools/clippy/tests/ui/cast_lossless_float.fixed new file mode 100644 index 00000000000..709d58b596c --- /dev/null +++ b/src/tools/clippy/tests/ui/cast_lossless_float.fixed @@ -0,0 +1,45 @@ +// run-rustfix + +#![allow(clippy::no_effect, clippy::unnecessary_operation, dead_code)] +#![warn(clippy::cast_lossless)] + +fn main() { + // Test clippy::cast_lossless with casts to floating-point types + let x0 = 1i8; + f32::from(x0); + f64::from(x0); + let x1 = 1u8; + f32::from(x1); + f64::from(x1); + let x2 = 1i16; + f32::from(x2); + f64::from(x2); + let x3 = 1u16; + f32::from(x3); + f64::from(x3); + let x4 = 1i32; + f64::from(x4); + let x5 = 1u32; + f64::from(x5); + + // Test with casts from floating-point types + f64::from(1.0f32); +} + +// The lint would suggest using `f64::from(input)` here but the `XX::from` function is not const, +// so we skip the lint if the expression is in a const fn. +// See #3656 +const fn abc(input: f32) -> f64 { + input as f64 +} + +// Same as the above issue. We can't suggest `::from` in const fns in impls +mod cast_lossless_in_impl { + struct A; + + impl A { + pub const fn convert(x: f32) -> f64 { + x as f64 + } + } +} diff --git a/src/tools/clippy/tests/ui/cast_lossless_float.rs b/src/tools/clippy/tests/ui/cast_lossless_float.rs new file mode 100644 index 00000000000..eb0aab88642 --- /dev/null +++ b/src/tools/clippy/tests/ui/cast_lossless_float.rs @@ -0,0 +1,45 @@ +// run-rustfix + +#![allow(clippy::no_effect, clippy::unnecessary_operation, dead_code)] +#![warn(clippy::cast_lossless)] + +fn main() { + // Test clippy::cast_lossless with casts to floating-point types + let x0 = 1i8; + x0 as f32; + x0 as f64; + let x1 = 1u8; + x1 as f32; + x1 as f64; + let x2 = 1i16; + x2 as f32; + x2 as f64; + let x3 = 1u16; + x3 as f32; + x3 as f64; + let x4 = 1i32; + x4 as f64; + let x5 = 1u32; + x5 as f64; + + // Test with casts from floating-point types + 1.0f32 as f64; +} + +// The lint would suggest using `f64::from(input)` here but the `XX::from` function is not const, +// so we skip the lint if the expression is in a const fn. +// See #3656 +const fn abc(input: f32) -> f64 { + input as f64 +} + +// Same as the above issue. We can't suggest `::from` in const fns in impls +mod cast_lossless_in_impl { + struct A; + + impl A { + pub const fn convert(x: f32) -> f64 { + x as f64 + } + } +} diff --git a/src/tools/clippy/tests/ui/cast_lossless_float.stderr b/src/tools/clippy/tests/ui/cast_lossless_float.stderr new file mode 100644 index 00000000000..0ed09f3083c --- /dev/null +++ b/src/tools/clippy/tests/ui/cast_lossless_float.stderr @@ -0,0 +1,70 @@ +error: casting `i8` to `f32` may become silently lossy if you later change the type + --> $DIR/cast_lossless_float.rs:9:5 + | +LL | x0 as f32; + | ^^^^^^^^^ help: try: `f32::from(x0)` + | + = note: `-D clippy::cast-lossless` implied by `-D warnings` + +error: casting `i8` to `f64` may become silently lossy if you later change the type + --> $DIR/cast_lossless_float.rs:10:5 + | +LL | x0 as f64; + | ^^^^^^^^^ help: try: `f64::from(x0)` + +error: casting `u8` to `f32` may become silently lossy if you later change the type + --> $DIR/cast_lossless_float.rs:12:5 + | +LL | x1 as f32; + | ^^^^^^^^^ help: try: `f32::from(x1)` + +error: casting `u8` to `f64` may become silently lossy if you later change the type + --> $DIR/cast_lossless_float.rs:13:5 + | +LL | x1 as f64; + | ^^^^^^^^^ help: try: `f64::from(x1)` + +error: casting `i16` to `f32` may become silently lossy if you later change the type + --> $DIR/cast_lossless_float.rs:15:5 + | +LL | x2 as f32; + | ^^^^^^^^^ help: try: `f32::from(x2)` + +error: casting `i16` to `f64` may become silently lossy if you later change the type + --> $DIR/cast_lossless_float.rs:16:5 + | +LL | x2 as f64; + | ^^^^^^^^^ help: try: `f64::from(x2)` + +error: casting `u16` to `f32` may become silently lossy if you later change the type + --> $DIR/cast_lossless_float.rs:18:5 + | +LL | x3 as f32; + | ^^^^^^^^^ help: try: `f32::from(x3)` + +error: casting `u16` to `f64` may become silently lossy if you later change the type + --> $DIR/cast_lossless_float.rs:19:5 + | +LL | x3 as f64; + | ^^^^^^^^^ help: try: `f64::from(x3)` + +error: casting `i32` to `f64` may become silently lossy if you later change the type + --> $DIR/cast_lossless_float.rs:21:5 + | +LL | x4 as f64; + | ^^^^^^^^^ help: try: `f64::from(x4)` + +error: casting `u32` to `f64` may become silently lossy if you later change the type + --> $DIR/cast_lossless_float.rs:23:5 + | +LL | x5 as f64; + | ^^^^^^^^^ help: try: `f64::from(x5)` + +error: casting `f32` to `f64` may become silently lossy if you later change the type + --> $DIR/cast_lossless_float.rs:26:5 + | +LL | 1.0f32 as f64; + | ^^^^^^^^^^^^^ help: try: `f64::from(1.0f32)` + +error: aborting due to 11 previous errors + diff --git a/src/tools/clippy/tests/ui/cast_lossless_integer.fixed b/src/tools/clippy/tests/ui/cast_lossless_integer.fixed new file mode 100644 index 00000000000..03e49adb117 --- /dev/null +++ b/src/tools/clippy/tests/ui/cast_lossless_integer.fixed @@ -0,0 +1,47 @@ +// run-rustfix + +#![allow(clippy::no_effect, clippy::unnecessary_operation, dead_code)] +#![warn(clippy::cast_lossless)] + +fn main() { + // Test clippy::cast_lossless with casts to integer types + i16::from(1i8); + i32::from(1i8); + i64::from(1i8); + i16::from(1u8); + i32::from(1u8); + i64::from(1u8); + u16::from(1u8); + u32::from(1u8); + u64::from(1u8); + i32::from(1i16); + i64::from(1i16); + i32::from(1u16); + i64::from(1u16); + u32::from(1u16); + u64::from(1u16); + i64::from(1i32); + i64::from(1u32); + u64::from(1u32); + + // Test with an expression wrapped in parens + u16::from(1u8 + 1u8); +} + +// The lint would suggest using `f64::from(input)` here but the `XX::from` function is not const, +// so we skip the lint if the expression is in a const fn. +// See #3656 +const fn abc(input: u16) -> u32 { + input as u32 +} + +// Same as the above issue. We can't suggest `::from` in const fns in impls +mod cast_lossless_in_impl { + struct A; + + impl A { + pub const fn convert(x: u32) -> u64 { + x as u64 + } + } +} diff --git a/src/tools/clippy/tests/ui/cast_lossless_integer.rs b/src/tools/clippy/tests/ui/cast_lossless_integer.rs new file mode 100644 index 00000000000..6a984d24596 --- /dev/null +++ b/src/tools/clippy/tests/ui/cast_lossless_integer.rs @@ -0,0 +1,47 @@ +// run-rustfix + +#![allow(clippy::no_effect, clippy::unnecessary_operation, dead_code)] +#![warn(clippy::cast_lossless)] + +fn main() { + // Test clippy::cast_lossless with casts to integer types + 1i8 as i16; + 1i8 as i32; + 1i8 as i64; + 1u8 as i16; + 1u8 as i32; + 1u8 as i64; + 1u8 as u16; + 1u8 as u32; + 1u8 as u64; + 1i16 as i32; + 1i16 as i64; + 1u16 as i32; + 1u16 as i64; + 1u16 as u32; + 1u16 as u64; + 1i32 as i64; + 1u32 as i64; + 1u32 as u64; + + // Test with an expression wrapped in parens + (1u8 + 1u8) as u16; +} + +// The lint would suggest using `f64::from(input)` here but the `XX::from` function is not const, +// so we skip the lint if the expression is in a const fn. +// See #3656 +const fn abc(input: u16) -> u32 { + input as u32 +} + +// Same as the above issue. We can't suggest `::from` in const fns in impls +mod cast_lossless_in_impl { + struct A; + + impl A { + pub const fn convert(x: u32) -> u64 { + x as u64 + } + } +} diff --git a/src/tools/clippy/tests/ui/cast_lossless_integer.stderr b/src/tools/clippy/tests/ui/cast_lossless_integer.stderr new file mode 100644 index 00000000000..8e2890f9c28 --- /dev/null +++ b/src/tools/clippy/tests/ui/cast_lossless_integer.stderr @@ -0,0 +1,118 @@ +error: casting `i8` to `i16` may become silently lossy if you later change the type + --> $DIR/cast_lossless_integer.rs:8:5 + | +LL | 1i8 as i16; + | ^^^^^^^^^^ help: try: `i16::from(1i8)` + | + = note: `-D clippy::cast-lossless` implied by `-D warnings` + +error: casting `i8` to `i32` may become silently lossy if you later change the type + --> $DIR/cast_lossless_integer.rs:9:5 + | +LL | 1i8 as i32; + | ^^^^^^^^^^ help: try: `i32::from(1i8)` + +error: casting `i8` to `i64` may become silently lossy if you later change the type + --> $DIR/cast_lossless_integer.rs:10:5 + | +LL | 1i8 as i64; + | ^^^^^^^^^^ help: try: `i64::from(1i8)` + +error: casting `u8` to `i16` may become silently lossy if you later change the type + --> $DIR/cast_lossless_integer.rs:11:5 + | +LL | 1u8 as i16; + | ^^^^^^^^^^ help: try: `i16::from(1u8)` + +error: casting `u8` to `i32` may become silently lossy if you later change the type + --> $DIR/cast_lossless_integer.rs:12:5 + | +LL | 1u8 as i32; + | ^^^^^^^^^^ help: try: `i32::from(1u8)` + +error: casting `u8` to `i64` may become silently lossy if you later change the type + --> $DIR/cast_lossless_integer.rs:13:5 + | +LL | 1u8 as i64; + | ^^^^^^^^^^ help: try: `i64::from(1u8)` + +error: casting `u8` to `u16` may become silently lossy if you later change the type + --> $DIR/cast_lossless_integer.rs:14:5 + | +LL | 1u8 as u16; + | ^^^^^^^^^^ help: try: `u16::from(1u8)` + +error: casting `u8` to `u32` may become silently lossy if you later change the type + --> $DIR/cast_lossless_integer.rs:15:5 + | +LL | 1u8 as u32; + | ^^^^^^^^^^ help: try: `u32::from(1u8)` + +error: casting `u8` to `u64` may become silently lossy if you later change the type + --> $DIR/cast_lossless_integer.rs:16:5 + | +LL | 1u8 as u64; + | ^^^^^^^^^^ help: try: `u64::from(1u8)` + +error: casting `i16` to `i32` may become silently lossy if you later change the type + --> $DIR/cast_lossless_integer.rs:17:5 + | +LL | 1i16 as i32; + | ^^^^^^^^^^^ help: try: `i32::from(1i16)` + +error: casting `i16` to `i64` may become silently lossy if you later change the type + --> $DIR/cast_lossless_integer.rs:18:5 + | +LL | 1i16 as i64; + | ^^^^^^^^^^^ help: try: `i64::from(1i16)` + +error: casting `u16` to `i32` may become silently lossy if you later change the type + --> $DIR/cast_lossless_integer.rs:19:5 + | +LL | 1u16 as i32; + | ^^^^^^^^^^^ help: try: `i32::from(1u16)` + +error: casting `u16` to `i64` may become silently lossy if you later change the type + --> $DIR/cast_lossless_integer.rs:20:5 + | +LL | 1u16 as i64; + | ^^^^^^^^^^^ help: try: `i64::from(1u16)` + +error: casting `u16` to `u32` may become silently lossy if you later change the type + --> $DIR/cast_lossless_integer.rs:21:5 + | +LL | 1u16 as u32; + | ^^^^^^^^^^^ help: try: `u32::from(1u16)` + +error: casting `u16` to `u64` may become silently lossy if you later change the type + --> $DIR/cast_lossless_integer.rs:22:5 + | +LL | 1u16 as u64; + | ^^^^^^^^^^^ help: try: `u64::from(1u16)` + +error: casting `i32` to `i64` may become silently lossy if you later change the type + --> $DIR/cast_lossless_integer.rs:23:5 + | +LL | 1i32 as i64; + | ^^^^^^^^^^^ help: try: `i64::from(1i32)` + +error: casting `u32` to `i64` may become silently lossy if you later change the type + --> $DIR/cast_lossless_integer.rs:24:5 + | +LL | 1u32 as i64; + | ^^^^^^^^^^^ help: try: `i64::from(1u32)` + +error: casting `u32` to `u64` may become silently lossy if you later change the type + --> $DIR/cast_lossless_integer.rs:25:5 + | +LL | 1u32 as u64; + | ^^^^^^^^^^^ help: try: `u64::from(1u32)` + +error: casting `u8` to `u16` may become silently lossy if you later change the type + --> $DIR/cast_lossless_integer.rs:28:5 + | +LL | (1u8 + 1u8) as u16; + | ^^^^^^^^^^^^^^^^^^ help: try: `u16::from(1u8 + 1u8)` + +error: aborting due to 19 previous errors + diff --git a/src/tools/clippy/tests/ui/cast_ref_to_mut.rs b/src/tools/clippy/tests/ui/cast_ref_to_mut.rs new file mode 100644 index 00000000000..089e5cfabe4 --- /dev/null +++ b/src/tools/clippy/tests/ui/cast_ref_to_mut.rs @@ -0,0 +1,31 @@ +#![warn(clippy::cast_ref_to_mut)] +#![allow(clippy::no_effect)] + +extern "C" { + // N.B., mutability can be easily incorrect in FFI calls -- as + // in C, the default is mutable pointers. + fn ffi(c: *mut u8); + fn int_ffi(c: *mut i32); +} + +fn main() { + let s = String::from("Hello"); + let a = &s; + unsafe { + let num = &3i32; + let mut_num = &mut 3i32; + // Should be warned against + (*(a as *const _ as *mut String)).push_str(" world"); + *(a as *const _ as *mut _) = String::from("Replaced"); + *(a as *const _ as *mut String) += " world"; + // Shouldn't be warned against + println!("{}", *(num as *const _ as *const i16)); + println!("{}", *(mut_num as *mut _ as *mut i16)); + ffi(a.as_ptr() as *mut _); + int_ffi(num as *const _ as *mut _); + int_ffi(&3 as *const _ as *mut _); + let mut value = 3; + let value: *const i32 = &mut value; + *(value as *const i16 as *mut i16) = 42; + } +} diff --git a/src/tools/clippy/tests/ui/cast_ref_to_mut.stderr b/src/tools/clippy/tests/ui/cast_ref_to_mut.stderr new file mode 100644 index 00000000000..aacd99437d9 --- /dev/null +++ b/src/tools/clippy/tests/ui/cast_ref_to_mut.stderr @@ -0,0 +1,22 @@ +error: casting `&T` to `&mut T` may cause undefined behavior, consider instead using an `UnsafeCell` + --> $DIR/cast_ref_to_mut.rs:18:9 + | +LL | (*(a as *const _ as *mut String)).push_str(" world"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::cast-ref-to-mut` implied by `-D warnings` + +error: casting `&T` to `&mut T` may cause undefined behavior, consider instead using an `UnsafeCell` + --> $DIR/cast_ref_to_mut.rs:19:9 + | +LL | *(a as *const _ as *mut _) = String::from("Replaced"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: casting `&T` to `&mut T` may cause undefined behavior, consider instead using an `UnsafeCell` + --> $DIR/cast_ref_to_mut.rs:20:9 + | +LL | *(a as *const _ as *mut String) += " world"; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/cast_size.rs b/src/tools/clippy/tests/ui/cast_size.rs new file mode 100644 index 00000000000..595109be46b --- /dev/null +++ b/src/tools/clippy/tests/ui/cast_size.rs @@ -0,0 +1,35 @@ +// ignore-32bit +#[warn( + clippy::cast_precision_loss, + clippy::cast_possible_truncation, + clippy::cast_sign_loss, + clippy::cast_possible_wrap, + clippy::cast_lossless +)] +#[allow(clippy::no_effect, clippy::unnecessary_operation)] +fn main() { + // Casting from *size + 1isize as i8; + let x0 = 1isize; + let x1 = 1usize; + x0 as f64; + x1 as f64; + x0 as f32; + x1 as f32; + 1isize as i32; + 1isize as u32; + 1usize as u32; + 1usize as i32; + // Casting to *size + 1i64 as isize; + 1i64 as usize; + 1u64 as isize; + 1u64 as usize; + 1u32 as isize; + 1u32 as usize; // Should not trigger any lint + 1i32 as isize; // Neither should this + 1i32 as usize; + // Big integer literal to float + 999_999_999 as f32; + 9_999_999_999_999_999usize as f64; +} diff --git a/src/tools/clippy/tests/ui/cast_size.stderr b/src/tools/clippy/tests/ui/cast_size.stderr new file mode 100644 index 00000000000..95552f2e285 --- /dev/null +++ b/src/tools/clippy/tests/ui/cast_size.stderr @@ -0,0 +1,116 @@ +error: casting `isize` to `i8` may truncate the value + --> $DIR/cast_size.rs:12:5 + | +LL | 1isize as i8; + | ^^^^^^^^^^^^ + | + = note: `-D clippy::cast-possible-truncation` implied by `-D warnings` + +error: casting `isize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`isize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide) + --> $DIR/cast_size.rs:15:5 + | +LL | x0 as f64; + | ^^^^^^^^^ + | + = note: `-D clippy::cast-precision-loss` implied by `-D warnings` + +error: casting `usize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`usize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide) + --> $DIR/cast_size.rs:16:5 + | +LL | x1 as f64; + | ^^^^^^^^^ + +error: casting `isize` to `f32` causes a loss of precision (`isize` is 32 or 64 bits wide, but `f32`'s mantissa is only 23 bits wide) + --> $DIR/cast_size.rs:17:5 + | +LL | x0 as f32; + | ^^^^^^^^^ + +error: casting `usize` to `f32` causes a loss of precision (`usize` is 32 or 64 bits wide, but `f32`'s mantissa is only 23 bits wide) + --> $DIR/cast_size.rs:18:5 + | +LL | x1 as f32; + | ^^^^^^^^^ + +error: casting `isize` to `i32` may truncate the value on targets with 64-bit wide pointers + --> $DIR/cast_size.rs:19:5 + | +LL | 1isize as i32; + | ^^^^^^^^^^^^^ + +error: casting `isize` to `u32` may truncate the value on targets with 64-bit wide pointers + --> $DIR/cast_size.rs:20:5 + | +LL | 1isize as u32; + | ^^^^^^^^^^^^^ + +error: casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers + --> $DIR/cast_size.rs:21:5 + | +LL | 1usize as u32; + | ^^^^^^^^^^^^^ + +error: casting `usize` to `i32` may truncate the value on targets with 64-bit wide pointers + --> $DIR/cast_size.rs:22:5 + | +LL | 1usize as i32; + | ^^^^^^^^^^^^^ + +error: casting `usize` to `i32` may wrap around the value on targets with 32-bit wide pointers + --> $DIR/cast_size.rs:22:5 + | +LL | 1usize as i32; + | ^^^^^^^^^^^^^ + | + = note: `-D clippy::cast-possible-wrap` implied by `-D warnings` + +error: casting `i64` to `isize` may truncate the value on targets with 32-bit wide pointers + --> $DIR/cast_size.rs:24:5 + | +LL | 1i64 as isize; + | ^^^^^^^^^^^^^ + +error: casting `i64` to `usize` may truncate the value on targets with 32-bit wide pointers + --> $DIR/cast_size.rs:25:5 + | +LL | 1i64 as usize; + | ^^^^^^^^^^^^^ + +error: casting `u64` to `isize` may truncate the value on targets with 32-bit wide pointers + --> $DIR/cast_size.rs:26:5 + | +LL | 1u64 as isize; + | ^^^^^^^^^^^^^ + +error: casting `u64` to `isize` may wrap around the value on targets with 64-bit wide pointers + --> $DIR/cast_size.rs:26:5 + | +LL | 1u64 as isize; + | ^^^^^^^^^^^^^ + +error: casting `u64` to `usize` may truncate the value on targets with 32-bit wide pointers + --> $DIR/cast_size.rs:27:5 + | +LL | 1u64 as usize; + | ^^^^^^^^^^^^^ + +error: casting `u32` to `isize` may wrap around the value on targets with 32-bit wide pointers + --> $DIR/cast_size.rs:28:5 + | +LL | 1u32 as isize; + | ^^^^^^^^^^^^^ + +error: casting `i32` to `f32` causes a loss of precision (`i32` is 32 bits wide, but `f32`'s mantissa is only 23 bits wide) + --> $DIR/cast_size.rs:33:5 + | +LL | 999_999_999 as f32; + | ^^^^^^^^^^^^^^^^^^ + +error: casting `usize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`usize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide) + --> $DIR/cast_size.rs:34:5 + | +LL | 9_999_999_999_999_999usize as f64; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 18 previous errors + diff --git a/src/tools/clippy/tests/ui/cast_size_32bit.rs b/src/tools/clippy/tests/ui/cast_size_32bit.rs new file mode 100644 index 00000000000..99aac6deca3 --- /dev/null +++ b/src/tools/clippy/tests/ui/cast_size_32bit.rs @@ -0,0 +1,35 @@ +// ignore-64bit +#[warn( + clippy::cast_precision_loss, + clippy::cast_possible_truncation, + clippy::cast_sign_loss, + clippy::cast_possible_wrap, + clippy::cast_lossless +)] +#[allow(clippy::no_effect, clippy::unnecessary_operation)] +fn main() { + // Casting from *size + 1isize as i8; + let x0 = 1isize; + let x1 = 1usize; + x0 as f64; + x1 as f64; + x0 as f32; + x1 as f32; + 1isize as i32; + 1isize as u32; + 1usize as u32; + 1usize as i32; + // Casting to *size + 1i64 as isize; + 1i64 as usize; + 1u64 as isize; + 1u64 as usize; + 1u32 as isize; + 1u32 as usize; // Should not trigger any lint + 1i32 as isize; // Neither should this + 1i32 as usize; + // Big integer literal to float + 999_999_999 as f32; + 3_999_999_999usize as f64; +} diff --git a/src/tools/clippy/tests/ui/cast_size_32bit.stderr b/src/tools/clippy/tests/ui/cast_size_32bit.stderr new file mode 100644 index 00000000000..2eec51895f5 --- /dev/null +++ b/src/tools/clippy/tests/ui/cast_size_32bit.stderr @@ -0,0 +1,132 @@ +error: casting `isize` to `i8` may truncate the value + --> $DIR/cast_size_32bit.rs:12:5 + | +LL | 1isize as i8; + | ^^^^^^^^^^^^ + | + = note: `-D clippy::cast-possible-truncation` implied by `-D warnings` + +error: casting `isize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`isize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide) + --> $DIR/cast_size_32bit.rs:15:5 + | +LL | x0 as f64; + | ^^^^^^^^^ + | + = note: `-D clippy::cast-precision-loss` implied by `-D warnings` + +error: casting `isize` to `f64` may become silently lossy if you later change the type + --> $DIR/cast_size_32bit.rs:15:5 + | +LL | x0 as f64; + | ^^^^^^^^^ help: try: `f64::from(x0)` + | + = note: `-D clippy::cast-lossless` implied by `-D warnings` + +error: casting `usize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`usize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide) + --> $DIR/cast_size_32bit.rs:16:5 + | +LL | x1 as f64; + | ^^^^^^^^^ + +error: casting `usize` to `f64` may become silently lossy if you later change the type + --> $DIR/cast_size_32bit.rs:16:5 + | +LL | x1 as f64; + | ^^^^^^^^^ help: try: `f64::from(x1)` + +error: casting `isize` to `f32` causes a loss of precision (`isize` is 32 or 64 bits wide, but `f32`'s mantissa is only 23 bits wide) + --> $DIR/cast_size_32bit.rs:17:5 + | +LL | x0 as f32; + | ^^^^^^^^^ + +error: casting `usize` to `f32` causes a loss of precision (`usize` is 32 or 64 bits wide, but `f32`'s mantissa is only 23 bits wide) + --> $DIR/cast_size_32bit.rs:18:5 + | +LL | x1 as f32; + | ^^^^^^^^^ + +error: casting `isize` to `i32` may truncate the value on targets with 64-bit wide pointers + --> $DIR/cast_size_32bit.rs:19:5 + | +LL | 1isize as i32; + | ^^^^^^^^^^^^^ + +error: casting `isize` to `u32` may truncate the value on targets with 64-bit wide pointers + --> $DIR/cast_size_32bit.rs:20:5 + | +LL | 1isize as u32; + | ^^^^^^^^^^^^^ + +error: casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers + --> $DIR/cast_size_32bit.rs:21:5 + | +LL | 1usize as u32; + | ^^^^^^^^^^^^^ + +error: casting `usize` to `i32` may truncate the value on targets with 64-bit wide pointers + --> $DIR/cast_size_32bit.rs:22:5 + | +LL | 1usize as i32; + | ^^^^^^^^^^^^^ + +error: casting `usize` to `i32` may wrap around the value on targets with 32-bit wide pointers + --> $DIR/cast_size_32bit.rs:22:5 + | +LL | 1usize as i32; + | ^^^^^^^^^^^^^ + | + = note: `-D clippy::cast-possible-wrap` implied by `-D warnings` + +error: casting `i64` to `isize` may truncate the value on targets with 32-bit wide pointers + --> $DIR/cast_size_32bit.rs:24:5 + | +LL | 1i64 as isize; + | ^^^^^^^^^^^^^ + +error: casting `i64` to `usize` may truncate the value on targets with 32-bit wide pointers + --> $DIR/cast_size_32bit.rs:25:5 + | +LL | 1i64 as usize; + | ^^^^^^^^^^^^^ + +error: casting `u64` to `isize` may truncate the value on targets with 32-bit wide pointers + --> $DIR/cast_size_32bit.rs:26:5 + | +LL | 1u64 as isize; + | ^^^^^^^^^^^^^ + +error: casting `u64` to `isize` may wrap around the value on targets with 64-bit wide pointers + --> $DIR/cast_size_32bit.rs:26:5 + | +LL | 1u64 as isize; + | ^^^^^^^^^^^^^ + +error: casting `u64` to `usize` may truncate the value on targets with 32-bit wide pointers + --> $DIR/cast_size_32bit.rs:27:5 + | +LL | 1u64 as usize; + | ^^^^^^^^^^^^^ + +error: casting `u32` to `isize` may wrap around the value on targets with 32-bit wide pointers + --> $DIR/cast_size_32bit.rs:28:5 + | +LL | 1u32 as isize; + | ^^^^^^^^^^^^^ + +error: casting `i32` to `f32` causes a loss of precision (`i32` is 32 bits wide, but `f32`'s mantissa is only 23 bits wide) + --> $DIR/cast_size_32bit.rs:33:5 + | +LL | 999_999_999 as f32; + | ^^^^^^^^^^^^^^^^^^ + +error: casting integer literal to `f64` is unnecessary + --> $DIR/cast_size_32bit.rs:34:5 + | +LL | 3_999_999_999usize as f64; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `3999999999_f64` + | + = note: `-D clippy::unnecessary-cast` implied by `-D warnings` + +error: aborting due to 20 previous errors + diff --git a/src/tools/clippy/tests/ui/cfg_attr_rustfmt.fixed b/src/tools/clippy/tests/ui/cfg_attr_rustfmt.fixed new file mode 100644 index 00000000000..4e583a25b94 --- /dev/null +++ b/src/tools/clippy/tests/ui/cfg_attr_rustfmt.fixed @@ -0,0 +1,31 @@ +// run-rustfix +#![feature(stmt_expr_attributes)] + +#![allow(unused, clippy::no_effect)] +#![warn(clippy::deprecated_cfg_attr)] + +// This doesn't get linted, see known problems +#![cfg_attr(rustfmt, rustfmt_skip)] + +#[rustfmt::skip] +trait Foo +{ +fn foo( +); +} + +fn skip_on_statements() { + #[rustfmt::skip] + 5+3; +} + +#[rustfmt::skip] +fn main() { + foo::f(); +} + +mod foo { + #![cfg_attr(rustfmt, rustfmt_skip)] + + pub fn f() {} +} diff --git a/src/tools/clippy/tests/ui/cfg_attr_rustfmt.rs b/src/tools/clippy/tests/ui/cfg_attr_rustfmt.rs new file mode 100644 index 00000000000..9c0fcf6fb45 --- /dev/null +++ b/src/tools/clippy/tests/ui/cfg_attr_rustfmt.rs @@ -0,0 +1,31 @@ +// run-rustfix +#![feature(stmt_expr_attributes)] + +#![allow(unused, clippy::no_effect)] +#![warn(clippy::deprecated_cfg_attr)] + +// This doesn't get linted, see known problems +#![cfg_attr(rustfmt, rustfmt_skip)] + +#[rustfmt::skip] +trait Foo +{ +fn foo( +); +} + +fn skip_on_statements() { + #[cfg_attr(rustfmt, rustfmt::skip)] + 5+3; +} + +#[cfg_attr(rustfmt, rustfmt_skip)] +fn main() { + foo::f(); +} + +mod foo { + #![cfg_attr(rustfmt, rustfmt_skip)] + + pub fn f() {} +} diff --git a/src/tools/clippy/tests/ui/cfg_attr_rustfmt.stderr b/src/tools/clippy/tests/ui/cfg_attr_rustfmt.stderr new file mode 100644 index 00000000000..c1efd47db90 --- /dev/null +++ b/src/tools/clippy/tests/ui/cfg_attr_rustfmt.stderr @@ -0,0 +1,16 @@ +error: `cfg_attr` is deprecated for rustfmt and got replaced by tool attributes + --> $DIR/cfg_attr_rustfmt.rs:18:5 + | +LL | #[cfg_attr(rustfmt, rustfmt::skip)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `#[rustfmt::skip]` + | + = note: `-D clippy::deprecated-cfg-attr` implied by `-D warnings` + +error: `cfg_attr` is deprecated for rustfmt and got replaced by tool attributes + --> $DIR/cfg_attr_rustfmt.rs:22:1 + | +LL | #[cfg_attr(rustfmt, rustfmt_skip)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `#[rustfmt::skip]` + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/char_lit_as_u8.rs b/src/tools/clippy/tests/ui/char_lit_as_u8.rs new file mode 100644 index 00000000000..0a53a3d6490 --- /dev/null +++ b/src/tools/clippy/tests/ui/char_lit_as_u8.rs @@ -0,0 +1,5 @@ +#![warn(clippy::char_lit_as_u8)] + +fn main() { + let _ = '❤' as u8; // no suggestion, since a byte literal won't work. +} diff --git a/src/tools/clippy/tests/ui/char_lit_as_u8.stderr b/src/tools/clippy/tests/ui/char_lit_as_u8.stderr new file mode 100644 index 00000000000..b9836d2f255 --- /dev/null +++ b/src/tools/clippy/tests/ui/char_lit_as_u8.stderr @@ -0,0 +1,11 @@ +error: casting a character literal to `u8` truncates + --> $DIR/char_lit_as_u8.rs:4:13 + | +LL | let _ = '❤' as u8; // no suggestion, since a byte literal won't work. + | ^^^^^^^^^ + | + = note: `-D clippy::char-lit-as-u8` implied by `-D warnings` + = note: `char` is four bytes wide, but `u8` is a single byte + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/char_lit_as_u8_suggestions.fixed b/src/tools/clippy/tests/ui/char_lit_as_u8_suggestions.fixed new file mode 100644 index 00000000000..3dc3cb4e757 --- /dev/null +++ b/src/tools/clippy/tests/ui/char_lit_as_u8_suggestions.fixed @@ -0,0 +1,10 @@ +// run-rustfix + +#![warn(clippy::char_lit_as_u8)] + +fn main() { + let _ = b'a'; + let _ = b'\n'; + let _ = b'\0'; + let _ = b'\x01'; +} diff --git a/src/tools/clippy/tests/ui/char_lit_as_u8_suggestions.rs b/src/tools/clippy/tests/ui/char_lit_as_u8_suggestions.rs new file mode 100644 index 00000000000..d379a023494 --- /dev/null +++ b/src/tools/clippy/tests/ui/char_lit_as_u8_suggestions.rs @@ -0,0 +1,10 @@ +// run-rustfix + +#![warn(clippy::char_lit_as_u8)] + +fn main() { + let _ = 'a' as u8; + let _ = '\n' as u8; + let _ = '\0' as u8; + let _ = '\x01' as u8; +} diff --git a/src/tools/clippy/tests/ui/char_lit_as_u8_suggestions.stderr b/src/tools/clippy/tests/ui/char_lit_as_u8_suggestions.stderr new file mode 100644 index 00000000000..bf7cb1607b4 --- /dev/null +++ b/src/tools/clippy/tests/ui/char_lit_as_u8_suggestions.stderr @@ -0,0 +1,35 @@ +error: casting a character literal to `u8` truncates + --> $DIR/char_lit_as_u8_suggestions.rs:6:13 + | +LL | let _ = 'a' as u8; + | ^^^^^^^^^ help: use a byte literal instead: `b'a'` + | + = note: `-D clippy::char-lit-as-u8` implied by `-D warnings` + = note: `char` is four bytes wide, but `u8` is a single byte + +error: casting a character literal to `u8` truncates + --> $DIR/char_lit_as_u8_suggestions.rs:7:13 + | +LL | let _ = '/n' as u8; + | ^^^^^^^^^^ help: use a byte literal instead: `b'/n'` + | + = note: `char` is four bytes wide, but `u8` is a single byte + +error: casting a character literal to `u8` truncates + --> $DIR/char_lit_as_u8_suggestions.rs:8:13 + | +LL | let _ = '/0' as u8; + | ^^^^^^^^^^ help: use a byte literal instead: `b'/0'` + | + = note: `char` is four bytes wide, but `u8` is a single byte + +error: casting a character literal to `u8` truncates + --> $DIR/char_lit_as_u8_suggestions.rs:9:13 + | +LL | let _ = '/x01' as u8; + | ^^^^^^^^^^^^ help: use a byte literal instead: `b'/x01'` + | + = note: `char` is four bytes wide, but `u8` is a single byte + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/checked_conversions.fixed b/src/tools/clippy/tests/ui/checked_conversions.fixed new file mode 100644 index 00000000000..7febd6f3761 --- /dev/null +++ b/src/tools/clippy/tests/ui/checked_conversions.fixed @@ -0,0 +1,106 @@ +// run-rustfix + +#![warn(clippy::checked_conversions)] +#![allow(clippy::cast_lossless)] +#![allow(dead_code)] +use std::convert::TryFrom; + +// Positive tests + +// Signed to unsigned + +fn i64_to_u32(value: i64) -> Option<u32> { + if u32::try_from(value).is_ok() { + Some(value as u32) + } else { + None + } +} + +fn i64_to_u16(value: i64) -> Option<u16> { + if u16::try_from(value).is_ok() { + Some(value as u16) + } else { + None + } +} + +fn isize_to_u8(value: isize) -> Option<u8> { + if u8::try_from(value).is_ok() { + Some(value as u8) + } else { + None + } +} + +// Signed to signed + +fn i64_to_i32(value: i64) -> Option<i32> { + if i32::try_from(value).is_ok() { + Some(value as i32) + } else { + None + } +} + +fn i64_to_i16(value: i64) -> Option<i16> { + if i16::try_from(value).is_ok() { + Some(value as i16) + } else { + None + } +} + +// Unsigned to X + +fn u32_to_i32(value: u32) -> Option<i32> { + if i32::try_from(value).is_ok() { + Some(value as i32) + } else { + None + } +} + +fn usize_to_isize(value: usize) -> isize { + if isize::try_from(value).is_ok() && value as i32 == 5 { + 5 + } else { + 1 + } +} + +fn u32_to_u16(value: u32) -> isize { + if u16::try_from(value).is_ok() && value as i32 == 5 { + 5 + } else { + 1 + } +} + +// Negative tests + +fn no_i64_to_i32(value: i64) -> Option<i32> { + if value <= (i32::max_value() as i64) && value >= 0 { + Some(value as i32) + } else { + None + } +} + +fn no_isize_to_u8(value: isize) -> Option<u8> { + if value <= (u8::max_value() as isize) && value >= (u8::min_value() as isize) { + Some(value as u8) + } else { + None + } +} + +fn i8_to_u8(value: i8) -> Option<u8> { + if value >= 0 { + Some(value as u8) + } else { + None + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/checked_conversions.rs b/src/tools/clippy/tests/ui/checked_conversions.rs new file mode 100644 index 00000000000..a643354e243 --- /dev/null +++ b/src/tools/clippy/tests/ui/checked_conversions.rs @@ -0,0 +1,106 @@ +// run-rustfix + +#![warn(clippy::checked_conversions)] +#![allow(clippy::cast_lossless)] +#![allow(dead_code)] +use std::convert::TryFrom; + +// Positive tests + +// Signed to unsigned + +fn i64_to_u32(value: i64) -> Option<u32> { + if value <= (u32::max_value() as i64) && value >= 0 { + Some(value as u32) + } else { + None + } +} + +fn i64_to_u16(value: i64) -> Option<u16> { + if value <= i64::from(u16::max_value()) && value >= 0 { + Some(value as u16) + } else { + None + } +} + +fn isize_to_u8(value: isize) -> Option<u8> { + if value <= (u8::max_value() as isize) && value >= 0 { + Some(value as u8) + } else { + None + } +} + +// Signed to signed + +fn i64_to_i32(value: i64) -> Option<i32> { + if value <= (i32::max_value() as i64) && value >= (i32::min_value() as i64) { + Some(value as i32) + } else { + None + } +} + +fn i64_to_i16(value: i64) -> Option<i16> { + if value <= i64::from(i16::max_value()) && value >= i64::from(i16::min_value()) { + Some(value as i16) + } else { + None + } +} + +// Unsigned to X + +fn u32_to_i32(value: u32) -> Option<i32> { + if value <= i32::max_value() as u32 { + Some(value as i32) + } else { + None + } +} + +fn usize_to_isize(value: usize) -> isize { + if value <= isize::max_value() as usize && value as i32 == 5 { + 5 + } else { + 1 + } +} + +fn u32_to_u16(value: u32) -> isize { + if value <= u16::max_value() as u32 && value as i32 == 5 { + 5 + } else { + 1 + } +} + +// Negative tests + +fn no_i64_to_i32(value: i64) -> Option<i32> { + if value <= (i32::max_value() as i64) && value >= 0 { + Some(value as i32) + } else { + None + } +} + +fn no_isize_to_u8(value: isize) -> Option<u8> { + if value <= (u8::max_value() as isize) && value >= (u8::min_value() as isize) { + Some(value as u8) + } else { + None + } +} + +fn i8_to_u8(value: i8) -> Option<u8> { + if value >= 0 { + Some(value as u8) + } else { + None + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/checked_conversions.stderr b/src/tools/clippy/tests/ui/checked_conversions.stderr new file mode 100644 index 00000000000..f678f009621 --- /dev/null +++ b/src/tools/clippy/tests/ui/checked_conversions.stderr @@ -0,0 +1,52 @@ +error: Checked cast can be simplified. + --> $DIR/checked_conversions.rs:13:8 + | +LL | if value <= (u32::max_value() as i64) && value >= 0 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()` + | + = note: `-D clippy::checked-conversions` implied by `-D warnings` + +error: Checked cast can be simplified. + --> $DIR/checked_conversions.rs:21:8 + | +LL | if value <= i64::from(u16::max_value()) && value >= 0 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` + +error: Checked cast can be simplified. + --> $DIR/checked_conversions.rs:29:8 + | +LL | if value <= (u8::max_value() as isize) && value >= 0 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u8::try_from(value).is_ok()` + +error: Checked cast can be simplified. + --> $DIR/checked_conversions.rs:39:8 + | +LL | if value <= (i32::max_value() as i64) && value >= (i32::min_value() as i64) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` + +error: Checked cast can be simplified. + --> $DIR/checked_conversions.rs:47:8 + | +LL | if value <= i64::from(i16::max_value()) && value >= i64::from(i16::min_value()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i16::try_from(value).is_ok()` + +error: Checked cast can be simplified. + --> $DIR/checked_conversions.rs:57:8 + | +LL | if value <= i32::max_value() as u32 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` + +error: Checked cast can be simplified. + --> $DIR/checked_conversions.rs:65:8 + | +LL | if value <= isize::max_value() as usize && value as i32 == 5 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `isize::try_from(value).is_ok()` + +error: Checked cast can be simplified. + --> $DIR/checked_conversions.rs:73:8 + | +LL | if value <= u16::max_value() as u32 && value as i32 == 5 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` + +error: aborting due to 8 previous errors + diff --git a/src/tools/clippy/tests/ui/checked_conversions.stdout b/src/tools/clippy/tests/ui/checked_conversions.stdout new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/src/tools/clippy/tests/ui/checked_conversions.stdout diff --git a/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals.rs b/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals.rs new file mode 100644 index 00000000000..c986c992a07 --- /dev/null +++ b/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals.rs @@ -0,0 +1,54 @@ +#![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] +#![allow(clippy::if_same_then_else)] + +fn test_complex_conditions() { + let x: Result<(), ()> = Ok(()); + let y: Result<(), ()> = Ok(()); + if x.is_ok() && y.is_err() { + x.unwrap(); // unnecessary + x.unwrap_err(); // will panic + y.unwrap(); // will panic + y.unwrap_err(); // unnecessary + } else { + // not statically determinable whether any of the following will always succeed or always fail: + x.unwrap(); + x.unwrap_err(); + y.unwrap(); + y.unwrap_err(); + } + + if x.is_ok() || y.is_ok() { + // not statically determinable whether any of the following will always succeed or always fail: + x.unwrap(); + y.unwrap(); + } else { + x.unwrap(); // will panic + x.unwrap_err(); // unnecessary + y.unwrap(); // will panic + y.unwrap_err(); // unnecessary + } + let z: Result<(), ()> = Ok(()); + if x.is_ok() && !(y.is_ok() || z.is_err()) { + x.unwrap(); // unnecessary + x.unwrap_err(); // will panic + y.unwrap(); // will panic + y.unwrap_err(); // unnecessary + z.unwrap(); // unnecessary + z.unwrap_err(); // will panic + } + if x.is_ok() || !(y.is_ok() && z.is_err()) { + // not statically determinable whether any of the following will always succeed or always fail: + x.unwrap(); + y.unwrap(); + z.unwrap(); + } else { + x.unwrap(); // will panic + x.unwrap_err(); // unnecessary + y.unwrap(); // unnecessary + y.unwrap_err(); // will panic + z.unwrap(); // will panic + z.unwrap_err(); // unnecessary + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals.stderr b/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals.stderr new file mode 100644 index 00000000000..dc666bab460 --- /dev/null +++ b/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals.stderr @@ -0,0 +1,192 @@ +error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. + --> $DIR/complex_conditionals.rs:8:9 + | +LL | if x.is_ok() && y.is_err() { + | --------- the check is happening here +LL | x.unwrap(); // unnecessary + | ^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/complex_conditionals.rs:1:35 + | +LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: This call to `unwrap_err()` will always panic. + --> $DIR/complex_conditionals.rs:9:9 + | +LL | if x.is_ok() && y.is_err() { + | --------- because of this check +LL | x.unwrap(); // unnecessary +LL | x.unwrap_err(); // will panic + | ^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/complex_conditionals.rs:1:9 + | +LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: This call to `unwrap()` will always panic. + --> $DIR/complex_conditionals.rs:10:9 + | +LL | if x.is_ok() && y.is_err() { + | ---------- because of this check +... +LL | y.unwrap(); // will panic + | ^^^^^^^^^^ + +error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. + --> $DIR/complex_conditionals.rs:11:9 + | +LL | if x.is_ok() && y.is_err() { + | ---------- the check is happening here +... +LL | y.unwrap_err(); // unnecessary + | ^^^^^^^^^^^^^^ + +error: This call to `unwrap()` will always panic. + --> $DIR/complex_conditionals.rs:25:9 + | +LL | if x.is_ok() || y.is_ok() { + | --------- because of this check +... +LL | x.unwrap(); // will panic + | ^^^^^^^^^^ + +error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. + --> $DIR/complex_conditionals.rs:26:9 + | +LL | if x.is_ok() || y.is_ok() { + | --------- the check is happening here +... +LL | x.unwrap_err(); // unnecessary + | ^^^^^^^^^^^^^^ + +error: This call to `unwrap()` will always panic. + --> $DIR/complex_conditionals.rs:27:9 + | +LL | if x.is_ok() || y.is_ok() { + | --------- because of this check +... +LL | y.unwrap(); // will panic + | ^^^^^^^^^^ + +error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. + --> $DIR/complex_conditionals.rs:28:9 + | +LL | if x.is_ok() || y.is_ok() { + | --------- the check is happening here +... +LL | y.unwrap_err(); // unnecessary + | ^^^^^^^^^^^^^^ + +error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. + --> $DIR/complex_conditionals.rs:32:9 + | +LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { + | --------- the check is happening here +LL | x.unwrap(); // unnecessary + | ^^^^^^^^^^ + +error: This call to `unwrap_err()` will always panic. + --> $DIR/complex_conditionals.rs:33:9 + | +LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { + | --------- because of this check +LL | x.unwrap(); // unnecessary +LL | x.unwrap_err(); // will panic + | ^^^^^^^^^^^^^^ + +error: This call to `unwrap()` will always panic. + --> $DIR/complex_conditionals.rs:34:9 + | +LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { + | --------- because of this check +... +LL | y.unwrap(); // will panic + | ^^^^^^^^^^ + +error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. + --> $DIR/complex_conditionals.rs:35:9 + | +LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { + | --------- the check is happening here +... +LL | y.unwrap_err(); // unnecessary + | ^^^^^^^^^^^^^^ + +error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. + --> $DIR/complex_conditionals.rs:36:9 + | +LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { + | ---------- the check is happening here +... +LL | z.unwrap(); // unnecessary + | ^^^^^^^^^^ + +error: This call to `unwrap_err()` will always panic. + --> $DIR/complex_conditionals.rs:37:9 + | +LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { + | ---------- because of this check +... +LL | z.unwrap_err(); // will panic + | ^^^^^^^^^^^^^^ + +error: This call to `unwrap()` will always panic. + --> $DIR/complex_conditionals.rs:45:9 + | +LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { + | --------- because of this check +... +LL | x.unwrap(); // will panic + | ^^^^^^^^^^ + +error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. + --> $DIR/complex_conditionals.rs:46:9 + | +LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { + | --------- the check is happening here +... +LL | x.unwrap_err(); // unnecessary + | ^^^^^^^^^^^^^^ + +error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. + --> $DIR/complex_conditionals.rs:47:9 + | +LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { + | --------- the check is happening here +... +LL | y.unwrap(); // unnecessary + | ^^^^^^^^^^ + +error: This call to `unwrap_err()` will always panic. + --> $DIR/complex_conditionals.rs:48:9 + | +LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { + | --------- because of this check +... +LL | y.unwrap_err(); // will panic + | ^^^^^^^^^^^^^^ + +error: This call to `unwrap()` will always panic. + --> $DIR/complex_conditionals.rs:49:9 + | +LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { + | ---------- because of this check +... +LL | z.unwrap(); // will panic + | ^^^^^^^^^^ + +error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. + --> $DIR/complex_conditionals.rs:50:9 + | +LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { + | ---------- the check is happening here +... +LL | z.unwrap_err(); // unnecessary + | ^^^^^^^^^^^^^^ + +error: aborting due to 20 previous errors + diff --git a/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals_nested.rs b/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals_nested.rs new file mode 100644 index 00000000000..2307996a48f --- /dev/null +++ b/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals_nested.rs @@ -0,0 +1,15 @@ +#![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] +#![allow(clippy::if_same_then_else)] + +fn test_nested() { + fn nested() { + let x = Some(()); + if x.is_some() { + x.unwrap(); // unnecessary + } else { + x.unwrap(); // will panic + } + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals_nested.stderr b/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals_nested.stderr new file mode 100644 index 00000000000..e4d085470c3 --- /dev/null +++ b/src/tools/clippy/tests/ui/checked_unwrap/complex_conditionals_nested.stderr @@ -0,0 +1,31 @@ +error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. + --> $DIR/complex_conditionals_nested.rs:8:13 + | +LL | if x.is_some() { + | ----------- the check is happening here +LL | x.unwrap(); // unnecessary + | ^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/complex_conditionals_nested.rs:1:35 + | +LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: This call to `unwrap()` will always panic. + --> $DIR/complex_conditionals_nested.rs:10:13 + | +LL | if x.is_some() { + | ----------- because of this check +... +LL | x.unwrap(); // will panic + | ^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/complex_conditionals_nested.rs:1:9 + | +LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.rs b/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.rs new file mode 100644 index 00000000000..b0fc26ff76d --- /dev/null +++ b/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.rs @@ -0,0 +1,53 @@ +#![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] +#![allow(clippy::if_same_then_else)] + +macro_rules! m { + ($a:expr) => { + if $a.is_some() { + $a.unwrap(); // unnecessary + } + }; +} + +fn main() { + let x = Some(()); + if x.is_some() { + x.unwrap(); // unnecessary + } else { + x.unwrap(); // will panic + } + if x.is_none() { + x.unwrap(); // will panic + } else { + x.unwrap(); // unnecessary + } + m!(x); + let mut x: Result<(), ()> = Ok(()); + if x.is_ok() { + x.unwrap(); // unnecessary + x.unwrap_err(); // will panic + } else { + x.unwrap(); // will panic + x.unwrap_err(); // unnecessary + } + if x.is_err() { + x.unwrap(); // will panic + x.unwrap_err(); // unnecessary + } else { + x.unwrap(); // unnecessary + x.unwrap_err(); // will panic + } + if x.is_ok() { + x = Err(()); + x.unwrap(); // not unnecessary because of mutation of x + // it will always panic but the lint is not smart enough to see this (it only + // checks if conditions). + } else { + x = Ok(()); + x.unwrap_err(); // not unnecessary because of mutation of x + // it will always panic but the lint is not smart enough to see this (it + // only checks if conditions). + } + + assert!(x.is_ok(), "{:?}", x.unwrap_err()); // ok, it's a common test pattern +} diff --git a/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.stderr b/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.stderr new file mode 100644 index 00000000000..e40542e2e4f --- /dev/null +++ b/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.stderr @@ -0,0 +1,131 @@ +error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. + --> $DIR/simple_conditionals.rs:15:9 + | +LL | if x.is_some() { + | ----------- the check is happening here +LL | x.unwrap(); // unnecessary + | ^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/simple_conditionals.rs:1:35 + | +LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: This call to `unwrap()` will always panic. + --> $DIR/simple_conditionals.rs:17:9 + | +LL | if x.is_some() { + | ----------- because of this check +... +LL | x.unwrap(); // will panic + | ^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/simple_conditionals.rs:1:9 + | +LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: This call to `unwrap()` will always panic. + --> $DIR/simple_conditionals.rs:20:9 + | +LL | if x.is_none() { + | ----------- because of this check +LL | x.unwrap(); // will panic + | ^^^^^^^^^^ + +error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. + --> $DIR/simple_conditionals.rs:22:9 + | +LL | if x.is_none() { + | ----------- the check is happening here +... +LL | x.unwrap(); // unnecessary + | ^^^^^^^^^^ + +error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. + --> $DIR/simple_conditionals.rs:7:13 + | +LL | if $a.is_some() { + | ------------ the check is happening here +LL | $a.unwrap(); // unnecessary + | ^^^^^^^^^^^ +... +LL | m!(x); + | ------ in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. + --> $DIR/simple_conditionals.rs:27:9 + | +LL | if x.is_ok() { + | --------- the check is happening here +LL | x.unwrap(); // unnecessary + | ^^^^^^^^^^ + +error: This call to `unwrap_err()` will always panic. + --> $DIR/simple_conditionals.rs:28:9 + | +LL | if x.is_ok() { + | --------- because of this check +LL | x.unwrap(); // unnecessary +LL | x.unwrap_err(); // will panic + | ^^^^^^^^^^^^^^ + +error: This call to `unwrap()` will always panic. + --> $DIR/simple_conditionals.rs:30:9 + | +LL | if x.is_ok() { + | --------- because of this check +... +LL | x.unwrap(); // will panic + | ^^^^^^^^^^ + +error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. + --> $DIR/simple_conditionals.rs:31:9 + | +LL | if x.is_ok() { + | --------- the check is happening here +... +LL | x.unwrap_err(); // unnecessary + | ^^^^^^^^^^^^^^ + +error: This call to `unwrap()` will always panic. + --> $DIR/simple_conditionals.rs:34:9 + | +LL | if x.is_err() { + | ---------- because of this check +LL | x.unwrap(); // will panic + | ^^^^^^^^^^ + +error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. + --> $DIR/simple_conditionals.rs:35:9 + | +LL | if x.is_err() { + | ---------- the check is happening here +LL | x.unwrap(); // will panic +LL | x.unwrap_err(); // unnecessary + | ^^^^^^^^^^^^^^ + +error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. + --> $DIR/simple_conditionals.rs:37:9 + | +LL | if x.is_err() { + | ---------- the check is happening here +... +LL | x.unwrap(); // unnecessary + | ^^^^^^^^^^ + +error: This call to `unwrap_err()` will always panic. + --> $DIR/simple_conditionals.rs:38:9 + | +LL | if x.is_err() { + | ---------- because of this check +... +LL | x.unwrap_err(); // will panic + | ^^^^^^^^^^^^^^ + +error: aborting due to 13 previous errors + diff --git a/src/tools/clippy/tests/ui/clone_on_copy_impl.rs b/src/tools/clippy/tests/ui/clone_on_copy_impl.rs new file mode 100644 index 00000000000..8f9f2a0db8c --- /dev/null +++ b/src/tools/clippy/tests/ui/clone_on_copy_impl.rs @@ -0,0 +1,22 @@ +use std::fmt; +use std::marker::PhantomData; + +pub struct Key<T> { + #[doc(hidden)] + pub __name: &'static str, + #[doc(hidden)] + pub __phantom: PhantomData<T>, +} + +impl<T> Copy for Key<T> {} + +impl<T> Clone for Key<T> { + fn clone(&self) -> Self { + Key { + __name: self.__name, + __phantom: self.__phantom, + } + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/clone_on_copy_mut.rs b/src/tools/clippy/tests/ui/clone_on_copy_mut.rs new file mode 100644 index 00000000000..5bfa256623b --- /dev/null +++ b/src/tools/clippy/tests/ui/clone_on_copy_mut.rs @@ -0,0 +1,18 @@ +pub fn dec_read_dec(i: &mut i32) -> i32 { + *i -= 1; + let ret = *i; + *i -= 1; + ret +} + +pub fn minus_1(i: &i32) -> i32 { + dec_read_dec(&mut i.clone()) +} + +fn main() { + let mut i = 10; + assert_eq!(minus_1(&i), 9); + assert_eq!(i, 10); + assert_eq!(dec_read_dec(&mut i), 9); + assert_eq!(i, 8); +} diff --git a/src/tools/clippy/tests/ui/cmp_nan.rs b/src/tools/clippy/tests/ui/cmp_nan.rs new file mode 100644 index 00000000000..64ca52b010a --- /dev/null +++ b/src/tools/clippy/tests/ui/cmp_nan.rs @@ -0,0 +1,34 @@ +const NAN_F32: f32 = f32::NAN; +const NAN_F64: f64 = f64::NAN; + +#[warn(clippy::cmp_nan)] +#[allow(clippy::float_cmp, clippy::no_effect, clippy::unnecessary_operation)] +fn main() { + let x = 5f32; + x == f32::NAN; + x != f32::NAN; + x < f32::NAN; + x > f32::NAN; + x <= f32::NAN; + x >= f32::NAN; + x == NAN_F32; + x != NAN_F32; + x < NAN_F32; + x > NAN_F32; + x <= NAN_F32; + x >= NAN_F32; + + let y = 0f64; + y == f64::NAN; + y != f64::NAN; + y < f64::NAN; + y > f64::NAN; + y <= f64::NAN; + y >= f64::NAN; + y == NAN_F64; + y != NAN_F64; + y < NAN_F64; + y > NAN_F64; + y <= NAN_F64; + y >= NAN_F64; +} diff --git a/src/tools/clippy/tests/ui/cmp_nan.stderr b/src/tools/clippy/tests/ui/cmp_nan.stderr new file mode 100644 index 00000000000..867516661a5 --- /dev/null +++ b/src/tools/clippy/tests/ui/cmp_nan.stderr @@ -0,0 +1,148 @@ +error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead + --> $DIR/cmp_nan.rs:8:5 + | +LL | x == f32::NAN; + | ^^^^^^^^^^^^^ + | + = note: `-D clippy::cmp-nan` implied by `-D warnings` + +error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead + --> $DIR/cmp_nan.rs:9:5 + | +LL | x != f32::NAN; + | ^^^^^^^^^^^^^ + +error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead + --> $DIR/cmp_nan.rs:10:5 + | +LL | x < f32::NAN; + | ^^^^^^^^^^^^ + +error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead + --> $DIR/cmp_nan.rs:11:5 + | +LL | x > f32::NAN; + | ^^^^^^^^^^^^ + +error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead + --> $DIR/cmp_nan.rs:12:5 + | +LL | x <= f32::NAN; + | ^^^^^^^^^^^^^ + +error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead + --> $DIR/cmp_nan.rs:13:5 + | +LL | x >= f32::NAN; + | ^^^^^^^^^^^^^ + +error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead + --> $DIR/cmp_nan.rs:14:5 + | +LL | x == NAN_F32; + | ^^^^^^^^^^^^ + +error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead + --> $DIR/cmp_nan.rs:15:5 + | +LL | x != NAN_F32; + | ^^^^^^^^^^^^ + +error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead + --> $DIR/cmp_nan.rs:16:5 + | +LL | x < NAN_F32; + | ^^^^^^^^^^^ + +error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead + --> $DIR/cmp_nan.rs:17:5 + | +LL | x > NAN_F32; + | ^^^^^^^^^^^ + +error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead + --> $DIR/cmp_nan.rs:18:5 + | +LL | x <= NAN_F32; + | ^^^^^^^^^^^^ + +error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead + --> $DIR/cmp_nan.rs:19:5 + | +LL | x >= NAN_F32; + | ^^^^^^^^^^^^ + +error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead + --> $DIR/cmp_nan.rs:22:5 + | +LL | y == f64::NAN; + | ^^^^^^^^^^^^^ + +error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead + --> $DIR/cmp_nan.rs:23:5 + | +LL | y != f64::NAN; + | ^^^^^^^^^^^^^ + +error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead + --> $DIR/cmp_nan.rs:24:5 + | +LL | y < f64::NAN; + | ^^^^^^^^^^^^ + +error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead + --> $DIR/cmp_nan.rs:25:5 + | +LL | y > f64::NAN; + | ^^^^^^^^^^^^ + +error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead + --> $DIR/cmp_nan.rs:26:5 + | +LL | y <= f64::NAN; + | ^^^^^^^^^^^^^ + +error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead + --> $DIR/cmp_nan.rs:27:5 + | +LL | y >= f64::NAN; + | ^^^^^^^^^^^^^ + +error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead + --> $DIR/cmp_nan.rs:28:5 + | +LL | y == NAN_F64; + | ^^^^^^^^^^^^ + +error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead + --> $DIR/cmp_nan.rs:29:5 + | +LL | y != NAN_F64; + | ^^^^^^^^^^^^ + +error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead + --> $DIR/cmp_nan.rs:30:5 + | +LL | y < NAN_F64; + | ^^^^^^^^^^^ + +error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead + --> $DIR/cmp_nan.rs:31:5 + | +LL | y > NAN_F64; + | ^^^^^^^^^^^ + +error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead + --> $DIR/cmp_nan.rs:32:5 + | +LL | y <= NAN_F64; + | ^^^^^^^^^^^^ + +error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead + --> $DIR/cmp_nan.rs:33:5 + | +LL | y >= NAN_F64; + | ^^^^^^^^^^^^ + +error: aborting due to 24 previous errors + diff --git a/src/tools/clippy/tests/ui/cmp_null.rs b/src/tools/clippy/tests/ui/cmp_null.rs new file mode 100644 index 00000000000..2d2d04178c3 --- /dev/null +++ b/src/tools/clippy/tests/ui/cmp_null.rs @@ -0,0 +1,17 @@ +#![warn(clippy::cmp_null)] +#![allow(unused_mut)] + +use std::ptr; + +fn main() { + let x = 0; + let p: *const usize = &x; + if p == ptr::null() { + println!("This is surprising!"); + } + let mut y = 0; + let mut m: *mut usize = &mut y; + if m == ptr::null_mut() { + println!("This is surprising, too!"); + } +} diff --git a/src/tools/clippy/tests/ui/cmp_null.stderr b/src/tools/clippy/tests/ui/cmp_null.stderr new file mode 100644 index 00000000000..b563a2ebec2 --- /dev/null +++ b/src/tools/clippy/tests/ui/cmp_null.stderr @@ -0,0 +1,16 @@ +error: Comparing with null is better expressed by the `.is_null()` method + --> $DIR/cmp_null.rs:9:8 + | +LL | if p == ptr::null() { + | ^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::cmp-null` implied by `-D warnings` + +error: Comparing with null is better expressed by the `.is_null()` method + --> $DIR/cmp_null.rs:14:8 + | +LL | if m == ptr::null_mut() { + | ^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.fixed b/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.fixed new file mode 100644 index 00000000000..05fb96339e3 --- /dev/null +++ b/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.fixed @@ -0,0 +1,72 @@ +// run-rustfix + +#[warn(clippy::cmp_owned)] +#[allow(clippy::unnecessary_operation, clippy::no_effect, unused_must_use, clippy::eq_op)] +fn main() { + fn with_to_string(x: &str) { + x != "foo"; + + "foo" != x; + } + + let x = "oh"; + + with_to_string(x); + + x != "foo"; + + x != "foo"; + + 42.to_string() == "42"; + + Foo == Foo; + + "abc".chars().filter(|c| *c != 'X'); + + "abc".chars().filter(|c| *c != 'X'); +} + +struct Foo; + +impl PartialEq for Foo { + // Allow this here, because it emits the lint + // without a suggestion. This is tested in + // `tests/ui/cmp_owned/without_suggestion.rs` + #[allow(clippy::cmp_owned)] + fn eq(&self, other: &Self) -> bool { + self.to_owned() == *other + } +} + +impl ToOwned for Foo { + type Owned = Bar; + fn to_owned(&self) -> Bar { + Bar + } +} + +#[derive(PartialEq)] +struct Bar; + +impl PartialEq<Foo> for Bar { + fn eq(&self, _: &Foo) -> bool { + true + } +} + +impl std::borrow::Borrow<Foo> for Bar { + fn borrow(&self) -> &Foo { + static FOO: Foo = Foo; + &FOO + } +} + +#[derive(PartialEq)] +struct Baz; + +impl ToOwned for Baz { + type Owned = Baz; + fn to_owned(&self) -> Baz { + Baz + } +} diff --git a/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.rs b/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.rs new file mode 100644 index 00000000000..0a02825ed82 --- /dev/null +++ b/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.rs @@ -0,0 +1,72 @@ +// run-rustfix + +#[warn(clippy::cmp_owned)] +#[allow(clippy::unnecessary_operation, clippy::no_effect, unused_must_use, clippy::eq_op)] +fn main() { + fn with_to_string(x: &str) { + x != "foo".to_string(); + + "foo".to_string() != x; + } + + let x = "oh"; + + with_to_string(x); + + x != "foo".to_owned(); + + x != String::from("foo"); + + 42.to_string() == "42"; + + Foo.to_owned() == Foo; + + "abc".chars().filter(|c| c.to_owned() != 'X'); + + "abc".chars().filter(|c| *c != 'X'); +} + +struct Foo; + +impl PartialEq for Foo { + // Allow this here, because it emits the lint + // without a suggestion. This is tested in + // `tests/ui/cmp_owned/without_suggestion.rs` + #[allow(clippy::cmp_owned)] + fn eq(&self, other: &Self) -> bool { + self.to_owned() == *other + } +} + +impl ToOwned for Foo { + type Owned = Bar; + fn to_owned(&self) -> Bar { + Bar + } +} + +#[derive(PartialEq)] +struct Bar; + +impl PartialEq<Foo> for Bar { + fn eq(&self, _: &Foo) -> bool { + true + } +} + +impl std::borrow::Borrow<Foo> for Bar { + fn borrow(&self) -> &Foo { + static FOO: Foo = Foo; + &FOO + } +} + +#[derive(PartialEq)] +struct Baz; + +impl ToOwned for Baz { + type Owned = Baz; + fn to_owned(&self) -> Baz { + Baz + } +} diff --git a/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.stderr b/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.stderr new file mode 100644 index 00000000000..2f333e6ea8e --- /dev/null +++ b/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.stderr @@ -0,0 +1,40 @@ +error: this creates an owned instance just for comparison + --> $DIR/with_suggestion.rs:7:14 + | +LL | x != "foo".to_string(); + | ^^^^^^^^^^^^^^^^^ help: try: `"foo"` + | + = note: `-D clippy::cmp-owned` implied by `-D warnings` + +error: this creates an owned instance just for comparison + --> $DIR/with_suggestion.rs:9:9 + | +LL | "foo".to_string() != x; + | ^^^^^^^^^^^^^^^^^ help: try: `"foo"` + +error: this creates an owned instance just for comparison + --> $DIR/with_suggestion.rs:16:10 + | +LL | x != "foo".to_owned(); + | ^^^^^^^^^^^^^^^^ help: try: `"foo"` + +error: this creates an owned instance just for comparison + --> $DIR/with_suggestion.rs:18:10 + | +LL | x != String::from("foo"); + | ^^^^^^^^^^^^^^^^^^^ help: try: `"foo"` + +error: this creates an owned instance just for comparison + --> $DIR/with_suggestion.rs:22:5 + | +LL | Foo.to_owned() == Foo; + | ^^^^^^^^^^^^^^ help: try: `Foo` + +error: this creates an owned instance just for comparison + --> $DIR/with_suggestion.rs:24:30 + | +LL | "abc".chars().filter(|c| c.to_owned() != 'X'); + | ^^^^^^^^^^^^ help: try: `*c` + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/cmp_owned/without_suggestion.rs b/src/tools/clippy/tests/ui/cmp_owned/without_suggestion.rs new file mode 100644 index 00000000000..9ab8795474c --- /dev/null +++ b/src/tools/clippy/tests/ui/cmp_owned/without_suggestion.rs @@ -0,0 +1,52 @@ +#[allow(clippy::unnecessary_operation)] + +fn main() { + let x = &Baz; + let y = &Baz; + y.to_owned() == *x; + + let x = &&Baz; + let y = &Baz; + y.to_owned() == **x; +} + +struct Foo; + +impl PartialEq for Foo { + fn eq(&self, other: &Self) -> bool { + self.to_owned() == *other + } +} + +impl ToOwned for Foo { + type Owned = Bar; + fn to_owned(&self) -> Bar { + Bar + } +} + +#[derive(PartialEq)] +struct Baz; + +impl ToOwned for Baz { + type Owned = Baz; + fn to_owned(&self) -> Baz { + Baz + } +} + +#[derive(PartialEq)] +struct Bar; + +impl PartialEq<Foo> for Bar { + fn eq(&self, _: &Foo) -> bool { + true + } +} + +impl std::borrow::Borrow<Foo> for Bar { + fn borrow(&self) -> &Foo { + static FOO: Foo = Foo; + &FOO + } +} diff --git a/src/tools/clippy/tests/ui/cmp_owned/without_suggestion.stderr b/src/tools/clippy/tests/ui/cmp_owned/without_suggestion.stderr new file mode 100644 index 00000000000..6e8a5ad2a17 --- /dev/null +++ b/src/tools/clippy/tests/ui/cmp_owned/without_suggestion.stderr @@ -0,0 +1,22 @@ +error: this creates an owned instance just for comparison + --> $DIR/without_suggestion.rs:6:5 + | +LL | y.to_owned() == *x; + | ^^^^^^^^^^^^^^^^^^ try implementing the comparison without allocating + | + = note: `-D clippy::cmp-owned` implied by `-D warnings` + +error: this creates an owned instance just for comparison + --> $DIR/without_suggestion.rs:10:5 + | +LL | y.to_owned() == **x; + | ^^^^^^^^^^^^^^^^^^^ try implementing the comparison without allocating + +error: this creates an owned instance just for comparison + --> $DIR/without_suggestion.rs:17:9 + | +LL | self.to_owned() == *other + | ^^^^^^^^^^^^^^^^^^^^^^^^^ try implementing the comparison without allocating + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/cognitive_complexity.rs b/src/tools/clippy/tests/ui/cognitive_complexity.rs new file mode 100644 index 00000000000..1d3fe405521 --- /dev/null +++ b/src/tools/clippy/tests/ui/cognitive_complexity.rs @@ -0,0 +1,395 @@ +#![allow(clippy::all)] +#![warn(clippy::cognitive_complexity)] +#![allow(unused)] + +#[rustfmt::skip] +fn main() { + if true { + println!("a"); + } + if true { + println!("a"); + } + if true { + println!("a"); + } + if true { + println!("a"); + } + if true { + println!("a"); + } + if true { + println!("a"); + } + if true { + println!("a"); + } + if true { + println!("a"); + } + if true { + println!("a"); + } + if true { + println!("a"); + } + if true { + println!("a"); + } + if true { + println!("a"); + } + if true { + println!("a"); + } + if true { + println!("a"); + } + if true { + println!("a"); + } + if true { + println!("a"); + } + if true { + println!("a"); + } + if true { + println!("a"); + } + if true { + println!("a"); + } + if true { + println!("a"); + } + if true { + println!("a"); + } + if true { + println!("a"); + } + if true { + println!("a"); + } + if true { + println!("a"); + } + if true { + println!("a"); + } + if true { + println!("a"); + } + if true { + println!("a"); + } +} + +#[clippy::cognitive_complexity = "1"] +fn kaboom() { + let n = 0; + 'a: for i in 0..20 { + 'b: for j in i..20 { + for k in j..20 { + if k == 5 { + break 'b; + } + if j == 3 && k == 6 { + continue 'a; + } + if k == j { + continue; + } + println!("bake"); + } + } + println!("cake"); + } +} + +fn bloo() { + match 42 { + 0 => println!("hi"), + 1 => println!("hai"), + 2 => println!("hey"), + 3 => println!("hallo"), + 4 => println!("hello"), + 5 => println!("salut"), + 6 => println!("good morning"), + 7 => println!("good evening"), + 8 => println!("good afternoon"), + 9 => println!("good night"), + 10 => println!("bonjour"), + 11 => println!("hej"), + 12 => println!("hej hej"), + 13 => println!("greetings earthling"), + 14 => println!("take us to you leader"), + 15 | 17 | 19 | 21 | 23 | 25 | 27 | 29 | 31 | 33 => println!("take us to you leader"), + 35 | 37 | 39 | 41 | 43 | 45 | 47 | 49 | 51 | 53 => println!("there is no undefined behavior"), + 55 | 57 | 59 | 61 | 63 | 65 | 67 | 69 | 71 | 73 => println!("I know borrow-fu"), + _ => println!("bye"), + } +} + +// Short circuiting operations don't increase the complexity of a function. +// Note that the minimum complexity of a function is 1. +#[clippy::cognitive_complexity = "1"] +fn lots_of_short_circuits() -> bool { + true && false && true && false && true && false && true +} + +#[clippy::cognitive_complexity = "1"] +fn lots_of_short_circuits2() -> bool { + true || false || true || false || true || false || true +} + +#[clippy::cognitive_complexity = "1"] +fn baa() { + let x = || match 99 { + 0 => 0, + 1 => 1, + 2 => 2, + 4 => 4, + 6 => 6, + 9 => 9, + _ => 42, + }; + if x() == 42 { + println!("x"); + } else { + println!("not x"); + } +} + +#[clippy::cognitive_complexity = "1"] +fn bar() { + match 99 { + 0 => println!("hi"), + _ => println!("bye"), + } +} + +#[test] +#[clippy::cognitive_complexity = "1"] +/// Tests are usually complex but simple at the same time. `clippy::cognitive_complexity` used to +/// give lots of false-positives in tests. +fn dont_warn_on_tests() { + match 99 { + 0 => println!("hi"), + _ => println!("bye"), + } +} + +#[clippy::cognitive_complexity = "1"] +fn barr() { + match 99 { + 0 => println!("hi"), + 1 => println!("bla"), + 2 | 3 => println!("blub"), + _ => println!("bye"), + } +} + +#[clippy::cognitive_complexity = "1"] +fn barr2() { + match 99 { + 0 => println!("hi"), + 1 => println!("bla"), + 2 | 3 => println!("blub"), + _ => println!("bye"), + } + match 99 { + 0 => println!("hi"), + 1 => println!("bla"), + 2 | 3 => println!("blub"), + _ => println!("bye"), + } +} + +#[clippy::cognitive_complexity = "1"] +fn barrr() { + match 99 { + 0 => println!("hi"), + 1 => panic!("bla"), + 2 | 3 => println!("blub"), + _ => println!("bye"), + } +} + +#[clippy::cognitive_complexity = "1"] +fn barrr2() { + match 99 { + 0 => println!("hi"), + 1 => panic!("bla"), + 2 | 3 => println!("blub"), + _ => println!("bye"), + } + match 99 { + 0 => println!("hi"), + 1 => panic!("bla"), + 2 | 3 => println!("blub"), + _ => println!("bye"), + } +} + +#[clippy::cognitive_complexity = "1"] +fn barrrr() { + match 99 { + 0 => println!("hi"), + 1 => println!("bla"), + 2 | 3 => panic!("blub"), + _ => println!("bye"), + } +} + +#[clippy::cognitive_complexity = "1"] +fn barrrr2() { + match 99 { + 0 => println!("hi"), + 1 => println!("bla"), + 2 | 3 => panic!("blub"), + _ => println!("bye"), + } + match 99 { + 0 => println!("hi"), + 1 => println!("bla"), + 2 | 3 => panic!("blub"), + _ => println!("bye"), + } +} + +#[clippy::cognitive_complexity = "1"] +fn cake() { + if 4 == 5 { + println!("yea"); + } else { + panic!("meh"); + } + println!("whee"); +} + +#[clippy::cognitive_complexity = "1"] +pub fn read_file(input_path: &str) -> String { + use std::fs::File; + use std::io::{Read, Write}; + use std::path::Path; + let mut file = match File::open(&Path::new(input_path)) { + Ok(f) => f, + Err(err) => { + panic!("Can't open {}: {}", input_path, err); + }, + }; + + let mut bytes = Vec::new(); + + match file.read_to_end(&mut bytes) { + Ok(..) => {}, + Err(_) => { + panic!("Can't read {}", input_path); + }, + }; + + match String::from_utf8(bytes) { + Ok(contents) => contents, + Err(_) => { + panic!("{} is not UTF-8 encoded", input_path); + }, + } +} + +enum Void {} + +#[clippy::cognitive_complexity = "1"] +fn void(void: Void) { + if true { + match void {} + } +} + +#[clippy::cognitive_complexity = "1"] +fn mcarton_sees_all() { + panic!("meh"); + panic!("möh"); +} + +#[clippy::cognitive_complexity = "1"] +fn try_() -> Result<i32, &'static str> { + match 5 { + 5 => Ok(5), + _ => return Err("bla"), + } +} + +#[clippy::cognitive_complexity = "1"] +fn try_again() -> Result<i32, &'static str> { + let _ = Ok(42)?; + let _ = Ok(43)?; + let _ = Ok(44)?; + let _ = Ok(45)?; + let _ = Ok(46)?; + let _ = Ok(47)?; + let _ = Ok(48)?; + let _ = Ok(49)?; + match 5 { + 5 => Ok(5), + _ => return Err("bla"), + } +} + +#[clippy::cognitive_complexity = "1"] +fn early() -> Result<i32, &'static str> { + return Ok(5); + return Ok(5); + return Ok(5); + return Ok(5); + return Ok(5); + return Ok(5); + return Ok(5); + return Ok(5); + return Ok(5); +} + +#[rustfmt::skip] +#[clippy::cognitive_complexity = "1"] +fn early_ret() -> i32 { + let a = if true { 42 } else { return 0; }; + let a = if a < 99 { 42 } else { return 0; }; + let a = if a < 99 { 42 } else { return 0; }; + let a = if a < 99 { 42 } else { return 0; }; + let a = if a < 99 { 42 } else { return 0; }; + let a = if a < 99 { 42 } else { return 0; }; + let a = if a < 99 { 42 } else { return 0; }; + let a = if a < 99 { 42 } else { return 0; }; + let a = if a < 99 { 42 } else { return 0; }; + let a = if a < 99 { 42 } else { return 0; }; + let a = if a < 99 { 42 } else { return 0; }; + let a = if a < 99 { 42 } else { return 0; }; + match 5 { + 5 => 5, + _ => return 6, + } +} + +#[clippy::cognitive_complexity = "1"] +fn closures() { + let x = |a: i32, b: i32| -> i32 { + if true { + println!("moo"); + } + + a + b + }; +} + +struct Moo; + +#[clippy::cognitive_complexity = "1"] +impl Moo { + fn moo(&self) { + if true { + println!("moo"); + } + } +} diff --git a/src/tools/clippy/tests/ui/cognitive_complexity.stderr b/src/tools/clippy/tests/ui/cognitive_complexity.stderr new file mode 100644 index 00000000000..a0ddc673abc --- /dev/null +++ b/src/tools/clippy/tests/ui/cognitive_complexity.stderr @@ -0,0 +1,139 @@ +error: the function has a cognitive complexity of (28/25) + --> $DIR/cognitive_complexity.rs:6:4 + | +LL | fn main() { + | ^^^^ + | + = note: `-D clippy::cognitive-complexity` implied by `-D warnings` + = help: you could split it up into multiple smaller functions + +error: the function has a cognitive complexity of (7/1) + --> $DIR/cognitive_complexity.rs:91:4 + | +LL | fn kaboom() { + | ^^^^^^ + | + = help: you could split it up into multiple smaller functions + +error: the function has a cognitive complexity of (2/1) + --> $DIR/cognitive_complexity.rs:149:4 + | +LL | fn baa() { + | ^^^ + | + = help: you could split it up into multiple smaller functions + +error: the function has a cognitive complexity of (2/1) + --> $DIR/cognitive_complexity.rs:150:13 + | +LL | let x = || match 99 { + | ^^ + | + = help: you could split it up into multiple smaller functions + +error: the function has a cognitive complexity of (2/1) + --> $DIR/cognitive_complexity.rs:167:4 + | +LL | fn bar() { + | ^^^ + | + = help: you could split it up into multiple smaller functions + +error: the function has a cognitive complexity of (2/1) + --> $DIR/cognitive_complexity.rs:186:4 + | +LL | fn barr() { + | ^^^^ + | + = help: you could split it up into multiple smaller functions + +error: the function has a cognitive complexity of (3/1) + --> $DIR/cognitive_complexity.rs:196:4 + | +LL | fn barr2() { + | ^^^^^ + | + = help: you could split it up into multiple smaller functions + +error: the function has a cognitive complexity of (2/1) + --> $DIR/cognitive_complexity.rs:212:4 + | +LL | fn barrr() { + | ^^^^^ + | + = help: you could split it up into multiple smaller functions + +error: the function has a cognitive complexity of (3/1) + --> $DIR/cognitive_complexity.rs:222:4 + | +LL | fn barrr2() { + | ^^^^^^ + | + = help: you could split it up into multiple smaller functions + +error: the function has a cognitive complexity of (2/1) + --> $DIR/cognitive_complexity.rs:238:4 + | +LL | fn barrrr() { + | ^^^^^^ + | + = help: you could split it up into multiple smaller functions + +error: the function has a cognitive complexity of (3/1) + --> $DIR/cognitive_complexity.rs:248:4 + | +LL | fn barrrr2() { + | ^^^^^^^ + | + = help: you could split it up into multiple smaller functions + +error: the function has a cognitive complexity of (2/1) + --> $DIR/cognitive_complexity.rs:264:4 + | +LL | fn cake() { + | ^^^^ + | + = help: you could split it up into multiple smaller functions + +error: the function has a cognitive complexity of (4/1) + --> $DIR/cognitive_complexity.rs:274:8 + | +LL | pub fn read_file(input_path: &str) -> String { + | ^^^^^^^^^ + | + = help: you could split it up into multiple smaller functions + +error: the function has a cognitive complexity of (2/1) + --> $DIR/cognitive_complexity.rs:305:4 + | +LL | fn void(void: Void) { + | ^^^^ + | + = help: you could split it up into multiple smaller functions + +error: the function has a cognitive complexity of (8/1) + --> $DIR/cognitive_complexity.rs:356:4 + | +LL | fn early_ret() -> i32 { + | ^^^^^^^^^ + | + = help: you could split it up into multiple smaller functions + +error: the function has a cognitive complexity of (2/1) + --> $DIR/cognitive_complexity.rs:377:13 + | +LL | let x = |a: i32, b: i32| -> i32 { + | ^^^^^^^^^^^^^^^^ + | + = help: you could split it up into multiple smaller functions + +error: the function has a cognitive complexity of (2/1) + --> $DIR/cognitive_complexity.rs:390:8 + | +LL | fn moo(&self) { + | ^^^ + | + = help: you could split it up into multiple smaller functions + +error: aborting due to 17 previous errors + diff --git a/src/tools/clippy/tests/ui/cognitive_complexity_attr_used.rs b/src/tools/clippy/tests/ui/cognitive_complexity_attr_used.rs new file mode 100644 index 00000000000..403eff566ed --- /dev/null +++ b/src/tools/clippy/tests/ui/cognitive_complexity_attr_used.rs @@ -0,0 +1,15 @@ +#![warn(clippy::cognitive_complexity)] +#![warn(unused)] + +fn main() { + kaboom(); +} + +#[clippy::cognitive_complexity = "0"] +fn kaboom() { + if 42 == 43 { + panic!(); + } else if "cake" == "lie" { + println!("what?"); + } +} diff --git a/src/tools/clippy/tests/ui/cognitive_complexity_attr_used.stderr b/src/tools/clippy/tests/ui/cognitive_complexity_attr_used.stderr new file mode 100644 index 00000000000..f5ff53dda60 --- /dev/null +++ b/src/tools/clippy/tests/ui/cognitive_complexity_attr_used.stderr @@ -0,0 +1,11 @@ +error: the function has a cognitive complexity of (3/0) + --> $DIR/cognitive_complexity_attr_used.rs:9:4 + | +LL | fn kaboom() { + | ^^^^^^ + | + = note: `-D clippy::cognitive-complexity` implied by `-D warnings` + = help: you could split it up into multiple smaller functions + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/collapsible_else_if.fixed b/src/tools/clippy/tests/ui/collapsible_else_if.fixed new file mode 100644 index 00000000000..ce2a1c28c8a --- /dev/null +++ b/src/tools/clippy/tests/ui/collapsible_else_if.fixed @@ -0,0 +1,66 @@ +// run-rustfix +#![allow(clippy::assertions_on_constants)] + +#[rustfmt::skip] +#[warn(clippy::collapsible_if)] +fn main() { + let x = "hello"; + let y = "world"; + // Collapse `else { if .. }` to `else if ..` + if x == "hello" { + print!("Hello "); + } else if y == "world" { + println!("world!") + } + + if x == "hello" { + print!("Hello "); + } else if let Some(42) = Some(42) { + println!("world!") + } + + if x == "hello" { + print!("Hello "); + } else if y == "world" { + println!("world") + } + else { + println!("!") + } + + if x == "hello" { + print!("Hello "); + } else if let Some(42) = Some(42) { + println!("world") + } + else { + println!("!") + } + + if let Some(42) = Some(42) { + print!("Hello "); + } else if let Some(42) = Some(42) { + println!("world") + } + else { + println!("!") + } + + if let Some(42) = Some(42) { + print!("Hello "); + } else if x == "hello" { + println!("world") + } + else { + println!("!") + } + + if let Some(42) = Some(42) { + print!("Hello "); + } else if let Some(42) = Some(42) { + println!("world") + } + else { + println!("!") + } +} diff --git a/src/tools/clippy/tests/ui/collapsible_else_if.rs b/src/tools/clippy/tests/ui/collapsible_else_if.rs new file mode 100644 index 00000000000..99c40b8d38e --- /dev/null +++ b/src/tools/clippy/tests/ui/collapsible_else_if.rs @@ -0,0 +1,80 @@ +// run-rustfix +#![allow(clippy::assertions_on_constants)] + +#[rustfmt::skip] +#[warn(clippy::collapsible_if)] +fn main() { + let x = "hello"; + let y = "world"; + // Collapse `else { if .. }` to `else if ..` + if x == "hello" { + print!("Hello "); + } else { + if y == "world" { + println!("world!") + } + } + + if x == "hello" { + print!("Hello "); + } else { + if let Some(42) = Some(42) { + println!("world!") + } + } + + if x == "hello" { + print!("Hello "); + } else { + if y == "world" { + println!("world") + } + else { + println!("!") + } + } + + if x == "hello" { + print!("Hello "); + } else { + if let Some(42) = Some(42) { + println!("world") + } + else { + println!("!") + } + } + + if let Some(42) = Some(42) { + print!("Hello "); + } else { + if let Some(42) = Some(42) { + println!("world") + } + else { + println!("!") + } + } + + if let Some(42) = Some(42) { + print!("Hello "); + } else { + if x == "hello" { + println!("world") + } + else { + println!("!") + } + } + + if let Some(42) = Some(42) { + print!("Hello "); + } else { + if let Some(42) = Some(42) { + println!("world") + } + else { + println!("!") + } + } +} diff --git a/src/tools/clippy/tests/ui/collapsible_else_if.stderr b/src/tools/clippy/tests/ui/collapsible_else_if.stderr new file mode 100644 index 00000000000..28048999e8e --- /dev/null +++ b/src/tools/clippy/tests/ui/collapsible_else_if.stderr @@ -0,0 +1,154 @@ +error: this `else { if .. }` block can be collapsed + --> $DIR/collapsible_else_if.rs:12:12 + | +LL | } else { + | ____________^ +LL | | if y == "world" { +LL | | println!("world!") +LL | | } +LL | | } + | |_____^ + | + = note: `-D clippy::collapsible-if` implied by `-D warnings` +help: try + | +LL | } else if y == "world" { +LL | println!("world!") +LL | } + | + +error: this `else { if .. }` block can be collapsed + --> $DIR/collapsible_else_if.rs:20:12 + | +LL | } else { + | ____________^ +LL | | if let Some(42) = Some(42) { +LL | | println!("world!") +LL | | } +LL | | } + | |_____^ + | +help: try + | +LL | } else if let Some(42) = Some(42) { +LL | println!("world!") +LL | } + | + +error: this `else { if .. }` block can be collapsed + --> $DIR/collapsible_else_if.rs:28:12 + | +LL | } else { + | ____________^ +LL | | if y == "world" { +LL | | println!("world") +LL | | } +... | +LL | | } +LL | | } + | |_____^ + | +help: try + | +LL | } else if y == "world" { +LL | println!("world") +LL | } +LL | else { +LL | println!("!") +LL | } + | + +error: this `else { if .. }` block can be collapsed + --> $DIR/collapsible_else_if.rs:39:12 + | +LL | } else { + | ____________^ +LL | | if let Some(42) = Some(42) { +LL | | println!("world") +LL | | } +... | +LL | | } +LL | | } + | |_____^ + | +help: try + | +LL | } else if let Some(42) = Some(42) { +LL | println!("world") +LL | } +LL | else { +LL | println!("!") +LL | } + | + +error: this `else { if .. }` block can be collapsed + --> $DIR/collapsible_else_if.rs:50:12 + | +LL | } else { + | ____________^ +LL | | if let Some(42) = Some(42) { +LL | | println!("world") +LL | | } +... | +LL | | } +LL | | } + | |_____^ + | +help: try + | +LL | } else if let Some(42) = Some(42) { +LL | println!("world") +LL | } +LL | else { +LL | println!("!") +LL | } + | + +error: this `else { if .. }` block can be collapsed + --> $DIR/collapsible_else_if.rs:61:12 + | +LL | } else { + | ____________^ +LL | | if x == "hello" { +LL | | println!("world") +LL | | } +... | +LL | | } +LL | | } + | |_____^ + | +help: try + | +LL | } else if x == "hello" { +LL | println!("world") +LL | } +LL | else { +LL | println!("!") +LL | } + | + +error: this `else { if .. }` block can be collapsed + --> $DIR/collapsible_else_if.rs:72:12 + | +LL | } else { + | ____________^ +LL | | if let Some(42) = Some(42) { +LL | | println!("world") +LL | | } +... | +LL | | } +LL | | } + | |_____^ + | +help: try + | +LL | } else if let Some(42) = Some(42) { +LL | println!("world") +LL | } +LL | else { +LL | println!("!") +LL | } + | + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/tests/ui/collapsible_if.fixed b/src/tools/clippy/tests/ui/collapsible_if.fixed new file mode 100644 index 00000000000..561283fc8e7 --- /dev/null +++ b/src/tools/clippy/tests/ui/collapsible_if.fixed @@ -0,0 +1,138 @@ +// run-rustfix +#![allow(clippy::assertions_on_constants)] + +#[rustfmt::skip] +#[warn(clippy::collapsible_if)] +fn main() { + let x = "hello"; + let y = "world"; + if x == "hello" && y == "world" { + println!("Hello world!"); + } + + if (x == "hello" || x == "world") && (y == "world" || y == "hello") { + println!("Hello world!"); + } + + if x == "hello" && x == "world" && (y == "world" || y == "hello") { + println!("Hello world!"); + } + + if (x == "hello" || x == "world") && y == "world" && y == "hello" { + println!("Hello world!"); + } + + if x == "hello" && x == "world" && y == "world" && y == "hello" { + println!("Hello world!"); + } + + if 42 == 1337 && 'a' != 'A' { + println!("world!") + } + + // Works because any if with an else statement cannot be collapsed. + if x == "hello" { + if y == "world" { + println!("Hello world!"); + } + } else { + println!("Not Hello world"); + } + + if x == "hello" { + if y == "world" { + println!("Hello world!"); + } else { + println!("Hello something else"); + } + } + + if x == "hello" { + print!("Hello "); + if y == "world" { + println!("world!") + } + } + + if true { + } else { + assert!(true); // assert! is just an `if` + } + + + // The following tests check for the fix of https://github.com/rust-lang/rust-clippy/issues/798 + if x == "hello" {// Not collapsible + if y == "world" { + println!("Hello world!"); + } + } + + if x == "hello" { // Not collapsible + if y == "world" { + println!("Hello world!"); + } + } + + if x == "hello" { + // Not collapsible + if y == "world" { + println!("Hello world!"); + } + } + + if x == "hello" && y == "world" { // Collapsible + println!("Hello world!"); + } + + if x == "hello" { + print!("Hello "); + } else { + // Not collapsible + if y == "world" { + println!("world!") + } + } + + if x == "hello" { + print!("Hello "); + } else { + // Not collapsible + if let Some(42) = Some(42) { + println!("world!") + } + } + + if x == "hello" { + /* Not collapsible */ + if y == "world" { + println!("Hello world!"); + } + } + + if x == "hello" { /* Not collapsible */ + if y == "world" { + println!("Hello world!"); + } + } + + // Test behavior wrt. `let_chains`. + // None of the cases below should be collapsed. + fn truth() -> bool { true } + + // Prefix: + if let 0 = 1 { + if truth() {} + } + + // Suffix: + if truth() { + if let 0 = 1 {} + } + + // Midfix: + if truth() { + if let 0 = 1 { + if truth() {} + } + } +} diff --git a/src/tools/clippy/tests/ui/collapsible_if.rs b/src/tools/clippy/tests/ui/collapsible_if.rs new file mode 100644 index 00000000000..dc9d9b451c0 --- /dev/null +++ b/src/tools/clippy/tests/ui/collapsible_if.rs @@ -0,0 +1,152 @@ +// run-rustfix +#![allow(clippy::assertions_on_constants)] + +#[rustfmt::skip] +#[warn(clippy::collapsible_if)] +fn main() { + let x = "hello"; + let y = "world"; + if x == "hello" { + if y == "world" { + println!("Hello world!"); + } + } + + if x == "hello" || x == "world" { + if y == "world" || y == "hello" { + println!("Hello world!"); + } + } + + if x == "hello" && x == "world" { + if y == "world" || y == "hello" { + println!("Hello world!"); + } + } + + if x == "hello" || x == "world" { + if y == "world" && y == "hello" { + println!("Hello world!"); + } + } + + if x == "hello" && x == "world" { + if y == "world" && y == "hello" { + println!("Hello world!"); + } + } + + if 42 == 1337 { + if 'a' != 'A' { + println!("world!") + } + } + + // Works because any if with an else statement cannot be collapsed. + if x == "hello" { + if y == "world" { + println!("Hello world!"); + } + } else { + println!("Not Hello world"); + } + + if x == "hello" { + if y == "world" { + println!("Hello world!"); + } else { + println!("Hello something else"); + } + } + + if x == "hello" { + print!("Hello "); + if y == "world" { + println!("world!") + } + } + + if true { + } else { + assert!(true); // assert! is just an `if` + } + + + // The following tests check for the fix of https://github.com/rust-lang/rust-clippy/issues/798 + if x == "hello" {// Not collapsible + if y == "world" { + println!("Hello world!"); + } + } + + if x == "hello" { // Not collapsible + if y == "world" { + println!("Hello world!"); + } + } + + if x == "hello" { + // Not collapsible + if y == "world" { + println!("Hello world!"); + } + } + + if x == "hello" { + if y == "world" { // Collapsible + println!("Hello world!"); + } + } + + if x == "hello" { + print!("Hello "); + } else { + // Not collapsible + if y == "world" { + println!("world!") + } + } + + if x == "hello" { + print!("Hello "); + } else { + // Not collapsible + if let Some(42) = Some(42) { + println!("world!") + } + } + + if x == "hello" { + /* Not collapsible */ + if y == "world" { + println!("Hello world!"); + } + } + + if x == "hello" { /* Not collapsible */ + if y == "world" { + println!("Hello world!"); + } + } + + // Test behavior wrt. `let_chains`. + // None of the cases below should be collapsed. + fn truth() -> bool { true } + + // Prefix: + if let 0 = 1 { + if truth() {} + } + + // Suffix: + if truth() { + if let 0 = 1 {} + } + + // Midfix: + if truth() { + if let 0 = 1 { + if truth() {} + } + } +} diff --git a/src/tools/clippy/tests/ui/collapsible_if.stderr b/src/tools/clippy/tests/ui/collapsible_if.stderr new file mode 100644 index 00000000000..6440ff41be8 --- /dev/null +++ b/src/tools/clippy/tests/ui/collapsible_if.stderr @@ -0,0 +1,122 @@ +error: this `if` statement can be collapsed + --> $DIR/collapsible_if.rs:9:5 + | +LL | / if x == "hello" { +LL | | if y == "world" { +LL | | println!("Hello world!"); +LL | | } +LL | | } + | |_____^ + | + = note: `-D clippy::collapsible-if` implied by `-D warnings` +help: try + | +LL | if x == "hello" && y == "world" { +LL | println!("Hello world!"); +LL | } + | + +error: this `if` statement can be collapsed + --> $DIR/collapsible_if.rs:15:5 + | +LL | / if x == "hello" || x == "world" { +LL | | if y == "world" || y == "hello" { +LL | | println!("Hello world!"); +LL | | } +LL | | } + | |_____^ + | +help: try + | +LL | if (x == "hello" || x == "world") && (y == "world" || y == "hello") { +LL | println!("Hello world!"); +LL | } + | + +error: this `if` statement can be collapsed + --> $DIR/collapsible_if.rs:21:5 + | +LL | / if x == "hello" && x == "world" { +LL | | if y == "world" || y == "hello" { +LL | | println!("Hello world!"); +LL | | } +LL | | } + | |_____^ + | +help: try + | +LL | if x == "hello" && x == "world" && (y == "world" || y == "hello") { +LL | println!("Hello world!"); +LL | } + | + +error: this `if` statement can be collapsed + --> $DIR/collapsible_if.rs:27:5 + | +LL | / if x == "hello" || x == "world" { +LL | | if y == "world" && y == "hello" { +LL | | println!("Hello world!"); +LL | | } +LL | | } + | |_____^ + | +help: try + | +LL | if (x == "hello" || x == "world") && y == "world" && y == "hello" { +LL | println!("Hello world!"); +LL | } + | + +error: this `if` statement can be collapsed + --> $DIR/collapsible_if.rs:33:5 + | +LL | / if x == "hello" && x == "world" { +LL | | if y == "world" && y == "hello" { +LL | | println!("Hello world!"); +LL | | } +LL | | } + | |_____^ + | +help: try + | +LL | if x == "hello" && x == "world" && y == "world" && y == "hello" { +LL | println!("Hello world!"); +LL | } + | + +error: this `if` statement can be collapsed + --> $DIR/collapsible_if.rs:39:5 + | +LL | / if 42 == 1337 { +LL | | if 'a' != 'A' { +LL | | println!("world!") +LL | | } +LL | | } + | |_____^ + | +help: try + | +LL | if 42 == 1337 && 'a' != 'A' { +LL | println!("world!") +LL | } + | + +error: this `if` statement can be collapsed + --> $DIR/collapsible_if.rs:95:5 + | +LL | / if x == "hello" { +LL | | if y == "world" { // Collapsible +LL | | println!("Hello world!"); +LL | | } +LL | | } + | |_____^ + | +help: try + | +LL | if x == "hello" && y == "world" { // Collapsible +LL | println!("Hello world!"); +LL | } + | + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/tests/ui/collapsible_span_lint_calls.fixed b/src/tools/clippy/tests/ui/collapsible_span_lint_calls.fixed new file mode 100644 index 00000000000..e588c23345e --- /dev/null +++ b/src/tools/clippy/tests/ui/collapsible_span_lint_calls.fixed @@ -0,0 +1,91 @@ +// run-rustfix +#![deny(clippy::internal)] +#![feature(rustc_private)] + +extern crate rustc_ast; +extern crate rustc_errors; +extern crate rustc_lint; +extern crate rustc_session; +extern crate rustc_span; + +use rustc_ast::ast::Expr; +use rustc_errors::{Applicability, DiagnosticBuilder}; +use rustc_lint::{EarlyContext, EarlyLintPass, Lint, LintContext}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Span; + +#[allow(unused_variables)] +pub fn span_lint_and_then<'a, T: LintContext, F>(cx: &'a T, lint: &'static Lint, sp: Span, msg: &str, f: F) +where + F: for<'b> FnOnce(&mut DiagnosticBuilder<'b>), +{ +} + +#[allow(unused_variables)] +fn span_lint_and_help<'a, T: LintContext>( + cx: &'a T, + lint: &'static Lint, + span: Span, + msg: &str, + option_span: Option<Span>, + help: &str, +) { +} + +#[allow(unused_variables)] +fn span_lint_and_note<'a, T: LintContext>( + cx: &'a T, + lint: &'static Lint, + span: Span, + msg: &str, + note_span: Option<Span>, + note: &str, +) { +} + +#[allow(unused_variables)] +fn span_lint_and_sugg<'a, T: LintContext>( + cx: &'a T, + lint: &'static Lint, + sp: Span, + msg: &str, + help: &str, + sugg: String, + applicability: Applicability, +) { +} + +declare_tool_lint! { + pub clippy::TEST_LINT, + Warn, + "", + report_in_external_macro: true +} + +declare_lint_pass!(Pass => [TEST_LINT]); + +impl EarlyLintPass for Pass { + fn check_expr(&mut self, cx: &EarlyContext, expr: &Expr) { + let lint_msg = "lint message"; + let help_msg = "help message"; + let note_msg = "note message"; + let sugg = "new_call()"; + let predicate = true; + + span_lint_and_sugg(cx, TEST_LINT, expr.span, lint_msg, help_msg, sugg.to_string(), Applicability::MachineApplicable); + span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), help_msg); + span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, None, help_msg); + span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), note_msg); + span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, None, note_msg); + + // This expr shouldn't trigger this lint. + span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { + db.note(note_msg); + if predicate { + db.note(note_msg); + } + }) + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/collapsible_span_lint_calls.rs b/src/tools/clippy/tests/ui/collapsible_span_lint_calls.rs new file mode 100644 index 00000000000..d5dd3bb562b --- /dev/null +++ b/src/tools/clippy/tests/ui/collapsible_span_lint_calls.rs @@ -0,0 +1,101 @@ +// run-rustfix +#![deny(clippy::internal)] +#![feature(rustc_private)] + +extern crate rustc_ast; +extern crate rustc_errors; +extern crate rustc_lint; +extern crate rustc_session; +extern crate rustc_span; + +use rustc_ast::ast::Expr; +use rustc_errors::{Applicability, DiagnosticBuilder}; +use rustc_lint::{EarlyContext, EarlyLintPass, Lint, LintContext}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Span; + +#[allow(unused_variables)] +pub fn span_lint_and_then<'a, T: LintContext, F>(cx: &'a T, lint: &'static Lint, sp: Span, msg: &str, f: F) +where + F: for<'b> FnOnce(&mut DiagnosticBuilder<'b>), +{ +} + +#[allow(unused_variables)] +fn span_lint_and_help<'a, T: LintContext>( + cx: &'a T, + lint: &'static Lint, + span: Span, + msg: &str, + option_span: Option<Span>, + help: &str, +) { +} + +#[allow(unused_variables)] +fn span_lint_and_note<'a, T: LintContext>( + cx: &'a T, + lint: &'static Lint, + span: Span, + msg: &str, + note_span: Option<Span>, + note: &str, +) { +} + +#[allow(unused_variables)] +fn span_lint_and_sugg<'a, T: LintContext>( + cx: &'a T, + lint: &'static Lint, + sp: Span, + msg: &str, + help: &str, + sugg: String, + applicability: Applicability, +) { +} + +declare_tool_lint! { + pub clippy::TEST_LINT, + Warn, + "", + report_in_external_macro: true +} + +declare_lint_pass!(Pass => [TEST_LINT]); + +impl EarlyLintPass for Pass { + fn check_expr(&mut self, cx: &EarlyContext, expr: &Expr) { + let lint_msg = "lint message"; + let help_msg = "help message"; + let note_msg = "note message"; + let sugg = "new_call()"; + let predicate = true; + + span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { + db.span_suggestion(expr.span, help_msg, sugg.to_string(), Applicability::MachineApplicable); + }); + span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { + db.span_help(expr.span, help_msg); + }); + span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { + db.help(help_msg); + }); + span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { + db.span_note(expr.span, note_msg); + }); + span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { + db.note(note_msg); + }); + + // This expr shouldn't trigger this lint. + span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { + db.note(note_msg); + if predicate { + db.note(note_msg); + } + }) + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/collapsible_span_lint_calls.stderr b/src/tools/clippy/tests/ui/collapsible_span_lint_calls.stderr new file mode 100644 index 00000000000..874d4a9f255 --- /dev/null +++ b/src/tools/clippy/tests/ui/collapsible_span_lint_calls.stderr @@ -0,0 +1,49 @@ +error: this call is collapsible + --> $DIR/collapsible_span_lint_calls.rs:75:9 + | +LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { +LL | | db.span_suggestion(expr.span, help_msg, sugg.to_string(), Applicability::MachineApplicable); +LL | | }); + | |__________^ help: collapse into: `span_lint_and_sugg(cx, TEST_LINT, expr.span, lint_msg, help_msg, sugg.to_string(), Applicability::MachineApplicable)` + | +note: the lint level is defined here + --> $DIR/collapsible_span_lint_calls.rs:2:9 + | +LL | #![deny(clippy::internal)] + | ^^^^^^^^^^^^^^^^ + = note: `#[deny(clippy::collapsible_span_lint_calls)]` implied by `#[deny(clippy::internal)]` + +error: this call is collapsible + --> $DIR/collapsible_span_lint_calls.rs:78:9 + | +LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { +LL | | db.span_help(expr.span, help_msg); +LL | | }); + | |__________^ help: collapse into: `span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), help_msg)` + +error: this call is collapsible + --> $DIR/collapsible_span_lint_calls.rs:81:9 + | +LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { +LL | | db.help(help_msg); +LL | | }); + | |__________^ help: collapse into: `span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, None, help_msg)` + +error: this call is collspible + --> $DIR/collapsible_span_lint_calls.rs:84:9 + | +LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { +LL | | db.span_note(expr.span, note_msg); +LL | | }); + | |__________^ help: collapse into: `span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), note_msg)` + +error: this call is collspible + --> $DIR/collapsible_span_lint_calls.rs:87:9 + | +LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { +LL | | db.note(note_msg); +LL | | }); + | |__________^ help: collapse into: `span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, None, note_msg)` + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/comparison_chain.rs b/src/tools/clippy/tests/ui/comparison_chain.rs new file mode 100644 index 00000000000..9c2128469de --- /dev/null +++ b/src/tools/clippy/tests/ui/comparison_chain.rs @@ -0,0 +1,140 @@ +#![allow(dead_code)] +#![warn(clippy::comparison_chain)] + +fn a() {} +fn b() {} +fn c() {} + +fn f(x: u8, y: u8, z: u8) { + // Ignored: Only one branch + if x > y { + a() + } + + if x > y { + a() + } else if x < y { + b() + } + + // Ignored: Only one explicit conditional + if x > y { + a() + } else { + b() + } + + if x > y { + a() + } else if x < y { + b() + } else { + c() + } + + if x > y { + a() + } else if y > x { + b() + } else { + c() + } + + if x > 1 { + a() + } else if x < 1 { + b() + } else if x == 1 { + c() + } + + // Ignored: Binop args are not equivalent + if x > 1 { + a() + } else if y > 1 { + b() + } else { + c() + } + + // Ignored: Binop args are not equivalent + if x > y { + a() + } else if x > z { + b() + } else if y > z { + c() + } + + // Ignored: Not binary comparisons + if true { + a() + } else if false { + b() + } else { + c() + } +} + +#[allow(clippy::float_cmp)] +fn g(x: f64, y: f64, z: f64) { + // Ignored: f64 doesn't implement Ord + if x > y { + a() + } else if x < y { + b() + } + + // Ignored: f64 doesn't implement Ord + if x > y { + a() + } else if x < y { + b() + } else { + c() + } + + // Ignored: f64 doesn't implement Ord + if x > y { + a() + } else if y > x { + b() + } else { + c() + } + + // Ignored: f64 doesn't implement Ord + if x > 1.0 { + a() + } else if x < 1.0 { + b() + } else if x == 1.0 { + c() + } +} + +fn h<T: Ord>(x: T, y: T, z: T) { + if x > y { + a() + } else if x < y { + b() + } + + if x > y { + a() + } else if x < y { + b() + } else { + c() + } + + if x > y { + a() + } else if y > x { + b() + } else { + c() + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/comparison_chain.stderr b/src/tools/clippy/tests/ui/comparison_chain.stderr new file mode 100644 index 00000000000..69db88b03b5 --- /dev/null +++ b/src/tools/clippy/tests/ui/comparison_chain.stderr @@ -0,0 +1,97 @@ +error: `if` chain can be rewritten with `match` + --> $DIR/comparison_chain.rs:14:5 + | +LL | / if x > y { +LL | | a() +LL | | } else if x < y { +LL | | b() +LL | | } + | |_____^ + | + = note: `-D clippy::comparison-chain` implied by `-D warnings` + = help: Consider rewriting the `if` chain to use `cmp` and `match`. + +error: `if` chain can be rewritten with `match` + --> $DIR/comparison_chain.rs:27:5 + | +LL | / if x > y { +LL | | a() +LL | | } else if x < y { +LL | | b() +LL | | } else { +LL | | c() +LL | | } + | |_____^ + | + = help: Consider rewriting the `if` chain to use `cmp` and `match`. + +error: `if` chain can be rewritten with `match` + --> $DIR/comparison_chain.rs:35:5 + | +LL | / if x > y { +LL | | a() +LL | | } else if y > x { +LL | | b() +LL | | } else { +LL | | c() +LL | | } + | |_____^ + | + = help: Consider rewriting the `if` chain to use `cmp` and `match`. + +error: `if` chain can be rewritten with `match` + --> $DIR/comparison_chain.rs:43:5 + | +LL | / if x > 1 { +LL | | a() +LL | | } else if x < 1 { +LL | | b() +LL | | } else if x == 1 { +LL | | c() +LL | | } + | |_____^ + | + = help: Consider rewriting the `if` chain to use `cmp` and `match`. + +error: `if` chain can be rewritten with `match` + --> $DIR/comparison_chain.rs:117:5 + | +LL | / if x > y { +LL | | a() +LL | | } else if x < y { +LL | | b() +LL | | } + | |_____^ + | + = help: Consider rewriting the `if` chain to use `cmp` and `match`. + +error: `if` chain can be rewritten with `match` + --> $DIR/comparison_chain.rs:123:5 + | +LL | / if x > y { +LL | | a() +LL | | } else if x < y { +LL | | b() +LL | | } else { +LL | | c() +LL | | } + | |_____^ + | + = help: Consider rewriting the `if` chain to use `cmp` and `match`. + +error: `if` chain can be rewritten with `match` + --> $DIR/comparison_chain.rs:131:5 + | +LL | / if x > y { +LL | | a() +LL | | } else if y > x { +LL | | b() +LL | | } else { +LL | | c() +LL | | } + | |_____^ + | + = help: Consider rewriting the `if` chain to use `cmp` and `match`. + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/tests/ui/complex_types.rs b/src/tools/clippy/tests/ui/complex_types.rs new file mode 100644 index 00000000000..be61fb6b9be --- /dev/null +++ b/src/tools/clippy/tests/ui/complex_types.rs @@ -0,0 +1,60 @@ +#![warn(clippy::all)] +#![allow(unused, clippy::needless_pass_by_value, clippy::vec_box)] +#![feature(associated_type_defaults)] + +type Alias = Vec<Vec<Box<(u32, u32, u32, u32)>>>; // no warning here + +const CST: (u32, (u32, (u32, (u32, u32)))) = (0, (0, (0, (0, 0)))); +static ST: (u32, (u32, (u32, (u32, u32)))) = (0, (0, (0, (0, 0)))); + +struct S { + f: Vec<Vec<Box<(u32, u32, u32, u32)>>>, +} + +struct TS(Vec<Vec<Box<(u32, u32, u32, u32)>>>); + +enum E { + Tuple(Vec<Vec<Box<(u32, u32, u32, u32)>>>), + Struct { f: Vec<Vec<Box<(u32, u32, u32, u32)>>> }, +} + +impl S { + const A: (u32, (u32, (u32, (u32, u32)))) = (0, (0, (0, (0, 0)))); + fn impl_method(&self, p: Vec<Vec<Box<(u32, u32, u32, u32)>>>) {} +} + +trait T { + const A: Vec<Vec<Box<(u32, u32, u32, u32)>>>; + type B = Vec<Vec<Box<(u32, u32, u32, u32)>>>; + fn method(&self, p: Vec<Vec<Box<(u32, u32, u32, u32)>>>); + fn def_method(&self, p: Vec<Vec<Box<(u32, u32, u32, u32)>>>) {} +} + +fn test1() -> Vec<Vec<Box<(u32, u32, u32, u32)>>> { + vec![] +} + +fn test2(_x: Vec<Vec<Box<(u32, u32, u32, u32)>>>) {} + +fn test3() { + let _y: Vec<Vec<Box<(u32, u32, u32, u32)>>> = vec![]; +} + +#[repr(C)] +struct D { + // should not warn, since we don't have control over the signature (#3222) + test4: extern "C" fn( + itself: &D, + a: usize, + b: usize, + c: usize, + d: usize, + e: usize, + f: usize, + g: usize, + h: usize, + i: usize, + ), +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/complex_types.stderr b/src/tools/clippy/tests/ui/complex_types.stderr new file mode 100644 index 00000000000..8f5dbd27956 --- /dev/null +++ b/src/tools/clippy/tests/ui/complex_types.stderr @@ -0,0 +1,94 @@ +error: very complex type used. Consider factoring parts into `type` definitions + --> $DIR/complex_types.rs:7:12 + | +LL | const CST: (u32, (u32, (u32, (u32, u32)))) = (0, (0, (0, (0, 0)))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::type-complexity` implied by `-D warnings` + +error: very complex type used. Consider factoring parts into `type` definitions + --> $DIR/complex_types.rs:8:12 + | +LL | static ST: (u32, (u32, (u32, (u32, u32)))) = (0, (0, (0, (0, 0)))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: very complex type used. Consider factoring parts into `type` definitions + --> $DIR/complex_types.rs:11:8 + | +LL | f: Vec<Vec<Box<(u32, u32, u32, u32)>>>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: very complex type used. Consider factoring parts into `type` definitions + --> $DIR/complex_types.rs:14:11 + | +LL | struct TS(Vec<Vec<Box<(u32, u32, u32, u32)>>>); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: very complex type used. Consider factoring parts into `type` definitions + --> $DIR/complex_types.rs:17:11 + | +LL | Tuple(Vec<Vec<Box<(u32, u32, u32, u32)>>>), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: very complex type used. Consider factoring parts into `type` definitions + --> $DIR/complex_types.rs:18:17 + | +LL | Struct { f: Vec<Vec<Box<(u32, u32, u32, u32)>>> }, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: very complex type used. Consider factoring parts into `type` definitions + --> $DIR/complex_types.rs:22:14 + | +LL | const A: (u32, (u32, (u32, (u32, u32)))) = (0, (0, (0, (0, 0)))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: very complex type used. Consider factoring parts into `type` definitions + --> $DIR/complex_types.rs:23:30 + | +LL | fn impl_method(&self, p: Vec<Vec<Box<(u32, u32, u32, u32)>>>) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: very complex type used. Consider factoring parts into `type` definitions + --> $DIR/complex_types.rs:27:14 + | +LL | const A: Vec<Vec<Box<(u32, u32, u32, u32)>>>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: very complex type used. Consider factoring parts into `type` definitions + --> $DIR/complex_types.rs:28:14 + | +LL | type B = Vec<Vec<Box<(u32, u32, u32, u32)>>>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: very complex type used. Consider factoring parts into `type` definitions + --> $DIR/complex_types.rs:29:25 + | +LL | fn method(&self, p: Vec<Vec<Box<(u32, u32, u32, u32)>>>); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: very complex type used. Consider factoring parts into `type` definitions + --> $DIR/complex_types.rs:30:29 + | +LL | fn def_method(&self, p: Vec<Vec<Box<(u32, u32, u32, u32)>>>) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: very complex type used. Consider factoring parts into `type` definitions + --> $DIR/complex_types.rs:33:15 + | +LL | fn test1() -> Vec<Vec<Box<(u32, u32, u32, u32)>>> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: very complex type used. Consider factoring parts into `type` definitions + --> $DIR/complex_types.rs:37:14 + | +LL | fn test2(_x: Vec<Vec<Box<(u32, u32, u32, u32)>>>) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: very complex type used. Consider factoring parts into `type` definitions + --> $DIR/complex_types.rs:40:13 + | +LL | let _y: Vec<Vec<Box<(u32, u32, u32, u32)>>> = vec![]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 15 previous errors + diff --git a/src/tools/clippy/tests/ui/copy_iterator.rs b/src/tools/clippy/tests/ui/copy_iterator.rs new file mode 100644 index 00000000000..e3d5928be23 --- /dev/null +++ b/src/tools/clippy/tests/ui/copy_iterator.rs @@ -0,0 +1,23 @@ +#![warn(clippy::copy_iterator)] + +#[derive(Copy, Clone)] +struct Countdown(u8); + +impl Iterator for Countdown { + type Item = u8; + + fn next(&mut self) -> Option<u8> { + self.0.checked_sub(1).map(|c| { + self.0 = c; + c + }) + } +} + +fn main() { + let my_iterator = Countdown(5); + let a: Vec<_> = my_iterator.take(1).collect(); + assert_eq!(a.len(), 1); + let b: Vec<_> = my_iterator.collect(); + assert_eq!(b.len(), 5); +} diff --git a/src/tools/clippy/tests/ui/copy_iterator.stderr b/src/tools/clippy/tests/ui/copy_iterator.stderr new file mode 100644 index 00000000000..f8ce6af7961 --- /dev/null +++ b/src/tools/clippy/tests/ui/copy_iterator.stderr @@ -0,0 +1,17 @@ +error: you are implementing `Iterator` on a `Copy` type + --> $DIR/copy_iterator.rs:6:1 + | +LL | / impl Iterator for Countdown { +LL | | type Item = u8; +LL | | +LL | | fn next(&mut self) -> Option<u8> { +... | +LL | | } +LL | | } + | |_^ + | + = note: `-D clippy::copy-iterator` implied by `-D warnings` + = note: consider implementing `IntoIterator` instead + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/crashes/associated-constant-ice.rs b/src/tools/clippy/tests/ui/crashes/associated-constant-ice.rs new file mode 100644 index 00000000000..4bb833795bb --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/associated-constant-ice.rs @@ -0,0 +1,15 @@ +// run-pass + +/// Test for https://github.com/rust-lang/rust-clippy/issues/1698 + +pub trait Trait { + const CONSTANT: u8; +} + +impl Trait for u8 { + const CONSTANT: u8 = 2; +} + +fn main() { + println!("{}", u8::CONSTANT * 10); +} diff --git a/src/tools/clippy/tests/ui/crashes/auxiliary/ice-4727-aux.rs b/src/tools/clippy/tests/ui/crashes/auxiliary/ice-4727-aux.rs new file mode 100644 index 00000000000..58a20caf6c6 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/auxiliary/ice-4727-aux.rs @@ -0,0 +1,9 @@ +pub trait Trait { + fn fun(par: &str) -> &str; +} + +impl Trait for str { + fn fun(par: &str) -> &str { + &par[0..1] + } +} diff --git a/src/tools/clippy/tests/ui/crashes/auxiliary/proc_macro_crash.rs b/src/tools/clippy/tests/ui/crashes/auxiliary/proc_macro_crash.rs new file mode 100644 index 00000000000..086548e58ed --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/auxiliary/proc_macro_crash.rs @@ -0,0 +1,37 @@ +// no-prefer-dynamic +// ^ compiletest by default builds all aux files as dylibs, but we don't want that for proc-macro +// crates. If we don't set this, compiletest will override the `crate_type` attribute below and +// compile this as dylib. Removing this then causes the test to fail because a `dylib` crate can't +// contain a proc-macro. + +#![feature(repr128)] +#![crate_type = "proc-macro"] + +extern crate proc_macro; + +use proc_macro::{Delimiter, Group, Ident, Span, TokenStream, TokenTree}; +use std::iter::FromIterator; + +#[proc_macro] +pub fn macro_test(input_stream: TokenStream) -> TokenStream { + let first_token = input_stream.into_iter().next().unwrap(); + let span = first_token.span(); + + TokenStream::from_iter(vec![ + TokenTree::Ident(Ident::new("fn", Span::call_site())), + TokenTree::Ident(Ident::new("code", Span::call_site())), + TokenTree::Group(Group::new(Delimiter::Parenthesis, TokenStream::new())), + TokenTree::Group(Group::new(Delimiter::Brace, { + let mut clause = Group::new(Delimiter::Brace, TokenStream::new()); + clause.set_span(span); + + TokenStream::from_iter(vec![ + TokenTree::Ident(Ident::new("if", Span::call_site())), + TokenTree::Ident(Ident::new("true", Span::call_site())), + TokenTree::Group(clause.clone()), + TokenTree::Ident(Ident::new("else", Span::call_site())), + TokenTree::Group(clause), + ]) + })), + ]) +} diff --git a/src/tools/clippy/tests/ui/crashes/auxiliary/use_self_macro.rs b/src/tools/clippy/tests/ui/crashes/auxiliary/use_self_macro.rs new file mode 100644 index 00000000000..a8a85b4baef --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/auxiliary/use_self_macro.rs @@ -0,0 +1,15 @@ +macro_rules! use_self { + ( + impl $ty:ident { + fn func(&$this:ident) { + [fields($($field:ident)*)] + } + } + ) => ( + impl $ty { + fn func(&$this) { + let $ty { $($field),* } = $this; + } + } + ) +} diff --git a/src/tools/clippy/tests/ui/crashes/cc_seme.rs b/src/tools/clippy/tests/ui/crashes/cc_seme.rs new file mode 100644 index 00000000000..c48c7e9e6c6 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/cc_seme.rs @@ -0,0 +1,29 @@ +// run-pass + +#[allow(dead_code)] + +/// Test for https://github.com/rust-lang/rust-clippy/issues/478 + +enum Baz { + One, + Two, +} + +struct Test { + t: Option<usize>, + b: Baz, +} + +fn main() {} + +pub fn foo() { + use Baz::*; + let x = Test { t: Some(0), b: One }; + + match x { + Test { t: Some(_), b: One } => unreachable!(), + Test { t: Some(42), b: Two } => unreachable!(), + Test { t: None, .. } => unreachable!(), + Test { .. } => unreachable!(), + } +} diff --git a/src/tools/clippy/tests/ui/crashes/enum-glob-import-crate.rs b/src/tools/clippy/tests/ui/crashes/enum-glob-import-crate.rs new file mode 100644 index 00000000000..db1fa871afe --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/enum-glob-import-crate.rs @@ -0,0 +1,8 @@ +// run-pass + +#![deny(clippy::all)] +#![allow(unused_imports)] + +use std::*; + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-1588.rs b/src/tools/clippy/tests/ui/crashes/ice-1588.rs new file mode 100644 index 00000000000..15d0f705b36 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-1588.rs @@ -0,0 +1,15 @@ +// run-pass + +#![allow(clippy::all)] + +/// Test for https://github.com/rust-lang/rust-clippy/issues/1588 + +fn main() { + match 1 { + 1 => {}, + 2 => { + [0; 1]; + }, + _ => {}, + } +} diff --git a/src/tools/clippy/tests/ui/crashes/ice-1782.rs b/src/tools/clippy/tests/ui/crashes/ice-1782.rs new file mode 100644 index 00000000000..1ca6b6976b3 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-1782.rs @@ -0,0 +1,28 @@ +// run-pass + +#![allow(dead_code, unused_variables)] + +/// Should not trigger an ICE in `SpanlessEq` / `consts::constant` +/// +/// Issue: https://github.com/rust-lang/rust-clippy/issues/1782 +use std::{mem, ptr}; + +fn spanless_eq_ice() { + let txt = "something"; + match txt { + "something" => unsafe { + ptr::write( + ptr::null_mut() as *mut u32, + mem::transmute::<[u8; 4], _>([0, 0, 0, 255]), + ) + }, + _ => unsafe { + ptr::write( + ptr::null_mut() as *mut u32, + mem::transmute::<[u8; 4], _>([13, 246, 24, 255]), + ) + }, + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-1969.rs b/src/tools/clippy/tests/ui/crashes/ice-1969.rs new file mode 100644 index 00000000000..837ec9df31a --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-1969.rs @@ -0,0 +1,15 @@ +// run-pass + +#![allow(clippy::all)] + +/// Test for https://github.com/rust-lang/rust-clippy/issues/1969 + +fn main() {} + +pub trait Convert { + type Action: From<*const f64>; + + fn convert(val: *const f64) -> Self::Action { + val.into() + } +} diff --git a/src/tools/clippy/tests/ui/crashes/ice-2499.rs b/src/tools/clippy/tests/ui/crashes/ice-2499.rs new file mode 100644 index 00000000000..ffef1631775 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-2499.rs @@ -0,0 +1,28 @@ +// run-pass + +#![allow(dead_code, clippy::char_lit_as_u8, clippy::needless_bool)] + +/// Should not trigger an ICE in `SpanlessHash` / `consts::constant` +/// +/// Issue: https://github.com/rust-lang/rust-clippy/issues/2499 + +fn f(s: &[u8]) -> bool { + let t = s[0] as char; + + match t { + 'E' | 'W' => {}, + 'T' => { + if s[0..4] != ['0' as u8; 4] { + return false; + } else { + return true; + } + }, + _ => { + return false; + }, + } + true +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-2594.rs b/src/tools/clippy/tests/ui/crashes/ice-2594.rs new file mode 100644 index 00000000000..ac19f1976e9 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-2594.rs @@ -0,0 +1,22 @@ +// run-pass + +#![allow(dead_code, unused_variables)] + +/// Should not trigger an ICE in `SpanlessHash` / `consts::constant` +/// +/// Issue: https://github.com/rust-lang/rust-clippy/issues/2594 + +fn spanless_hash_ice() { + let txt = "something"; + let empty_header: [u8; 1] = [1; 1]; + + match txt { + "something" => { + let mut headers = [empty_header; 1]; + }, + "" => (), + _ => (), + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-2636.rs b/src/tools/clippy/tests/ui/crashes/ice-2636.rs new file mode 100644 index 00000000000..e0b58157590 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-2636.rs @@ -0,0 +1,22 @@ +#![allow(dead_code)] + +enum Foo { + A, + B, + C, +} + +macro_rules! test_hash { + ($foo:expr, $($t:ident => $ord:expr),+ ) => { + use self::Foo::*; + match $foo { + $ ( & $t => $ord, + )* + }; + }; +} + +fn main() { + let a = Foo::A; + test_hash!(&a, A => 0, B => 1, C => 2); +} diff --git a/src/tools/clippy/tests/ui/crashes/ice-2636.stderr b/src/tools/clippy/tests/ui/crashes/ice-2636.stderr new file mode 100644 index 00000000000..53799b4fbf1 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-2636.stderr @@ -0,0 +1,17 @@ +error: you don't need to add `&` to both the expression and the patterns + --> $DIR/ice-2636.rs:12:9 + | +LL | / match $foo { +LL | | $ ( & $t => $ord, +LL | | )* +LL | | }; + | |_________^ +... +LL | test_hash!(&a, A => 0, B => 1, C => 2); + | --------------------------------------- in this macro invocation + | + = note: `-D clippy::match-ref-pats` implied by `-D warnings` + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/crashes/ice-2727.rs b/src/tools/clippy/tests/ui/crashes/ice-2727.rs new file mode 100644 index 00000000000..d832c286033 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-2727.rs @@ -0,0 +1,9 @@ +// run-pass + +/// Test for https://github.com/rust-lang/rust-clippy/issues/2727 + +pub fn f(new: fn()) { + new(); +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-2760.rs b/src/tools/clippy/tests/ui/crashes/ice-2760.rs new file mode 100644 index 00000000000..9e5e299c336 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-2760.rs @@ -0,0 +1,25 @@ +// run-pass + +#![allow( + unused_variables, + clippy::blacklisted_name, + clippy::needless_pass_by_value, + dead_code +)] + +/// This should not compile-fail with: +/// +/// error[E0277]: the trait bound `T: Foo` is not satisfied +// See rust-lang/rust-clippy#2760. + +trait Foo { + type Bar; +} + +struct Baz<T: Foo> { + bar: T::Bar, +} + +fn take<T: Foo>(baz: Baz<T>) {} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-2774.rs b/src/tools/clippy/tests/ui/crashes/ice-2774.rs new file mode 100644 index 00000000000..47f8e3b18ee --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-2774.rs @@ -0,0 +1,29 @@ +// run-pass + +use std::collections::HashSet; + +// See rust-lang/rust-clippy#2774. + +#[derive(Eq, PartialEq, Debug, Hash)] +pub struct Bar { + foo: Foo, +} + +#[derive(Eq, PartialEq, Debug, Hash)] +pub struct Foo {} + +#[allow(clippy::implicit_hasher)] +// This should not cause a "cannot relate bound region" ICE. +pub fn add_barfoos_to_foos<'a>(bars: &HashSet<&'a Bar>) { + let mut foos = HashSet::new(); + foos.extend(bars.iter().map(|b| &b.foo)); +} + +#[allow(clippy::implicit_hasher)] +// Also, this should not cause a "cannot relate bound region" ICE. +pub fn add_barfoos_to_foos2(bars: &HashSet<&Bar>) { + let mut foos = HashSet::new(); + foos.extend(bars.iter().map(|b| &b.foo)); +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-2862.rs b/src/tools/clippy/tests/ui/crashes/ice-2862.rs new file mode 100644 index 00000000000..47324ce1831 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-2862.rs @@ -0,0 +1,18 @@ +// run-pass + +/// Test for https://github.com/rust-lang/rust-clippy/issues/2862 + +pub trait FooMap { + fn map<B, F: Fn() -> B>(&self, f: F) -> B; +} + +impl FooMap for bool { + fn map<B, F: Fn() -> B>(&self, f: F) -> B { + f() + } +} + +fn main() { + let a = true; + a.map(|| false); +} diff --git a/src/tools/clippy/tests/ui/crashes/ice-2865.rs b/src/tools/clippy/tests/ui/crashes/ice-2865.rs new file mode 100644 index 00000000000..c4f6c0fed68 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-2865.rs @@ -0,0 +1,18 @@ +// run-pass + +#[allow(dead_code)] + +/// Test for https://github.com/rust-lang/rust-clippy/issues/2865 + +struct Ice { + size: String, +} + +impl<'a> From<String> for Ice { + fn from(_: String) -> Self { + let text = || "iceberg".to_string(); + Self { size: text() } + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-3151.rs b/src/tools/clippy/tests/ui/crashes/ice-3151.rs new file mode 100644 index 00000000000..ffad2d06b56 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-3151.rs @@ -0,0 +1,17 @@ +// run-pass + +/// Test for https://github.com/rust-lang/rust-clippy/issues/2865 + +#[derive(Clone)] +pub struct HashMap<V, S> { + hash_builder: S, + table: RawTable<V>, +} + +#[derive(Clone)] +pub struct RawTable<V> { + size: usize, + val: V, +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-3462.rs b/src/tools/clippy/tests/ui/crashes/ice-3462.rs new file mode 100644 index 00000000000..95c7dff9be3 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-3462.rs @@ -0,0 +1,25 @@ +// run-pass + +#![warn(clippy::all)] +#![allow(clippy::blacklisted_name)] +#![allow(unused)] + +/// Test for https://github.com/rust-lang/rust-clippy/issues/3462 + +enum Foo { + Bar, + Baz, +} + +fn bar(foo: Foo) { + macro_rules! baz { + () => { + if let Foo::Bar = foo {} + }; + } + + baz!(); + baz!(); +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-360.rs b/src/tools/clippy/tests/ui/crashes/ice-360.rs new file mode 100644 index 00000000000..6555c19ca6a --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-360.rs @@ -0,0 +1,12 @@ +fn main() {} + +fn no_panic<T>(slice: &[T]) { + let mut iter = slice.iter(); + loop { + let _ = match iter.next() { + Some(ele) => ele, + None => break, + }; + loop {} + } +} diff --git a/src/tools/clippy/tests/ui/crashes/ice-360.stderr b/src/tools/clippy/tests/ui/crashes/ice-360.stderr new file mode 100644 index 00000000000..84e31eaf2e9 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-360.stderr @@ -0,0 +1,24 @@ +error: this loop could be written as a `while let` loop + --> $DIR/ice-360.rs:5:5 + | +LL | / loop { +LL | | let _ = match iter.next() { +LL | | Some(ele) => ele, +LL | | None => break, +LL | | }; +LL | | loop {} +LL | | } + | |_____^ help: try: `while let Some(ele) = iter.next() { .. }` + | + = note: `-D clippy::while-let-loop` implied by `-D warnings` + +error: empty `loop {}` detected. You may want to either use `panic!()` or add `std::thread::sleep(..);` to the loop body. + --> $DIR/ice-360.rs:10:9 + | +LL | loop {} + | ^^^^^^^ + | + = note: `-D clippy::empty-loop` implied by `-D warnings` + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/crashes/ice-3717.rs b/src/tools/clippy/tests/ui/crashes/ice-3717.rs new file mode 100644 index 00000000000..f50714643fd --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-3717.rs @@ -0,0 +1,10 @@ +#![deny(clippy::implicit_hasher)] + +use std::collections::HashSet; + +fn main() {} + +pub fn ice_3717(_: &HashSet<usize>) { + let _ = [0u8; 0]; + let _: HashSet<usize> = HashSet::new(); +} diff --git a/src/tools/clippy/tests/ui/crashes/ice-3717.stderr b/src/tools/clippy/tests/ui/crashes/ice-3717.stderr new file mode 100644 index 00000000000..296c95abb96 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-3717.stderr @@ -0,0 +1,22 @@ +error: parameter of type `HashSet` should be generalized over different hashers + --> $DIR/ice-3717.rs:7:21 + | +LL | pub fn ice_3717(_: &HashSet<usize>) { + | ^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/ice-3717.rs:1:9 + | +LL | #![deny(clippy::implicit_hasher)] + | ^^^^^^^^^^^^^^^^^^^^^^^ +help: consider adding a type parameter + | +LL | pub fn ice_3717<S: ::std::hash::BuildHasher + Default>(_: &HashSet<usize, S>) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^ +help: ...and use generic constructor + | +LL | let _: HashSet<usize> = HashSet::default(); + | ^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/crashes/ice-3741.rs b/src/tools/clippy/tests/ui/crashes/ice-3741.rs new file mode 100644 index 00000000000..74b9c9c86c8 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-3741.rs @@ -0,0 +1,12 @@ +// aux-build:proc_macro_crash.rs +// run-pass + +#![feature(proc_macro_hygiene)] +#![warn(clippy::suspicious_else_formatting)] + +extern crate proc_macro_crash; +use proc_macro_crash::macro_test; + +fn main() { + macro_test!(2); +} diff --git a/src/tools/clippy/tests/ui/crashes/ice-3747.rs b/src/tools/clippy/tests/ui/crashes/ice-3747.rs new file mode 100644 index 00000000000..d0b44ebafee --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-3747.rs @@ -0,0 +1,19 @@ +// run-pass + +/// Test for https://github.com/rust-lang/rust-clippy/issues/3747 + +macro_rules! a { + ( $pub:tt $($attr:tt)* ) => { + $($attr)* $pub fn say_hello() {} + }; +} + +macro_rules! b { + () => { + a! { pub } + }; +} + +b! {} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-3891.rs b/src/tools/clippy/tests/ui/crashes/ice-3891.rs new file mode 100644 index 00000000000..05c5134c845 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-3891.rs @@ -0,0 +1,3 @@ +fn main() { + 1x; +} diff --git a/src/tools/clippy/tests/ui/crashes/ice-3891.stderr b/src/tools/clippy/tests/ui/crashes/ice-3891.stderr new file mode 100644 index 00000000000..5a285b0e714 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-3891.stderr @@ -0,0 +1,10 @@ +error: invalid suffix `x` for integer literal + --> $DIR/ice-3891.rs:2:5 + | +LL | 1x; + | ^^ invalid suffix `x` + | + = help: the suffix must be one of the integral types (`u32`, `isize`, etc) + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/crashes/ice-4121.rs b/src/tools/clippy/tests/ui/crashes/ice-4121.rs new file mode 100644 index 00000000000..e1a142fdcb6 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-4121.rs @@ -0,0 +1,13 @@ +use std::mem; + +pub struct Foo<A, B>(A, B); + +impl<A, B> Foo<A, B> { + const HOST_SIZE: usize = mem::size_of::<B>(); + + pub fn crash() -> bool { + Self::HOST_SIZE == 0 + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-4545.rs b/src/tools/clippy/tests/ui/crashes/ice-4545.rs new file mode 100644 index 00000000000..d9c9c2096d9 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-4545.rs @@ -0,0 +1,14 @@ +fn repro() { + trait Foo { + type Bar; + } + + #[allow(dead_code)] + struct Baz<T: Foo> { + field: T::Bar, + } +} + +fn main() { + repro(); +} diff --git a/src/tools/clippy/tests/ui/crashes/ice-4579.rs b/src/tools/clippy/tests/ui/crashes/ice-4579.rs new file mode 100644 index 00000000000..2e7e279f847 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-4579.rs @@ -0,0 +1,13 @@ +#![allow(clippy::single_match)] + +use std::ptr; + +fn main() { + match Some(0_usize) { + Some(_) => { + let s = "012345"; + unsafe { ptr::read(s.as_ptr().offset(1) as *const [u8; 5]) }; + }, + _ => (), + }; +} diff --git a/src/tools/clippy/tests/ui/crashes/ice-4671.rs b/src/tools/clippy/tests/ui/crashes/ice-4671.rs new file mode 100644 index 00000000000..64e8e776941 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-4671.rs @@ -0,0 +1,21 @@ +#![warn(clippy::use_self)] + +#[macro_use] +#[path = "auxiliary/use_self_macro.rs"] +mod use_self_macro; + +struct Foo { + a: u32, +} + +use_self! { + impl Foo { + fn func(&self) { + [fields( + a + )] + } + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-4727.rs b/src/tools/clippy/tests/ui/crashes/ice-4727.rs new file mode 100644 index 00000000000..cdb59caec67 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-4727.rs @@ -0,0 +1,8 @@ +// run-pass + +#![warn(clippy::use_self)] + +#[path = "auxiliary/ice-4727-aux.rs"] +mod aux; + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-4760.rs b/src/tools/clippy/tests/ui/crashes/ice-4760.rs new file mode 100644 index 00000000000..ead67d5ed1b --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-4760.rs @@ -0,0 +1,10 @@ +// run-pass +const COUNT: usize = 2; +struct Thing; +trait Dummy {} + +const _: () = { + impl Dummy for Thing where [i32; COUNT]: Sized {} +}; + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-4775.rs b/src/tools/clippy/tests/ui/crashes/ice-4775.rs new file mode 100644 index 00000000000..31e53e846d5 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-4775.rs @@ -0,0 +1,14 @@ +#![feature(const_generics)] +#![allow(incomplete_features)] + +pub struct ArrayWrapper<const N: usize>([usize; N]); + +impl<const N: usize> ArrayWrapper<{ N }> { + pub fn ice(&self) { + for i in self.0.iter() { + println!("{}", i); + } + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-4968.rs b/src/tools/clippy/tests/ui/crashes/ice-4968.rs new file mode 100644 index 00000000000..3822f174598 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-4968.rs @@ -0,0 +1,20 @@ +// check-pass + +// Test for https://github.com/rust-lang/rust-clippy/issues/4968 + +#![warn(clippy::unsound_collection_transmute)] + +trait Trait { + type Assoc; +} + +use std::mem::{self, ManuallyDrop}; + +#[allow(unused)] +fn func<T: Trait>(slice: Vec<T::Assoc>) { + unsafe { + let _: Vec<ManuallyDrop<T::Assoc>> = mem::transmute(slice); + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-5207.rs b/src/tools/clippy/tests/ui/crashes/ice-5207.rs new file mode 100644 index 00000000000..1b20c9defac --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-5207.rs @@ -0,0 +1,7 @@ +// edition:2018 + +// Regression test for https://github.com/rust-lang/rust-clippy/issues/5207 + +pub async fn bar<'a, T: 'a>(_: T) {} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-5223.rs b/src/tools/clippy/tests/ui/crashes/ice-5223.rs new file mode 100644 index 00000000000..9bb2e227fc1 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-5223.rs @@ -0,0 +1,18 @@ +// Regression test for #5233 + +#![feature(const_generics)] +#![allow(incomplete_features)] +#![warn(clippy::indexing_slicing, clippy::iter_cloned_collect)] + +pub struct KotomineArray<T, const N: usize> { + arr: [T; N], +} + +impl<T: std::clone::Clone, const N: usize> KotomineArray<T, N> { + pub fn ice(self) { + let _ = self.arr[..]; + let _ = self.arr.iter().cloned().collect::<Vec<_>>(); + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-5238.rs b/src/tools/clippy/tests/ui/crashes/ice-5238.rs new file mode 100644 index 00000000000..989eb6d4485 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-5238.rs @@ -0,0 +1,9 @@ +// Regression test for #5238 / https://github.com/rust-lang/rust/pull/69562 + +#![feature(generators, generator_trait)] + +fn main() { + let _ = || { + yield; + }; +} diff --git a/src/tools/clippy/tests/ui/crashes/ice-5497.rs b/src/tools/clippy/tests/ui/crashes/ice-5497.rs new file mode 100644 index 00000000000..0769bce5fc8 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-5497.rs @@ -0,0 +1,11 @@ +// reduced from rustc issue-69020-assoc-const-arith-overflow.rs +pub fn main() {} + +pub trait Foo { + const OOB: i32; +} + +impl<T: Foo> Foo for Vec<T> { + const OOB: i32 = [1][1] + T::OOB; + //~^ ERROR operation will panic +} diff --git a/src/tools/clippy/tests/ui/crashes/ice-700.rs b/src/tools/clippy/tests/ui/crashes/ice-700.rs new file mode 100644 index 00000000000..b06df83d51a --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-700.rs @@ -0,0 +1,11 @@ +// run-pass + +#![deny(clippy::all)] + +/// Test for https://github.com/rust-lang/rust-clippy/issues/700 + +fn core() {} + +fn main() { + core(); +} diff --git a/src/tools/clippy/tests/ui/crashes/ice_exacte_size.rs b/src/tools/clippy/tests/ui/crashes/ice_exacte_size.rs new file mode 100644 index 00000000000..e02eb28ab86 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice_exacte_size.rs @@ -0,0 +1,21 @@ +// run-pass + +#![deny(clippy::all)] + +/// Test for https://github.com/rust-lang/rust-clippy/issues/1336 + +#[allow(dead_code)] +struct Foo; + +impl Iterator for Foo { + type Item = (); + + fn next(&mut self) -> Option<()> { + let _ = self.len() == 0; + unimplemented!() + } +} + +impl ExactSizeIterator for Foo {} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/if_same_then_else.rs b/src/tools/clippy/tests/ui/crashes/if_same_then_else.rs new file mode 100644 index 00000000000..4ef992b05e7 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/if_same_then_else.rs @@ -0,0 +1,18 @@ +// run-pass + +#![allow(clippy::comparison_chain)] +#![deny(clippy::if_same_then_else)] + +/// Test for https://github.com/rust-lang/rust-clippy/issues/2426 + +fn main() {} + +pub fn foo(a: i32, b: i32) -> Option<&'static str> { + if a == b { + None + } else if a > b { + Some("a pfeil b") + } else { + None + } +} diff --git a/src/tools/clippy/tests/ui/crashes/inherent_impl.rs b/src/tools/clippy/tests/ui/crashes/inherent_impl.rs new file mode 100644 index 00000000000..aeb27b5ba8c --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/inherent_impl.rs @@ -0,0 +1,26 @@ +#![deny(clippy::multiple_inherent_impl)] + +/// Test for https://github.com/rust-lang/rust-clippy/issues/4578 + +macro_rules! impl_foo { + ($struct:ident) => { + impl $struct { + fn foo() {} + } + }; +} + +macro_rules! impl_bar { + ($struct:ident) => { + impl $struct { + fn bar() {} + } + }; +} + +struct MyStruct; + +impl_foo!(MyStruct); +impl_bar!(MyStruct); + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/issue-825.rs b/src/tools/clippy/tests/ui/crashes/issue-825.rs new file mode 100644 index 00000000000..3d4a88ab3c4 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/issue-825.rs @@ -0,0 +1,27 @@ +// run-pass + +#![allow(warnings)] + +/// Test for https://github.com/rust-lang/rust-clippy/issues/825 + +// this should compile in a reasonable amount of time +fn rust_type_id(name: &str) { + if "bool" == &name[..] + || "uint" == &name[..] + || "u8" == &name[..] + || "u16" == &name[..] + || "u32" == &name[..] + || "f32" == &name[..] + || "f64" == &name[..] + || "i8" == &name[..] + || "i16" == &name[..] + || "i32" == &name[..] + || "i64" == &name[..] + || "Self" == &name[..] + || "str" == &name[..] + { + unreachable!(); + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/issues_loop_mut_cond.rs b/src/tools/clippy/tests/ui/crashes/issues_loop_mut_cond.rs new file mode 100644 index 00000000000..c4acd5cda1b --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/issues_loop_mut_cond.rs @@ -0,0 +1,30 @@ +// run-pass + +#![allow(dead_code)] + +/// Issue: https://github.com/rust-lang/rust-clippy/issues/2596 +pub fn loop_on_block_condition(u: &mut isize) { + while { *u < 0 } { + *u += 1; + } +} + +/// https://github.com/rust-lang/rust-clippy/issues/2584 +fn loop_with_unsafe_condition(ptr: *const u8) { + let mut len = 0; + while unsafe { *ptr.offset(len) } != 0 { + len += 1; + } +} + +/// https://github.com/rust-lang/rust-clippy/issues/2710 +static mut RUNNING: bool = true; +fn loop_on_static_condition() { + unsafe { + while RUNNING { + RUNNING = false; + } + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/match_same_arms_const.rs b/src/tools/clippy/tests/ui/crashes/match_same_arms_const.rs new file mode 100644 index 00000000000..848f0ea52ca --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/match_same_arms_const.rs @@ -0,0 +1,20 @@ +// run-pass + +#![deny(clippy::match_same_arms)] + +/// Test for https://github.com/rust-lang/rust-clippy/issues/2427 + +const PRICE_OF_SWEETS: u32 = 5; +const PRICE_OF_KINDNESS: u32 = 0; +const PRICE_OF_DRINKS: u32 = 5; + +pub fn price(thing: &str) -> u32 { + match thing { + "rolo" => PRICE_OF_SWEETS, + "advice" => PRICE_OF_KINDNESS, + "juice" => PRICE_OF_DRINKS, + _ => panic!(), + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/mut_mut_macro.rs b/src/tools/clippy/tests/ui/crashes/mut_mut_macro.rs new file mode 100644 index 00000000000..d8fbaa54146 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/mut_mut_macro.rs @@ -0,0 +1,36 @@ +// run-pass + +#![deny(clippy::mut_mut, clippy::zero_ptr, clippy::cmp_nan)] +#![allow(dead_code)] + +// FIXME: compiletest + extern crates doesn't work together. To make this test work, it would need +// the following three lines and the lazy_static crate. +// +// #[macro_use] +// extern crate lazy_static; +// use std::collections::HashMap; + +/// ensure that we don't suggest `is_nan` and `is_null` inside constants +/// FIXME: once const fn is stable, suggest these functions again in constants + +const BAA: *const i32 = 0 as *const i32; +static mut BAR: *const i32 = BAA; +static mut FOO: *const i32 = 0 as *const i32; +static mut BUH: bool = 42.0 < f32::NAN; + +#[allow(unused_variables, unused_mut)] +fn main() { + /* + lazy_static! { + static ref MUT_MAP : HashMap<usize, &'static str> = { + let mut m = HashMap::new(); + m.insert(0, "zero"); + m + }; + static ref MUT_COUNT : usize = MUT_MAP.len(); + } + assert_eq!(*MUT_COUNT, 1); + */ + // FIXME: don't lint in array length, requires `check_body` + //let _ = [""; (42.0 < f32::NAN) as usize]; +} diff --git a/src/tools/clippy/tests/ui/crashes/needless_borrow_fp.rs b/src/tools/clippy/tests/ui/crashes/needless_borrow_fp.rs new file mode 100644 index 00000000000..48507efe1e9 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/needless_borrow_fp.rs @@ -0,0 +1,9 @@ +// run-pass + +#[deny(clippy::all)] +#[derive(Debug)] +pub enum Error { + Type(&'static str), +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/needless_lifetimes_impl_trait.rs b/src/tools/clippy/tests/ui/crashes/needless_lifetimes_impl_trait.rs new file mode 100644 index 00000000000..bd1fa4a0b1e --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/needless_lifetimes_impl_trait.rs @@ -0,0 +1,22 @@ +// run-pass + +#![deny(clippy::needless_lifetimes)] +#![allow(dead_code)] + +trait Foo {} + +struct Bar {} + +struct Baz<'a> { + bar: &'a Bar, +} + +impl<'a> Foo for Baz<'a> {} + +impl Bar { + fn baz<'a>(&'a self) -> impl Foo + 'a { + Baz { bar: self } + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/procedural_macro.rs b/src/tools/clippy/tests/ui/crashes/procedural_macro.rs new file mode 100644 index 00000000000..f79d9ab6460 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/procedural_macro.rs @@ -0,0 +1,13 @@ +// run-pass + +#[macro_use] +extern crate clippy_mini_macro_test; + +#[deny(warnings)] +fn main() { + let x = Foo; + println!("{:?}", x); +} + +#[derive(ClippyMiniMacroTest, Debug)] +struct Foo; diff --git a/src/tools/clippy/tests/ui/crashes/regressions.rs b/src/tools/clippy/tests/ui/crashes/regressions.rs new file mode 100644 index 00000000000..623ae51f9f0 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/regressions.rs @@ -0,0 +1,9 @@ +// run-pass + +#![allow(clippy::blacklisted_name)] + +pub fn foo(bar: *const u8) { + println!("{:#p}", bar); +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/returns.rs b/src/tools/clippy/tests/ui/crashes/returns.rs new file mode 100644 index 00000000000..f2153efc388 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/returns.rs @@ -0,0 +1,25 @@ +// run-pass + +/// Test for https://github.com/rust-lang/rust-clippy/issues/1346 + +#[deny(warnings)] +fn cfg_return() -> i32 { + #[cfg(unix)] + return 1; + #[cfg(not(unix))] + return 2; +} + +#[deny(warnings)] +fn cfg_let_and_return() -> i32 { + #[cfg(unix)] + let x = 1; + #[cfg(not(unix))] + let x = 2; + x +} + +fn main() { + cfg_return(); + cfg_let_and_return(); +} diff --git a/src/tools/clippy/tests/ui/crashes/shadow.rs b/src/tools/clippy/tests/ui/crashes/shadow.rs new file mode 100644 index 00000000000..843e8ef64dc --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/shadow.rs @@ -0,0 +1,6 @@ +fn main() { + let x: [i32; { + let u = 2; + 4 + }] = [2; { 4 }]; +} diff --git a/src/tools/clippy/tests/ui/crashes/single-match-else.rs b/src/tools/clippy/tests/ui/crashes/single-match-else.rs new file mode 100644 index 00000000000..3a4bbe310cc --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/single-match-else.rs @@ -0,0 +1,13 @@ +// run-pass + +#![warn(clippy::single_match_else)] + +//! Test for https://github.com/rust-lang/rust-clippy/issues/1588 + +fn main() { + let n = match (42, 43) { + (42, n) => n, + _ => panic!("typeck error"), + }; + assert_eq!(n, 43); +} diff --git a/src/tools/clippy/tests/ui/crashes/trivial_bounds.rs b/src/tools/clippy/tests/ui/crashes/trivial_bounds.rs new file mode 100644 index 00000000000..2bb95c18a39 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/trivial_bounds.rs @@ -0,0 +1,13 @@ +// run-pass + +#![feature(trivial_bounds)] +#![allow(unused, trivial_bounds)] + +fn test_trivial_bounds() +where + i32: Iterator, +{ + for _ in 2i32 {} +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/used_underscore_binding_macro.rs b/src/tools/clippy/tests/ui/crashes/used_underscore_binding_macro.rs new file mode 100644 index 00000000000..265017c51d9 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/used_underscore_binding_macro.rs @@ -0,0 +1,21 @@ +// run-pass + +#![allow(clippy::useless_attribute)] //issue #2910 + +#[macro_use] +extern crate serde_derive; + +/// Tests that we do not lint for unused underscores in a `MacroAttribute` +/// expansion +#[deny(clippy::used_underscore_binding)] +#[derive(Deserialize)] +struct MacroAttributesTest { + _foo: u32, +} + +#[test] +fn macro_attributes_test() { + let _ = MacroAttributesTest { _foo: 0 }; +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/whitelist/clippy.toml b/src/tools/clippy/tests/ui/crashes/whitelist/clippy.toml new file mode 100644 index 00000000000..9f87de20baf --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/whitelist/clippy.toml @@ -0,0 +1,3 @@ +# this is ignored by Clippy, but allowed for other tools like clippy-service +[third-party] +clippy-feature = "nightly" diff --git a/src/tools/clippy/tests/ui/crashes/whitelist/conf_whitelisted.rs b/src/tools/clippy/tests/ui/crashes/whitelist/conf_whitelisted.rs new file mode 100644 index 00000000000..f328e4d9d04 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/whitelist/conf_whitelisted.rs @@ -0,0 +1 @@ +fn main() {} diff --git a/src/tools/clippy/tests/ui/crate_level_checks/entrypoint_recursion.rs b/src/tools/clippy/tests/ui/crate_level_checks/entrypoint_recursion.rs new file mode 100644 index 00000000000..995787c5336 --- /dev/null +++ b/src/tools/clippy/tests/ui/crate_level_checks/entrypoint_recursion.rs @@ -0,0 +1,12 @@ +// ignore-macos +// ignore-windows + +#![feature(main)] + +#[warn(clippy::main_recursion)] +#[allow(unconditional_recursion)] +#[main] +fn a() { + println!("Hello, World!"); + a(); +} diff --git a/src/tools/clippy/tests/ui/crate_level_checks/entrypoint_recursion.stderr b/src/tools/clippy/tests/ui/crate_level_checks/entrypoint_recursion.stderr new file mode 100644 index 00000000000..f52fc949f6c --- /dev/null +++ b/src/tools/clippy/tests/ui/crate_level_checks/entrypoint_recursion.stderr @@ -0,0 +1,11 @@ +error: recursing into entrypoint `a` + --> $DIR/entrypoint_recursion.rs:11:5 + | +LL | a(); + | ^ + | + = note: `-D clippy::main-recursion` implied by `-D warnings` + = help: consider using another function for this recursion + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/crate_level_checks/no_std_main_recursion.rs b/src/tools/clippy/tests/ui/crate_level_checks/no_std_main_recursion.rs new file mode 100644 index 00000000000..25b1417be97 --- /dev/null +++ b/src/tools/clippy/tests/ui/crate_level_checks/no_std_main_recursion.rs @@ -0,0 +1,33 @@ +// ignore-macos +// ignore-windows + +#![feature(lang_items, link_args, start, libc)] +#![link_args = "-nostartfiles"] +#![no_std] + +use core::panic::PanicInfo; +use core::sync::atomic::{AtomicUsize, Ordering}; + +static N: AtomicUsize = AtomicUsize::new(0); + +#[warn(clippy::main_recursion)] +#[start] +fn main(argc: isize, argv: *const *const u8) -> isize { + let x = N.load(Ordering::Relaxed); + N.store(x + 1, Ordering::Relaxed); + + if x < 3 { + main(argc, argv); + } + + 0 +} + +#[allow(clippy::empty_loop)] +#[panic_handler] +fn panic(_info: &PanicInfo) -> ! { + loop {} +} + +#[lang = "eh_personality"] +extern "C" fn eh_personality() {} diff --git a/src/tools/clippy/tests/ui/crate_level_checks/std_main_recursion.rs b/src/tools/clippy/tests/ui/crate_level_checks/std_main_recursion.rs new file mode 100644 index 00000000000..89ff6609934 --- /dev/null +++ b/src/tools/clippy/tests/ui/crate_level_checks/std_main_recursion.rs @@ -0,0 +1,6 @@ +#[warn(clippy::main_recursion)] +#[allow(unconditional_recursion)] +fn main() { + println!("Hello, World!"); + main(); +} diff --git a/src/tools/clippy/tests/ui/crate_level_checks/std_main_recursion.stderr b/src/tools/clippy/tests/ui/crate_level_checks/std_main_recursion.stderr new file mode 100644 index 00000000000..0a260f9d230 --- /dev/null +++ b/src/tools/clippy/tests/ui/crate_level_checks/std_main_recursion.stderr @@ -0,0 +1,11 @@ +error: recursing into entrypoint `main` + --> $DIR/std_main_recursion.rs:5:5 + | +LL | main(); + | ^^^^ + | + = note: `-D clippy::main-recursion` implied by `-D warnings` + = help: consider using another function for this recursion + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/cstring.rs b/src/tools/clippy/tests/ui/cstring.rs new file mode 100644 index 00000000000..6cdd6b4ff6e --- /dev/null +++ b/src/tools/clippy/tests/ui/cstring.rs @@ -0,0 +1,24 @@ +#![deny(clippy::temporary_cstring_as_ptr)] + +fn main() {} + +fn temporary_cstring() { + use std::ffi::CString; + + CString::new("foo").unwrap().as_ptr(); + CString::new("foo").expect("dummy").as_ptr(); +} + +mod issue4375 { + use std::ffi::CString; + use std::os::raw::c_char; + + extern "C" { + fn foo(data: *const c_char); + } + + pub fn bar(v: &[u8]) { + let cstr = CString::new(v); + unsafe { foo(cstr.unwrap().as_ptr()) } + } +} diff --git a/src/tools/clippy/tests/ui/cstring.stderr b/src/tools/clippy/tests/ui/cstring.stderr new file mode 100644 index 00000000000..87cb29be577 --- /dev/null +++ b/src/tools/clippy/tests/ui/cstring.stderr @@ -0,0 +1,46 @@ +error: you are getting the inner pointer of a temporary `CString` + --> $DIR/cstring.rs:8:5 + | +LL | CString::new("foo").unwrap().as_ptr(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/cstring.rs:1:9 + | +LL | #![deny(clippy::temporary_cstring_as_ptr)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: that pointer will be invalid outside this expression +help: assign the `CString` to a variable to extend its lifetime + --> $DIR/cstring.rs:8:5 + | +LL | CString::new("foo").unwrap().as_ptr(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: you are getting the inner pointer of a temporary `CString` + --> $DIR/cstring.rs:9:5 + | +LL | CString::new("foo").expect("dummy").as_ptr(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: that pointer will be invalid outside this expression +help: assign the `CString` to a variable to extend its lifetime + --> $DIR/cstring.rs:9:5 + | +LL | CString::new("foo").expect("dummy").as_ptr(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: you are getting the inner pointer of a temporary `CString` + --> $DIR/cstring.rs:22:22 + | +LL | unsafe { foo(cstr.unwrap().as_ptr()) } + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: that pointer will be invalid outside this expression +help: assign the `CString` to a variable to extend its lifetime + --> $DIR/cstring.rs:22:22 + | +LL | unsafe { foo(cstr.unwrap().as_ptr()) } + | ^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/custom_ice_message.rs b/src/tools/clippy/tests/ui/custom_ice_message.rs new file mode 100644 index 00000000000..5b30c9d5721 --- /dev/null +++ b/src/tools/clippy/tests/ui/custom_ice_message.rs @@ -0,0 +1,10 @@ +// rustc-env:RUST_BACKTRACE=0 +// normalize-stderr-test: "Clippy version: .*" -> "Clippy version: foo" +// normalize-stderr-test: "internal_lints.rs:\d*:\d*" -> "internal_lints.rs" +// normalize-stderr-test: "', .*clippy_lints" -> "', clippy_lints" + +#![deny(clippy::internal)] + +fn it_looks_like_you_are_trying_to_kill_clippy() {} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/custom_ice_message.stderr b/src/tools/clippy/tests/ui/custom_ice_message.stderr new file mode 100644 index 00000000000..a9a65a38c10 --- /dev/null +++ b/src/tools/clippy/tests/ui/custom_ice_message.stderr @@ -0,0 +1,11 @@ +thread 'rustc' panicked at 'Would you like some help with that?', clippy_lints/src/utils/internal_lints.rs +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace + +error: internal compiler error: unexpected panic + +note: the compiler unexpectedly panicked. this is a bug. + +note: we would appreciate a bug report: https://github.com/rust-lang/rust-clippy/issues/new + +note: Clippy version: foo + diff --git a/src/tools/clippy/tests/ui/dbg_macro.rs b/src/tools/clippy/tests/ui/dbg_macro.rs new file mode 100644 index 00000000000..d2df7fbd3e8 --- /dev/null +++ b/src/tools/clippy/tests/ui/dbg_macro.rs @@ -0,0 +1,23 @@ +#![warn(clippy::dbg_macro)] + +fn foo(n: u32) -> u32 { + if let Some(n) = dbg!(n.checked_sub(4)) { + n + } else { + n + } +} + +fn factorial(n: u32) -> u32 { + if dbg!(n <= 1) { + dbg!(1) + } else { + dbg!(n * factorial(n - 1)) + } +} + +fn main() { + dbg!(42); + dbg!(dbg!(dbg!(42))); + foo(3) + dbg!(factorial(4)); +} diff --git a/src/tools/clippy/tests/ui/dbg_macro.stderr b/src/tools/clippy/tests/ui/dbg_macro.stderr new file mode 100644 index 00000000000..b8aafe96678 --- /dev/null +++ b/src/tools/clippy/tests/ui/dbg_macro.stderr @@ -0,0 +1,80 @@ +error: `dbg!` macro is intended as a debugging tool + --> $DIR/dbg_macro.rs:4:22 + | +LL | if let Some(n) = dbg!(n.checked_sub(4)) { + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::dbg-macro` implied by `-D warnings` +help: ensure to avoid having uses of it in version control + | +LL | if let Some(n) = n.checked_sub(4) { + | ^^^^^^^^^^^^^^^^ + +error: `dbg!` macro is intended as a debugging tool + --> $DIR/dbg_macro.rs:12:8 + | +LL | if dbg!(n <= 1) { + | ^^^^^^^^^^^^ + | +help: ensure to avoid having uses of it in version control + | +LL | if n <= 1 { + | ^^^^^^ + +error: `dbg!` macro is intended as a debugging tool + --> $DIR/dbg_macro.rs:13:9 + | +LL | dbg!(1) + | ^^^^^^^ + | +help: ensure to avoid having uses of it in version control + | +LL | 1 + | + +error: `dbg!` macro is intended as a debugging tool + --> $DIR/dbg_macro.rs:15:9 + | +LL | dbg!(n * factorial(n - 1)) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: ensure to avoid having uses of it in version control + | +LL | n * factorial(n - 1) + | + +error: `dbg!` macro is intended as a debugging tool + --> $DIR/dbg_macro.rs:20:5 + | +LL | dbg!(42); + | ^^^^^^^^ + | +help: ensure to avoid having uses of it in version control + | +LL | 42; + | ^^ + +error: `dbg!` macro is intended as a debugging tool + --> $DIR/dbg_macro.rs:21:5 + | +LL | dbg!(dbg!(dbg!(42))); + | ^^^^^^^^^^^^^^^^^^^^ + | +help: ensure to avoid having uses of it in version control + | +LL | dbg!(dbg!(42)); + | ^^^^^^^^^^^^^^ + +error: `dbg!` macro is intended as a debugging tool + --> $DIR/dbg_macro.rs:22:14 + | +LL | foo(3) + dbg!(factorial(4)); + | ^^^^^^^^^^^^^^^^^^ + | +help: ensure to avoid having uses of it in version control + | +LL | foo(3) + factorial(4); + | ^^^^^^^^^^^^ + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/tests/ui/debug_assert_with_mut_call.rs b/src/tools/clippy/tests/ui/debug_assert_with_mut_call.rs new file mode 100644 index 00000000000..477a47118d4 --- /dev/null +++ b/src/tools/clippy/tests/ui/debug_assert_with_mut_call.rs @@ -0,0 +1,133 @@ +// compile-flags: --edition=2018 +#![feature(custom_inner_attributes)] +#![rustfmt::skip] +#![warn(clippy::debug_assert_with_mut_call)] +#![allow(clippy::redundant_closure_call)] + +struct S; + +impl S { + fn bool_self_ref(&self) -> bool { false } + fn bool_self_mut(&mut self) -> bool { false } + fn bool_self_ref_arg_ref(&self, _: &u32) -> bool { false } + fn bool_self_ref_arg_mut(&self, _: &mut u32) -> bool { false } + fn bool_self_mut_arg_ref(&mut self, _: &u32) -> bool { false } + fn bool_self_mut_arg_mut(&mut self, _: &mut u32) -> bool { false } + + fn u32_self_ref(&self) -> u32 { 0 } + fn u32_self_mut(&mut self) -> u32 { 0 } + fn u32_self_ref_arg_ref(&self, _: &u32) -> u32 { 0 } + fn u32_self_ref_arg_mut(&self, _: &mut u32) -> u32 { 0 } + fn u32_self_mut_arg_ref(&mut self, _: &u32) -> u32 { 0 } + fn u32_self_mut_arg_mut(&mut self, _: &mut u32) -> u32 { 0 } +} + +fn bool_ref(_: &u32) -> bool { false } +fn bool_mut(_: &mut u32) -> bool { false } +fn u32_ref(_: &u32) -> u32 { 0 } +fn u32_mut(_: &mut u32) -> u32 { 0 } + +fn func_non_mutable() { + debug_assert!(bool_ref(&3)); + debug_assert!(!bool_ref(&3)); + + debug_assert_eq!(0, u32_ref(&3)); + debug_assert_eq!(u32_ref(&3), 0); + + debug_assert_ne!(1, u32_ref(&3)); + debug_assert_ne!(u32_ref(&3), 1); +} + +fn func_mutable() { + debug_assert!(bool_mut(&mut 3)); + debug_assert!(!bool_mut(&mut 3)); + + debug_assert_eq!(0, u32_mut(&mut 3)); + debug_assert_eq!(u32_mut(&mut 3), 0); + + debug_assert_ne!(1, u32_mut(&mut 3)); + debug_assert_ne!(u32_mut(&mut 3), 1); +} + +fn method_non_mutable() { + debug_assert!(S.bool_self_ref()); + debug_assert!(S.bool_self_ref_arg_ref(&3)); + + debug_assert_eq!(S.u32_self_ref(), 0); + debug_assert_eq!(S.u32_self_ref_arg_ref(&3), 0); + + debug_assert_ne!(S.u32_self_ref(), 1); + debug_assert_ne!(S.u32_self_ref_arg_ref(&3), 1); +} + +fn method_mutable() { + debug_assert!(S.bool_self_mut()); + debug_assert!(!S.bool_self_mut()); + debug_assert!(S.bool_self_ref_arg_mut(&mut 3)); + debug_assert!(S.bool_self_mut_arg_ref(&3)); + debug_assert!(S.bool_self_mut_arg_mut(&mut 3)); + + debug_assert_eq!(S.u32_self_mut(), 0); + debug_assert_eq!(S.u32_self_mut_arg_ref(&3), 0); + debug_assert_eq!(S.u32_self_ref_arg_mut(&mut 3), 0); + debug_assert_eq!(S.u32_self_mut_arg_mut(&mut 3), 0); + + debug_assert_ne!(S.u32_self_mut(), 1); + debug_assert_ne!(S.u32_self_mut_arg_ref(&3), 1); + debug_assert_ne!(S.u32_self_ref_arg_mut(&mut 3), 1); + debug_assert_ne!(S.u32_self_mut_arg_mut(&mut 3), 1); +} + +fn misc() { + // with variable + let mut v: Vec<u32> = vec![1, 2, 3, 4]; + debug_assert_eq!(v.get(0), Some(&1)); + debug_assert_ne!(v[0], 2); + debug_assert_eq!(v.pop(), Some(1)); + debug_assert_ne!(Some(3), v.pop()); + + let a = &mut 3; + debug_assert!(bool_mut(a)); + + // nested + debug_assert!(!(bool_ref(&u32_mut(&mut 3)))); + + // chained + debug_assert_eq!(v.pop().unwrap(), 3); + + // format args + debug_assert!(bool_ref(&3), "w/o format"); + debug_assert!(bool_mut(&mut 3), "w/o format"); + debug_assert!(bool_ref(&3), "{} format", "w/"); + debug_assert!(bool_mut(&mut 3), "{} format", "w/"); + + // sub block + let mut x = 42_u32; + debug_assert!({ + bool_mut(&mut x); + x > 10 + }); + + // closures + debug_assert!((|| { + let mut x = 42; + bool_mut(&mut x); + x > 10 + })()); +} + +async fn debug_await() { + debug_assert!(async { + true + }.await); +} + +fn main() { + func_non_mutable(); + func_mutable(); + method_non_mutable(); + method_mutable(); + + misc(); + debug_await(); +} diff --git a/src/tools/clippy/tests/ui/debug_assert_with_mut_call.stderr b/src/tools/clippy/tests/ui/debug_assert_with_mut_call.stderr new file mode 100644 index 00000000000..a2ca71b57a6 --- /dev/null +++ b/src/tools/clippy/tests/ui/debug_assert_with_mut_call.stderr @@ -0,0 +1,172 @@ +error: do not call a function with mutable arguments inside of `debug_assert!` + --> $DIR/debug_assert_with_mut_call.rs:42:19 + | +LL | debug_assert!(bool_mut(&mut 3)); + | ^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::debug-assert-with-mut-call` implied by `-D warnings` + +error: do not call a function with mutable arguments inside of `debug_assert!` + --> $DIR/debug_assert_with_mut_call.rs:43:20 + | +LL | debug_assert!(!bool_mut(&mut 3)); + | ^^^^^^^^^^^^^^^^ + +error: do not call a function with mutable arguments inside of `debug_assert_eq!` + --> $DIR/debug_assert_with_mut_call.rs:45:25 + | +LL | debug_assert_eq!(0, u32_mut(&mut 3)); + | ^^^^^^^^^^^^^^^ + +error: do not call a function with mutable arguments inside of `debug_assert_eq!` + --> $DIR/debug_assert_with_mut_call.rs:46:22 + | +LL | debug_assert_eq!(u32_mut(&mut 3), 0); + | ^^^^^^^^^^^^^^^ + +error: do not call a function with mutable arguments inside of `debug_assert_ne!` + --> $DIR/debug_assert_with_mut_call.rs:48:25 + | +LL | debug_assert_ne!(1, u32_mut(&mut 3)); + | ^^^^^^^^^^^^^^^ + +error: do not call a function with mutable arguments inside of `debug_assert_ne!` + --> $DIR/debug_assert_with_mut_call.rs:49:22 + | +LL | debug_assert_ne!(u32_mut(&mut 3), 1); + | ^^^^^^^^^^^^^^^ + +error: do not call a function with mutable arguments inside of `debug_assert!` + --> $DIR/debug_assert_with_mut_call.rs:64:19 + | +LL | debug_assert!(S.bool_self_mut()); + | ^^^^^^^^^^^^^^^^^ + +error: do not call a function with mutable arguments inside of `debug_assert!` + --> $DIR/debug_assert_with_mut_call.rs:65:20 + | +LL | debug_assert!(!S.bool_self_mut()); + | ^^^^^^^^^^^^^^^^^ + +error: do not call a function with mutable arguments inside of `debug_assert!` + --> $DIR/debug_assert_with_mut_call.rs:66:19 + | +LL | debug_assert!(S.bool_self_ref_arg_mut(&mut 3)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: do not call a function with mutable arguments inside of `debug_assert!` + --> $DIR/debug_assert_with_mut_call.rs:67:19 + | +LL | debug_assert!(S.bool_self_mut_arg_ref(&3)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: do not call a function with mutable arguments inside of `debug_assert!` + --> $DIR/debug_assert_with_mut_call.rs:68:19 + | +LL | debug_assert!(S.bool_self_mut_arg_mut(&mut 3)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: do not call a function with mutable arguments inside of `debug_assert_eq!` + --> $DIR/debug_assert_with_mut_call.rs:70:22 + | +LL | debug_assert_eq!(S.u32_self_mut(), 0); + | ^^^^^^^^^^^^^^^^ + +error: do not call a function with mutable arguments inside of `debug_assert_eq!` + --> $DIR/debug_assert_with_mut_call.rs:71:22 + | +LL | debug_assert_eq!(S.u32_self_mut_arg_ref(&3), 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: do not call a function with mutable arguments inside of `debug_assert_eq!` + --> $DIR/debug_assert_with_mut_call.rs:72:22 + | +LL | debug_assert_eq!(S.u32_self_ref_arg_mut(&mut 3), 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: do not call a function with mutable arguments inside of `debug_assert_eq!` + --> $DIR/debug_assert_with_mut_call.rs:73:22 + | +LL | debug_assert_eq!(S.u32_self_mut_arg_mut(&mut 3), 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: do not call a function with mutable arguments inside of `debug_assert_ne!` + --> $DIR/debug_assert_with_mut_call.rs:75:22 + | +LL | debug_assert_ne!(S.u32_self_mut(), 1); + | ^^^^^^^^^^^^^^^^ + +error: do not call a function with mutable arguments inside of `debug_assert_ne!` + --> $DIR/debug_assert_with_mut_call.rs:76:22 + | +LL | debug_assert_ne!(S.u32_self_mut_arg_ref(&3), 1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: do not call a function with mutable arguments inside of `debug_assert_ne!` + --> $DIR/debug_assert_with_mut_call.rs:77:22 + | +LL | debug_assert_ne!(S.u32_self_ref_arg_mut(&mut 3), 1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: do not call a function with mutable arguments inside of `debug_assert_ne!` + --> $DIR/debug_assert_with_mut_call.rs:78:22 + | +LL | debug_assert_ne!(S.u32_self_mut_arg_mut(&mut 3), 1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: do not call a function with mutable arguments inside of `debug_assert_eq!` + --> $DIR/debug_assert_with_mut_call.rs:86:22 + | +LL | debug_assert_eq!(v.pop(), Some(1)); + | ^^^^^^^ + +error: do not call a function with mutable arguments inside of `debug_assert_ne!` + --> $DIR/debug_assert_with_mut_call.rs:87:31 + | +LL | debug_assert_ne!(Some(3), v.pop()); + | ^^^^^^^ + +error: do not call a function with mutable arguments inside of `debug_assert!` + --> $DIR/debug_assert_with_mut_call.rs:90:19 + | +LL | debug_assert!(bool_mut(a)); + | ^^^^^^^^^^^ + +error: do not call a function with mutable arguments inside of `debug_assert!` + --> $DIR/debug_assert_with_mut_call.rs:93:31 + | +LL | debug_assert!(!(bool_ref(&u32_mut(&mut 3)))); + | ^^^^^^^^^^^^^^^ + +error: do not call a function with mutable arguments inside of `debug_assert_eq!` + --> $DIR/debug_assert_with_mut_call.rs:96:22 + | +LL | debug_assert_eq!(v.pop().unwrap(), 3); + | ^^^^^^^ + +error: do not call a function with mutable arguments inside of `debug_assert!` + --> $DIR/debug_assert_with_mut_call.rs:100:19 + | +LL | debug_assert!(bool_mut(&mut 3), "w/o format"); + | ^^^^^^^^^^^^^^^^ + +error: do not call a function with mutable arguments inside of `debug_assert!` + --> $DIR/debug_assert_with_mut_call.rs:102:19 + | +LL | debug_assert!(bool_mut(&mut 3), "{} format", "w/"); + | ^^^^^^^^^^^^^^^^ + +error: do not call a function with mutable arguments inside of `debug_assert!` + --> $DIR/debug_assert_with_mut_call.rs:107:9 + | +LL | bool_mut(&mut x); + | ^^^^^^^^^^^^^^^^ + +error: do not call a function with mutable arguments inside of `debug_assert!` + --> $DIR/debug_assert_with_mut_call.rs:114:9 + | +LL | bool_mut(&mut x); + | ^^^^^^^^^^^^^^^^ + +error: aborting due to 28 previous errors + diff --git a/src/tools/clippy/tests/ui/decimal_literal_representation.fixed b/src/tools/clippy/tests/ui/decimal_literal_representation.fixed new file mode 100644 index 00000000000..de391465125 --- /dev/null +++ b/src/tools/clippy/tests/ui/decimal_literal_representation.fixed @@ -0,0 +1,27 @@ +// run-rustfix + +#[warn(clippy::decimal_literal_representation)] +#[allow(unused_variables)] +#[rustfmt::skip] +fn main() { + let good = ( // Hex: + 127, // 0x7F + 256, // 0x100 + 511, // 0x1FF + 2048, // 0x800 + 4090, // 0xFFA + 16_371, // 0x3FF3 + 61_683, // 0xF0F3 + 2_131_750_925, // 0x7F0F_F00D + ); + let bad = ( // Hex: + 0x8005, // 0x8005 + 0xFF00, // 0xFF00 + 0x7F0F_F00F, // 0x7F0F_F00F + 0x7FFF_FFFF, // 0x7FFF_FFFF + #[allow(overflowing_literals)] + 0xF0F0_F0F0, // 0xF0F0_F0F0 + 0x8005_usize, // 0x8005_usize + 0x7F0F_F00F_isize, // 0x7F0F_F00F_isize + ); +} diff --git a/src/tools/clippy/tests/ui/decimal_literal_representation.rs b/src/tools/clippy/tests/ui/decimal_literal_representation.rs new file mode 100644 index 00000000000..55d07698e7e --- /dev/null +++ b/src/tools/clippy/tests/ui/decimal_literal_representation.rs @@ -0,0 +1,27 @@ +// run-rustfix + +#[warn(clippy::decimal_literal_representation)] +#[allow(unused_variables)] +#[rustfmt::skip] +fn main() { + let good = ( // Hex: + 127, // 0x7F + 256, // 0x100 + 511, // 0x1FF + 2048, // 0x800 + 4090, // 0xFFA + 16_371, // 0x3FF3 + 61_683, // 0xF0F3 + 2_131_750_925, // 0x7F0F_F00D + ); + let bad = ( // Hex: + 32_773, // 0x8005 + 65_280, // 0xFF00 + 2_131_750_927, // 0x7F0F_F00F + 2_147_483_647, // 0x7FFF_FFFF + #[allow(overflowing_literals)] + 4_042_322_160, // 0xF0F0_F0F0 + 32_773usize, // 0x8005_usize + 2_131_750_927isize, // 0x7F0F_F00F_isize + ); +} diff --git a/src/tools/clippy/tests/ui/decimal_literal_representation.stderr b/src/tools/clippy/tests/ui/decimal_literal_representation.stderr new file mode 100644 index 00000000000..8d50c8f83db --- /dev/null +++ b/src/tools/clippy/tests/ui/decimal_literal_representation.stderr @@ -0,0 +1,46 @@ +error: integer literal has a better hexadecimal representation + --> $DIR/decimal_literal_representation.rs:18:9 + | +LL | 32_773, // 0x8005 + | ^^^^^^ help: consider: `0x8005` + | + = note: `-D clippy::decimal-literal-representation` implied by `-D warnings` + +error: integer literal has a better hexadecimal representation + --> $DIR/decimal_literal_representation.rs:19:9 + | +LL | 65_280, // 0xFF00 + | ^^^^^^ help: consider: `0xFF00` + +error: integer literal has a better hexadecimal representation + --> $DIR/decimal_literal_representation.rs:20:9 + | +LL | 2_131_750_927, // 0x7F0F_F00F + | ^^^^^^^^^^^^^ help: consider: `0x7F0F_F00F` + +error: integer literal has a better hexadecimal representation + --> $DIR/decimal_literal_representation.rs:21:9 + | +LL | 2_147_483_647, // 0x7FFF_FFFF + | ^^^^^^^^^^^^^ help: consider: `0x7FFF_FFFF` + +error: integer literal has a better hexadecimal representation + --> $DIR/decimal_literal_representation.rs:23:9 + | +LL | 4_042_322_160, // 0xF0F0_F0F0 + | ^^^^^^^^^^^^^ help: consider: `0xF0F0_F0F0` + +error: integer literal has a better hexadecimal representation + --> $DIR/decimal_literal_representation.rs:24:9 + | +LL | 32_773usize, // 0x8005_usize + | ^^^^^^^^^^^ help: consider: `0x8005_usize` + +error: integer literal has a better hexadecimal representation + --> $DIR/decimal_literal_representation.rs:25:9 + | +LL | 2_131_750_927isize, // 0x7F0F_F00F_isize + | ^^^^^^^^^^^^^^^^^^ help: consider: `0x7F0F_F00F_isize` + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/tests/ui/declare_interior_mutable_const.rs b/src/tools/clippy/tests/ui/declare_interior_mutable_const.rs new file mode 100644 index 00000000000..b4003ed8932 --- /dev/null +++ b/src/tools/clippy/tests/ui/declare_interior_mutable_const.rs @@ -0,0 +1,93 @@ +#![warn(clippy::declare_interior_mutable_const)] + +use std::borrow::Cow; +use std::cell::Cell; +use std::fmt::Display; +use std::sync::atomic::AtomicUsize; +use std::sync::Once; + +const ATOMIC: AtomicUsize = AtomicUsize::new(5); //~ ERROR interior mutable +const CELL: Cell<usize> = Cell::new(6); //~ ERROR interior mutable +const ATOMIC_TUPLE: ([AtomicUsize; 1], Vec<AtomicUsize>, u8) = ([ATOMIC], Vec::new(), 7); +//~^ ERROR interior mutable + +macro_rules! declare_const { + ($name:ident: $ty:ty = $e:expr) => { + const $name: $ty = $e; + }; +} +declare_const!(_ONCE: Once = Once::new()); //~ ERROR interior mutable + +// const ATOMIC_REF: &AtomicUsize = &AtomicUsize::new(7); // This will simply trigger E0492. + +const INTEGER: u8 = 8; +const STRING: String = String::new(); +const STR: &str = "012345"; +const COW: Cow<str> = Cow::Borrowed("abcdef"); +//^ note: a const item of Cow is used in the `postgres` package. + +const NO_ANN: &dyn Display = &70; + +static STATIC_TUPLE: (AtomicUsize, String) = (ATOMIC, STRING); +//^ there should be no lints on this line + +#[allow(clippy::declare_interior_mutable_const)] +const ONCE_INIT: Once = Once::new(); + +trait Trait<T>: Copy { + type NonCopyType; + + const ATOMIC: AtomicUsize; //~ ERROR interior mutable + const INTEGER: u64; + const STRING: String; + const SELF: Self; // (no error) + const INPUT: T; + //~^ ERROR interior mutable + //~| HELP consider requiring `T` to be `Copy` + const ASSOC: Self::NonCopyType; + //~^ ERROR interior mutable + //~| HELP consider requiring `<Self as Trait<T>>::NonCopyType` to be `Copy` + + const AN_INPUT: T = Self::INPUT; + //~^ ERROR interior mutable + //~| ERROR consider requiring `T` to be `Copy` + declare_const!(ANOTHER_INPUT: T = Self::INPUT); //~ ERROR interior mutable +} + +trait Trait2 { + type CopyType: Copy; + + const SELF_2: Self; + //~^ ERROR interior mutable + //~| HELP consider requiring `Self` to be `Copy` + const ASSOC_2: Self::CopyType; // (no error) +} + +// we don't lint impl of traits, because an impl has no power to change the interface. +impl Trait<u32> for u64 { + type NonCopyType = u16; + + const ATOMIC: AtomicUsize = AtomicUsize::new(9); + const INTEGER: u64 = 10; + const STRING: String = String::new(); + const SELF: Self = 11; + const INPUT: u32 = 12; + const ASSOC: Self::NonCopyType = 13; +} + +struct Local<T, U>(T, U); + +impl<T: Trait2 + Trait<u32>, U: Trait2> Local<T, U> { + const ASSOC_3: AtomicUsize = AtomicUsize::new(14); //~ ERROR interior mutable + const COW: Cow<'static, str> = Cow::Borrowed("tuvwxy"); + const T_SELF: T = T::SELF_2; + const U_SELF: U = U::SELF_2; + //~^ ERROR interior mutable + //~| HELP consider requiring `U` to be `Copy` + const T_ASSOC: T::NonCopyType = T::ASSOC; + //~^ ERROR interior mutable + //~| HELP consider requiring `<T as Trait<u32>>::NonCopyType` to be `Copy` + const U_ASSOC: U::CopyType = U::ASSOC_2; +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/declare_interior_mutable_const.stderr b/src/tools/clippy/tests/ui/declare_interior_mutable_const.stderr new file mode 100644 index 00000000000..6a9a57361f9 --- /dev/null +++ b/src/tools/clippy/tests/ui/declare_interior_mutable_const.stderr @@ -0,0 +1,110 @@ +error: a `const` item should never be interior mutable + --> $DIR/declare_interior_mutable_const.rs:9:1 + | +LL | const ATOMIC: AtomicUsize = AtomicUsize::new(5); //~ ERROR interior mutable + | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | make this a static item (maybe with lazy_static) + | + = note: `-D clippy::declare-interior-mutable-const` implied by `-D warnings` + +error: a `const` item should never be interior mutable + --> $DIR/declare_interior_mutable_const.rs:10:1 + | +LL | const CELL: Cell<usize> = Cell::new(6); //~ ERROR interior mutable + | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | make this a static item (maybe with lazy_static) + +error: a `const` item should never be interior mutable + --> $DIR/declare_interior_mutable_const.rs:11:1 + | +LL | const ATOMIC_TUPLE: ([AtomicUsize; 1], Vec<AtomicUsize>, u8) = ([ATOMIC], Vec::new(), 7); + | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | make this a static item (maybe with lazy_static) + +error: a `const` item should never be interior mutable + --> $DIR/declare_interior_mutable_const.rs:16:9 + | +LL | const $name: $ty = $e; + | ^^^^^^^^^^^^^^^^^^^^^^ +... +LL | declare_const!(_ONCE: Once = Once::new()); //~ ERROR interior mutable + | ------------------------------------------ in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: a `const` item should never be interior mutable + --> $DIR/declare_interior_mutable_const.rs:40:5 + | +LL | const ATOMIC: AtomicUsize; //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: a `const` item should never be interior mutable + --> $DIR/declare_interior_mutable_const.rs:44:5 + | +LL | const INPUT: T; + | ^^^^^^^^^^^^^-^ + | | + | consider requiring `T` to be `Copy` + +error: a `const` item should never be interior mutable + --> $DIR/declare_interior_mutable_const.rs:47:5 + | +LL | const ASSOC: Self::NonCopyType; + | ^^^^^^^^^^^^^-----------------^ + | | + | consider requiring `<Self as Trait<T>>::NonCopyType` to be `Copy` + +error: a `const` item should never be interior mutable + --> $DIR/declare_interior_mutable_const.rs:51:5 + | +LL | const AN_INPUT: T = Self::INPUT; + | ^^^^^^^^^^^^^^^^-^^^^^^^^^^^^^^^ + | | + | consider requiring `T` to be `Copy` + +error: a `const` item should never be interior mutable + --> $DIR/declare_interior_mutable_const.rs:16:9 + | +LL | const $name: $ty = $e; + | ^^^^^^^^^^^^^^^^^^^^^^ +... +LL | declare_const!(ANOTHER_INPUT: T = Self::INPUT); //~ ERROR interior mutable + | ----------------------------------------------- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: a `const` item should never be interior mutable + --> $DIR/declare_interior_mutable_const.rs:60:5 + | +LL | const SELF_2: Self; + | ^^^^^^^^^^^^^^----^ + | | + | consider requiring `Self` to be `Copy` + +error: a `const` item should never be interior mutable + --> $DIR/declare_interior_mutable_const.rs:81:5 + | +LL | const ASSOC_3: AtomicUsize = AtomicUsize::new(14); //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: a `const` item should never be interior mutable + --> $DIR/declare_interior_mutable_const.rs:84:5 + | +LL | const U_SELF: U = U::SELF_2; + | ^^^^^^^^^^^^^^-^^^^^^^^^^^^^ + | | + | consider requiring `U` to be `Copy` + +error: a `const` item should never be interior mutable + --> $DIR/declare_interior_mutable_const.rs:87:5 + | +LL | const T_ASSOC: T::NonCopyType = T::ASSOC; + | ^^^^^^^^^^^^^^^--------------^^^^^^^^^^^^ + | | + | consider requiring `<T as Trait<u32>>::NonCopyType` to be `Copy` + +error: aborting due to 13 previous errors + diff --git a/src/tools/clippy/tests/ui/def_id_nocore.rs b/src/tools/clippy/tests/ui/def_id_nocore.rs new file mode 100644 index 00000000000..2a948d60b10 --- /dev/null +++ b/src/tools/clippy/tests/ui/def_id_nocore.rs @@ -0,0 +1,29 @@ +// ignore-windows +// ignore-macos + +#![feature(no_core, lang_items, start)] +#![no_core] + +#[link(name = "c")] +extern "C" {} + +#[lang = "sized"] +pub trait Sized {} +#[lang = "copy"] +pub trait Copy {} +#[lang = "freeze"] +pub unsafe trait Freeze {} + +#[lang = "start"] +#[start] +fn start(_argc: isize, _argv: *const *const u8) -> isize { + 0 +} + +pub struct A; + +impl A { + pub fn as_ref(self) -> &'static str { + "A" + } +} diff --git a/src/tools/clippy/tests/ui/def_id_nocore.stderr b/src/tools/clippy/tests/ui/def_id_nocore.stderr new file mode 100644 index 00000000000..ed87a50547d --- /dev/null +++ b/src/tools/clippy/tests/ui/def_id_nocore.stderr @@ -0,0 +1,10 @@ +error: methods called `as_*` usually take self by reference or self by mutable reference; consider choosing a less ambiguous name + --> $DIR/def_id_nocore.rs:26:19 + | +LL | pub fn as_ref(self) -> &'static str { + | ^^^^ + | + = note: `-D clippy::wrong-self-convention` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/default_lint.rs b/src/tools/clippy/tests/ui/default_lint.rs new file mode 100644 index 00000000000..053faae02ce --- /dev/null +++ b/src/tools/clippy/tests/ui/default_lint.rs @@ -0,0 +1,27 @@ +#![deny(clippy::internal)] +#![feature(rustc_private)] + +#[macro_use] +extern crate rustc_middle; +#[macro_use] +extern crate rustc_session; +extern crate rustc_lint; + +declare_tool_lint! { + pub clippy::TEST_LINT, + Warn, + "", + report_in_external_macro: true +} + +declare_tool_lint! { + pub clippy::TEST_LINT_DEFAULT, + Warn, + "default lint description", + report_in_external_macro: true +} + +declare_lint_pass!(Pass => [TEST_LINT]); +declare_lint_pass!(Pass2 => [TEST_LINT_DEFAULT]); + +fn main() {} diff --git a/src/tools/clippy/tests/ui/default_lint.stderr b/src/tools/clippy/tests/ui/default_lint.stderr new file mode 100644 index 00000000000..5c5836a7d29 --- /dev/null +++ b/src/tools/clippy/tests/ui/default_lint.stderr @@ -0,0 +1,21 @@ +error: the lint `TEST_LINT_DEFAULT` has the default lint description + --> $DIR/default_lint.rs:17:1 + | +LL | / declare_tool_lint! { +LL | | pub clippy::TEST_LINT_DEFAULT, +LL | | Warn, +LL | | "default lint description", +LL | | report_in_external_macro: true +LL | | } + | |_^ + | +note: the lint level is defined here + --> $DIR/default_lint.rs:1:9 + | +LL | #![deny(clippy::internal)] + | ^^^^^^^^^^^^^^^^ + = note: `#[deny(clippy::default_lint)]` implied by `#[deny(clippy::internal)]` + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/default_trait_access.rs b/src/tools/clippy/tests/ui/default_trait_access.rs new file mode 100644 index 00000000000..2f1490a7036 --- /dev/null +++ b/src/tools/clippy/tests/ui/default_trait_access.rs @@ -0,0 +1,103 @@ +#![warn(clippy::default_trait_access)] + +use std::default; +use std::default::Default as D2; +use std::string; + +fn main() { + let s1: String = Default::default(); + + let s2 = String::default(); + + let s3: String = D2::default(); + + let s4: String = std::default::Default::default(); + + let s5 = string::String::default(); + + let s6: String = default::Default::default(); + + let s7 = std::string::String::default(); + + let s8: String = DefaultFactory::make_t_badly(); + + let s9: String = DefaultFactory::make_t_nicely(); + + let s10 = DerivedDefault::default(); + + let s11: GenericDerivedDefault<String> = Default::default(); + + let s12 = GenericDerivedDefault::<String>::default(); + + let s13 = TupleDerivedDefault::default(); + + let s14: TupleDerivedDefault = Default::default(); + + let s15: ArrayDerivedDefault = Default::default(); + + let s16 = ArrayDerivedDefault::default(); + + let s17: TupleStructDerivedDefault = Default::default(); + + let s18 = TupleStructDerivedDefault::default(); + + let s19 = <DerivedDefault as Default>::default(); + + println!( + "[{}] [{}] [{}] [{}] [{}] [{}] [{}] [{}] [{}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}], [{:?}]", + s1, + s2, + s3, + s4, + s5, + s6, + s7, + s8, + s9, + s10, + s11, + s12, + s13, + s14, + s15, + s16, + s17, + s18, + s19, + ); +} + +struct DefaultFactory; + +impl DefaultFactory { + pub fn make_t_badly<T: Default>() -> T { + Default::default() + } + + pub fn make_t_nicely<T: Default>() -> T { + T::default() + } +} + +#[derive(Debug, Default)] +struct DerivedDefault { + pub s: String, +} + +#[derive(Debug, Default)] +struct GenericDerivedDefault<T: Default + std::fmt::Debug> { + pub s: T, +} + +#[derive(Debug, Default)] +struct TupleDerivedDefault { + pub s: (String, String), +} + +#[derive(Debug, Default)] +struct ArrayDerivedDefault { + pub s: [String; 10], +} + +#[derive(Debug, Default)] +struct TupleStructDerivedDefault(String); diff --git a/src/tools/clippy/tests/ui/default_trait_access.stderr b/src/tools/clippy/tests/ui/default_trait_access.stderr new file mode 100644 index 00000000000..453760c6b92 --- /dev/null +++ b/src/tools/clippy/tests/ui/default_trait_access.stderr @@ -0,0 +1,52 @@ +error: Calling `std::string::String::default()` is more clear than this expression + --> $DIR/default_trait_access.rs:8:22 + | +LL | let s1: String = Default::default(); + | ^^^^^^^^^^^^^^^^^^ help: try: `std::string::String::default()` + | + = note: `-D clippy::default-trait-access` implied by `-D warnings` + +error: Calling `std::string::String::default()` is more clear than this expression + --> $DIR/default_trait_access.rs:12:22 + | +LL | let s3: String = D2::default(); + | ^^^^^^^^^^^^^ help: try: `std::string::String::default()` + +error: Calling `std::string::String::default()` is more clear than this expression + --> $DIR/default_trait_access.rs:14:22 + | +LL | let s4: String = std::default::Default::default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::string::String::default()` + +error: Calling `std::string::String::default()` is more clear than this expression + --> $DIR/default_trait_access.rs:18:22 + | +LL | let s6: String = default::Default::default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::string::String::default()` + +error: Calling `GenericDerivedDefault<std::string::String>::default()` is more clear than this expression + --> $DIR/default_trait_access.rs:28:46 + | +LL | let s11: GenericDerivedDefault<String> = Default::default(); + | ^^^^^^^^^^^^^^^^^^ help: try: `GenericDerivedDefault<std::string::String>::default()` + +error: Calling `TupleDerivedDefault::default()` is more clear than this expression + --> $DIR/default_trait_access.rs:34:36 + | +LL | let s14: TupleDerivedDefault = Default::default(); + | ^^^^^^^^^^^^^^^^^^ help: try: `TupleDerivedDefault::default()` + +error: Calling `ArrayDerivedDefault::default()` is more clear than this expression + --> $DIR/default_trait_access.rs:36:36 + | +LL | let s15: ArrayDerivedDefault = Default::default(); + | ^^^^^^^^^^^^^^^^^^ help: try: `ArrayDerivedDefault::default()` + +error: Calling `TupleStructDerivedDefault::default()` is more clear than this expression + --> $DIR/default_trait_access.rs:40:42 + | +LL | let s17: TupleStructDerivedDefault = Default::default(); + | ^^^^^^^^^^^^^^^^^^ help: try: `TupleStructDerivedDefault::default()` + +error: aborting due to 8 previous errors + diff --git a/src/tools/clippy/tests/ui/deprecated.rs b/src/tools/clippy/tests/ui/deprecated.rs new file mode 100644 index 00000000000..188a641aa1a --- /dev/null +++ b/src/tools/clippy/tests/ui/deprecated.rs @@ -0,0 +1,11 @@ +#[warn(clippy::str_to_string)] +#[warn(clippy::string_to_string)] +#[warn(clippy::unstable_as_slice)] +#[warn(clippy::unstable_as_mut_slice)] +#[warn(clippy::misaligned_transmute)] +#[warn(clippy::unused_collect)] +#[warn(clippy::invalid_ref)] +#[warn(clippy::into_iter_on_array)] +#[warn(clippy::unused_label)] + +fn main() {} diff --git a/src/tools/clippy/tests/ui/deprecated.stderr b/src/tools/clippy/tests/ui/deprecated.stderr new file mode 100644 index 00000000000..a4efe3d15a9 --- /dev/null +++ b/src/tools/clippy/tests/ui/deprecated.stderr @@ -0,0 +1,64 @@ +error: lint `clippy::str_to_string` has been removed: `using `str::to_string` is common even today and specialization will likely happen soon` + --> $DIR/deprecated.rs:1:8 + | +LL | #[warn(clippy::str_to_string)] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D renamed-and-removed-lints` implied by `-D warnings` + +error: lint `clippy::string_to_string` has been removed: `using `string::to_string` is common even today and specialization will likely happen soon` + --> $DIR/deprecated.rs:2:8 + | +LL | #[warn(clippy::string_to_string)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: lint `clippy::unstable_as_slice` has been removed: ``Vec::as_slice` has been stabilized in 1.7` + --> $DIR/deprecated.rs:3:8 + | +LL | #[warn(clippy::unstable_as_slice)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: lint `clippy::unstable_as_mut_slice` has been removed: ``Vec::as_mut_slice` has been stabilized in 1.7` + --> $DIR/deprecated.rs:4:8 + | +LL | #[warn(clippy::unstable_as_mut_slice)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: lint `clippy::misaligned_transmute` has been removed: `this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr` + --> $DIR/deprecated.rs:5:8 + | +LL | #[warn(clippy::misaligned_transmute)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: lint `clippy::unused_collect` has been removed: ``collect` has been marked as #[must_use] in rustc and that covers all cases of this lint` + --> $DIR/deprecated.rs:6:8 + | +LL | #[warn(clippy::unused_collect)] + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: lint `clippy::invalid_ref` has been removed: `superseded by rustc lint `invalid_value`` + --> $DIR/deprecated.rs:7:8 + | +LL | #[warn(clippy::invalid_ref)] + | ^^^^^^^^^^^^^^^^^^^ + +error: lint `clippy::into_iter_on_array` has been removed: `this lint has been uplifted to rustc and is now called `array_into_iter`` + --> $DIR/deprecated.rs:8:8 + | +LL | #[warn(clippy::into_iter_on_array)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: lint `clippy::unused_label` has been removed: `this lint has been uplifted to rustc and is now called `unused_labels`` + --> $DIR/deprecated.rs:9:8 + | +LL | #[warn(clippy::unused_label)] + | ^^^^^^^^^^^^^^^^^^^^ + +error: lint `clippy::str_to_string` has been removed: `using `str::to_string` is common even today and specialization will likely happen soon` + --> $DIR/deprecated.rs:1:8 + | +LL | #[warn(clippy::str_to_string)] + | ^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 10 previous errors + diff --git a/src/tools/clippy/tests/ui/deprecated_old.rs b/src/tools/clippy/tests/ui/deprecated_old.rs new file mode 100644 index 00000000000..2e5c5b7ead1 --- /dev/null +++ b/src/tools/clippy/tests/ui/deprecated_old.rs @@ -0,0 +1,7 @@ +#[warn(str_to_string)] +#[warn(string_to_string)] +#[warn(unstable_as_slice)] +#[warn(unstable_as_mut_slice)] +#[warn(misaligned_transmute)] + +fn main() {} diff --git a/src/tools/clippy/tests/ui/deprecated_old.stderr b/src/tools/clippy/tests/ui/deprecated_old.stderr new file mode 100644 index 00000000000..ff3e9e8fcf3 --- /dev/null +++ b/src/tools/clippy/tests/ui/deprecated_old.stderr @@ -0,0 +1,40 @@ +error: lint `str_to_string` has been removed: `using `str::to_string` is common even today and specialization will likely happen soon` + --> $DIR/deprecated_old.rs:1:8 + | +LL | #[warn(str_to_string)] + | ^^^^^^^^^^^^^ + | + = note: `-D renamed-and-removed-lints` implied by `-D warnings` + +error: lint `string_to_string` has been removed: `using `string::to_string` is common even today and specialization will likely happen soon` + --> $DIR/deprecated_old.rs:2:8 + | +LL | #[warn(string_to_string)] + | ^^^^^^^^^^^^^^^^ + +error: lint `unstable_as_slice` has been removed: ``Vec::as_slice` has been stabilized in 1.7` + --> $DIR/deprecated_old.rs:3:8 + | +LL | #[warn(unstable_as_slice)] + | ^^^^^^^^^^^^^^^^^ + +error: lint `unstable_as_mut_slice` has been removed: ``Vec::as_mut_slice` has been stabilized in 1.7` + --> $DIR/deprecated_old.rs:4:8 + | +LL | #[warn(unstable_as_mut_slice)] + | ^^^^^^^^^^^^^^^^^^^^^ + +error: lint `misaligned_transmute` has been removed: `this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr` + --> $DIR/deprecated_old.rs:5:8 + | +LL | #[warn(misaligned_transmute)] + | ^^^^^^^^^^^^^^^^^^^^ + +error: lint `str_to_string` has been removed: `using `str::to_string` is common even today and specialization will likely happen soon` + --> $DIR/deprecated_old.rs:1:8 + | +LL | #[warn(str_to_string)] + | ^^^^^^^^^^^^^ + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/deref_addrof.fixed b/src/tools/clippy/tests/ui/deref_addrof.fixed new file mode 100644 index 00000000000..9e5b51d6d5e --- /dev/null +++ b/src/tools/clippy/tests/ui/deref_addrof.fixed @@ -0,0 +1,39 @@ +// run-rustfix + +fn get_number() -> usize { + 10 +} + +fn get_reference(n: &usize) -> &usize { + n +} + +#[allow(clippy::many_single_char_names, clippy::double_parens)] +#[allow(unused_variables, unused_parens)] +#[warn(clippy::deref_addrof)] +fn main() { + let a = 10; + let aref = &a; + + let b = a; + + let b = get_number(); + + let b = *get_reference(&a); + + let bytes: Vec<usize> = vec![1, 2, 3, 4]; + let b = bytes[1..2][0]; + + //This produces a suggestion of 'let b = (a);' which + //will trigger the 'unused_parens' lint + let b = (a); + + let b = a; + + #[rustfmt::skip] + let b = a; + + let b = &a; + + let b = *aref; +} diff --git a/src/tools/clippy/tests/ui/deref_addrof.rs b/src/tools/clippy/tests/ui/deref_addrof.rs new file mode 100644 index 00000000000..5641a73cbc1 --- /dev/null +++ b/src/tools/clippy/tests/ui/deref_addrof.rs @@ -0,0 +1,39 @@ +// run-rustfix + +fn get_number() -> usize { + 10 +} + +fn get_reference(n: &usize) -> &usize { + n +} + +#[allow(clippy::many_single_char_names, clippy::double_parens)] +#[allow(unused_variables, unused_parens)] +#[warn(clippy::deref_addrof)] +fn main() { + let a = 10; + let aref = &a; + + let b = *&a; + + let b = *&get_number(); + + let b = *get_reference(&a); + + let bytes: Vec<usize> = vec![1, 2, 3, 4]; + let b = *&bytes[1..2][0]; + + //This produces a suggestion of 'let b = (a);' which + //will trigger the 'unused_parens' lint + let b = *&(a); + + let b = *(&a); + + #[rustfmt::skip] + let b = *((&a)); + + let b = *&&a; + + let b = **&aref; +} diff --git a/src/tools/clippy/tests/ui/deref_addrof.stderr b/src/tools/clippy/tests/ui/deref_addrof.stderr new file mode 100644 index 00000000000..bc51719e8a7 --- /dev/null +++ b/src/tools/clippy/tests/ui/deref_addrof.stderr @@ -0,0 +1,52 @@ +error: immediately dereferencing a reference + --> $DIR/deref_addrof.rs:18:13 + | +LL | let b = *&a; + | ^^^ help: try this: `a` + | + = note: `-D clippy::deref-addrof` implied by `-D warnings` + +error: immediately dereferencing a reference + --> $DIR/deref_addrof.rs:20:13 + | +LL | let b = *&get_number(); + | ^^^^^^^^^^^^^^ help: try this: `get_number()` + +error: immediately dereferencing a reference + --> $DIR/deref_addrof.rs:25:13 + | +LL | let b = *&bytes[1..2][0]; + | ^^^^^^^^^^^^^^^^ help: try this: `bytes[1..2][0]` + +error: immediately dereferencing a reference + --> $DIR/deref_addrof.rs:29:13 + | +LL | let b = *&(a); + | ^^^^^ help: try this: `(a)` + +error: immediately dereferencing a reference + --> $DIR/deref_addrof.rs:31:13 + | +LL | let b = *(&a); + | ^^^^^ help: try this: `a` + +error: immediately dereferencing a reference + --> $DIR/deref_addrof.rs:34:13 + | +LL | let b = *((&a)); + | ^^^^^^^ help: try this: `a` + +error: immediately dereferencing a reference + --> $DIR/deref_addrof.rs:36:13 + | +LL | let b = *&&a; + | ^^^^ help: try this: `&a` + +error: immediately dereferencing a reference + --> $DIR/deref_addrof.rs:38:14 + | +LL | let b = **&aref; + | ^^^^^^ help: try this: `aref` + +error: aborting due to 8 previous errors + diff --git a/src/tools/clippy/tests/ui/deref_addrof_double_trigger.rs b/src/tools/clippy/tests/ui/deref_addrof_double_trigger.rs new file mode 100644 index 00000000000..4531943299c --- /dev/null +++ b/src/tools/clippy/tests/ui/deref_addrof_double_trigger.rs @@ -0,0 +1,23 @@ +// This test can't work with run-rustfix because it needs two passes of test+fix + +#[warn(clippy::deref_addrof)] +#[allow(unused_variables, unused_mut)] +fn main() { + let a = 10; + + //This produces a suggestion of 'let b = *&a;' which + //will trigger the 'clippy::deref_addrof' lint again + let b = **&&a; + + { + let mut x = 10; + let y = *&mut x; + } + + { + //This produces a suggestion of 'let y = *&mut x' which + //will trigger the 'clippy::deref_addrof' lint again + let mut x = 10; + let y = **&mut &mut x; + } +} diff --git a/src/tools/clippy/tests/ui/deref_addrof_double_trigger.stderr b/src/tools/clippy/tests/ui/deref_addrof_double_trigger.stderr new file mode 100644 index 00000000000..2c55a4ed6ac --- /dev/null +++ b/src/tools/clippy/tests/ui/deref_addrof_double_trigger.stderr @@ -0,0 +1,22 @@ +error: immediately dereferencing a reference + --> $DIR/deref_addrof_double_trigger.rs:10:14 + | +LL | let b = **&&a; + | ^^^^ help: try this: `&a` + | + = note: `-D clippy::deref-addrof` implied by `-D warnings` + +error: immediately dereferencing a reference + --> $DIR/deref_addrof_double_trigger.rs:14:17 + | +LL | let y = *&mut x; + | ^^^^^^^ help: try this: `x` + +error: immediately dereferencing a reference + --> $DIR/deref_addrof_double_trigger.rs:21:18 + | +LL | let y = **&mut &mut x; + | ^^^^^^^^^^^^ help: try this: `&mut x` + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/deref_addrof_macro.rs b/src/tools/clippy/tests/ui/deref_addrof_macro.rs new file mode 100644 index 00000000000..dcebd6c6e29 --- /dev/null +++ b/src/tools/clippy/tests/ui/deref_addrof_macro.rs @@ -0,0 +1,10 @@ +macro_rules! m { + ($($x:tt),*) => { &[$(($x, stringify!(x)),)*] }; +} + +#[warn(clippy::deref_addrof)] +fn f() -> [(i32, &'static str); 3] { + *m![1, 2, 3] // should be fine +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/dereference.fixed b/src/tools/clippy/tests/ui/dereference.fixed new file mode 100644 index 00000000000..459ca91b93b --- /dev/null +++ b/src/tools/clippy/tests/ui/dereference.fixed @@ -0,0 +1,93 @@ +// run-rustfix + +#![allow(unused_variables, clippy::many_single_char_names, clippy::clone_double_ref)] +#![warn(clippy::explicit_deref_methods)] + +use std::ops::{Deref, DerefMut}; + +fn concat(deref_str: &str) -> String { + format!("{}bar", deref_str) +} + +fn just_return(deref_str: &str) -> &str { + deref_str +} + +struct CustomVec(Vec<u8>); +impl Deref for CustomVec { + type Target = Vec<u8>; + + fn deref(&self) -> &Vec<u8> { + &self.0 + } +} + +fn main() { + let a: &mut String = &mut String::from("foo"); + + // these should require linting + + let b: &str = &*a; + + let b: &mut str = &mut *a; + + // both derefs should get linted here + let b: String = format!("{}, {}", &*a, &*a); + + println!("{}", &*a); + + #[allow(clippy::match_single_binding)] + match &*a { + _ => (), + } + + let b: String = concat(&*a); + + let b = &*just_return(a); + + let b: String = concat(&*just_return(a)); + + let b: &str = &*a.deref(); + + let opt_a = Some(a.clone()); + let b = &*opt_a.unwrap(); + + // following should not require linting + + let cv = CustomVec(vec![0, 42]); + let c = cv.deref()[0]; + + let b: &str = &*a.deref(); + + let b: String = a.deref().clone(); + + let b: usize = a.deref_mut().len(); + + let b: &usize = &a.deref().len(); + + let b: &str = &*a; + + let b: &mut str = &mut *a; + + macro_rules! expr_deref { + ($body:expr) => { + $body.deref() + }; + } + let b: &str = expr_deref!(a); + + // The struct does not implement Deref trait + #[derive(Copy, Clone)] + struct NoLint(u32); + impl NoLint { + pub fn deref(self) -> u32 { + self.0 + } + pub fn deref_mut(self) -> u32 { + self.0 + } + } + let no_lint = NoLint(42); + let b = no_lint.deref(); + let b = no_lint.deref_mut(); +} diff --git a/src/tools/clippy/tests/ui/dereference.rs b/src/tools/clippy/tests/ui/dereference.rs new file mode 100644 index 00000000000..8dc5272e67f --- /dev/null +++ b/src/tools/clippy/tests/ui/dereference.rs @@ -0,0 +1,93 @@ +// run-rustfix + +#![allow(unused_variables, clippy::many_single_char_names, clippy::clone_double_ref)] +#![warn(clippy::explicit_deref_methods)] + +use std::ops::{Deref, DerefMut}; + +fn concat(deref_str: &str) -> String { + format!("{}bar", deref_str) +} + +fn just_return(deref_str: &str) -> &str { + deref_str +} + +struct CustomVec(Vec<u8>); +impl Deref for CustomVec { + type Target = Vec<u8>; + + fn deref(&self) -> &Vec<u8> { + &self.0 + } +} + +fn main() { + let a: &mut String = &mut String::from("foo"); + + // these should require linting + + let b: &str = a.deref(); + + let b: &mut str = a.deref_mut(); + + // both derefs should get linted here + let b: String = format!("{}, {}", a.deref(), a.deref()); + + println!("{}", a.deref()); + + #[allow(clippy::match_single_binding)] + match a.deref() { + _ => (), + } + + let b: String = concat(a.deref()); + + let b = just_return(a).deref(); + + let b: String = concat(just_return(a).deref()); + + let b: &str = a.deref().deref(); + + let opt_a = Some(a.clone()); + let b = opt_a.unwrap().deref(); + + // following should not require linting + + let cv = CustomVec(vec![0, 42]); + let c = cv.deref()[0]; + + let b: &str = &*a.deref(); + + let b: String = a.deref().clone(); + + let b: usize = a.deref_mut().len(); + + let b: &usize = &a.deref().len(); + + let b: &str = &*a; + + let b: &mut str = &mut *a; + + macro_rules! expr_deref { + ($body:expr) => { + $body.deref() + }; + } + let b: &str = expr_deref!(a); + + // The struct does not implement Deref trait + #[derive(Copy, Clone)] + struct NoLint(u32); + impl NoLint { + pub fn deref(self) -> u32 { + self.0 + } + pub fn deref_mut(self) -> u32 { + self.0 + } + } + let no_lint = NoLint(42); + let b = no_lint.deref(); + let b = no_lint.deref_mut(); +} diff --git a/src/tools/clippy/tests/ui/dereference.stderr b/src/tools/clippy/tests/ui/dereference.stderr new file mode 100644 index 00000000000..d26b462a433 --- /dev/null +++ b/src/tools/clippy/tests/ui/dereference.stderr @@ -0,0 +1,70 @@ +error: explicit deref method call + --> $DIR/dereference.rs:30:19 + | +LL | let b: &str = a.deref(); + | ^^^^^^^^^ help: try this: `&*a` + | + = note: `-D clippy::explicit-deref-methods` implied by `-D warnings` + +error: explicit deref_mut method call + --> $DIR/dereference.rs:32:23 + | +LL | let b: &mut str = a.deref_mut(); + | ^^^^^^^^^^^^^ help: try this: `&mut *a` + +error: explicit deref method call + --> $DIR/dereference.rs:35:39 + | +LL | let b: String = format!("{}, {}", a.deref(), a.deref()); + | ^^^^^^^^^ help: try this: `&*a` + +error: explicit deref method call + --> $DIR/dereference.rs:35:50 + | +LL | let b: String = format!("{}, {}", a.deref(), a.deref()); + | ^^^^^^^^^ help: try this: `&*a` + +error: explicit deref method call + --> $DIR/dereference.rs:37:20 + | +LL | println!("{}", a.deref()); + | ^^^^^^^^^ help: try this: `&*a` + +error: explicit deref method call + --> $DIR/dereference.rs:40:11 + | +LL | match a.deref() { + | ^^^^^^^^^ help: try this: `&*a` + +error: explicit deref method call + --> $DIR/dereference.rs:44:28 + | +LL | let b: String = concat(a.deref()); + | ^^^^^^^^^ help: try this: `&*a` + +error: explicit deref method call + --> $DIR/dereference.rs:46:13 + | +LL | let b = just_return(a).deref(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&*just_return(a)` + +error: explicit deref method call + --> $DIR/dereference.rs:48:28 + | +LL | let b: String = concat(just_return(a).deref()); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&*just_return(a)` + +error: explicit deref method call + --> $DIR/dereference.rs:50:19 + | +LL | let b: &str = a.deref().deref(); + | ^^^^^^^^^^^^^^^^^ help: try this: `&*a.deref()` + +error: explicit deref method call + --> $DIR/dereference.rs:53:13 + | +LL | let b = opt_a.unwrap().deref(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&*opt_a.unwrap()` + +error: aborting due to 11 previous errors + diff --git a/src/tools/clippy/tests/ui/derive.rs b/src/tools/clippy/tests/ui/derive.rs new file mode 100644 index 00000000000..8fcb0e8b28d --- /dev/null +++ b/src/tools/clippy/tests/ui/derive.rs @@ -0,0 +1,74 @@ +#![feature(untagged_unions)] +#![allow(dead_code)] +#![warn(clippy::expl_impl_clone_on_copy)] + +#[derive(Copy)] +struct Qux; + +impl Clone for Qux { + fn clone(&self) -> Self { + Qux + } +} + +// looks like unions don't support deriving Clone for now +#[derive(Copy)] +union Union { + a: u8, +} + +impl Clone for Union { + fn clone(&self) -> Self { + Union { a: 42 } + } +} + +// See #666 +#[derive(Copy)] +struct Lt<'a> { + a: &'a u8, +} + +impl<'a> Clone for Lt<'a> { + fn clone(&self) -> Self { + unimplemented!() + } +} + +// Ok, `Clone` cannot be derived because of the big array +#[derive(Copy)] +struct BigArray { + a: [u8; 65], +} + +impl Clone for BigArray { + fn clone(&self) -> Self { + unimplemented!() + } +} + +// Ok, function pointers are not always Clone +#[derive(Copy)] +struct FnPtr { + a: fn() -> !, +} + +impl Clone for FnPtr { + fn clone(&self) -> Self { + unimplemented!() + } +} + +// Ok, generics +#[derive(Copy)] +struct Generic<T> { + a: T, +} + +impl<T> Clone for Generic<T> { + fn clone(&self) -> Self { + unimplemented!() + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/derive.stderr b/src/tools/clippy/tests/ui/derive.stderr new file mode 100644 index 00000000000..1328a9b3107 --- /dev/null +++ b/src/tools/clippy/tests/ui/derive.stderr @@ -0,0 +1,83 @@ +error: you are implementing `Clone` explicitly on a `Copy` type + --> $DIR/derive.rs:8:1 + | +LL | / impl Clone for Qux { +LL | | fn clone(&self) -> Self { +LL | | Qux +LL | | } +LL | | } + | |_^ + | + = note: `-D clippy::expl-impl-clone-on-copy` implied by `-D warnings` +note: consider deriving `Clone` or removing `Copy` + --> $DIR/derive.rs:8:1 + | +LL | / impl Clone for Qux { +LL | | fn clone(&self) -> Self { +LL | | Qux +LL | | } +LL | | } + | |_^ + +error: you are implementing `Clone` explicitly on a `Copy` type + --> $DIR/derive.rs:32:1 + | +LL | / impl<'a> Clone for Lt<'a> { +LL | | fn clone(&self) -> Self { +LL | | unimplemented!() +LL | | } +LL | | } + | |_^ + | +note: consider deriving `Clone` or removing `Copy` + --> $DIR/derive.rs:32:1 + | +LL | / impl<'a> Clone for Lt<'a> { +LL | | fn clone(&self) -> Self { +LL | | unimplemented!() +LL | | } +LL | | } + | |_^ + +error: you are implementing `Clone` explicitly on a `Copy` type + --> $DIR/derive.rs:44:1 + | +LL | / impl Clone for BigArray { +LL | | fn clone(&self) -> Self { +LL | | unimplemented!() +LL | | } +LL | | } + | |_^ + | +note: consider deriving `Clone` or removing `Copy` + --> $DIR/derive.rs:44:1 + | +LL | / impl Clone for BigArray { +LL | | fn clone(&self) -> Self { +LL | | unimplemented!() +LL | | } +LL | | } + | |_^ + +error: you are implementing `Clone` explicitly on a `Copy` type + --> $DIR/derive.rs:56:1 + | +LL | / impl Clone for FnPtr { +LL | | fn clone(&self) -> Self { +LL | | unimplemented!() +LL | | } +LL | | } + | |_^ + | +note: consider deriving `Clone` or removing `Copy` + --> $DIR/derive.rs:56:1 + | +LL | / impl Clone for FnPtr { +LL | | fn clone(&self) -> Self { +LL | | unimplemented!() +LL | | } +LL | | } + | |_^ + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/derive_hash_xor_eq.rs b/src/tools/clippy/tests/ui/derive_hash_xor_eq.rs new file mode 100644 index 00000000000..10abe22d53e --- /dev/null +++ b/src/tools/clippy/tests/ui/derive_hash_xor_eq.rs @@ -0,0 +1,54 @@ +#[derive(PartialEq, Hash)] +struct Foo; + +impl PartialEq<u64> for Foo { + fn eq(&self, _: &u64) -> bool { + true + } +} + +#[derive(Hash)] +struct Bar; + +impl PartialEq for Bar { + fn eq(&self, _: &Bar) -> bool { + true + } +} + +#[derive(Hash)] +struct Baz; + +impl PartialEq<Baz> for Baz { + fn eq(&self, _: &Baz) -> bool { + true + } +} + +#[derive(PartialEq)] +struct Bah; + +impl std::hash::Hash for Bah { + fn hash<H: std::hash::Hasher>(&self, _: &mut H) {} +} + +#[derive(PartialEq)] +struct Foo2; + +trait Hash {} + +// We don't want to lint on user-defined traits called `Hash` +impl Hash for Foo2 {} + +mod use_hash { + use std::hash::{Hash, Hasher}; + + #[derive(PartialEq)] + struct Foo3; + + impl Hash for Foo3 { + fn hash<H: std::hash::Hasher>(&self, _: &mut H) {} + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/derive_hash_xor_eq.stderr b/src/tools/clippy/tests/ui/derive_hash_xor_eq.stderr new file mode 100644 index 00000000000..2287a548fe4 --- /dev/null +++ b/src/tools/clippy/tests/ui/derive_hash_xor_eq.stderr @@ -0,0 +1,67 @@ +error: you are deriving `Hash` but have implemented `PartialEq` explicitly + --> $DIR/derive_hash_xor_eq.rs:10:10 + | +LL | #[derive(Hash)] + | ^^^^ + | + = note: `#[deny(clippy::derive_hash_xor_eq)]` on by default +note: `PartialEq` implemented here + --> $DIR/derive_hash_xor_eq.rs:13:1 + | +LL | / impl PartialEq for Bar { +LL | | fn eq(&self, _: &Bar) -> bool { +LL | | true +LL | | } +LL | | } + | |_^ + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: you are deriving `Hash` but have implemented `PartialEq` explicitly + --> $DIR/derive_hash_xor_eq.rs:19:10 + | +LL | #[derive(Hash)] + | ^^^^ + | +note: `PartialEq` implemented here + --> $DIR/derive_hash_xor_eq.rs:22:1 + | +LL | / impl PartialEq<Baz> for Baz { +LL | | fn eq(&self, _: &Baz) -> bool { +LL | | true +LL | | } +LL | | } + | |_^ + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: you are implementing `Hash` explicitly but have derived `PartialEq` + --> $DIR/derive_hash_xor_eq.rs:31:1 + | +LL | / impl std::hash::Hash for Bah { +LL | | fn hash<H: std::hash::Hasher>(&self, _: &mut H) {} +LL | | } + | |_^ + | +note: `PartialEq` implemented here + --> $DIR/derive_hash_xor_eq.rs:28:10 + | +LL | #[derive(PartialEq)] + | ^^^^^^^^^ + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: you are implementing `Hash` explicitly but have derived `PartialEq` + --> $DIR/derive_hash_xor_eq.rs:49:5 + | +LL | / impl Hash for Foo3 { +LL | | fn hash<H: std::hash::Hasher>(&self, _: &mut H) {} +LL | | } + | |_____^ + | +note: `PartialEq` implemented here + --> $DIR/derive_hash_xor_eq.rs:46:14 + | +LL | #[derive(PartialEq)] + | ^^^^^^^^^ + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/diverging_sub_expression.rs b/src/tools/clippy/tests/ui/diverging_sub_expression.rs new file mode 100644 index 00000000000..4df241c9fc3 --- /dev/null +++ b/src/tools/clippy/tests/ui/diverging_sub_expression.rs @@ -0,0 +1,42 @@ +#![warn(clippy::diverging_sub_expression)] +#![allow(clippy::match_same_arms, clippy::logic_bug)] + +#[allow(clippy::empty_loop)] +fn diverge() -> ! { + loop {} +} + +struct A; + +impl A { + fn foo(&self) -> ! { + diverge() + } +} + +#[allow(unused_variables, clippy::unnecessary_operation, clippy::short_circuit_statement)] +fn main() { + let b = true; + b || diverge(); + b || A.foo(); +} + +#[allow(dead_code, unused_variables)] +fn foobar() { + loop { + let x = match 5 { + 4 => return, + 5 => continue, + 6 => true || return, + 7 => true || continue, + 8 => break, + 9 => diverge(), + 3 => true || diverge(), + 10 => match 42 { + 99 => return, + _ => true || panic!("boo"), + }, + _ => true || break, + }; + } +} diff --git a/src/tools/clippy/tests/ui/diverging_sub_expression.stderr b/src/tools/clippy/tests/ui/diverging_sub_expression.stderr new file mode 100644 index 00000000000..170e7d92de4 --- /dev/null +++ b/src/tools/clippy/tests/ui/diverging_sub_expression.stderr @@ -0,0 +1,40 @@ +error: sub-expression diverges + --> $DIR/diverging_sub_expression.rs:20:10 + | +LL | b || diverge(); + | ^^^^^^^^^ + | + = note: `-D clippy::diverging-sub-expression` implied by `-D warnings` + +error: sub-expression diverges + --> $DIR/diverging_sub_expression.rs:21:10 + | +LL | b || A.foo(); + | ^^^^^^^ + +error: sub-expression diverges + --> $DIR/diverging_sub_expression.rs:30:26 + | +LL | 6 => true || return, + | ^^^^^^ + +error: sub-expression diverges + --> $DIR/diverging_sub_expression.rs:31:26 + | +LL | 7 => true || continue, + | ^^^^^^^^ + +error: sub-expression diverges + --> $DIR/diverging_sub_expression.rs:34:26 + | +LL | 3 => true || diverge(), + | ^^^^^^^^^ + +error: sub-expression diverges + --> $DIR/diverging_sub_expression.rs:39:26 + | +LL | _ => true || break, + | ^^^^^ + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/dlist.rs b/src/tools/clippy/tests/ui/dlist.rs new file mode 100644 index 00000000000..2940d2d2901 --- /dev/null +++ b/src/tools/clippy/tests/ui/dlist.rs @@ -0,0 +1,40 @@ +#![feature(associated_type_defaults)] +#![warn(clippy::linkedlist)] +#![allow(dead_code, clippy::needless_pass_by_value)] + +extern crate alloc; +use alloc::collections::linked_list::LinkedList; + +trait Foo { + type Baz = LinkedList<u8>; + fn foo(_: LinkedList<u8>); + const BAR: Option<LinkedList<u8>>; +} + +// Ok, we don’t want to warn for implementations; see issue #605. +impl Foo for LinkedList<u8> { + fn foo(_: LinkedList<u8>) {} + const BAR: Option<LinkedList<u8>> = None; +} + +struct Bar; +impl Bar { + fn foo(_: LinkedList<u8>) {} +} + +pub fn test(my_favourite_linked_list: LinkedList<u8>) { + println!("{:?}", my_favourite_linked_list) +} + +pub fn test_ret() -> Option<LinkedList<u8>> { + unimplemented!(); +} + +pub fn test_local_not_linted() { + let _: LinkedList<u8>; +} + +fn main() { + test(LinkedList::new()); + test_local_not_linted(); +} diff --git a/src/tools/clippy/tests/ui/dlist.stderr b/src/tools/clippy/tests/ui/dlist.stderr new file mode 100644 index 00000000000..64fde33c64f --- /dev/null +++ b/src/tools/clippy/tests/ui/dlist.stderr @@ -0,0 +1,51 @@ +error: I see you're using a LinkedList! Perhaps you meant some other data structure? + --> $DIR/dlist.rs:9:16 + | +LL | type Baz = LinkedList<u8>; + | ^^^^^^^^^^^^^^ + | + = note: `-D clippy::linkedlist` implied by `-D warnings` + = help: a `VecDeque` might work + +error: I see you're using a LinkedList! Perhaps you meant some other data structure? + --> $DIR/dlist.rs:10:15 + | +LL | fn foo(_: LinkedList<u8>); + | ^^^^^^^^^^^^^^ + | + = help: a `VecDeque` might work + +error: I see you're using a LinkedList! Perhaps you meant some other data structure? + --> $DIR/dlist.rs:11:23 + | +LL | const BAR: Option<LinkedList<u8>>; + | ^^^^^^^^^^^^^^ + | + = help: a `VecDeque` might work + +error: I see you're using a LinkedList! Perhaps you meant some other data structure? + --> $DIR/dlist.rs:22:15 + | +LL | fn foo(_: LinkedList<u8>) {} + | ^^^^^^^^^^^^^^ + | + = help: a `VecDeque` might work + +error: I see you're using a LinkedList! Perhaps you meant some other data structure? + --> $DIR/dlist.rs:25:39 + | +LL | pub fn test(my_favourite_linked_list: LinkedList<u8>) { + | ^^^^^^^^^^^^^^ + | + = help: a `VecDeque` might work + +error: I see you're using a LinkedList! Perhaps you meant some other data structure? + --> $DIR/dlist.rs:29:29 + | +LL | pub fn test_ret() -> Option<LinkedList<u8>> { + | ^^^^^^^^^^^^^^ + | + = help: a `VecDeque` might work + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/doc.rs b/src/tools/clippy/tests/ui/doc.rs new file mode 100644 index 00000000000..77620c857e6 --- /dev/null +++ b/src/tools/clippy/tests/ui/doc.rs @@ -0,0 +1,184 @@ +//! This file tests for the `DOC_MARKDOWN` lint. + +#![allow(dead_code)] +#![warn(clippy::doc_markdown)] +#![feature(custom_inner_attributes)] +#![rustfmt::skip] + +/// The foo_bar function does _nothing_. See also foo::bar. (note the dot there) +/// Markdown is _weird_. I mean _really weird_. This \_ is ok. So is `_`. But not Foo::some_fun +/// which should be reported only once despite being __doubly bad__. +/// Here be ::a::global:path. +/// That's not code ~NotInCodeBlock~. +/// be_sure_we_got_to_the_end_of_it +fn foo_bar() { +} + +/// That one tests multiline ticks. +/// ```rust +/// foo_bar FOO_BAR +/// _foo bar_ +/// ``` +/// +/// ~~~rust +/// foo_bar FOO_BAR +/// _foo bar_ +/// ~~~ +/// be_sure_we_got_to_the_end_of_it +fn multiline_codeblock() { +} + +/// This _is a test for +/// multiline +/// emphasis_. +/// be_sure_we_got_to_the_end_of_it +fn test_emphasis() { +} + +/// This tests units. See also #835. +/// kiB MiB GiB TiB PiB EiB +/// kib Mib Gib Tib Pib Eib +/// kB MB GB TB PB EB +/// kb Mb Gb Tb Pb Eb +/// 32kiB 32MiB 32GiB 32TiB 32PiB 32EiB +/// 32kib 32Mib 32Gib 32Tib 32Pib 32Eib +/// 32kB 32MB 32GB 32TB 32PB 32EB +/// 32kb 32Mb 32Gb 32Tb 32Pb 32Eb +/// NaN +/// be_sure_we_got_to_the_end_of_it +fn test_units() { +} + +/// This test has [a link_with_underscores][chunked-example] inside it. See #823. +/// See also [the issue tracker](https://github.com/rust-lang/rust-clippy/search?q=clippy::doc_markdown&type=Issues) +/// on GitHub (which is a camel-cased word, but is OK). And here is another [inline link][inline_link]. +/// It can also be [inline_link2]. +/// +/// [chunked-example]: https://en.wikipedia.org/wiki/Chunked_transfer_encoding#Example +/// [inline_link]: https://foobar +/// [inline_link2]: https://foobar +/// The `main` function is the entry point of the program. Here it only calls the `foo_bar` and +/// `multiline_ticks` functions. +/// +/// expression of the type `_ <bit_op> m <cmp_op> c` (where `<bit_op>` +/// is one of {`&`, '|'} and `<cmp_op>` is one of {`!=`, `>=`, `>` , +/// be_sure_we_got_to_the_end_of_it +fn main() { + foo_bar(); + multiline_codeblock(); + test_emphasis(); + test_units(); +} + +/// ## CamelCaseThing +/// Talks about `CamelCaseThing`. Titles should be ignored; see issue #897. +/// +/// # CamelCaseThing +/// +/// Not a title #897 CamelCaseThing +/// be_sure_we_got_to_the_end_of_it +fn issue897() { +} + +/// I am confused by brackets? (`x_y`) +/// I am confused by brackets? (foo `x_y`) +/// I am confused by brackets? (`x_y` foo) +/// be_sure_we_got_to_the_end_of_it +fn issue900() { +} + +/// Diesel queries also have a similar problem to [Iterator][iterator], where +/// /// More talking +/// returning them from a function requires exposing the implementation of that +/// function. The [`helper_types`][helper_types] module exists to help with this, +/// but you might want to hide the return type or have it conditionally change. +/// Boxing can achieve both. +/// +/// [iterator]: https://doc.rust-lang.org/stable/std/iter/trait.Iterator.html +/// [helper_types]: ../helper_types/index.html +/// be_sure_we_got_to_the_end_of_it +fn issue883() { +} + +/// `foo_bar +/// baz_quz` +/// [foo +/// bar](https://doc.rust-lang.org/stable/std/iter/trait.IteratorFooBar.html) +fn multiline() { +} + +/** E.g., serialization of an empty list: FooBar +``` +That's in a code block: `PackedNode` +``` + +And BarQuz too. +be_sure_we_got_to_the_end_of_it +*/ +fn issue1073() { +} + +/** E.g., serialization of an empty list: FooBar +``` +That's in a code block: PackedNode +``` + +And BarQuz too. +be_sure_we_got_to_the_end_of_it +*/ +fn issue1073_alt() { +} + +/// Tests more than three quotes: +/// ```` +/// DoNotWarn +/// ``` +/// StillDont +/// ```` +/// be_sure_we_got_to_the_end_of_it +fn four_quotes() { +} + +/// See [NIST SP 800-56A, revision 2]. +/// +/// [NIST SP 800-56A, revision 2]: +/// https://github.com/rust-lang/rust-clippy/issues/902#issuecomment-261919419 +fn issue_902_comment() {} + +#[cfg_attr(feature = "a", doc = " ```")] +#[cfg_attr(not(feature = "a"), doc = " ```ignore")] +/// fn main() { +/// let s = "localhost:10000".to_string(); +/// println!("{}", s); +/// } +/// ``` +fn issue_1469() {} + +/** + * This is a doc comment that should not be a list + *This would also be an error under a strict common mark interpretation + */ +fn issue_1920() {} + +/// Ok: <http://www.unicode.org/reports/tr9/#Reordering_Resolved_Levels> +/// +/// Not ok: http://www.unicode.org +/// Not ok: https://www.unicode.org +/// Not ok: http://www.unicode.org/ +/// Not ok: http://www.unicode.org/reports/tr9/#Reordering_Resolved_Levels +fn issue_1832() {} + +/// Ok: CamelCase (It should not be surrounded by backticks) +fn issue_2395() {} + +/// An iterator over mycrate::Collection's values. +/// It should not lint a `'static` lifetime in ticks. +fn issue_2210() {} + +/// This should not cause the lint to trigger: +/// #REQ-data-family.lint_partof_exists +fn issue_2343() {} + +/// This should not cause an ICE: +/// __|_ _|__||_| +fn pulldown_cmark_crash() {} diff --git a/src/tools/clippy/tests/ui/doc.stderr b/src/tools/clippy/tests/ui/doc.stderr new file mode 100644 index 00000000000..ae9bb394cb9 --- /dev/null +++ b/src/tools/clippy/tests/ui/doc.stderr @@ -0,0 +1,184 @@ +error: you should put `foo_bar` between ticks in the documentation + --> $DIR/doc.rs:8:9 + | +LL | /// The foo_bar function does _nothing_. See also foo::bar. (note the dot there) + | ^^^^^^^ + | + = note: `-D clippy::doc-markdown` implied by `-D warnings` + +error: you should put `foo::bar` between ticks in the documentation + --> $DIR/doc.rs:8:51 + | +LL | /// The foo_bar function does _nothing_. See also foo::bar. (note the dot there) + | ^^^^^^^^ + +error: you should put `Foo::some_fun` between ticks in the documentation + --> $DIR/doc.rs:9:83 + | +LL | /// Markdown is _weird_. I mean _really weird_. This /_ is ok. So is `_`. But not Foo::some_fun + | ^^^^^^^^^^^^^ + +error: you should put `a::global:path` between ticks in the documentation + --> $DIR/doc.rs:11:15 + | +LL | /// Here be ::a::global:path. + | ^^^^^^^^^^^^^^ + +error: you should put `NotInCodeBlock` between ticks in the documentation + --> $DIR/doc.rs:12:22 + | +LL | /// That's not code ~NotInCodeBlock~. + | ^^^^^^^^^^^^^^ + +error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation + --> $DIR/doc.rs:13:5 + | +LL | /// be_sure_we_got_to_the_end_of_it + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation + --> $DIR/doc.rs:27:5 + | +LL | /// be_sure_we_got_to_the_end_of_it + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation + --> $DIR/doc.rs:34:5 + | +LL | /// be_sure_we_got_to_the_end_of_it + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation + --> $DIR/doc.rs:48:5 + | +LL | /// be_sure_we_got_to_the_end_of_it + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: you should put `link_with_underscores` between ticks in the documentation + --> $DIR/doc.rs:52:22 + | +LL | /// This test has [a link_with_underscores][chunked-example] inside it. See #823. + | ^^^^^^^^^^^^^^^^^^^^^ + +error: you should put `inline_link2` between ticks in the documentation + --> $DIR/doc.rs:55:21 + | +LL | /// It can also be [inline_link2]. + | ^^^^^^^^^^^^ + +error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation + --> $DIR/doc.rs:65:5 + | +LL | /// be_sure_we_got_to_the_end_of_it + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: you should put `CamelCaseThing` between ticks in the documentation + --> $DIR/doc.rs:73:8 + | +LL | /// ## CamelCaseThing + | ^^^^^^^^^^^^^^ + +error: you should put `CamelCaseThing` between ticks in the documentation + --> $DIR/doc.rs:76:7 + | +LL | /// # CamelCaseThing + | ^^^^^^^^^^^^^^ + +error: you should put `CamelCaseThing` between ticks in the documentation + --> $DIR/doc.rs:78:22 + | +LL | /// Not a title #897 CamelCaseThing + | ^^^^^^^^^^^^^^ + +error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation + --> $DIR/doc.rs:79:5 + | +LL | /// be_sure_we_got_to_the_end_of_it + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation + --> $DIR/doc.rs:86:5 + | +LL | /// be_sure_we_got_to_the_end_of_it + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation + --> $DIR/doc.rs:99:5 + | +LL | /// be_sure_we_got_to_the_end_of_it + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: you should put `FooBar` between ticks in the documentation + --> $DIR/doc.rs:110:43 + | +LL | /** E.g., serialization of an empty list: FooBar + | ^^^^^^ + +error: you should put `BarQuz` between ticks in the documentation + --> $DIR/doc.rs:115:5 + | +LL | And BarQuz too. + | ^^^^^^ + +error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation + --> $DIR/doc.rs:116:1 + | +LL | be_sure_we_got_to_the_end_of_it + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: you should put `FooBar` between ticks in the documentation + --> $DIR/doc.rs:121:43 + | +LL | /** E.g., serialization of an empty list: FooBar + | ^^^^^^ + +error: you should put `BarQuz` between ticks in the documentation + --> $DIR/doc.rs:126:5 + | +LL | And BarQuz too. + | ^^^^^^ + +error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation + --> $DIR/doc.rs:127:1 + | +LL | be_sure_we_got_to_the_end_of_it + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation + --> $DIR/doc.rs:138:5 + | +LL | /// be_sure_we_got_to_the_end_of_it + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: you should put bare URLs between `<`/`>` or make a proper Markdown link + --> $DIR/doc.rs:165:13 + | +LL | /// Not ok: http://www.unicode.org + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: you should put bare URLs between `<`/`>` or make a proper Markdown link + --> $DIR/doc.rs:166:13 + | +LL | /// Not ok: https://www.unicode.org + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: you should put bare URLs between `<`/`>` or make a proper Markdown link + --> $DIR/doc.rs:167:13 + | +LL | /// Not ok: http://www.unicode.org/ + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: you should put bare URLs between `<`/`>` or make a proper Markdown link + --> $DIR/doc.rs:168:13 + | +LL | /// Not ok: http://www.unicode.org/reports/tr9/#Reordering_Resolved_Levels + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: you should put `mycrate::Collection` between ticks in the documentation + --> $DIR/doc.rs:174:22 + | +LL | /// An iterator over mycrate::Collection's values. + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 30 previous errors + diff --git a/src/tools/clippy/tests/ui/doc_errors.rs b/src/tools/clippy/tests/ui/doc_errors.rs new file mode 100644 index 00000000000..445fc8d31d7 --- /dev/null +++ b/src/tools/clippy/tests/ui/doc_errors.rs @@ -0,0 +1,103 @@ +// edition:2018 +#![warn(clippy::missing_errors_doc)] + +use std::io; + +pub fn pub_fn_missing_errors_header() -> Result<(), ()> { + unimplemented!(); +} + +pub async fn async_pub_fn_missing_errors_header() -> Result<(), ()> { + unimplemented!(); +} + +/// This is not sufficiently documented. +pub fn pub_fn_returning_io_result() -> io::Result<()> { + unimplemented!(); +} + +/// This is not sufficiently documented. +pub async fn async_pub_fn_returning_io_result() -> io::Result<()> { + unimplemented!(); +} + +/// # Errors +/// A description of the errors goes here. +pub fn pub_fn_with_errors_header() -> Result<(), ()> { + unimplemented!(); +} + +/// # Errors +/// A description of the errors goes here. +pub async fn async_pub_fn_with_errors_header() -> Result<(), ()> { + unimplemented!(); +} + +/// This function doesn't require the documentation because it is private +fn priv_fn_missing_errors_header() -> Result<(), ()> { + unimplemented!(); +} + +/// This function doesn't require the documentation because it is private +async fn async_priv_fn_missing_errors_header() -> Result<(), ()> { + unimplemented!(); +} + +pub struct Struct1; + +impl Struct1 { + /// This is not sufficiently documented. + pub fn pub_method_missing_errors_header() -> Result<(), ()> { + unimplemented!(); + } + + /// This is not sufficiently documented. + pub async fn async_pub_method_missing_errors_header() -> Result<(), ()> { + unimplemented!(); + } + + /// # Errors + /// A description of the errors goes here. + pub fn pub_method_with_errors_header() -> Result<(), ()> { + unimplemented!(); + } + + /// # Errors + /// A description of the errors goes here. + pub async fn async_pub_method_with_errors_header() -> Result<(), ()> { + unimplemented!(); + } + + /// This function doesn't require the documentation because it is private. + fn priv_method_missing_errors_header() -> Result<(), ()> { + unimplemented!(); + } + + /// This function doesn't require the documentation because it is private. + async fn async_priv_method_missing_errors_header() -> Result<(), ()> { + unimplemented!(); + } +} + +pub trait Trait1 { + /// This is not sufficiently documented. + fn trait_method_missing_errors_header() -> Result<(), ()>; + + /// # Errors + /// A description of the errors goes here. + fn trait_method_with_errors_header() -> Result<(), ()>; +} + +impl Trait1 for Struct1 { + fn trait_method_missing_errors_header() -> Result<(), ()> { + unimplemented!(); + } + + fn trait_method_with_errors_header() -> Result<(), ()> { + unimplemented!(); + } +} + +fn main() -> Result<(), ()> { + Ok(()) +} diff --git a/src/tools/clippy/tests/ui/doc_errors.stderr b/src/tools/clippy/tests/ui/doc_errors.stderr new file mode 100644 index 00000000000..f44d6693d30 --- /dev/null +++ b/src/tools/clippy/tests/ui/doc_errors.stderr @@ -0,0 +1,58 @@ +error: docs for function returning `Result` missing `# Errors` section + --> $DIR/doc_errors.rs:6:1 + | +LL | / pub fn pub_fn_missing_errors_header() -> Result<(), ()> { +LL | | unimplemented!(); +LL | | } + | |_^ + | + = note: `-D clippy::missing-errors-doc` implied by `-D warnings` + +error: docs for function returning `Result` missing `# Errors` section + --> $DIR/doc_errors.rs:10:1 + | +LL | / pub async fn async_pub_fn_missing_errors_header() -> Result<(), ()> { +LL | | unimplemented!(); +LL | | } + | |_^ + +error: docs for function returning `Result` missing `# Errors` section + --> $DIR/doc_errors.rs:15:1 + | +LL | / pub fn pub_fn_returning_io_result() -> io::Result<()> { +LL | | unimplemented!(); +LL | | } + | |_^ + +error: docs for function returning `Result` missing `# Errors` section + --> $DIR/doc_errors.rs:20:1 + | +LL | / pub async fn async_pub_fn_returning_io_result() -> io::Result<()> { +LL | | unimplemented!(); +LL | | } + | |_^ + +error: docs for function returning `Result` missing `# Errors` section + --> $DIR/doc_errors.rs:50:5 + | +LL | / pub fn pub_method_missing_errors_header() -> Result<(), ()> { +LL | | unimplemented!(); +LL | | } + | |_____^ + +error: docs for function returning `Result` missing `# Errors` section + --> $DIR/doc_errors.rs:55:5 + | +LL | / pub async fn async_pub_method_missing_errors_header() -> Result<(), ()> { +LL | | unimplemented!(); +LL | | } + | |_____^ + +error: docs for function returning `Result` missing `# Errors` section + --> $DIR/doc_errors.rs:84:5 + | +LL | fn trait_method_missing_errors_header() -> Result<(), ()>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/tests/ui/doc_unsafe.rs b/src/tools/clippy/tests/ui/doc_unsafe.rs new file mode 100644 index 00000000000..484aa72d59a --- /dev/null +++ b/src/tools/clippy/tests/ui/doc_unsafe.rs @@ -0,0 +1,100 @@ +// aux-build:doc_unsafe_macros.rs + +#[macro_use] +extern crate doc_unsafe_macros; + +/// This is not sufficiently documented +pub unsafe fn destroy_the_planet() { + unimplemented!(); +} + +/// This one is +/// +/// # Safety +/// +/// This function shouldn't be called unless the horsemen are ready +pub unsafe fn apocalypse(universe: &mut ()) { + unimplemented!(); +} + +/// This is a private function, so docs aren't necessary +unsafe fn you_dont_see_me() { + unimplemented!(); +} + +mod private_mod { + pub unsafe fn only_crate_wide_accessible() { + unimplemented!(); + } + + pub unsafe fn republished() { + unimplemented!(); + } +} + +pub use private_mod::republished; + +pub trait UnsafeTrait { + unsafe fn woefully_underdocumented(self); + + /// # Safety + unsafe fn at_least_somewhat_documented(self); +} + +pub struct Struct; + +impl UnsafeTrait for Struct { + unsafe fn woefully_underdocumented(self) { + // all is well + } + + unsafe fn at_least_somewhat_documented(self) { + // all is still well + } +} + +impl Struct { + pub unsafe fn more_undocumented_unsafe() -> Self { + unimplemented!(); + } + + /// # Safety + pub unsafe fn somewhat_documented(&self) { + unimplemented!(); + } + + unsafe fn private(&self) { + unimplemented!(); + } +} + +macro_rules! very_unsafe { + () => { + pub unsafe fn whee() { + unimplemented!() + } + + /// # Safety + /// + /// Please keep the seat belt fastened + pub unsafe fn drive() { + whee() + } + }; +} + +very_unsafe!(); + +// we don't lint code from external macros +undocd_unsafe!(); + +fn main() { + unsafe { + you_dont_see_me(); + destroy_the_planet(); + let mut universe = (); + apocalypse(&mut universe); + private_mod::only_crate_wide_accessible(); + drive(); + } +} diff --git a/src/tools/clippy/tests/ui/doc_unsafe.stderr b/src/tools/clippy/tests/ui/doc_unsafe.stderr new file mode 100644 index 00000000000..c784d41ba17 --- /dev/null +++ b/src/tools/clippy/tests/ui/doc_unsafe.stderr @@ -0,0 +1,47 @@ +error: unsafe function's docs miss `# Safety` section + --> $DIR/doc_unsafe.rs:7:1 + | +LL | / pub unsafe fn destroy_the_planet() { +LL | | unimplemented!(); +LL | | } + | |_^ + | + = note: `-D clippy::missing-safety-doc` implied by `-D warnings` + +error: unsafe function's docs miss `# Safety` section + --> $DIR/doc_unsafe.rs:30:5 + | +LL | / pub unsafe fn republished() { +LL | | unimplemented!(); +LL | | } + | |_____^ + +error: unsafe function's docs miss `# Safety` section + --> $DIR/doc_unsafe.rs:38:5 + | +LL | unsafe fn woefully_underdocumented(self); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: unsafe function's docs miss `# Safety` section + --> $DIR/doc_unsafe.rs:57:5 + | +LL | / pub unsafe fn more_undocumented_unsafe() -> Self { +LL | | unimplemented!(); +LL | | } + | |_____^ + +error: unsafe function's docs miss `# Safety` section + --> $DIR/doc_unsafe.rs:73:9 + | +LL | / pub unsafe fn whee() { +LL | | unimplemented!() +LL | | } + | |_________^ +... +LL | very_unsafe!(); + | --------------- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/double_comparison.fixed b/src/tools/clippy/tests/ui/double_comparison.fixed new file mode 100644 index 00000000000..bb6cdaa667d --- /dev/null +++ b/src/tools/clippy/tests/ui/double_comparison.fixed @@ -0,0 +1,30 @@ +// run-rustfix + +fn main() { + let x = 1; + let y = 2; + if x <= y { + // do something + } + if x <= y { + // do something + } + if x >= y { + // do something + } + if x >= y { + // do something + } + if x != y { + // do something + } + if x != y { + // do something + } + if x == y { + // do something + } + if x == y { + // do something + } +} diff --git a/src/tools/clippy/tests/ui/double_comparison.rs b/src/tools/clippy/tests/ui/double_comparison.rs new file mode 100644 index 00000000000..9a2a9068a28 --- /dev/null +++ b/src/tools/clippy/tests/ui/double_comparison.rs @@ -0,0 +1,30 @@ +// run-rustfix + +fn main() { + let x = 1; + let y = 2; + if x == y || x < y { + // do something + } + if x < y || x == y { + // do something + } + if x == y || x > y { + // do something + } + if x > y || x == y { + // do something + } + if x < y || x > y { + // do something + } + if x > y || x < y { + // do something + } + if x <= y && x >= y { + // do something + } + if x >= y && x <= y { + // do something + } +} diff --git a/src/tools/clippy/tests/ui/double_comparison.stderr b/src/tools/clippy/tests/ui/double_comparison.stderr new file mode 100644 index 00000000000..5dcda7b3af4 --- /dev/null +++ b/src/tools/clippy/tests/ui/double_comparison.stderr @@ -0,0 +1,52 @@ +error: This binary expression can be simplified + --> $DIR/double_comparison.rs:6:8 + | +LL | if x == y || x < y { + | ^^^^^^^^^^^^^^^ help: try: `x <= y` + | + = note: `-D clippy::double-comparisons` implied by `-D warnings` + +error: This binary expression can be simplified + --> $DIR/double_comparison.rs:9:8 + | +LL | if x < y || x == y { + | ^^^^^^^^^^^^^^^ help: try: `x <= y` + +error: This binary expression can be simplified + --> $DIR/double_comparison.rs:12:8 + | +LL | if x == y || x > y { + | ^^^^^^^^^^^^^^^ help: try: `x >= y` + +error: This binary expression can be simplified + --> $DIR/double_comparison.rs:15:8 + | +LL | if x > y || x == y { + | ^^^^^^^^^^^^^^^ help: try: `x >= y` + +error: This binary expression can be simplified + --> $DIR/double_comparison.rs:18:8 + | +LL | if x < y || x > y { + | ^^^^^^^^^^^^^^ help: try: `x != y` + +error: This binary expression can be simplified + --> $DIR/double_comparison.rs:21:8 + | +LL | if x > y || x < y { + | ^^^^^^^^^^^^^^ help: try: `x != y` + +error: This binary expression can be simplified + --> $DIR/double_comparison.rs:24:8 + | +LL | if x <= y && x >= y { + | ^^^^^^^^^^^^^^^^ help: try: `x == y` + +error: This binary expression can be simplified + --> $DIR/double_comparison.rs:27:8 + | +LL | if x >= y && x <= y { + | ^^^^^^^^^^^^^^^^ help: try: `x == y` + +error: aborting due to 8 previous errors + diff --git a/src/tools/clippy/tests/ui/double_must_use.rs b/src/tools/clippy/tests/ui/double_must_use.rs new file mode 100644 index 00000000000..a48e675e4ea --- /dev/null +++ b/src/tools/clippy/tests/ui/double_must_use.rs @@ -0,0 +1,27 @@ +#![warn(clippy::double_must_use)] + +#[must_use] +pub fn must_use_result() -> Result<(), ()> { + unimplemented!(); +} + +#[must_use] +pub fn must_use_tuple() -> (Result<(), ()>, u8) { + unimplemented!(); +} + +#[must_use] +pub fn must_use_array() -> [Result<(), ()>; 1] { + unimplemented!(); +} + +#[must_use = "With note"] +pub fn must_use_with_note() -> Result<(), ()> { + unimplemented!(); +} + +fn main() { + must_use_result(); + must_use_tuple(); + must_use_with_note(); +} diff --git a/src/tools/clippy/tests/ui/double_must_use.stderr b/src/tools/clippy/tests/ui/double_must_use.stderr new file mode 100644 index 00000000000..bc37785294f --- /dev/null +++ b/src/tools/clippy/tests/ui/double_must_use.stderr @@ -0,0 +1,27 @@ +error: this function has an empty `#[must_use]` attribute, but returns a type already marked as `#[must_use]` + --> $DIR/double_must_use.rs:4:1 + | +LL | pub fn must_use_result() -> Result<(), ()> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::double-must-use` implied by `-D warnings` + = help: either add some descriptive text or remove the attribute + +error: this function has an empty `#[must_use]` attribute, but returns a type already marked as `#[must_use]` + --> $DIR/double_must_use.rs:9:1 + | +LL | pub fn must_use_tuple() -> (Result<(), ()>, u8) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: either add some descriptive text or remove the attribute + +error: this function has an empty `#[must_use]` attribute, but returns a type already marked as `#[must_use]` + --> $DIR/double_must_use.rs:14:1 + | +LL | pub fn must_use_array() -> [Result<(), ()>; 1] { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: either add some descriptive text or remove the attribute + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/double_neg.rs b/src/tools/clippy/tests/ui/double_neg.rs new file mode 100644 index 00000000000..d47dfcb5ba1 --- /dev/null +++ b/src/tools/clippy/tests/ui/double_neg.rs @@ -0,0 +1,7 @@ +#[warn(clippy::double_neg)] +fn main() { + let x = 1; + -x; + -(-x); + --x; +} diff --git a/src/tools/clippy/tests/ui/double_neg.stderr b/src/tools/clippy/tests/ui/double_neg.stderr new file mode 100644 index 00000000000..d82ed05f054 --- /dev/null +++ b/src/tools/clippy/tests/ui/double_neg.stderr @@ -0,0 +1,10 @@ +error: `--x` could be misinterpreted as pre-decrement by C programmers, is usually a no-op + --> $DIR/double_neg.rs:6:5 + | +LL | --x; + | ^^^ + | + = note: `-D clippy::double-neg` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/double_parens.rs b/src/tools/clippy/tests/ui/double_parens.rs new file mode 100644 index 00000000000..9c7590c7dd6 --- /dev/null +++ b/src/tools/clippy/tests/ui/double_parens.rs @@ -0,0 +1,56 @@ +#![warn(clippy::double_parens)] +#![allow(dead_code)] +#![feature(custom_inner_attributes)] +#![rustfmt::skip] + +fn dummy_fn<T>(_: T) {} + +struct DummyStruct; + +impl DummyStruct { + fn dummy_method<T>(self, _: T) {} +} + +fn simple_double_parens() -> i32 { + ((0)) +} + +fn fn_double_parens() { + dummy_fn((0)); +} + +fn method_double_parens(x: DummyStruct) { + x.dummy_method((0)); +} + +fn tuple_double_parens() -> (i32, i32) { + ((1, 2)) +} + +fn unit_double_parens() { + (()) +} + +fn fn_tuple_ok() { + dummy_fn((1, 2)); +} + +fn method_tuple_ok(x: DummyStruct) { + x.dummy_method((1, 2)); +} + +fn fn_unit_ok() { + dummy_fn(()); +} + +fn method_unit_ok(x: DummyStruct) { + x.dummy_method(()); +} + +// Issue #3206 +fn inside_macro() { + assert_eq!((1, 2), (1, 2), "Error"); + assert_eq!(((1, 2)), (1, 2), "Error"); +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/double_parens.stderr b/src/tools/clippy/tests/ui/double_parens.stderr new file mode 100644 index 00000000000..0e4c9b5682d --- /dev/null +++ b/src/tools/clippy/tests/ui/double_parens.stderr @@ -0,0 +1,40 @@ +error: Consider removing unnecessary double parentheses + --> $DIR/double_parens.rs:15:5 + | +LL | ((0)) + | ^^^^^ + | + = note: `-D clippy::double-parens` implied by `-D warnings` + +error: Consider removing unnecessary double parentheses + --> $DIR/double_parens.rs:19:14 + | +LL | dummy_fn((0)); + | ^^^ + +error: Consider removing unnecessary double parentheses + --> $DIR/double_parens.rs:23:20 + | +LL | x.dummy_method((0)); + | ^^^ + +error: Consider removing unnecessary double parentheses + --> $DIR/double_parens.rs:27:5 + | +LL | ((1, 2)) + | ^^^^^^^^ + +error: Consider removing unnecessary double parentheses + --> $DIR/double_parens.rs:31:5 + | +LL | (()) + | ^^^^ + +error: Consider removing unnecessary double parentheses + --> $DIR/double_parens.rs:53:16 + | +LL | assert_eq!(((1, 2)), (1, 2), "Error"); + | ^^^^^^^^ + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/drop_bounds.rs b/src/tools/clippy/tests/ui/drop_bounds.rs new file mode 100644 index 00000000000..6d6a9dc0783 --- /dev/null +++ b/src/tools/clippy/tests/ui/drop_bounds.rs @@ -0,0 +1,8 @@ +#![allow(unused)] +fn foo<T: Drop>() {} +fn bar<T>() +where + T: Drop, +{ +} +fn main() {} diff --git a/src/tools/clippy/tests/ui/drop_bounds.stderr b/src/tools/clippy/tests/ui/drop_bounds.stderr new file mode 100644 index 00000000000..5d360ef30a1 --- /dev/null +++ b/src/tools/clippy/tests/ui/drop_bounds.stderr @@ -0,0 +1,16 @@ +error: Bounds of the form `T: Drop` are useless. Use `std::mem::needs_drop` to detect if a type has drop glue. + --> $DIR/drop_bounds.rs:2:11 + | +LL | fn foo<T: Drop>() {} + | ^^^^ + | + = note: `#[deny(clippy::drop_bounds)]` on by default + +error: Bounds of the form `T: Drop` are useless. Use `std::mem::needs_drop` to detect if a type has drop glue. + --> $DIR/drop_bounds.rs:5:8 + | +LL | T: Drop, + | ^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/drop_forget_copy.rs b/src/tools/clippy/tests/ui/drop_forget_copy.rs new file mode 100644 index 00000000000..9ddd6d64701 --- /dev/null +++ b/src/tools/clippy/tests/ui/drop_forget_copy.rs @@ -0,0 +1,66 @@ +#![warn(clippy::drop_copy, clippy::forget_copy)] +#![allow(clippy::toplevel_ref_arg, clippy::drop_ref, clippy::forget_ref, unused_mut)] + +use std::mem::{drop, forget}; +use std::vec::Vec; + +#[derive(Copy, Clone)] +struct SomeStruct {} + +struct AnotherStruct { + x: u8, + y: u8, + z: Vec<u8>, +} + +impl Clone for AnotherStruct { + fn clone(&self) -> AnotherStruct { + AnotherStruct { + x: self.x, + y: self.y, + z: self.z.clone(), + } + } +} + +fn main() { + let s1 = SomeStruct {}; + let s2 = s1; + let s3 = &s1; + let mut s4 = s1; + let ref s5 = s1; + + drop(s1); + drop(s2); + drop(s3); + drop(s4); + drop(s5); + + forget(s1); + forget(s2); + forget(s3); + forget(s4); + forget(s5); + + let a1 = AnotherStruct { + x: 255, + y: 0, + z: vec![1, 2, 3], + }; + let a2 = &a1; + let mut a3 = a1.clone(); + let ref a4 = a1; + let a5 = a1.clone(); + + drop(a2); + drop(a3); + drop(a4); + drop(a5); + + forget(a2); + let a3 = &a1; + forget(a3); + forget(a4); + let a5 = a1.clone(); + forget(a5); +} diff --git a/src/tools/clippy/tests/ui/drop_forget_copy.stderr b/src/tools/clippy/tests/ui/drop_forget_copy.stderr new file mode 100644 index 00000000000..82a4f047ba8 --- /dev/null +++ b/src/tools/clippy/tests/ui/drop_forget_copy.stderr @@ -0,0 +1,76 @@ +error: calls to `std::mem::drop` with a value that implements `Copy`. Dropping a copy leaves the original intact. + --> $DIR/drop_forget_copy.rs:33:5 + | +LL | drop(s1); + | ^^^^^^^^ + | + = note: `-D clippy::drop-copy` implied by `-D warnings` +note: argument has type SomeStruct + --> $DIR/drop_forget_copy.rs:33:10 + | +LL | drop(s1); + | ^^ + +error: calls to `std::mem::drop` with a value that implements `Copy`. Dropping a copy leaves the original intact. + --> $DIR/drop_forget_copy.rs:34:5 + | +LL | drop(s2); + | ^^^^^^^^ + | +note: argument has type SomeStruct + --> $DIR/drop_forget_copy.rs:34:10 + | +LL | drop(s2); + | ^^ + +error: calls to `std::mem::drop` with a value that implements `Copy`. Dropping a copy leaves the original intact. + --> $DIR/drop_forget_copy.rs:36:5 + | +LL | drop(s4); + | ^^^^^^^^ + | +note: argument has type SomeStruct + --> $DIR/drop_forget_copy.rs:36:10 + | +LL | drop(s4); + | ^^ + +error: calls to `std::mem::forget` with a value that implements `Copy`. Forgetting a copy leaves the original intact. + --> $DIR/drop_forget_copy.rs:39:5 + | +LL | forget(s1); + | ^^^^^^^^^^ + | + = note: `-D clippy::forget-copy` implied by `-D warnings` +note: argument has type SomeStruct + --> $DIR/drop_forget_copy.rs:39:12 + | +LL | forget(s1); + | ^^ + +error: calls to `std::mem::forget` with a value that implements `Copy`. Forgetting a copy leaves the original intact. + --> $DIR/drop_forget_copy.rs:40:5 + | +LL | forget(s2); + | ^^^^^^^^^^ + | +note: argument has type SomeStruct + --> $DIR/drop_forget_copy.rs:40:12 + | +LL | forget(s2); + | ^^ + +error: calls to `std::mem::forget` with a value that implements `Copy`. Forgetting a copy leaves the original intact. + --> $DIR/drop_forget_copy.rs:42:5 + | +LL | forget(s4); + | ^^^^^^^^^^ + | +note: argument has type SomeStruct + --> $DIR/drop_forget_copy.rs:42:12 + | +LL | forget(s4); + | ^^ + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/drop_ref.rs b/src/tools/clippy/tests/ui/drop_ref.rs new file mode 100644 index 00000000000..9181d789d4f --- /dev/null +++ b/src/tools/clippy/tests/ui/drop_ref.rs @@ -0,0 +1,72 @@ +#![warn(clippy::drop_ref)] +#![allow(clippy::toplevel_ref_arg)] + +use std::mem::drop; + +struct SomeStruct; + +fn main() { + drop(&SomeStruct); + + let mut owned1 = SomeStruct; + drop(&owned1); + drop(&&owned1); + drop(&mut owned1); + drop(owned1); //OK + + let reference1 = &SomeStruct; + drop(reference1); + + let reference2 = &mut SomeStruct; + drop(reference2); + + let ref reference3 = SomeStruct; + drop(reference3); +} + +#[allow(dead_code)] +fn test_generic_fn_drop<T>(val: T) { + drop(&val); + drop(val); //OK +} + +#[allow(dead_code)] +fn test_similarly_named_function() { + fn drop<T>(_val: T) {} + drop(&SomeStruct); //OK; call to unrelated function which happens to have the same name + std::mem::drop(&SomeStruct); +} + +#[derive(Copy, Clone)] +pub struct Error; +fn produce_half_owl_error() -> Result<(), Error> { + Ok(()) +} + +fn produce_half_owl_ok() -> Result<bool, ()> { + Ok(true) +} + +#[allow(dead_code)] +fn test_owl_result() -> Result<(), ()> { + produce_half_owl_error().map_err(|_| ())?; + produce_half_owl_ok().map(|_| ())?; + // the following should not be linted, + // we should not force users to use toilet closures + // to produce owl results when drop is more convenient + produce_half_owl_error().map_err(drop)?; + produce_half_owl_ok().map_err(drop)?; + Ok(()) +} + +#[allow(dead_code)] +fn test_owl_result_2() -> Result<u8, ()> { + produce_half_owl_error().map_err(|_| ())?; + produce_half_owl_ok().map(|_| ())?; + // the following should not be linted, + // we should not force users to use toilet closures + // to produce owl results when drop is more convenient + produce_half_owl_error().map_err(drop)?; + produce_half_owl_ok().map(drop)?; + Ok(1) +} diff --git a/src/tools/clippy/tests/ui/drop_ref.stderr b/src/tools/clippy/tests/ui/drop_ref.stderr new file mode 100644 index 00000000000..35ae88b78a4 --- /dev/null +++ b/src/tools/clippy/tests/ui/drop_ref.stderr @@ -0,0 +1,111 @@ +error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. + --> $DIR/drop_ref.rs:9:5 + | +LL | drop(&SomeStruct); + | ^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::drop-ref` implied by `-D warnings` +note: argument has type `&SomeStruct` + --> $DIR/drop_ref.rs:9:10 + | +LL | drop(&SomeStruct); + | ^^^^^^^^^^^ + +error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. + --> $DIR/drop_ref.rs:12:5 + | +LL | drop(&owned1); + | ^^^^^^^^^^^^^ + | +note: argument has type `&SomeStruct` + --> $DIR/drop_ref.rs:12:10 + | +LL | drop(&owned1); + | ^^^^^^^ + +error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. + --> $DIR/drop_ref.rs:13:5 + | +LL | drop(&&owned1); + | ^^^^^^^^^^^^^^ + | +note: argument has type `&&SomeStruct` + --> $DIR/drop_ref.rs:13:10 + | +LL | drop(&&owned1); + | ^^^^^^^^ + +error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. + --> $DIR/drop_ref.rs:14:5 + | +LL | drop(&mut owned1); + | ^^^^^^^^^^^^^^^^^ + | +note: argument has type `&mut SomeStruct` + --> $DIR/drop_ref.rs:14:10 + | +LL | drop(&mut owned1); + | ^^^^^^^^^^^ + +error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. + --> $DIR/drop_ref.rs:18:5 + | +LL | drop(reference1); + | ^^^^^^^^^^^^^^^^ + | +note: argument has type `&SomeStruct` + --> $DIR/drop_ref.rs:18:10 + | +LL | drop(reference1); + | ^^^^^^^^^^ + +error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. + --> $DIR/drop_ref.rs:21:5 + | +LL | drop(reference2); + | ^^^^^^^^^^^^^^^^ + | +note: argument has type `&mut SomeStruct` + --> $DIR/drop_ref.rs:21:10 + | +LL | drop(reference2); + | ^^^^^^^^^^ + +error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. + --> $DIR/drop_ref.rs:24:5 + | +LL | drop(reference3); + | ^^^^^^^^^^^^^^^^ + | +note: argument has type `&SomeStruct` + --> $DIR/drop_ref.rs:24:10 + | +LL | drop(reference3); + | ^^^^^^^^^^ + +error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. + --> $DIR/drop_ref.rs:29:5 + | +LL | drop(&val); + | ^^^^^^^^^^ + | +note: argument has type `&T` + --> $DIR/drop_ref.rs:29:10 + | +LL | drop(&val); + | ^^^^ + +error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. + --> $DIR/drop_ref.rs:37:5 + | +LL | std::mem::drop(&SomeStruct); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: argument has type `&SomeStruct` + --> $DIR/drop_ref.rs:37:20 + | +LL | std::mem::drop(&SomeStruct); + | ^^^^^^^^^^^ + +error: aborting due to 9 previous errors + diff --git a/src/tools/clippy/tests/ui/duplicate_underscore_argument.rs b/src/tools/clippy/tests/ui/duplicate_underscore_argument.rs new file mode 100644 index 00000000000..54d748c7ce2 --- /dev/null +++ b/src/tools/clippy/tests/ui/duplicate_underscore_argument.rs @@ -0,0 +1,10 @@ +#![warn(clippy::duplicate_underscore_argument)] +#[allow(dead_code, unused)] + +fn join_the_dark_side(darth: i32, _darth: i32) {} +fn join_the_light_side(knight: i32, _master: i32) {} // the Force is strong with this one + +fn main() { + join_the_dark_side(0, 0); + join_the_light_side(0, 0); +} diff --git a/src/tools/clippy/tests/ui/duplicate_underscore_argument.stderr b/src/tools/clippy/tests/ui/duplicate_underscore_argument.stderr new file mode 100644 index 00000000000..f71614a5fd1 --- /dev/null +++ b/src/tools/clippy/tests/ui/duplicate_underscore_argument.stderr @@ -0,0 +1,10 @@ +error: `darth` already exists, having another argument having almost the same name makes code comprehension and documentation more difficult + --> $DIR/duplicate_underscore_argument.rs:4:23 + | +LL | fn join_the_dark_side(darth: i32, _darth: i32) {} + | ^^^^^ + | + = note: `-D clippy::duplicate-underscore-argument` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/duration_subsec.fixed b/src/tools/clippy/tests/ui/duration_subsec.fixed new file mode 100644 index 00000000000..ee5c7863eff --- /dev/null +++ b/src/tools/clippy/tests/ui/duration_subsec.fixed @@ -0,0 +1,29 @@ +// run-rustfix +#![allow(dead_code)] +#![warn(clippy::duration_subsec)] + +use std::time::Duration; + +fn main() { + let dur = Duration::new(5, 0); + + let bad_millis_1 = dur.subsec_millis(); + let bad_millis_2 = dur.subsec_millis(); + let good_millis = dur.subsec_millis(); + assert_eq!(bad_millis_1, good_millis); + assert_eq!(bad_millis_2, good_millis); + + let bad_micros = dur.subsec_micros(); + let good_micros = dur.subsec_micros(); + assert_eq!(bad_micros, good_micros); + + // Handle refs + let _ = (&dur).subsec_micros(); + + // Handle constants + const NANOS_IN_MICRO: u32 = 1_000; + let _ = dur.subsec_micros(); + + // Other literals aren't linted + let _ = dur.subsec_nanos() / 699; +} diff --git a/src/tools/clippy/tests/ui/duration_subsec.rs b/src/tools/clippy/tests/ui/duration_subsec.rs new file mode 100644 index 00000000000..3c9d2a28621 --- /dev/null +++ b/src/tools/clippy/tests/ui/duration_subsec.rs @@ -0,0 +1,29 @@ +// run-rustfix +#![allow(dead_code)] +#![warn(clippy::duration_subsec)] + +use std::time::Duration; + +fn main() { + let dur = Duration::new(5, 0); + + let bad_millis_1 = dur.subsec_micros() / 1_000; + let bad_millis_2 = dur.subsec_nanos() / 1_000_000; + let good_millis = dur.subsec_millis(); + assert_eq!(bad_millis_1, good_millis); + assert_eq!(bad_millis_2, good_millis); + + let bad_micros = dur.subsec_nanos() / 1_000; + let good_micros = dur.subsec_micros(); + assert_eq!(bad_micros, good_micros); + + // Handle refs + let _ = (&dur).subsec_nanos() / 1_000; + + // Handle constants + const NANOS_IN_MICRO: u32 = 1_000; + let _ = dur.subsec_nanos() / NANOS_IN_MICRO; + + // Other literals aren't linted + let _ = dur.subsec_nanos() / 699; +} diff --git a/src/tools/clippy/tests/ui/duration_subsec.stderr b/src/tools/clippy/tests/ui/duration_subsec.stderr new file mode 100644 index 00000000000..bd8adc2c570 --- /dev/null +++ b/src/tools/clippy/tests/ui/duration_subsec.stderr @@ -0,0 +1,34 @@ +error: Calling `subsec_millis()` is more concise than this calculation + --> $DIR/duration_subsec.rs:10:24 + | +LL | let bad_millis_1 = dur.subsec_micros() / 1_000; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `dur.subsec_millis()` + | + = note: `-D clippy::duration-subsec` implied by `-D warnings` + +error: Calling `subsec_millis()` is more concise than this calculation + --> $DIR/duration_subsec.rs:11:24 + | +LL | let bad_millis_2 = dur.subsec_nanos() / 1_000_000; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `dur.subsec_millis()` + +error: Calling `subsec_micros()` is more concise than this calculation + --> $DIR/duration_subsec.rs:16:22 + | +LL | let bad_micros = dur.subsec_nanos() / 1_000; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `dur.subsec_micros()` + +error: Calling `subsec_micros()` is more concise than this calculation + --> $DIR/duration_subsec.rs:21:13 + | +LL | let _ = (&dur).subsec_nanos() / 1_000; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(&dur).subsec_micros()` + +error: Calling `subsec_micros()` is more concise than this calculation + --> $DIR/duration_subsec.rs:25:13 + | +LL | let _ = dur.subsec_nanos() / NANOS_IN_MICRO; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `dur.subsec_micros()` + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/else_if_without_else.rs b/src/tools/clippy/tests/ui/else_if_without_else.rs new file mode 100644 index 00000000000..879b3ac398e --- /dev/null +++ b/src/tools/clippy/tests/ui/else_if_without_else.rs @@ -0,0 +1,58 @@ +#![warn(clippy::all)] +#![warn(clippy::else_if_without_else)] + +fn bla1() -> bool { + unimplemented!() +} +fn bla2() -> bool { + unimplemented!() +} +fn bla3() -> bool { + unimplemented!() +} + +fn main() { + if bla1() { + println!("if"); + } + + if bla1() { + println!("if"); + } else { + println!("else"); + } + + if bla1() { + println!("if"); + } else if bla2() { + println!("else if"); + } else { + println!("else") + } + + if bla1() { + println!("if"); + } else if bla2() { + println!("else if 1"); + } else if bla3() { + println!("else if 2"); + } else { + println!("else") + } + + if bla1() { + println!("if"); + } else if bla2() { + //~ ERROR else if without else + println!("else if"); + } + + if bla1() { + println!("if"); + } else if bla2() { + println!("else if 1"); + } else if bla3() { + //~ ERROR else if without else + println!("else if 2"); + } +} diff --git a/src/tools/clippy/tests/ui/else_if_without_else.stderr b/src/tools/clippy/tests/ui/else_if_without_else.stderr new file mode 100644 index 00000000000..6f47658cfb1 --- /dev/null +++ b/src/tools/clippy/tests/ui/else_if_without_else.stderr @@ -0,0 +1,27 @@ +error: `if` expression with an `else if`, but without a final `else` + --> $DIR/else_if_without_else.rs:45:12 + | +LL | } else if bla2() { + | ____________^ +LL | | //~ ERROR else if without else +LL | | println!("else if"); +LL | | } + | |_____^ + | + = note: `-D clippy::else-if-without-else` implied by `-D warnings` + = help: add an `else` block here + +error: `if` expression with an `else if`, but without a final `else` + --> $DIR/else_if_without_else.rs:54:12 + | +LL | } else if bla3() { + | ____________^ +LL | | //~ ERROR else if without else +LL | | println!("else if 2"); +LL | | } + | |_____^ + | + = help: add an `else` block here + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/empty_enum.rs b/src/tools/clippy/tests/ui/empty_enum.rs new file mode 100644 index 00000000000..12428f29625 --- /dev/null +++ b/src/tools/clippy/tests/ui/empty_enum.rs @@ -0,0 +1,6 @@ +#![allow(dead_code)] +#![warn(clippy::empty_enum)] + +enum Empty {} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/empty_enum.stderr b/src/tools/clippy/tests/ui/empty_enum.stderr new file mode 100644 index 00000000000..466dfbe7cee --- /dev/null +++ b/src/tools/clippy/tests/ui/empty_enum.stderr @@ -0,0 +1,11 @@ +error: enum with no variants + --> $DIR/empty_enum.rs:4:1 + | +LL | enum Empty {} + | ^^^^^^^^^^^^^ + | + = note: `-D clippy::empty-enum` implied by `-D warnings` + = help: consider using the uninhabited type `!` (never type) or a wrapper around it to introduce a type which can't be instantiated + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/empty_line_after_outer_attribute.rs b/src/tools/clippy/tests/ui/empty_line_after_outer_attribute.rs new file mode 100644 index 00000000000..5343dff9da1 --- /dev/null +++ b/src/tools/clippy/tests/ui/empty_line_after_outer_attribute.rs @@ -0,0 +1,96 @@ +#![warn(clippy::empty_line_after_outer_attr)] +#![allow(clippy::assertions_on_constants)] +#![feature(custom_inner_attributes)] +#![rustfmt::skip] + +// This should produce a warning +#[crate_type = "lib"] + +/// some comment +fn with_one_newline_and_comment() { assert!(true) } + +// This should not produce a warning +#[crate_type = "lib"] +/// some comment +fn with_no_newline_and_comment() { assert!(true) } + + +// This should produce a warning +#[crate_type = "lib"] + +fn with_one_newline() { assert!(true) } + +// This should produce a warning, too +#[crate_type = "lib"] + + +fn with_two_newlines() { assert!(true) } + + +// This should produce a warning +#[crate_type = "lib"] + +enum Baz { + One, + Two +} + +// This should produce a warning +#[crate_type = "lib"] + +struct Foo { + one: isize, + two: isize +} + +// This should produce a warning +#[crate_type = "lib"] + +mod foo { +} + +/// This doc comment should not produce a warning + +/** This is also a doc comment and should not produce a warning + */ + +// This should not produce a warning +#[allow(non_camel_case_types)] +#[allow(missing_docs)] +#[allow(missing_docs)] +fn three_attributes() { assert!(true) } + +// This should not produce a warning +#[doc = " +Returns the escaped value of the textual representation of + +"] +pub fn function() -> bool { + true +} + +// This should not produce a warning +#[derive(Clone, Copy)] +pub enum FooFighter { + Bar1, + + Bar2, + + Bar3, + + Bar4 +} + +// This should not produce a warning because the empty line is inside a block comment +#[crate_type = "lib"] +/* + +*/ +pub struct S; + +// This should not produce a warning +#[crate_type = "lib"] +/* test */ +pub struct T; + +fn main() { } diff --git a/src/tools/clippy/tests/ui/empty_line_after_outer_attribute.stderr b/src/tools/clippy/tests/ui/empty_line_after_outer_attribute.stderr new file mode 100644 index 00000000000..d8c9786541f --- /dev/null +++ b/src/tools/clippy/tests/ui/empty_line_after_outer_attribute.stderr @@ -0,0 +1,54 @@ +error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? + --> $DIR/empty_line_after_outer_attribute.rs:7:1 + | +LL | / #[crate_type = "lib"] +LL | | +LL | | /// some comment +LL | | fn with_one_newline_and_comment() { assert!(true) } + | |_ + | + = note: `-D clippy::empty-line-after-outer-attr` implied by `-D warnings` + +error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? + --> $DIR/empty_line_after_outer_attribute.rs:19:1 + | +LL | / #[crate_type = "lib"] +LL | | +LL | | fn with_one_newline() { assert!(true) } + | |_ + +error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? + --> $DIR/empty_line_after_outer_attribute.rs:24:1 + | +LL | / #[crate_type = "lib"] +LL | | +LL | | +LL | | fn with_two_newlines() { assert!(true) } + | |_ + +error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? + --> $DIR/empty_line_after_outer_attribute.rs:31:1 + | +LL | / #[crate_type = "lib"] +LL | | +LL | | enum Baz { + | |_ + +error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? + --> $DIR/empty_line_after_outer_attribute.rs:39:1 + | +LL | / #[crate_type = "lib"] +LL | | +LL | | struct Foo { + | |_ + +error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? + --> $DIR/empty_line_after_outer_attribute.rs:47:1 + | +LL | / #[crate_type = "lib"] +LL | | +LL | | mod foo { + | |_ + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/empty_loop.rs b/src/tools/clippy/tests/ui/empty_loop.rs new file mode 100644 index 00000000000..8fd7697eb3b --- /dev/null +++ b/src/tools/clippy/tests/ui/empty_loop.rs @@ -0,0 +1,51 @@ +// aux-build:macro_rules.rs + +#![warn(clippy::empty_loop)] + +#[macro_use] +extern crate macro_rules; + +fn should_trigger() { + loop {} + loop { + loop {} + } + + 'outer: loop { + 'inner: loop {} + } +} + +fn should_not_trigger() { + loop { + panic!("This is fine") + } + let ten_millis = std::time::Duration::from_millis(10); + loop { + std::thread::sleep(ten_millis) + } + + #[allow(clippy::never_loop)] + 'outer: loop { + 'inner: loop { + break 'inner; + } + break 'outer; + } + + // Make sure `allow` works for this lint + #[allow(clippy::empty_loop)] + loop {} + + // We don't lint loops inside macros + macro_rules! foo { + () => { + loop {} + }; + } + + // We don't lint external macros + foofoo!() +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/empty_loop.stderr b/src/tools/clippy/tests/ui/empty_loop.stderr new file mode 100644 index 00000000000..e44c58ea770 --- /dev/null +++ b/src/tools/clippy/tests/ui/empty_loop.stderr @@ -0,0 +1,22 @@ +error: empty `loop {}` detected. You may want to either use `panic!()` or add `std::thread::sleep(..);` to the loop body. + --> $DIR/empty_loop.rs:9:5 + | +LL | loop {} + | ^^^^^^^ + | + = note: `-D clippy::empty-loop` implied by `-D warnings` + +error: empty `loop {}` detected. You may want to either use `panic!()` or add `std::thread::sleep(..);` to the loop body. + --> $DIR/empty_loop.rs:11:9 + | +LL | loop {} + | ^^^^^^^ + +error: empty `loop {}` detected. You may want to either use `panic!()` or add `std::thread::sleep(..);` to the loop body. + --> $DIR/empty_loop.rs:15:9 + | +LL | 'inner: loop {} + | ^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/entry_fixable.fixed b/src/tools/clippy/tests/ui/entry_fixable.fixed new file mode 100644 index 00000000000..dcdaae7e724 --- /dev/null +++ b/src/tools/clippy/tests/ui/entry_fixable.fixed @@ -0,0 +1,15 @@ +// run-rustfix + +#![allow(unused, clippy::needless_pass_by_value)] +#![warn(clippy::map_entry)] + +use std::collections::{BTreeMap, HashMap}; +use std::hash::Hash; + +fn foo() {} + +fn insert_if_absent0<K: Eq + Hash, V>(m: &mut HashMap<K, V>, k: K, v: V) { + m.entry(k).or_insert(v); +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/entry_fixable.rs b/src/tools/clippy/tests/ui/entry_fixable.rs new file mode 100644 index 00000000000..55d5b21568d --- /dev/null +++ b/src/tools/clippy/tests/ui/entry_fixable.rs @@ -0,0 +1,17 @@ +// run-rustfix + +#![allow(unused, clippy::needless_pass_by_value)] +#![warn(clippy::map_entry)] + +use std::collections::{BTreeMap, HashMap}; +use std::hash::Hash; + +fn foo() {} + +fn insert_if_absent0<K: Eq + Hash, V>(m: &mut HashMap<K, V>, k: K, v: V) { + if !m.contains_key(&k) { + m.insert(k, v); + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/entry_fixable.stderr b/src/tools/clippy/tests/ui/entry_fixable.stderr new file mode 100644 index 00000000000..87403200ced --- /dev/null +++ b/src/tools/clippy/tests/ui/entry_fixable.stderr @@ -0,0 +1,12 @@ +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> $DIR/entry_fixable.rs:12:5 + | +LL | / if !m.contains_key(&k) { +LL | | m.insert(k, v); +LL | | } + | |_____^ help: consider using: `m.entry(k).or_insert(v);` + | + = note: `-D clippy::map-entry` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/entry_unfixable.rs b/src/tools/clippy/tests/ui/entry_unfixable.rs new file mode 100644 index 00000000000..f530fc023cf --- /dev/null +++ b/src/tools/clippy/tests/ui/entry_unfixable.rs @@ -0,0 +1,73 @@ +#![allow(unused, clippy::needless_pass_by_value)] +#![warn(clippy::map_entry)] + +use std::collections::{BTreeMap, HashMap}; +use std::hash::Hash; + +fn foo() {} + +fn insert_if_absent2<K: Eq + Hash, V>(m: &mut HashMap<K, V>, k: K, v: V) { + if !m.contains_key(&k) { + m.insert(k, v) + } else { + None + }; +} + +fn insert_if_present2<K: Eq + Hash, V>(m: &mut HashMap<K, V>, k: K, v: V) { + if m.contains_key(&k) { + None + } else { + m.insert(k, v) + }; +} + +fn insert_if_absent3<K: Eq + Hash, V>(m: &mut HashMap<K, V>, k: K, v: V) { + if !m.contains_key(&k) { + foo(); + m.insert(k, v) + } else { + None + }; +} + +fn insert_if_present3<K: Eq + Hash, V>(m: &mut HashMap<K, V>, k: K, v: V) { + if m.contains_key(&k) { + None + } else { + foo(); + m.insert(k, v) + }; +} + +fn insert_in_btreemap<K: Ord, V>(m: &mut BTreeMap<K, V>, k: K, v: V) { + if !m.contains_key(&k) { + foo(); + m.insert(k, v) + } else { + None + }; +} + +// should not trigger +fn insert_other_if_absent<K: Eq + Hash, V>(m: &mut HashMap<K, V>, k: K, o: K, v: V) { + if !m.contains_key(&k) { + m.insert(o, v); + } +} + +// should not trigger, because the one uses different HashMap from another one +fn insert_from_different_map<K: Eq + Hash, V>(m: HashMap<K, V>, n: &mut HashMap<K, V>, k: K, v: V) { + if !m.contains_key(&k) { + n.insert(k, v); + } +} + +// should not trigger, because the one uses different HashMap from another one +fn insert_from_different_map2<K: Eq + Hash, V>(m: &mut HashMap<K, V>, n: &mut HashMap<K, V>, k: K, v: V) { + if !m.contains_key(&k) { + n.insert(k, v); + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/entry_unfixable.stderr b/src/tools/clippy/tests/ui/entry_unfixable.stderr new file mode 100644 index 00000000000..e58c8d22dc4 --- /dev/null +++ b/src/tools/clippy/tests/ui/entry_unfixable.stderr @@ -0,0 +1,57 @@ +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> $DIR/entry_unfixable.rs:10:5 + | +LL | / if !m.contains_key(&k) { +LL | | m.insert(k, v) +LL | | } else { +LL | | None +LL | | }; + | |_____^ consider using `m.entry(k)` + | + = note: `-D clippy::map-entry` implied by `-D warnings` + +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> $DIR/entry_unfixable.rs:18:5 + | +LL | / if m.contains_key(&k) { +LL | | None +LL | | } else { +LL | | m.insert(k, v) +LL | | }; + | |_____^ consider using `m.entry(k)` + +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> $DIR/entry_unfixable.rs:26:5 + | +LL | / if !m.contains_key(&k) { +LL | | foo(); +LL | | m.insert(k, v) +LL | | } else { +LL | | None +LL | | }; + | |_____^ consider using `m.entry(k)` + +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> $DIR/entry_unfixable.rs:35:5 + | +LL | / if m.contains_key(&k) { +LL | | None +LL | | } else { +LL | | foo(); +LL | | m.insert(k, v) +LL | | }; + | |_____^ consider using `m.entry(k)` + +error: usage of `contains_key` followed by `insert` on a `BTreeMap` + --> $DIR/entry_unfixable.rs:44:5 + | +LL | / if !m.contains_key(&k) { +LL | | foo(); +LL | | m.insert(k, v) +LL | | } else { +LL | | None +LL | | }; + | |_____^ consider using `m.entry(k)` + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/enum_clike_unportable_variant.rs b/src/tools/clippy/tests/ui/enum_clike_unportable_variant.rs new file mode 100644 index 00000000000..7d6842f5b54 --- /dev/null +++ b/src/tools/clippy/tests/ui/enum_clike_unportable_variant.rs @@ -0,0 +1,50 @@ +// ignore-x86 + +#![warn(clippy::enum_clike_unportable_variant)] +#![allow(unused, non_upper_case_globals)] + +#[repr(usize)] +enum NonPortable { + X = 0x1_0000_0000, + Y = 0, + Z = 0x7FFF_FFFF, + A = 0xFFFF_FFFF, +} + +enum NonPortableNoHint { + X = 0x1_0000_0000, + Y = 0, + Z = 0x7FFF_FFFF, + A = 0xFFFF_FFFF, +} + +#[repr(isize)] +enum NonPortableSigned { + X = -1, + Y = 0x7FFF_FFFF, + Z = 0xFFFF_FFFF, + A = 0x1_0000_0000, + B = i32::MIN as isize, + C = (i32::MIN as isize) - 1, +} + +enum NonPortableSignedNoHint { + X = -1, + Y = 0x7FFF_FFFF, + Z = 0xFFFF_FFFF, + A = 0x1_0000_0000, +} + +#[repr(usize)] +enum NonPortable2 { + X = <usize as Trait>::Number, + Y = 0, +} + +trait Trait { + const Number: usize = 0x1_0000_0000; +} + +impl Trait for usize {} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/enum_clike_unportable_variant.stderr b/src/tools/clippy/tests/ui/enum_clike_unportable_variant.stderr new file mode 100644 index 00000000000..71f3f5e083e --- /dev/null +++ b/src/tools/clippy/tests/ui/enum_clike_unportable_variant.stderr @@ -0,0 +1,58 @@ +error: Clike enum variant discriminant is not portable to 32-bit targets + --> $DIR/enum_clike_unportable_variant.rs:8:5 + | +LL | X = 0x1_0000_0000, + | ^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::enum-clike-unportable-variant` implied by `-D warnings` + +error: Clike enum variant discriminant is not portable to 32-bit targets + --> $DIR/enum_clike_unportable_variant.rs:15:5 + | +LL | X = 0x1_0000_0000, + | ^^^^^^^^^^^^^^^^^ + +error: Clike enum variant discriminant is not portable to 32-bit targets + --> $DIR/enum_clike_unportable_variant.rs:18:5 + | +LL | A = 0xFFFF_FFFF, + | ^^^^^^^^^^^^^^^ + +error: Clike enum variant discriminant is not portable to 32-bit targets + --> $DIR/enum_clike_unportable_variant.rs:25:5 + | +LL | Z = 0xFFFF_FFFF, + | ^^^^^^^^^^^^^^^ + +error: Clike enum variant discriminant is not portable to 32-bit targets + --> $DIR/enum_clike_unportable_variant.rs:26:5 + | +LL | A = 0x1_0000_0000, + | ^^^^^^^^^^^^^^^^^ + +error: Clike enum variant discriminant is not portable to 32-bit targets + --> $DIR/enum_clike_unportable_variant.rs:28:5 + | +LL | C = (i32::MIN as isize) - 1, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: Clike enum variant discriminant is not portable to 32-bit targets + --> $DIR/enum_clike_unportable_variant.rs:34:5 + | +LL | Z = 0xFFFF_FFFF, + | ^^^^^^^^^^^^^^^ + +error: Clike enum variant discriminant is not portable to 32-bit targets + --> $DIR/enum_clike_unportable_variant.rs:35:5 + | +LL | A = 0x1_0000_0000, + | ^^^^^^^^^^^^^^^^^ + +error: Clike enum variant discriminant is not portable to 32-bit targets + --> $DIR/enum_clike_unportable_variant.rs:40:5 + | +LL | X = <usize as Trait>::Number, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 9 previous errors + diff --git a/src/tools/clippy/tests/ui/enum_glob_use.fixed b/src/tools/clippy/tests/ui/enum_glob_use.fixed new file mode 100644 index 00000000000..a98216758bb --- /dev/null +++ b/src/tools/clippy/tests/ui/enum_glob_use.fixed @@ -0,0 +1,30 @@ +// run-rustfix + +#![warn(clippy::enum_glob_use)] +#![allow(unused)] +#![warn(unused_imports)] + +use std::cmp::Ordering::Less; + +enum Enum { + Foo, +} + +use self::Enum::Foo; + +mod in_fn_test { + fn blarg() { + use crate::Enum::Foo; + + let _ = Foo; + } +} + +mod blurg { + pub use std::cmp::Ordering::*; // ok, re-export +} + +fn main() { + let _ = Foo; + let _ = Less; +} diff --git a/src/tools/clippy/tests/ui/enum_glob_use.rs b/src/tools/clippy/tests/ui/enum_glob_use.rs new file mode 100644 index 00000000000..5d929c9731d --- /dev/null +++ b/src/tools/clippy/tests/ui/enum_glob_use.rs @@ -0,0 +1,30 @@ +// run-rustfix + +#![warn(clippy::enum_glob_use)] +#![allow(unused)] +#![warn(unused_imports)] + +use std::cmp::Ordering::*; + +enum Enum { + Foo, +} + +use self::Enum::*; + +mod in_fn_test { + fn blarg() { + use crate::Enum::*; + + let _ = Foo; + } +} + +mod blurg { + pub use std::cmp::Ordering::*; // ok, re-export +} + +fn main() { + let _ = Foo; + let _ = Less; +} diff --git a/src/tools/clippy/tests/ui/enum_glob_use.stderr b/src/tools/clippy/tests/ui/enum_glob_use.stderr new file mode 100644 index 00000000000..69531aed39b --- /dev/null +++ b/src/tools/clippy/tests/ui/enum_glob_use.stderr @@ -0,0 +1,22 @@ +error: usage of wildcard import for enum variants + --> $DIR/enum_glob_use.rs:7:5 + | +LL | use std::cmp::Ordering::*; + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `std::cmp::Ordering::Less` + | + = note: `-D clippy::enum-glob-use` implied by `-D warnings` + +error: usage of wildcard import for enum variants + --> $DIR/enum_glob_use.rs:13:5 + | +LL | use self::Enum::*; + | ^^^^^^^^^^^^^ help: try: `self::Enum::Foo` + +error: usage of wildcard import for enum variants + --> $DIR/enum_glob_use.rs:17:13 + | +LL | use crate::Enum::*; + | ^^^^^^^^^^^^^^ help: try: `crate::Enum::Foo` + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/enum_variants.rs b/src/tools/clippy/tests/ui/enum_variants.rs new file mode 100644 index 00000000000..01774a2a984 --- /dev/null +++ b/src/tools/clippy/tests/ui/enum_variants.rs @@ -0,0 +1,136 @@ +#![feature(non_ascii_idents)] +#![warn(clippy::enum_variant_names, clippy::pub_enum_variant_names)] +#![allow(non_camel_case_types)] + +enum FakeCallType { + CALL, + CREATE, +} + +enum FakeCallType2 { + CALL, + CREATELL, +} + +enum Foo { + cFoo, + cBar, + cBaz, +} + +enum Fooo { + cFoo, // no error, threshold is 3 variants by default + cBar, +} + +enum Food { + FoodGood, + FoodMiddle, + FoodBad, +} + +enum Stuff { + StuffBad, // no error +} + +enum BadCallType { + CallTypeCall, + CallTypeCreate, + CallTypeDestroy, +} + +enum TwoCallType { + // no error + CallTypeCall, + CallTypeCreate, +} + +enum Consts { + ConstantInt, + ConstantCake, + ConstantLie, +} + +enum Two { + // no error here + ConstantInt, + ConstantInfer, +} + +enum Something { + CCall, + CCreate, + CCryogenize, +} + +enum Seal { + With, + Without, +} + +enum Seall { + With, + WithOut, + Withbroken, +} + +enum Sealll { + With, + WithOut, +} + +enum Seallll { + WithOutCake, + WithOutTea, + WithOut, +} + +enum NonCaps { + Prefix的, + PrefixTea, + PrefixCake, +} + +pub enum PubSeall { + WithOutCake, + WithOutTea, + WithOut, +} + +#[allow(clippy::pub_enum_variant_names)] +mod allowed { + pub enum PubAllowed { + SomeThis, + SomeThat, + SomeOtherWhat, + } +} + +// should not lint +enum Pat { + Foo, + Bar, + Path, +} + +// should not lint +enum N { + Pos, + Neg, + Float, +} + +// should not lint +enum Peek { + Peek1, + Peek2, + Peek3, +} + +// should not lint +pub enum NetworkLayer { + Layer2, + Layer3, +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/enum_variants.stderr b/src/tools/clippy/tests/ui/enum_variants.stderr new file mode 100644 index 00000000000..2835391de7f --- /dev/null +++ b/src/tools/clippy/tests/ui/enum_variants.stderr @@ -0,0 +1,101 @@ +error: Variant name ends with the enum's name + --> $DIR/enum_variants.rs:16:5 + | +LL | cFoo, + | ^^^^ + | + = note: `-D clippy::enum-variant-names` implied by `-D warnings` + +error: Variant name starts with the enum's name + --> $DIR/enum_variants.rs:27:5 + | +LL | FoodGood, + | ^^^^^^^^ + +error: Variant name starts with the enum's name + --> $DIR/enum_variants.rs:28:5 + | +LL | FoodMiddle, + | ^^^^^^^^^^ + +error: Variant name starts with the enum's name + --> $DIR/enum_variants.rs:29:5 + | +LL | FoodBad, + | ^^^^^^^ + +error: All variants have the same prefix: `Food` + --> $DIR/enum_variants.rs:26:1 + | +LL | / enum Food { +LL | | FoodGood, +LL | | FoodMiddle, +LL | | FoodBad, +LL | | } + | |_^ + | + = help: remove the prefixes and use full paths to the variants instead of glob imports + +error: All variants have the same prefix: `CallType` + --> $DIR/enum_variants.rs:36:1 + | +LL | / enum BadCallType { +LL | | CallTypeCall, +LL | | CallTypeCreate, +LL | | CallTypeDestroy, +LL | | } + | |_^ + | + = help: remove the prefixes and use full paths to the variants instead of glob imports + +error: All variants have the same prefix: `Constant` + --> $DIR/enum_variants.rs:48:1 + | +LL | / enum Consts { +LL | | ConstantInt, +LL | | ConstantCake, +LL | | ConstantLie, +LL | | } + | |_^ + | + = help: remove the prefixes and use full paths to the variants instead of glob imports + +error: All variants have the same prefix: `With` + --> $DIR/enum_variants.rs:82:1 + | +LL | / enum Seallll { +LL | | WithOutCake, +LL | | WithOutTea, +LL | | WithOut, +LL | | } + | |_^ + | + = help: remove the prefixes and use full paths to the variants instead of glob imports + +error: All variants have the same prefix: `Prefix` + --> $DIR/enum_variants.rs:88:1 + | +LL | / enum NonCaps { +LL | | Prefix的, +LL | | PrefixTea, +LL | | PrefixCake, +LL | | } + | |_^ + | + = help: remove the prefixes and use full paths to the variants instead of glob imports + +error: All variants have the same prefix: `With` + --> $DIR/enum_variants.rs:94:1 + | +LL | / pub enum PubSeall { +LL | | WithOutCake, +LL | | WithOutTea, +LL | | WithOut, +LL | | } + | |_^ + | + = note: `-D clippy::pub-enum-variant-names` implied by `-D warnings` + = help: remove the prefixes and use full paths to the variants instead of glob imports + +error: aborting due to 10 previous errors + diff --git a/src/tools/clippy/tests/ui/eq_op.rs b/src/tools/clippy/tests/ui/eq_op.rs new file mode 100644 index 00000000000..272b0900a31 --- /dev/null +++ b/src/tools/clippy/tests/ui/eq_op.rs @@ -0,0 +1,87 @@ +// does not test any rustfixable lints + +#[rustfmt::skip] +#[warn(clippy::eq_op)] +#[allow(clippy::identity_op, clippy::double_parens, clippy::many_single_char_names)] +#[allow(clippy::no_effect, unused_variables, clippy::unnecessary_operation, clippy::short_circuit_statement)] +#[allow(clippy::nonminimal_bool)] +#[allow(unused)] +fn main() { + // simple values and comparisons + 1 == 1; + "no" == "no"; + // even though I agree that no means no ;-) + false != false; + 1.5 < 1.5; + 1u64 >= 1u64; + + // casts, methods, parentheses + (1 as u64) & (1 as u64); + 1 ^ ((((((1)))))); + + // unary and binary operators + (-(2) < -(2)); + ((1 + 1) & (1 + 1) == (1 + 1) & (1 + 1)); + (1 * 2) + (3 * 4) == 1 * 2 + 3 * 4; + + // various other things + ([1] != [1]); + ((1, 2) != (1, 2)); + vec![1, 2, 3] == vec![1, 2, 3]; //no error yet, as we don't match macros + + // const folding + 1 + 1 == 2; + 1 - 1 == 0; + + 1 - 1; + 1 / 1; + true && true; + + true || true; + + + let a: u32 = 0; + let b: u32 = 0; + + a == b && b == a; + a != b && b != a; + a < b && b > a; + a <= b && b >= a; + + let mut a = vec![1]; + a == a; + 2*a.len() == 2*a.len(); // ok, functions + a.pop() == a.pop(); // ok, functions + + check_ignore_macro(); + + // named constants + const A: u32 = 10; + const B: u32 = 10; + const C: u32 = A / B; // ok, different named constants + const D: u32 = A / A; +} + +#[rustfmt::skip] +macro_rules! check_if_named_foo { + ($expression:expr) => ( + if stringify!($expression) == "foo" { + println!("foo!"); + } else { + println!("not foo."); + } + ) +} + +macro_rules! bool_macro { + ($expression:expr) => { + true + }; +} + +#[allow(clippy::short_circuit_statement)] +fn check_ignore_macro() { + check_if_named_foo!(foo); + // checks if the lint ignores macros with `!` operator + !bool_macro!(1) && !bool_macro!(""); +} diff --git a/src/tools/clippy/tests/ui/eq_op.stderr b/src/tools/clippy/tests/ui/eq_op.stderr new file mode 100644 index 00000000000..5b80e6078ee --- /dev/null +++ b/src/tools/clippy/tests/ui/eq_op.stderr @@ -0,0 +1,166 @@ +error: equal expressions as operands to `==` + --> $DIR/eq_op.rs:11:5 + | +LL | 1 == 1; + | ^^^^^^ + | + = note: `-D clippy::eq-op` implied by `-D warnings` + +error: equal expressions as operands to `==` + --> $DIR/eq_op.rs:12:5 + | +LL | "no" == "no"; + | ^^^^^^^^^^^^ + +error: equal expressions as operands to `!=` + --> $DIR/eq_op.rs:14:5 + | +LL | false != false; + | ^^^^^^^^^^^^^^ + +error: equal expressions as operands to `<` + --> $DIR/eq_op.rs:15:5 + | +LL | 1.5 < 1.5; + | ^^^^^^^^^ + +error: equal expressions as operands to `>=` + --> $DIR/eq_op.rs:16:5 + | +LL | 1u64 >= 1u64; + | ^^^^^^^^^^^^ + +error: equal expressions as operands to `&` + --> $DIR/eq_op.rs:19:5 + | +LL | (1 as u64) & (1 as u64); + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: equal expressions as operands to `^` + --> $DIR/eq_op.rs:20:5 + | +LL | 1 ^ ((((((1)))))); + | ^^^^^^^^^^^^^^^^^ + +error: equal expressions as operands to `<` + --> $DIR/eq_op.rs:23:5 + | +LL | (-(2) < -(2)); + | ^^^^^^^^^^^^^ + +error: equal expressions as operands to `==` + --> $DIR/eq_op.rs:24:5 + | +LL | ((1 + 1) & (1 + 1) == (1 + 1) & (1 + 1)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: equal expressions as operands to `&` + --> $DIR/eq_op.rs:24:6 + | +LL | ((1 + 1) & (1 + 1) == (1 + 1) & (1 + 1)); + | ^^^^^^^^^^^^^^^^^ + +error: equal expressions as operands to `&` + --> $DIR/eq_op.rs:24:27 + | +LL | ((1 + 1) & (1 + 1) == (1 + 1) & (1 + 1)); + | ^^^^^^^^^^^^^^^^^ + +error: equal expressions as operands to `==` + --> $DIR/eq_op.rs:25:5 + | +LL | (1 * 2) + (3 * 4) == 1 * 2 + 3 * 4; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: equal expressions as operands to `!=` + --> $DIR/eq_op.rs:28:5 + | +LL | ([1] != [1]); + | ^^^^^^^^^^^^ + +error: equal expressions as operands to `!=` + --> $DIR/eq_op.rs:29:5 + | +LL | ((1, 2) != (1, 2)); + | ^^^^^^^^^^^^^^^^^^ + +error: equal expressions as operands to `==` + --> $DIR/eq_op.rs:33:5 + | +LL | 1 + 1 == 2; + | ^^^^^^^^^^ + +error: equal expressions as operands to `==` + --> $DIR/eq_op.rs:34:5 + | +LL | 1 - 1 == 0; + | ^^^^^^^^^^ + +error: equal expressions as operands to `-` + --> $DIR/eq_op.rs:34:5 + | +LL | 1 - 1 == 0; + | ^^^^^ + +error: equal expressions as operands to `-` + --> $DIR/eq_op.rs:36:5 + | +LL | 1 - 1; + | ^^^^^ + +error: equal expressions as operands to `/` + --> $DIR/eq_op.rs:37:5 + | +LL | 1 / 1; + | ^^^^^ + +error: equal expressions as operands to `&&` + --> $DIR/eq_op.rs:38:5 + | +LL | true && true; + | ^^^^^^^^^^^^ + +error: equal expressions as operands to `||` + --> $DIR/eq_op.rs:40:5 + | +LL | true || true; + | ^^^^^^^^^^^^ + +error: equal expressions as operands to `&&` + --> $DIR/eq_op.rs:46:5 + | +LL | a == b && b == a; + | ^^^^^^^^^^^^^^^^ + +error: equal expressions as operands to `&&` + --> $DIR/eq_op.rs:47:5 + | +LL | a != b && b != a; + | ^^^^^^^^^^^^^^^^ + +error: equal expressions as operands to `&&` + --> $DIR/eq_op.rs:48:5 + | +LL | a < b && b > a; + | ^^^^^^^^^^^^^^ + +error: equal expressions as operands to `&&` + --> $DIR/eq_op.rs:49:5 + | +LL | a <= b && b >= a; + | ^^^^^^^^^^^^^^^^ + +error: equal expressions as operands to `==` + --> $DIR/eq_op.rs:52:5 + | +LL | a == a; + | ^^^^^^ + +error: equal expressions as operands to `/` + --> $DIR/eq_op.rs:62:20 + | +LL | const D: u32 = A / A; + | ^^^^^ + +error: aborting due to 27 previous errors + diff --git a/src/tools/clippy/tests/ui/erasing_op.rs b/src/tools/clippy/tests/ui/erasing_op.rs new file mode 100644 index 00000000000..1540062a4bc --- /dev/null +++ b/src/tools/clippy/tests/ui/erasing_op.rs @@ -0,0 +1,9 @@ +#[allow(clippy::no_effect)] +#[warn(clippy::erasing_op)] +fn main() { + let x: u8 = 0; + + x * 0; + 0 & x; + 0 / x; +} diff --git a/src/tools/clippy/tests/ui/erasing_op.stderr b/src/tools/clippy/tests/ui/erasing_op.stderr new file mode 100644 index 00000000000..e54ce85f98e --- /dev/null +++ b/src/tools/clippy/tests/ui/erasing_op.stderr @@ -0,0 +1,22 @@ +error: this operation will always return zero. This is likely not the intended outcome + --> $DIR/erasing_op.rs:6:5 + | +LL | x * 0; + | ^^^^^ + | + = note: `-D clippy::erasing-op` implied by `-D warnings` + +error: this operation will always return zero. This is likely not the intended outcome + --> $DIR/erasing_op.rs:7:5 + | +LL | 0 & x; + | ^^^^^ + +error: this operation will always return zero. This is likely not the intended outcome + --> $DIR/erasing_op.rs:8:5 + | +LL | 0 / x; + | ^^^^^ + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/escape_analysis.rs b/src/tools/clippy/tests/ui/escape_analysis.rs new file mode 100644 index 00000000000..c0a52d832c0 --- /dev/null +++ b/src/tools/clippy/tests/ui/escape_analysis.rs @@ -0,0 +1,176 @@ +#![feature(box_syntax)] +#![allow( + clippy::borrowed_box, + clippy::needless_pass_by_value, + clippy::unused_unit, + clippy::redundant_clone, + clippy::match_single_binding +)] +#![warn(clippy::boxed_local)] + +#[derive(Clone)] +struct A; + +impl A { + fn foo(&self) {} +} + +trait Z { + fn bar(&self); +} + +impl Z for A { + fn bar(&self) { + //nothing + } +} + +fn main() {} + +fn ok_box_trait(boxed_trait: &Box<dyn Z>) { + let boxed_local = boxed_trait; + // done +} + +fn warn_call() { + let x = box A; + x.foo(); +} + +fn warn_arg(x: Box<A>) { + x.foo(); +} + +fn nowarn_closure_arg() { + let x = Some(box A); + x.map_or((), |x| take_ref(&x)); +} + +fn warn_rename_call() { + let x = box A; + + let y = x; + y.foo(); // via autoderef +} + +fn warn_notuse() { + let bz = box A; +} + +fn warn_pass() { + let bz = box A; + take_ref(&bz); // via deref coercion +} + +fn nowarn_return() -> Box<A> { + box A // moved out, "escapes" +} + +fn nowarn_move() { + let bx = box A; + drop(bx) // moved in, "escapes" +} +fn nowarn_call() { + let bx = box A; + bx.clone(); // method only available to Box, not via autoderef +} + +fn nowarn_pass() { + let bx = box A; + take_box(&bx); // fn needs &Box +} + +fn take_box(x: &Box<A>) {} +fn take_ref(x: &A) {} + +fn nowarn_ref_take() { + // false positive, should actually warn + let x = box A; + let y = &x; + take_box(y); +} + +fn nowarn_match() { + let x = box A; // moved into a match + match x { + y => drop(y), + } +} + +fn warn_match() { + let x = box A; + match &x { + // not moved + ref y => (), + } +} + +fn nowarn_large_array() { + // should not warn, is large array + // and should not be on stack + let x = box [1; 10000]; + match &x { + // not moved + ref y => (), + } +} + +/// ICE regression test +pub trait Foo { + type Item; +} + +impl<'a> Foo for &'a () { + type Item = (); +} + +pub struct PeekableSeekable<I: Foo> { + _peeked: I::Item, +} + +pub fn new(_needs_name: Box<PeekableSeekable<&()>>) -> () {} + +/// Regression for #916, #1123 +/// +/// This shouldn't warn for `boxed_local`as the implementation of a trait +/// can't change much about the trait definition. +trait BoxedAction { + fn do_sth(self: Box<Self>); +} + +impl BoxedAction for u64 { + fn do_sth(self: Box<Self>) { + println!("{}", *self) + } +} + +/// Regression for #1478 +/// +/// This shouldn't warn for `boxed_local`as self itself is a box type. +trait MyTrait { + fn do_sth(self); +} + +impl<T> MyTrait for Box<T> { + fn do_sth(self) {} +} + +// Issue #3739 - capture in closures +mod issue_3739 { + use super::A; + + fn consume<T>(_: T) {} + fn borrow<T>(_: &T) {} + + fn closure_consume(x: Box<A>) { + let _ = move || { + consume(x); + }; + } + + fn closure_borrow(x: Box<A>) { + let _ = || { + borrow(&x); + }; + } +} diff --git a/src/tools/clippy/tests/ui/escape_analysis.stderr b/src/tools/clippy/tests/ui/escape_analysis.stderr new file mode 100644 index 00000000000..c86a769a3da --- /dev/null +++ b/src/tools/clippy/tests/ui/escape_analysis.stderr @@ -0,0 +1,16 @@ +error: local variable doesn't need to be boxed here + --> $DIR/escape_analysis.rs:40:13 + | +LL | fn warn_arg(x: Box<A>) { + | ^ + | + = note: `-D clippy::boxed-local` implied by `-D warnings` + +error: local variable doesn't need to be boxed here + --> $DIR/escape_analysis.rs:131:12 + | +LL | pub fn new(_needs_name: Box<PeekableSeekable<&()>>) -> () {} + | ^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/eta.fixed b/src/tools/clippy/tests/ui/eta.fixed new file mode 100644 index 00000000000..1b34c2f74eb --- /dev/null +++ b/src/tools/clippy/tests/ui/eta.fixed @@ -0,0 +1,204 @@ +// run-rustfix + +#![allow( + unused, + clippy::no_effect, + clippy::redundant_closure_call, + clippy::many_single_char_names, + clippy::needless_pass_by_value, + clippy::option_map_unit_fn +)] +#![warn( + clippy::redundant_closure, + clippy::redundant_closure_for_method_calls, + clippy::needless_borrow +)] + +use std::path::PathBuf; + +fn main() { + let a = Some(1u8).map(foo); + meta(foo); + let c = Some(1u8).map(|a| {1+2; foo}(a)); + let d = Some(1u8).map(|a| foo((|b| foo2(b))(a))); //is adjusted? + all(&[1, 2, 3], &2, |x, y| below(x, y)); //is adjusted + unsafe { + Some(1u8).map(|a| unsafe_fn(a)); // unsafe fn + } + + // See #815 + let e = Some(1u8).map(|a| divergent(a)); + let e = Some(1u8).map(generic); + let e = Some(1u8).map(generic); + // See #515 + let a: Option<Box<dyn (::std::ops::Deref<Target = [i32]>)>> = + Some(vec![1i32, 2]).map(|v| -> Box<dyn (::std::ops::Deref<Target = [i32]>)> { Box::new(v) }); +} + +trait TestTrait { + fn trait_foo(self) -> bool; + fn trait_foo_ref(&self) -> bool; +} + +struct TestStruct<'a> { + some_ref: &'a i32, +} + +impl<'a> TestStruct<'a> { + fn foo(self) -> bool { + false + } + unsafe fn foo_unsafe(self) -> bool { + true + } +} + +impl<'a> TestTrait for TestStruct<'a> { + fn trait_foo(self) -> bool { + false + } + fn trait_foo_ref(&self) -> bool { + false + } +} + +impl<'a> std::ops::Deref for TestStruct<'a> { + type Target = char; + fn deref(&self) -> &char { + &'a' + } +} + +fn test_redundant_closures_containing_method_calls() { + let i = 10; + let e = Some(TestStruct { some_ref: &i }).map(TestStruct::foo); + let e = Some(TestStruct { some_ref: &i }).map(TestStruct::foo); + let e = Some(TestStruct { some_ref: &i }).map(TestTrait::trait_foo); + let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo_ref()); + let e = Some(TestStruct { some_ref: &i }).map(TestTrait::trait_foo); + let e = Some(&mut vec![1, 2, 3]).map(std::vec::Vec::clear); + let e = Some(&mut vec![1, 2, 3]).map(std::vec::Vec::clear); + unsafe { + let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo_unsafe()); + } + let e = Some("str").map(std::string::ToString::to_string); + let e = Some("str").map(str::to_string); + let e = Some('a').map(char::to_uppercase); + let e = Some('a').map(char::to_uppercase); + let e: std::vec::Vec<usize> = vec!['a', 'b', 'c'].iter().map(|c| c.len_utf8()).collect(); + let e: std::vec::Vec<char> = vec!['a', 'b', 'c'].iter().map(char::to_ascii_uppercase).collect(); + let e: std::vec::Vec<char> = vec!['a', 'b', 'c'].iter().map(char::to_ascii_uppercase).collect(); + let p = Some(PathBuf::new()); + let e = p.as_ref().and_then(|s| s.to_str()); + let c = Some(TestStruct { some_ref: &i }) + .as_ref() + .map(|c| c.to_ascii_uppercase()); + + fn test_different_borrow_levels<T>(t: &[&T]) + where + T: TestTrait, + { + t.iter().filter(|x| x.trait_foo_ref()); + t.iter().map(|x| x.trait_foo_ref()); + } + + let mut some = Some(|x| x * x); + let arr = [Ok(1), Err(2)]; + let _: Vec<_> = arr.iter().map(|x| x.map_err(|e| some.take().unwrap()(e))).collect(); +} + +struct Thunk<T>(Box<dyn FnMut() -> T>); + +impl<T> Thunk<T> { + fn new<F: 'static + FnOnce() -> T>(f: F) -> Thunk<T> { + let mut option = Some(f); + // This should not trigger redundant_closure (#1439) + Thunk(Box::new(move || option.take().unwrap()())) + } + + fn unwrap(self) -> T { + let Thunk(mut f) = self; + f() + } +} + +fn foobar() { + let thunk = Thunk::new(|| println!("Hello, world!")); + thunk.unwrap() +} + +fn meta<F>(f: F) +where + F: Fn(u8), +{ + f(1u8) +} + +fn foo(_: u8) {} + +fn foo2(_: u8) -> u8 { + 1u8 +} + +fn all<X, F>(x: &[X], y: &X, f: F) -> bool +where + F: Fn(&X, &X) -> bool, +{ + x.iter().all(|e| f(e, y)) +} + +fn below(x: &u8, y: &u8) -> bool { + x < y +} + +unsafe fn unsafe_fn(_: u8) {} + +fn divergent(_: u8) -> ! { + unimplemented!() +} + +fn generic<T>(_: T) -> u8 { + 0 +} + +fn passes_fn_mut(mut x: Box<dyn FnMut()>) { + requires_fn_once(|| x()); +} +fn requires_fn_once<T: FnOnce()>(_: T) {} + +fn test_redundant_closure_with_function_pointer() { + type FnPtrType = fn(u8); + let foo_ptr: FnPtrType = foo; + let a = Some(1u8).map(foo_ptr); +} + +fn test_redundant_closure_with_another_closure() { + let closure = |a| println!("{}", a); + let a = Some(1u8).map(closure); +} + +fn make_lazy(f: impl Fn() -> fn(u8) -> u8) -> impl Fn(u8) -> u8 { + // Currently f is called when result of make_lazy is called. + // If the closure is removed, f will be called when make_lazy itself is + // called. This changes semantics, so the closure must stay. + Box::new(move |x| f()(x)) +} + +fn call<F: FnOnce(&mut String) -> String>(f: F) -> String { + f(&mut "Hello".to_owned()) +} +fn test_difference_in_mutability() { + call(|s| s.clone()); +} + +struct Bar; +impl std::ops::Deref for Bar { + type Target = str; + fn deref(&self) -> &str { + "hi" + } +} + +fn test_deref_with_trait_method() { + let _ = [Bar].iter().map(|s| s.to_string()).collect::<Vec<_>>(); +} diff --git a/src/tools/clippy/tests/ui/eta.rs b/src/tools/clippy/tests/ui/eta.rs new file mode 100644 index 00000000000..4f050bd8479 --- /dev/null +++ b/src/tools/clippy/tests/ui/eta.rs @@ -0,0 +1,204 @@ +// run-rustfix + +#![allow( + unused, + clippy::no_effect, + clippy::redundant_closure_call, + clippy::many_single_char_names, + clippy::needless_pass_by_value, + clippy::option_map_unit_fn +)] +#![warn( + clippy::redundant_closure, + clippy::redundant_closure_for_method_calls, + clippy::needless_borrow +)] + +use std::path::PathBuf; + +fn main() { + let a = Some(1u8).map(|a| foo(a)); + meta(|a| foo(a)); + let c = Some(1u8).map(|a| {1+2; foo}(a)); + let d = Some(1u8).map(|a| foo((|b| foo2(b))(a))); //is adjusted? + all(&[1, 2, 3], &&2, |x, y| below(x, y)); //is adjusted + unsafe { + Some(1u8).map(|a| unsafe_fn(a)); // unsafe fn + } + + // See #815 + let e = Some(1u8).map(|a| divergent(a)); + let e = Some(1u8).map(|a| generic(a)); + let e = Some(1u8).map(generic); + // See #515 + let a: Option<Box<dyn (::std::ops::Deref<Target = [i32]>)>> = + Some(vec![1i32, 2]).map(|v| -> Box<dyn (::std::ops::Deref<Target = [i32]>)> { Box::new(v) }); +} + +trait TestTrait { + fn trait_foo(self) -> bool; + fn trait_foo_ref(&self) -> bool; +} + +struct TestStruct<'a> { + some_ref: &'a i32, +} + +impl<'a> TestStruct<'a> { + fn foo(self) -> bool { + false + } + unsafe fn foo_unsafe(self) -> bool { + true + } +} + +impl<'a> TestTrait for TestStruct<'a> { + fn trait_foo(self) -> bool { + false + } + fn trait_foo_ref(&self) -> bool { + false + } +} + +impl<'a> std::ops::Deref for TestStruct<'a> { + type Target = char; + fn deref(&self) -> &char { + &'a' + } +} + +fn test_redundant_closures_containing_method_calls() { + let i = 10; + let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo()); + let e = Some(TestStruct { some_ref: &i }).map(TestStruct::foo); + let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo()); + let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo_ref()); + let e = Some(TestStruct { some_ref: &i }).map(TestTrait::trait_foo); + let e = Some(&mut vec![1, 2, 3]).map(|v| v.clear()); + let e = Some(&mut vec![1, 2, 3]).map(std::vec::Vec::clear); + unsafe { + let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo_unsafe()); + } + let e = Some("str").map(|s| s.to_string()); + let e = Some("str").map(str::to_string); + let e = Some('a').map(|s| s.to_uppercase()); + let e = Some('a').map(char::to_uppercase); + let e: std::vec::Vec<usize> = vec!['a', 'b', 'c'].iter().map(|c| c.len_utf8()).collect(); + let e: std::vec::Vec<char> = vec!['a', 'b', 'c'].iter().map(|c| c.to_ascii_uppercase()).collect(); + let e: std::vec::Vec<char> = vec!['a', 'b', 'c'].iter().map(char::to_ascii_uppercase).collect(); + let p = Some(PathBuf::new()); + let e = p.as_ref().and_then(|s| s.to_str()); + let c = Some(TestStruct { some_ref: &i }) + .as_ref() + .map(|c| c.to_ascii_uppercase()); + + fn test_different_borrow_levels<T>(t: &[&T]) + where + T: TestTrait, + { + t.iter().filter(|x| x.trait_foo_ref()); + t.iter().map(|x| x.trait_foo_ref()); + } + + let mut some = Some(|x| x * x); + let arr = [Ok(1), Err(2)]; + let _: Vec<_> = arr.iter().map(|x| x.map_err(|e| some.take().unwrap()(e))).collect(); +} + +struct Thunk<T>(Box<dyn FnMut() -> T>); + +impl<T> Thunk<T> { + fn new<F: 'static + FnOnce() -> T>(f: F) -> Thunk<T> { + let mut option = Some(f); + // This should not trigger redundant_closure (#1439) + Thunk(Box::new(move || option.take().unwrap()())) + } + + fn unwrap(self) -> T { + let Thunk(mut f) = self; + f() + } +} + +fn foobar() { + let thunk = Thunk::new(|| println!("Hello, world!")); + thunk.unwrap() +} + +fn meta<F>(f: F) +where + F: Fn(u8), +{ + f(1u8) +} + +fn foo(_: u8) {} + +fn foo2(_: u8) -> u8 { + 1u8 +} + +fn all<X, F>(x: &[X], y: &X, f: F) -> bool +where + F: Fn(&X, &X) -> bool, +{ + x.iter().all(|e| f(e, y)) +} + +fn below(x: &u8, y: &u8) -> bool { + x < y +} + +unsafe fn unsafe_fn(_: u8) {} + +fn divergent(_: u8) -> ! { + unimplemented!() +} + +fn generic<T>(_: T) -> u8 { + 0 +} + +fn passes_fn_mut(mut x: Box<dyn FnMut()>) { + requires_fn_once(|| x()); +} +fn requires_fn_once<T: FnOnce()>(_: T) {} + +fn test_redundant_closure_with_function_pointer() { + type FnPtrType = fn(u8); + let foo_ptr: FnPtrType = foo; + let a = Some(1u8).map(|a| foo_ptr(a)); +} + +fn test_redundant_closure_with_another_closure() { + let closure = |a| println!("{}", a); + let a = Some(1u8).map(|a| closure(a)); +} + +fn make_lazy(f: impl Fn() -> fn(u8) -> u8) -> impl Fn(u8) -> u8 { + // Currently f is called when result of make_lazy is called. + // If the closure is removed, f will be called when make_lazy itself is + // called. This changes semantics, so the closure must stay. + Box::new(move |x| f()(x)) +} + +fn call<F: FnOnce(&mut String) -> String>(f: F) -> String { + f(&mut "Hello".to_owned()) +} +fn test_difference_in_mutability() { + call(|s| s.clone()); +} + +struct Bar; +impl std::ops::Deref for Bar { + type Target = str; + fn deref(&self) -> &str { + "hi" + } +} + +fn test_deref_with_trait_method() { + let _ = [Bar].iter().map(|s| s.to_string()).collect::<Vec<_>>(); +} diff --git a/src/tools/clippy/tests/ui/eta.stderr b/src/tools/clippy/tests/ui/eta.stderr new file mode 100644 index 00000000000..c4713ca8083 --- /dev/null +++ b/src/tools/clippy/tests/ui/eta.stderr @@ -0,0 +1,80 @@ +error: redundant closure found + --> $DIR/eta.rs:20:27 + | +LL | let a = Some(1u8).map(|a| foo(a)); + | ^^^^^^^^^^ help: remove closure as shown: `foo` + | + = note: `-D clippy::redundant-closure` implied by `-D warnings` + +error: redundant closure found + --> $DIR/eta.rs:21:10 + | +LL | meta(|a| foo(a)); + | ^^^^^^^^^^ help: remove closure as shown: `foo` + +error: this expression borrows a reference that is immediately dereferenced by the compiler + --> $DIR/eta.rs:24:21 + | +LL | all(&[1, 2, 3], &&2, |x, y| below(x, y)); //is adjusted + | ^^^ help: change this to: `&2` + | + = note: `-D clippy::needless-borrow` implied by `-D warnings` + +error: redundant closure found + --> $DIR/eta.rs:31:27 + | +LL | let e = Some(1u8).map(|a| generic(a)); + | ^^^^^^^^^^^^^^ help: remove closure as shown: `generic` + +error: redundant closure found + --> $DIR/eta.rs:74:51 + | +LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo()); + | ^^^^^^^^^^^ help: remove closure as shown: `TestStruct::foo` + | + = note: `-D clippy::redundant-closure-for-method-calls` implied by `-D warnings` + +error: redundant closure found + --> $DIR/eta.rs:76:51 + | +LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo()); + | ^^^^^^^^^^^^^^^^^ help: remove closure as shown: `TestTrait::trait_foo` + +error: redundant closure found + --> $DIR/eta.rs:79:42 + | +LL | let e = Some(&mut vec![1, 2, 3]).map(|v| v.clear()); + | ^^^^^^^^^^^^^ help: remove closure as shown: `std::vec::Vec::clear` + +error: redundant closure found + --> $DIR/eta.rs:84:29 + | +LL | let e = Some("str").map(|s| s.to_string()); + | ^^^^^^^^^^^^^^^^^ help: remove closure as shown: `std::string::ToString::to_string` + +error: redundant closure found + --> $DIR/eta.rs:86:27 + | +LL | let e = Some('a').map(|s| s.to_uppercase()); + | ^^^^^^^^^^^^^^^^^^^^ help: remove closure as shown: `char::to_uppercase` + +error: redundant closure found + --> $DIR/eta.rs:89:65 + | +LL | let e: std::vec::Vec<char> = vec!['a', 'b', 'c'].iter().map(|c| c.to_ascii_uppercase()).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove closure as shown: `char::to_ascii_uppercase` + +error: redundant closure found + --> $DIR/eta.rs:172:27 + | +LL | let a = Some(1u8).map(|a| foo_ptr(a)); + | ^^^^^^^^^^^^^^ help: remove closure as shown: `foo_ptr` + +error: redundant closure found + --> $DIR/eta.rs:177:27 + | +LL | let a = Some(1u8).map(|a| closure(a)); + | ^^^^^^^^^^^^^^ help: remove closure as shown: `closure` + +error: aborting due to 12 previous errors + diff --git a/src/tools/clippy/tests/ui/eval_order_dependence.rs b/src/tools/clippy/tests/ui/eval_order_dependence.rs new file mode 100644 index 00000000000..d806bc6d401 --- /dev/null +++ b/src/tools/clippy/tests/ui/eval_order_dependence.rs @@ -0,0 +1,109 @@ +#[warn(clippy::eval_order_dependence)] +#[allow( + unused_assignments, + unused_variables, + clippy::many_single_char_names, + clippy::no_effect, + dead_code, + clippy::blacklisted_name +)] +fn main() { + let mut x = 0; + let a = { + x = 1; + 1 + } + x; + + // Example from iss#277 + x += { + x = 20; + 2 + }; + + // Does it work in weird places? + // ...in the base for a struct expression? + struct Foo { + a: i32, + b: i32, + }; + let base = Foo { a: 4, b: 5 }; + let foo = Foo { + a: x, + ..{ + x = 6; + base + } + }; + // ...inside a closure? + let closure = || { + let mut x = 0; + x += { + x = 20; + 2 + }; + }; + // ...not across a closure? + let mut y = 0; + let b = (y, || y = 1); + + // && and || evaluate left-to-right. + let a = { + x = 1; + true + } && (x == 3); + let a = { + x = 1; + true + } || (x == 3); + + // Make sure we don't get confused by alpha conversion. + let a = { + let mut x = 1; + x = 2; + 1 + } + x; + + // No warning if we don't read the variable... + x = { + x = 20; + 2 + }; + // ...if the assignment is in a closure... + let b = { + || { + x = 1; + }; + 1 + } + x; + // ... or the access is under an address. + let b = ( + { + let p = &x; + 1 + }, + { + x = 1; + x + }, + ); + + // Limitation: l-values other than simple variables don't trigger + // the warning. + let mut tup = (0, 0); + let c = { + tup.0 = 1; + 1 + } + tup.0; + // Limitation: you can get away with a read under address-of. + let mut z = 0; + let b = ( + &{ + z = x; + x + }, + { + x = 3; + x + }, + ); +} diff --git a/src/tools/clippy/tests/ui/eval_order_dependence.stderr b/src/tools/clippy/tests/ui/eval_order_dependence.stderr new file mode 100644 index 00000000000..8f4fa2228f7 --- /dev/null +++ b/src/tools/clippy/tests/ui/eval_order_dependence.stderr @@ -0,0 +1,51 @@ +error: unsequenced read of a variable + --> $DIR/eval_order_dependence.rs:15:9 + | +LL | } + x; + | ^ + | + = note: `-D clippy::eval-order-dependence` implied by `-D warnings` +note: whether read occurs before this write depends on evaluation order + --> $DIR/eval_order_dependence.rs:13:9 + | +LL | x = 1; + | ^^^^^ + +error: unsequenced read of a variable + --> $DIR/eval_order_dependence.rs:18:5 + | +LL | x += { + | ^ + | +note: whether read occurs before this write depends on evaluation order + --> $DIR/eval_order_dependence.rs:19:9 + | +LL | x = 20; + | ^^^^^^ + +error: unsequenced read of a variable + --> $DIR/eval_order_dependence.rs:31:12 + | +LL | a: x, + | ^ + | +note: whether read occurs before this write depends on evaluation order + --> $DIR/eval_order_dependence.rs:33:13 + | +LL | x = 6; + | ^^^^^ + +error: unsequenced read of a variable + --> $DIR/eval_order_dependence.rs:40:9 + | +LL | x += { + | ^ + | +note: whether read occurs before this write depends on evaluation order + --> $DIR/eval_order_dependence.rs:41:13 + | +LL | x = 20; + | ^^^^^^ + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/excessive_precision.fixed b/src/tools/clippy/tests/ui/excessive_precision.fixed new file mode 100644 index 00000000000..bf0325fec79 --- /dev/null +++ b/src/tools/clippy/tests/ui/excessive_precision.fixed @@ -0,0 +1,63 @@ +// run-rustfix +#![warn(clippy::excessive_precision)] +#![allow(dead_code, unused_variables, clippy::print_literal)] + +fn main() { + // Consts + const GOOD32: f32 = 0.123_456; + const GOOD32_SM: f32 = 0.000_000_000_1; + const GOOD32_DOT: f32 = 10_000_000_000.0; + const GOOD32_EDGE: f32 = 1.000_000_8; + const GOOD64: f64 = 0.123_456_789_012; + const GOOD64_SM: f32 = 0.000_000_000_000_000_1; + const GOOD64_DOT: f32 = 10_000_000_000_000_000.0; + + const BAD32_1: f32 = 0.123_456_79_f32; + const BAD32_2: f32 = 0.123_456_79; + const BAD32_3: f32 = 0.1; + const BAD32_EDGE: f32 = 1.000_001; + + const BAD64_1: f64 = 0.123_456_789_012_345_66_f64; + const BAD64_2: f64 = 0.123_456_789_012_345_66; + const BAD64_3: f64 = 0.1; + + // Literal as param + println!("{:?}", 8.888_888_888_888_89); + + // // TODO add inferred type tests for f32 + // Locals + let good32: f32 = 0.123_456_f32; + let good32_2: f32 = 0.123_456; + + let good64: f64 = 0.123_456_789_012; + let good64_suf: f64 = 0.123_456_789_012f64; + let good64_inf = 0.123_456_789_012; + + let bad32: f32 = 1.123_456_8; + let bad32_suf: f32 = 1.123_456_8_f32; + let bad32_inf = 1.123_456_8_f32; + + let bad64: f64 = 0.123_456_789_012_345_66; + let bad64_suf: f64 = 0.123_456_789_012_345_66_f64; + let bad64_inf = 0.123_456_789_012_345_66; + + // Vectors + let good_vec32: Vec<f32> = vec![0.123_456]; + let good_vec64: Vec<f64> = vec![0.123_456_789]; + + let bad_vec32: Vec<f32> = vec![0.123_456_79]; + let bad_vec64: Vec<f64> = vec![0.123_456_789_123_456_78]; + + // Exponential float notation + let good_e32: f32 = 1e-10; + let bad_e32: f32 = 1.123_456_8e-10; + + let good_bige32: f32 = 1E-10; + let bad_bige32: f32 = 1.123_456_8E-10; + + // Inferred type + let good_inferred: f32 = 1f32 * 1_000_000_000.; + + // issue #2840 + let num = 0.000_000_000_01e-10f64; +} diff --git a/src/tools/clippy/tests/ui/excessive_precision.rs b/src/tools/clippy/tests/ui/excessive_precision.rs new file mode 100644 index 00000000000..ce4722a90f9 --- /dev/null +++ b/src/tools/clippy/tests/ui/excessive_precision.rs @@ -0,0 +1,63 @@ +// run-rustfix +#![warn(clippy::excessive_precision)] +#![allow(dead_code, unused_variables, clippy::print_literal)] + +fn main() { + // Consts + const GOOD32: f32 = 0.123_456; + const GOOD32_SM: f32 = 0.000_000_000_1; + const GOOD32_DOT: f32 = 10_000_000_000.0; + const GOOD32_EDGE: f32 = 1.000_000_8; + const GOOD64: f64 = 0.123_456_789_012; + const GOOD64_SM: f32 = 0.000_000_000_000_000_1; + const GOOD64_DOT: f32 = 10_000_000_000_000_000.0; + + const BAD32_1: f32 = 0.123_456_789_f32; + const BAD32_2: f32 = 0.123_456_789; + const BAD32_3: f32 = 0.100_000_000_000_1; + const BAD32_EDGE: f32 = 1.000_000_9; + + const BAD64_1: f64 = 0.123_456_789_012_345_67f64; + const BAD64_2: f64 = 0.123_456_789_012_345_67; + const BAD64_3: f64 = 0.100_000_000_000_000_000_1; + + // Literal as param + println!("{:?}", 8.888_888_888_888_888_888_888); + + // // TODO add inferred type tests for f32 + // Locals + let good32: f32 = 0.123_456_f32; + let good32_2: f32 = 0.123_456; + + let good64: f64 = 0.123_456_789_012; + let good64_suf: f64 = 0.123_456_789_012f64; + let good64_inf = 0.123_456_789_012; + + let bad32: f32 = 1.123_456_789; + let bad32_suf: f32 = 1.123_456_789_f32; + let bad32_inf = 1.123_456_789_f32; + + let bad64: f64 = 0.123_456_789_012_345_67; + let bad64_suf: f64 = 0.123_456_789_012_345_67f64; + let bad64_inf = 0.123_456_789_012_345_67; + + // Vectors + let good_vec32: Vec<f32> = vec![0.123_456]; + let good_vec64: Vec<f64> = vec![0.123_456_789]; + + let bad_vec32: Vec<f32> = vec![0.123_456_789]; + let bad_vec64: Vec<f64> = vec![0.123_456_789_123_456_789]; + + // Exponential float notation + let good_e32: f32 = 1e-10; + let bad_e32: f32 = 1.123_456_788_888e-10; + + let good_bige32: f32 = 1E-10; + let bad_bige32: f32 = 1.123_456_788_888E-10; + + // Inferred type + let good_inferred: f32 = 1f32 * 1_000_000_000.; + + // issue #2840 + let num = 0.000_000_000_01e-10f64; +} diff --git a/src/tools/clippy/tests/ui/excessive_precision.stderr b/src/tools/clippy/tests/ui/excessive_precision.stderr new file mode 100644 index 00000000000..599773f2f70 --- /dev/null +++ b/src/tools/clippy/tests/ui/excessive_precision.stderr @@ -0,0 +1,112 @@ +error: float has excessive precision + --> $DIR/excessive_precision.rs:15:26 + | +LL | const BAD32_1: f32 = 0.123_456_789_f32; + | ^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_79_f32` + | + = note: `-D clippy::excessive-precision` implied by `-D warnings` + +error: float has excessive precision + --> $DIR/excessive_precision.rs:16:26 + | +LL | const BAD32_2: f32 = 0.123_456_789; + | ^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_79` + +error: float has excessive precision + --> $DIR/excessive_precision.rs:17:26 + | +LL | const BAD32_3: f32 = 0.100_000_000_000_1; + | ^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.1` + +error: float has excessive precision + --> $DIR/excessive_precision.rs:18:29 + | +LL | const BAD32_EDGE: f32 = 1.000_000_9; + | ^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.000_001` + +error: float has excessive precision + --> $DIR/excessive_precision.rs:20:26 + | +LL | const BAD64_1: f64 = 0.123_456_789_012_345_67f64; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_789_012_345_66_f64` + +error: float has excessive precision + --> $DIR/excessive_precision.rs:21:26 + | +LL | const BAD64_2: f64 = 0.123_456_789_012_345_67; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_789_012_345_66` + +error: float has excessive precision + --> $DIR/excessive_precision.rs:22:26 + | +LL | const BAD64_3: f64 = 0.100_000_000_000_000_000_1; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.1` + +error: float has excessive precision + --> $DIR/excessive_precision.rs:25:22 + | +LL | println!("{:?}", 8.888_888_888_888_888_888_888); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `8.888_888_888_888_89` + +error: float has excessive precision + --> $DIR/excessive_precision.rs:36:22 + | +LL | let bad32: f32 = 1.123_456_789; + | ^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.123_456_8` + +error: float has excessive precision + --> $DIR/excessive_precision.rs:37:26 + | +LL | let bad32_suf: f32 = 1.123_456_789_f32; + | ^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.123_456_8_f32` + +error: float has excessive precision + --> $DIR/excessive_precision.rs:38:21 + | +LL | let bad32_inf = 1.123_456_789_f32; + | ^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.123_456_8_f32` + +error: float has excessive precision + --> $DIR/excessive_precision.rs:40:22 + | +LL | let bad64: f64 = 0.123_456_789_012_345_67; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_789_012_345_66` + +error: float has excessive precision + --> $DIR/excessive_precision.rs:41:26 + | +LL | let bad64_suf: f64 = 0.123_456_789_012_345_67f64; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_789_012_345_66_f64` + +error: float has excessive precision + --> $DIR/excessive_precision.rs:42:21 + | +LL | let bad64_inf = 0.123_456_789_012_345_67; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_789_012_345_66` + +error: float has excessive precision + --> $DIR/excessive_precision.rs:48:36 + | +LL | let bad_vec32: Vec<f32> = vec![0.123_456_789]; + | ^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_79` + +error: float has excessive precision + --> $DIR/excessive_precision.rs:49:36 + | +LL | let bad_vec64: Vec<f64> = vec![0.123_456_789_123_456_789]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_789_123_456_78` + +error: float has excessive precision + --> $DIR/excessive_precision.rs:53:24 + | +LL | let bad_e32: f32 = 1.123_456_788_888e-10; + | ^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.123_456_8e-10` + +error: float has excessive precision + --> $DIR/excessive_precision.rs:56:27 + | +LL | let bad_bige32: f32 = 1.123_456_788_888E-10; + | ^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.123_456_8E-10` + +error: aborting due to 18 previous errors + diff --git a/src/tools/clippy/tests/ui/exit1.rs b/src/tools/clippy/tests/ui/exit1.rs new file mode 100644 index 00000000000..4eac6eb7467 --- /dev/null +++ b/src/tools/clippy/tests/ui/exit1.rs @@ -0,0 +1,15 @@ +#[warn(clippy::exit)] + +fn not_main() { + if true { + std::process::exit(4); + } +} + +fn main() { + if true { + std::process::exit(2); + }; + not_main(); + std::process::exit(1); +} diff --git a/src/tools/clippy/tests/ui/exit1.stderr b/src/tools/clippy/tests/ui/exit1.stderr new file mode 100644 index 00000000000..a8d3956aa27 --- /dev/null +++ b/src/tools/clippy/tests/ui/exit1.stderr @@ -0,0 +1,10 @@ +error: usage of `process::exit` + --> $DIR/exit1.rs:5:9 + | +LL | std::process::exit(4); + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::exit` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/exit2.rs b/src/tools/clippy/tests/ui/exit2.rs new file mode 100644 index 00000000000..4b693ed7083 --- /dev/null +++ b/src/tools/clippy/tests/ui/exit2.rs @@ -0,0 +1,13 @@ +#[warn(clippy::exit)] + +fn also_not_main() { + std::process::exit(3); +} + +fn main() { + if true { + std::process::exit(2); + }; + also_not_main(); + std::process::exit(1); +} diff --git a/src/tools/clippy/tests/ui/exit2.stderr b/src/tools/clippy/tests/ui/exit2.stderr new file mode 100644 index 00000000000..7263e156a9d --- /dev/null +++ b/src/tools/clippy/tests/ui/exit2.stderr @@ -0,0 +1,10 @@ +error: usage of `process::exit` + --> $DIR/exit2.rs:4:5 + | +LL | std::process::exit(3); + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::exit` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/exit3.rs b/src/tools/clippy/tests/ui/exit3.rs new file mode 100644 index 00000000000..9dc0e1015a4 --- /dev/null +++ b/src/tools/clippy/tests/ui/exit3.rs @@ -0,0 +1,8 @@ +#[warn(clippy::exit)] + +fn main() { + if true { + std::process::exit(2); + }; + std::process::exit(1); +} diff --git a/src/tools/clippy/tests/ui/expect.rs b/src/tools/clippy/tests/ui/expect.rs new file mode 100644 index 00000000000..0bd4252c49a --- /dev/null +++ b/src/tools/clippy/tests/ui/expect.rs @@ -0,0 +1,16 @@ +#![warn(clippy::option_expect_used, clippy::result_expect_used)] + +fn expect_option() { + let opt = Some(0); + let _ = opt.expect(""); +} + +fn expect_result() { + let res: Result<u8, ()> = Ok(0); + let _ = res.expect(""); +} + +fn main() { + expect_option(); + expect_result(); +} diff --git a/src/tools/clippy/tests/ui/expect.stderr b/src/tools/clippy/tests/ui/expect.stderr new file mode 100644 index 00000000000..adf9f4f1921 --- /dev/null +++ b/src/tools/clippy/tests/ui/expect.stderr @@ -0,0 +1,20 @@ +error: used `expect()` on `an Option` value + --> $DIR/expect.rs:5:13 + | +LL | let _ = opt.expect(""); + | ^^^^^^^^^^^^^^ + | + = note: `-D clippy::option-expect-used` implied by `-D warnings` + = help: if this value is an `None`, it will panic + +error: used `expect()` on `a Result` value + --> $DIR/expect.rs:10:13 + | +LL | let _ = res.expect(""); + | ^^^^^^^^^^^^^^ + | + = note: `-D clippy::result-expect-used` implied by `-D warnings` + = help: if this value is an `Err`, it will panic + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/expect_fun_call.fixed b/src/tools/clippy/tests/ui/expect_fun_call.fixed new file mode 100644 index 00000000000..f3d8a941a92 --- /dev/null +++ b/src/tools/clippy/tests/ui/expect_fun_call.fixed @@ -0,0 +1,94 @@ +// run-rustfix + +#![warn(clippy::expect_fun_call)] + +/// Checks implementation of the `EXPECT_FUN_CALL` lint + +fn main() { + struct Foo; + + impl Foo { + fn new() -> Self { + Foo + } + + fn expect(&self, msg: &str) { + panic!("{}", msg) + } + } + + let with_some = Some("value"); + with_some.expect("error"); + + let with_none: Option<i32> = None; + with_none.expect("error"); + + let error_code = 123_i32; + let with_none_and_format: Option<i32> = None; + with_none_and_format.unwrap_or_else(|| panic!("Error {}: fake error", error_code)); + + let with_none_and_as_str: Option<i32> = None; + with_none_and_as_str.unwrap_or_else(|| panic!("Error {}: fake error", error_code)); + + let with_ok: Result<(), ()> = Ok(()); + with_ok.expect("error"); + + let with_err: Result<(), ()> = Err(()); + with_err.expect("error"); + + let error_code = 123_i32; + let with_err_and_format: Result<(), ()> = Err(()); + with_err_and_format.unwrap_or_else(|_| panic!("Error {}: fake error", error_code)); + + let with_err_and_as_str: Result<(), ()> = Err(()); + with_err_and_as_str.unwrap_or_else(|_| panic!("Error {}: fake error", error_code)); + + let with_dummy_type = Foo::new(); + with_dummy_type.expect("another test string"); + + let with_dummy_type_and_format = Foo::new(); + with_dummy_type_and_format.expect(&format!("Error {}: fake error", error_code)); + + let with_dummy_type_and_as_str = Foo::new(); + with_dummy_type_and_as_str.expect(format!("Error {}: fake error", error_code).as_str()); + + //Issue #2937 + Some("foo").unwrap_or_else(|| panic!("{} {}", 1, 2)); + + //Issue #2979 - this should not lint + { + let msg = "bar"; + Some("foo").expect(msg); + } + + { + fn get_string() -> String { + "foo".to_string() + } + + fn get_static_str() -> &'static str { + "foo" + } + + fn get_non_static_str(_: &u32) -> &str { + "foo" + } + + Some("foo").unwrap_or_else(|| { panic!(get_string()) }); + Some("foo").unwrap_or_else(|| { panic!(get_string()) }); + Some("foo").unwrap_or_else(|| { panic!(get_string()) }); + + Some("foo").unwrap_or_else(|| { panic!(get_static_str()) }); + Some("foo").unwrap_or_else(|| { panic!(get_non_static_str(&0).to_string()) }); + } + + //Issue #3839 + Some(true).unwrap_or_else(|| panic!("key {}, {}", 1, 2)); + + //Issue #4912 - the receiver is a &Option + { + let opt = Some(1); + let opt_ref = &opt; + opt_ref.unwrap_or_else(|| panic!("{:?}", opt_ref)); + } +} diff --git a/src/tools/clippy/tests/ui/expect_fun_call.rs b/src/tools/clippy/tests/ui/expect_fun_call.rs new file mode 100644 index 00000000000..60bbaa89d42 --- /dev/null +++ b/src/tools/clippy/tests/ui/expect_fun_call.rs @@ -0,0 +1,94 @@ +// run-rustfix + +#![warn(clippy::expect_fun_call)] + +/// Checks implementation of the `EXPECT_FUN_CALL` lint + +fn main() { + struct Foo; + + impl Foo { + fn new() -> Self { + Foo + } + + fn expect(&self, msg: &str) { + panic!("{}", msg) + } + } + + let with_some = Some("value"); + with_some.expect("error"); + + let with_none: Option<i32> = None; + with_none.expect("error"); + + let error_code = 123_i32; + let with_none_and_format: Option<i32> = None; + with_none_and_format.expect(&format!("Error {}: fake error", error_code)); + + let with_none_and_as_str: Option<i32> = None; + with_none_and_as_str.expect(format!("Error {}: fake error", error_code).as_str()); + + let with_ok: Result<(), ()> = Ok(()); + with_ok.expect("error"); + + let with_err: Result<(), ()> = Err(()); + with_err.expect("error"); + + let error_code = 123_i32; + let with_err_and_format: Result<(), ()> = Err(()); + with_err_and_format.expect(&format!("Error {}: fake error", error_code)); + + let with_err_and_as_str: Result<(), ()> = Err(()); + with_err_and_as_str.expect(format!("Error {}: fake error", error_code).as_str()); + + let with_dummy_type = Foo::new(); + with_dummy_type.expect("another test string"); + + let with_dummy_type_and_format = Foo::new(); + with_dummy_type_and_format.expect(&format!("Error {}: fake error", error_code)); + + let with_dummy_type_and_as_str = Foo::new(); + with_dummy_type_and_as_str.expect(format!("Error {}: fake error", error_code).as_str()); + + //Issue #2937 + Some("foo").expect(format!("{} {}", 1, 2).as_ref()); + + //Issue #2979 - this should not lint + { + let msg = "bar"; + Some("foo").expect(msg); + } + + { + fn get_string() -> String { + "foo".to_string() + } + + fn get_static_str() -> &'static str { + "foo" + } + + fn get_non_static_str(_: &u32) -> &str { + "foo" + } + + Some("foo").expect(&get_string()); + Some("foo").expect(get_string().as_ref()); + Some("foo").expect(get_string().as_str()); + + Some("foo").expect(get_static_str()); + Some("foo").expect(get_non_static_str(&0)); + } + + //Issue #3839 + Some(true).expect(&format!("key {}, {}", 1, 2)); + + //Issue #4912 - the receiver is a &Option + { + let opt = Some(1); + let opt_ref = &opt; + opt_ref.expect(&format!("{:?}", opt_ref)); + } +} diff --git a/src/tools/clippy/tests/ui/expect_fun_call.stderr b/src/tools/clippy/tests/ui/expect_fun_call.stderr new file mode 100644 index 00000000000..a492e2df89d --- /dev/null +++ b/src/tools/clippy/tests/ui/expect_fun_call.stderr @@ -0,0 +1,76 @@ +error: use of `expect` followed by a function call + --> $DIR/expect_fun_call.rs:28:26 + | +LL | with_none_and_format.expect(&format!("Error {}: fake error", error_code)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("Error {}: fake error", error_code))` + | + = note: `-D clippy::expect-fun-call` implied by `-D warnings` + +error: use of `expect` followed by a function call + --> $DIR/expect_fun_call.rs:31:26 + | +LL | with_none_and_as_str.expect(format!("Error {}: fake error", error_code).as_str()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("Error {}: fake error", error_code))` + +error: use of `expect` followed by a function call + --> $DIR/expect_fun_call.rs:41:25 + | +LL | with_err_and_format.expect(&format!("Error {}: fake error", error_code)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|_| panic!("Error {}: fake error", error_code))` + +error: use of `expect` followed by a function call + --> $DIR/expect_fun_call.rs:44:25 + | +LL | with_err_and_as_str.expect(format!("Error {}: fake error", error_code).as_str()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|_| panic!("Error {}: fake error", error_code))` + +error: use of `expect` followed by a function call + --> $DIR/expect_fun_call.rs:56:17 + | +LL | Some("foo").expect(format!("{} {}", 1, 2).as_ref()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("{} {}", 1, 2))` + +error: use of `expect` followed by a function call + --> $DIR/expect_fun_call.rs:77:21 + | +LL | Some("foo").expect(&get_string()); + | ^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!(get_string()) })` + +error: use of `expect` followed by a function call + --> $DIR/expect_fun_call.rs:78:21 + | +LL | Some("foo").expect(get_string().as_ref()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!(get_string()) })` + +error: use of `expect` followed by a function call + --> $DIR/expect_fun_call.rs:79:21 + | +LL | Some("foo").expect(get_string().as_str()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!(get_string()) })` + +error: use of `expect` followed by a function call + --> $DIR/expect_fun_call.rs:81:21 + | +LL | Some("foo").expect(get_static_str()); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!(get_static_str()) })` + +error: use of `expect` followed by a function call + --> $DIR/expect_fun_call.rs:82:21 + | +LL | Some("foo").expect(get_non_static_str(&0)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!(get_non_static_str(&0).to_string()) })` + +error: use of `expect` followed by a function call + --> $DIR/expect_fun_call.rs:86:16 + | +LL | Some(true).expect(&format!("key {}, {}", 1, 2)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("key {}, {}", 1, 2))` + +error: use of `expect` followed by a function call + --> $DIR/expect_fun_call.rs:92:17 + | +LL | opt_ref.expect(&format!("{:?}", opt_ref)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("{:?}", opt_ref))` + +error: aborting due to 12 previous errors + diff --git a/src/tools/clippy/tests/ui/explicit_counter_loop.rs b/src/tools/clippy/tests/ui/explicit_counter_loop.rs new file mode 100644 index 00000000000..aa6ef162fe4 --- /dev/null +++ b/src/tools/clippy/tests/ui/explicit_counter_loop.rs @@ -0,0 +1,147 @@ +#![warn(clippy::explicit_counter_loop)] + +fn main() { + let mut vec = vec![1, 2, 3, 4]; + let mut _index = 0; + for _v in &vec { + _index += 1 + } + + let mut _index = 1; + _index = 0; + for _v in &vec { + _index += 1 + } + + let mut _index = 0; + for _v in &mut vec { + _index += 1; + } + + let mut _index = 0; + for _v in vec { + _index += 1; + } +} + +mod issue_1219 { + pub fn test() { + // should not trigger the lint because variable is used after the loop #473 + let vec = vec![1, 2, 3]; + let mut index = 0; + for _v in &vec { + index += 1 + } + println!("index: {}", index); + + // should not trigger the lint because the count is conditional #1219 + let text = "banana"; + let mut count = 0; + for ch in text.chars() { + if ch == 'a' { + continue; + } + count += 1; + println!("{}", count); + } + + // should not trigger the lint because the count is conditional + let text = "banana"; + let mut count = 0; + for ch in text.chars() { + if ch == 'a' { + count += 1; + } + println!("{}", count); + } + + // should trigger the lint because the count is not conditional + let text = "banana"; + let mut count = 0; + for ch in text.chars() { + count += 1; + if ch == 'a' { + continue; + } + println!("{}", count); + } + + // should trigger the lint because the count is not conditional + let text = "banana"; + let mut count = 0; + for ch in text.chars() { + count += 1; + for i in 0..2 { + let _ = 123; + } + println!("{}", count); + } + + // should not trigger the lint because the count is incremented multiple times + let text = "banana"; + let mut count = 0; + for ch in text.chars() { + count += 1; + for i in 0..2 { + count += 1; + } + println!("{}", count); + } + } +} + +mod issue_3308 { + pub fn test() { + // should not trigger the lint because the count is incremented multiple times + let mut skips = 0; + let erasures = vec![]; + for i in 0..10 { + while erasures.contains(&(i + skips)) { + skips += 1; + } + println!("{}", skips); + } + + // should not trigger the lint because the count is incremented multiple times + let mut skips = 0; + for i in 0..10 { + let mut j = 0; + while j < 5 { + skips += 1; + j += 1; + } + println!("{}", skips); + } + + // should not trigger the lint because the count is incremented multiple times + let mut skips = 0; + for i in 0..10 { + for j in 0..5 { + skips += 1; + } + println!("{}", skips); + } + } +} + +mod issue_1670 { + pub fn test() { + let mut count = 0; + for _i in 3..10 { + count += 1; + } + } +} + +mod issue_4732 { + pub fn test() { + let slice = &[1, 2, 3]; + let mut index = 0; + + // should not trigger the lint because the count is used after the loop + for _v in slice { + index += 1 + } + let _closure = || println!("index: {}", index); + } +} diff --git a/src/tools/clippy/tests/ui/explicit_counter_loop.stderr b/src/tools/clippy/tests/ui/explicit_counter_loop.stderr new file mode 100644 index 00000000000..931af46efe6 --- /dev/null +++ b/src/tools/clippy/tests/ui/explicit_counter_loop.stderr @@ -0,0 +1,46 @@ +error: the variable `_index` is used as a loop counter. + --> $DIR/explicit_counter_loop.rs:6:5 + | +LL | for _v in &vec { + | ^^^^^^^^^^^^^^ help: consider using: `for (_index, _v) in vec.iter().enumerate()` + | + = note: `-D clippy::explicit-counter-loop` implied by `-D warnings` + +error: the variable `_index` is used as a loop counter. + --> $DIR/explicit_counter_loop.rs:12:5 + | +LL | for _v in &vec { + | ^^^^^^^^^^^^^^ help: consider using: `for (_index, _v) in vec.iter().enumerate()` + +error: the variable `_index` is used as a loop counter. + --> $DIR/explicit_counter_loop.rs:17:5 + | +LL | for _v in &mut vec { + | ^^^^^^^^^^^^^^^^^^ help: consider using: `for (_index, _v) in vec.iter_mut().enumerate()` + +error: the variable `_index` is used as a loop counter. + --> $DIR/explicit_counter_loop.rs:22:5 + | +LL | for _v in vec { + | ^^^^^^^^^^^^^ help: consider using: `for (_index, _v) in vec.into_iter().enumerate()` + +error: the variable `count` is used as a loop counter. + --> $DIR/explicit_counter_loop.rs:61:9 + | +LL | for ch in text.chars() { + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `for (count, ch) in text.chars().enumerate()` + +error: the variable `count` is used as a loop counter. + --> $DIR/explicit_counter_loop.rs:72:9 + | +LL | for ch in text.chars() { + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `for (count, ch) in text.chars().enumerate()` + +error: the variable `count` is used as a loop counter. + --> $DIR/explicit_counter_loop.rs:130:9 + | +LL | for _i in 3..10 { + | ^^^^^^^^^^^^^^^ help: consider using: `for (count, _i) in (3..10).enumerate()` + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/tests/ui/explicit_write.fixed b/src/tools/clippy/tests/ui/explicit_write.fixed new file mode 100644 index 00000000000..692d2ca675f --- /dev/null +++ b/src/tools/clippy/tests/ui/explicit_write.fixed @@ -0,0 +1,51 @@ +// run-rustfix +#![allow(unused_imports)] +#![warn(clippy::explicit_write)] + +fn stdout() -> String { + String::new() +} + +fn stderr() -> String { + String::new() +} + +fn main() { + // these should warn + { + use std::io::Write; + print!("test"); + eprint!("test"); + println!("test"); + eprintln!("test"); + print!("test"); + eprint!("test"); + + // including newlines + println!("test\ntest"); + eprintln!("test\ntest"); + } + // these should not warn, different destination + { + use std::fmt::Write; + let mut s = String::new(); + write!(s, "test").unwrap(); + write!(s, "test").unwrap(); + writeln!(s, "test").unwrap(); + writeln!(s, "test").unwrap(); + s.write_fmt(format_args!("test")).unwrap(); + s.write_fmt(format_args!("test")).unwrap(); + write!(stdout(), "test").unwrap(); + write!(stderr(), "test").unwrap(); + writeln!(stdout(), "test").unwrap(); + writeln!(stderr(), "test").unwrap(); + stdout().write_fmt(format_args!("test")).unwrap(); + stderr().write_fmt(format_args!("test")).unwrap(); + } + // these should not warn, no unwrap + { + use std::io::Write; + std::io::stdout().write_fmt(format_args!("test")).expect("no stdout"); + std::io::stderr().write_fmt(format_args!("test")).expect("no stderr"); + } +} diff --git a/src/tools/clippy/tests/ui/explicit_write.rs b/src/tools/clippy/tests/ui/explicit_write.rs new file mode 100644 index 00000000000..455c5ef55d0 --- /dev/null +++ b/src/tools/clippy/tests/ui/explicit_write.rs @@ -0,0 +1,51 @@ +// run-rustfix +#![allow(unused_imports)] +#![warn(clippy::explicit_write)] + +fn stdout() -> String { + String::new() +} + +fn stderr() -> String { + String::new() +} + +fn main() { + // these should warn + { + use std::io::Write; + write!(std::io::stdout(), "test").unwrap(); + write!(std::io::stderr(), "test").unwrap(); + writeln!(std::io::stdout(), "test").unwrap(); + writeln!(std::io::stderr(), "test").unwrap(); + std::io::stdout().write_fmt(format_args!("test")).unwrap(); + std::io::stderr().write_fmt(format_args!("test")).unwrap(); + + // including newlines + writeln!(std::io::stdout(), "test\ntest").unwrap(); + writeln!(std::io::stderr(), "test\ntest").unwrap(); + } + // these should not warn, different destination + { + use std::fmt::Write; + let mut s = String::new(); + write!(s, "test").unwrap(); + write!(s, "test").unwrap(); + writeln!(s, "test").unwrap(); + writeln!(s, "test").unwrap(); + s.write_fmt(format_args!("test")).unwrap(); + s.write_fmt(format_args!("test")).unwrap(); + write!(stdout(), "test").unwrap(); + write!(stderr(), "test").unwrap(); + writeln!(stdout(), "test").unwrap(); + writeln!(stderr(), "test").unwrap(); + stdout().write_fmt(format_args!("test")).unwrap(); + stderr().write_fmt(format_args!("test")).unwrap(); + } + // these should not warn, no unwrap + { + use std::io::Write; + std::io::stdout().write_fmt(format_args!("test")).expect("no stdout"); + std::io::stderr().write_fmt(format_args!("test")).expect("no stderr"); + } +} diff --git a/src/tools/clippy/tests/ui/explicit_write.stderr b/src/tools/clippy/tests/ui/explicit_write.stderr new file mode 100644 index 00000000000..9feef9c0dc8 --- /dev/null +++ b/src/tools/clippy/tests/ui/explicit_write.stderr @@ -0,0 +1,52 @@ +error: use of `write!(stdout(), ...).unwrap()` + --> $DIR/explicit_write.rs:17:9 + | +LL | write!(std::io::stdout(), "test").unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `print!("test")` + | + = note: `-D clippy::explicit-write` implied by `-D warnings` + +error: use of `write!(stderr(), ...).unwrap()` + --> $DIR/explicit_write.rs:18:9 + | +LL | write!(std::io::stderr(), "test").unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprint!("test")` + +error: use of `writeln!(stdout(), ...).unwrap()` + --> $DIR/explicit_write.rs:19:9 + | +LL | writeln!(std::io::stdout(), "test").unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `println!("test")` + +error: use of `writeln!(stderr(), ...).unwrap()` + --> $DIR/explicit_write.rs:20:9 + | +LL | writeln!(std::io::stderr(), "test").unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprintln!("test")` + +error: use of `stdout().write_fmt(...).unwrap()` + --> $DIR/explicit_write.rs:21:9 + | +LL | std::io::stdout().write_fmt(format_args!("test")).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `print!("test")` + +error: use of `stderr().write_fmt(...).unwrap()` + --> $DIR/explicit_write.rs:22:9 + | +LL | std::io::stderr().write_fmt(format_args!("test")).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprint!("test")` + +error: use of `writeln!(stdout(), ...).unwrap()` + --> $DIR/explicit_write.rs:25:9 + | +LL | writeln!(std::io::stdout(), "test/ntest").unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `println!("test/ntest")` + +error: use of `writeln!(stderr(), ...).unwrap()` + --> $DIR/explicit_write.rs:26:9 + | +LL | writeln!(std::io::stderr(), "test/ntest").unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprintln!("test/ntest")` + +error: aborting due to 8 previous errors + diff --git a/src/tools/clippy/tests/ui/explicit_write_non_rustfix.rs b/src/tools/clippy/tests/ui/explicit_write_non_rustfix.rs new file mode 100644 index 00000000000..f21e8ef935b --- /dev/null +++ b/src/tools/clippy/tests/ui/explicit_write_non_rustfix.rs @@ -0,0 +1,8 @@ +#![allow(unused_imports, clippy::blacklisted_name)] +#![warn(clippy::explicit_write)] + +fn main() { + use std::io::Write; + let bar = "bar"; + writeln!(std::io::stderr(), "foo {}", bar).unwrap(); +} diff --git a/src/tools/clippy/tests/ui/explicit_write_non_rustfix.stderr b/src/tools/clippy/tests/ui/explicit_write_non_rustfix.stderr new file mode 100644 index 00000000000..77cadb99bb5 --- /dev/null +++ b/src/tools/clippy/tests/ui/explicit_write_non_rustfix.stderr @@ -0,0 +1,10 @@ +error: use of `writeln!(stderr(), ...).unwrap()`. Consider using `eprintln!` instead + --> $DIR/explicit_write_non_rustfix.rs:7:5 + | +LL | writeln!(std::io::stderr(), "foo {}", bar).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::explicit-write` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/extra_unused_lifetimes.rs b/src/tools/clippy/tests/ui/extra_unused_lifetimes.rs new file mode 100644 index 00000000000..ddbf4e98c51 --- /dev/null +++ b/src/tools/clippy/tests/ui/extra_unused_lifetimes.rs @@ -0,0 +1,69 @@ +#![allow(unused, dead_code, clippy::needless_lifetimes, clippy::needless_pass_by_value)] +#![warn(clippy::extra_unused_lifetimes)] + +fn empty() {} + +fn used_lt<'a>(x: &'a u8) {} + +fn unused_lt<'a>(x: u8) {} + +fn unused_lt_transitive<'a, 'b: 'a>(x: &'b u8) { + // 'a is useless here since it's not directly bound +} + +fn lt_return<'a, 'b: 'a>(x: &'b u8) -> &'a u8 { + panic!() +} + +fn lt_return_only<'a>() -> &'a u8 { + panic!() +} + +fn unused_lt_blergh<'a>(x: Option<Box<dyn Send + 'a>>) {} + +trait Foo<'a> { + fn x(&self, a: &'a u8); +} + +impl<'a> Foo<'a> for u8 { + fn x(&self, a: &'a u8) {} +} + +struct Bar; + +impl Bar { + fn x<'a>(&self) {} +} + +// test for #489 (used lifetimes in bounds) +pub fn parse<'a, I: Iterator<Item = &'a str>>(_it: &mut I) { + unimplemented!() +} +pub fn parse2<'a, I>(_it: &mut I) +where + I: Iterator<Item = &'a str>, +{ + unimplemented!() +} + +struct X { + x: u32, +} + +impl X { + fn self_ref_with_lifetime<'a>(&'a self) {} + fn explicit_self_with_lifetime<'a>(self: &'a Self) {} +} + +// Methods implementing traits must have matching lifetimes +mod issue4291 { + trait BadTrait { + fn unused_lt<'a>(x: u8) {} + } + + impl BadTrait for () { + fn unused_lt<'a>(_x: u8) {} + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/extra_unused_lifetimes.stderr b/src/tools/clippy/tests/ui/extra_unused_lifetimes.stderr new file mode 100644 index 00000000000..16bbb1c037d --- /dev/null +++ b/src/tools/clippy/tests/ui/extra_unused_lifetimes.stderr @@ -0,0 +1,28 @@ +error: this lifetime isn't used in the function definition + --> $DIR/extra_unused_lifetimes.rs:8:14 + | +LL | fn unused_lt<'a>(x: u8) {} + | ^^ + | + = note: `-D clippy::extra-unused-lifetimes` implied by `-D warnings` + +error: this lifetime isn't used in the function definition + --> $DIR/extra_unused_lifetimes.rs:10:25 + | +LL | fn unused_lt_transitive<'a, 'b: 'a>(x: &'b u8) { + | ^^ + +error: this lifetime isn't used in the function definition + --> $DIR/extra_unused_lifetimes.rs:35:10 + | +LL | fn x<'a>(&self) {} + | ^^ + +error: this lifetime isn't used in the function definition + --> $DIR/extra_unused_lifetimes.rs:61:22 + | +LL | fn unused_lt<'a>(x: u8) {} + | ^^ + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/fallible_impl_from.rs b/src/tools/clippy/tests/ui/fallible_impl_from.rs new file mode 100644 index 00000000000..679f4a7dc35 --- /dev/null +++ b/src/tools/clippy/tests/ui/fallible_impl_from.rs @@ -0,0 +1,76 @@ +#![deny(clippy::fallible_impl_from)] + +// docs example +struct Foo(i32); +impl From<String> for Foo { + fn from(s: String) -> Self { + Foo(s.parse().unwrap()) + } +} + +struct Valid(Vec<u8>); + +impl<'a> From<&'a str> for Valid { + fn from(s: &'a str) -> Valid { + Valid(s.to_owned().into_bytes()) + } +} +impl From<usize> for Valid { + fn from(i: usize) -> Valid { + Valid(Vec::with_capacity(i)) + } +} + +struct Invalid; + +impl From<usize> for Invalid { + fn from(i: usize) -> Invalid { + if i != 42 { + panic!(); + } + Invalid + } +} + +impl From<Option<String>> for Invalid { + fn from(s: Option<String>) -> Invalid { + let s = s.unwrap(); + if !s.is_empty() { + panic!(42); + } else if s.parse::<u32>().unwrap() != 42 { + panic!("{:?}", s); + } + Invalid + } +} + +trait ProjStrTrait { + type ProjString; +} +impl<T> ProjStrTrait for Box<T> { + type ProjString = String; +} +impl<'a> From<&'a mut <Box<u32> as ProjStrTrait>::ProjString> for Invalid { + fn from(s: &'a mut <Box<u32> as ProjStrTrait>::ProjString) -> Invalid { + if s.parse::<u32>().ok().unwrap() != 42 { + panic!("{:?}", s); + } + Invalid + } +} + +struct Unreachable; + +impl From<String> for Unreachable { + fn from(s: String) -> Unreachable { + if s.is_empty() { + return Unreachable; + } + match s.chars().next() { + Some(_) => Unreachable, + None => unreachable!(), // do not lint the unreachable macro + } + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/fallible_impl_from.stderr b/src/tools/clippy/tests/ui/fallible_impl_from.stderr new file mode 100644 index 00000000000..ab976b947b3 --- /dev/null +++ b/src/tools/clippy/tests/ui/fallible_impl_from.stderr @@ -0,0 +1,93 @@ +error: consider implementing `TryFrom` instead + --> $DIR/fallible_impl_from.rs:5:1 + | +LL | / impl From<String> for Foo { +LL | | fn from(s: String) -> Self { +LL | | Foo(s.parse().unwrap()) +LL | | } +LL | | } + | |_^ + | +note: the lint level is defined here + --> $DIR/fallible_impl_from.rs:1:9 + | +LL | #![deny(clippy::fallible_impl_from)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail. +note: potential failure(s) + --> $DIR/fallible_impl_from.rs:7:13 + | +LL | Foo(s.parse().unwrap()) + | ^^^^^^^^^^^^^^^^^^ + +error: consider implementing `TryFrom` instead + --> $DIR/fallible_impl_from.rs:26:1 + | +LL | / impl From<usize> for Invalid { +LL | | fn from(i: usize) -> Invalid { +LL | | if i != 42 { +LL | | panic!(); +... | +LL | | } +LL | | } + | |_^ + | + = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail. +note: potential failure(s) + --> $DIR/fallible_impl_from.rs:29:13 + | +LL | panic!(); + | ^^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: consider implementing `TryFrom` instead + --> $DIR/fallible_impl_from.rs:35:1 + | +LL | / impl From<Option<String>> for Invalid { +LL | | fn from(s: Option<String>) -> Invalid { +LL | | let s = s.unwrap(); +LL | | if !s.is_empty() { +... | +LL | | } +LL | | } + | |_^ + | + = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail. +note: potential failure(s) + --> $DIR/fallible_impl_from.rs:37:17 + | +LL | let s = s.unwrap(); + | ^^^^^^^^^^ +LL | if !s.is_empty() { +LL | panic!(42); + | ^^^^^^^^^^^ +LL | } else if s.parse::<u32>().unwrap() != 42 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | panic!("{:?}", s); + | ^^^^^^^^^^^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: consider implementing `TryFrom` instead + --> $DIR/fallible_impl_from.rs:53:1 + | +LL | / impl<'a> From<&'a mut <Box<u32> as ProjStrTrait>::ProjString> for Invalid { +LL | | fn from(s: &'a mut <Box<u32> as ProjStrTrait>::ProjString) -> Invalid { +LL | | if s.parse::<u32>().ok().unwrap() != 42 { +LL | | panic!("{:?}", s); +... | +LL | | } +LL | | } + | |_^ + | + = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail. +note: potential failure(s) + --> $DIR/fallible_impl_from.rs:55:12 + | +LL | if s.parse::<u32>().ok().unwrap() != 42 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | panic!("{:?}", s); + | ^^^^^^^^^^^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/filetype_is_file.rs b/src/tools/clippy/tests/ui/filetype_is_file.rs new file mode 100644 index 00000000000..5de8fe8cdd7 --- /dev/null +++ b/src/tools/clippy/tests/ui/filetype_is_file.rs @@ -0,0 +1,23 @@ +#![warn(clippy::filetype_is_file)] + +fn main() -> std::io::Result<()> { + use std::fs; + use std::ops::BitOr; + + // !filetype.is_dir() + if fs::metadata("foo.txt")?.file_type().is_file() { + // read file + } + + // positive of filetype.is_dir() + if !fs::metadata("foo.txt")?.file_type().is_file() { + // handle dir + } + + // false positive of filetype.is_dir() + if !fs::metadata("foo.txt")?.file_type().is_file().bitor(true) { + // ... + } + + Ok(()) +} diff --git a/src/tools/clippy/tests/ui/filetype_is_file.stderr b/src/tools/clippy/tests/ui/filetype_is_file.stderr new file mode 100644 index 00000000000..cd1e3ac37fe --- /dev/null +++ b/src/tools/clippy/tests/ui/filetype_is_file.stderr @@ -0,0 +1,27 @@ +error: `FileType::is_file()` only covers regular files + --> $DIR/filetype_is_file.rs:8:8 + | +LL | if fs::metadata("foo.txt")?.file_type().is_file() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::filetype-is-file` implied by `-D warnings` + = help: use `!FileType::is_dir()` instead + +error: `!FileType::is_file()` only denies regular files + --> $DIR/filetype_is_file.rs:13:8 + | +LL | if !fs::metadata("foo.txt")?.file_type().is_file() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `FileType::is_dir()` instead + +error: `FileType::is_file()` only covers regular files + --> $DIR/filetype_is_file.rs:18:9 + | +LL | if !fs::metadata("foo.txt")?.file_type().is_file().bitor(true) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `!FileType::is_dir()` instead + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/filter_map_next.rs b/src/tools/clippy/tests/ui/filter_map_next.rs new file mode 100644 index 00000000000..f5d051be198 --- /dev/null +++ b/src/tools/clippy/tests/ui/filter_map_next.rs @@ -0,0 +1,20 @@ +#![warn(clippy::all, clippy::pedantic)] + +fn main() { + let a = ["1", "lol", "3", "NaN", "5"]; + + let element: Option<i32> = a.iter().filter_map(|s| s.parse().ok()).next(); + assert_eq!(element, Some(1)); + + #[rustfmt::skip] + let _: Option<u32> = vec![1, 2, 3, 4, 5, 6] + .into_iter() + .filter_map(|x| { + if x == 2 { + Some(x * 2) + } else { + None + } + }) + .next(); +} diff --git a/src/tools/clippy/tests/ui/filter_map_next.stderr b/src/tools/clippy/tests/ui/filter_map_next.stderr new file mode 100644 index 00000000000..d69ae212414 --- /dev/null +++ b/src/tools/clippy/tests/ui/filter_map_next.stderr @@ -0,0 +1,24 @@ +error: called `filter_map(p).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(p)` instead. + --> $DIR/filter_map_next.rs:6:32 + | +LL | let element: Option<i32> = a.iter().filter_map(|s| s.parse().ok()).next(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::filter-map-next` implied by `-D warnings` + = note: replace `filter_map(|s| s.parse().ok()).next()` with `find_map(|s| s.parse().ok())` + +error: called `filter_map(p).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(p)` instead. + --> $DIR/filter_map_next.rs:10:26 + | +LL | let _: Option<u32> = vec![1, 2, 3, 4, 5, 6] + | __________________________^ +LL | | .into_iter() +LL | | .filter_map(|x| { +LL | | if x == 2 { +... | +LL | | }) +LL | | .next(); + | |_______________^ + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/filter_methods.rs b/src/tools/clippy/tests/ui/filter_methods.rs new file mode 100644 index 00000000000..ef434245fd7 --- /dev/null +++ b/src/tools/clippy/tests/ui/filter_methods.rs @@ -0,0 +1,24 @@ +#![warn(clippy::all, clippy::pedantic)] +#![allow(clippy::missing_docs_in_private_items)] + +fn main() { + let _: Vec<_> = vec![5; 6].into_iter().filter(|&x| x == 0).map(|x| x * 2).collect(); + + let _: Vec<_> = vec![5_i8; 6] + .into_iter() + .filter(|&x| x == 0) + .flat_map(|x| x.checked_mul(2)) + .collect(); + + let _: Vec<_> = vec![5_i8; 6] + .into_iter() + .filter_map(|x| x.checked_mul(2)) + .flat_map(|x| x.checked_mul(2)) + .collect(); + + let _: Vec<_> = vec![5_i8; 6] + .into_iter() + .filter_map(|x| x.checked_mul(2)) + .map(|x| x.checked_mul(2)) + .collect(); +} diff --git a/src/tools/clippy/tests/ui/filter_methods.stderr b/src/tools/clippy/tests/ui/filter_methods.stderr new file mode 100644 index 00000000000..84a957a374c --- /dev/null +++ b/src/tools/clippy/tests/ui/filter_methods.stderr @@ -0,0 +1,47 @@ +error: called `filter(p).map(q)` on an `Iterator` + --> $DIR/filter_methods.rs:5:21 + | +LL | let _: Vec<_> = vec![5; 6].into_iter().filter(|&x| x == 0).map(|x| x * 2).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::filter-map` implied by `-D warnings` + = help: this is more succinctly expressed by calling `.filter_map(..)` instead + +error: called `filter(p).flat_map(q)` on an `Iterator` + --> $DIR/filter_methods.rs:7:21 + | +LL | let _: Vec<_> = vec![5_i8; 6] + | _____________________^ +LL | | .into_iter() +LL | | .filter(|&x| x == 0) +LL | | .flat_map(|x| x.checked_mul(2)) + | |_______________________________________^ + | + = help: this is more succinctly expressed by calling `.flat_map(..)` and filtering by returning `iter::empty()` + +error: called `filter_map(p).flat_map(q)` on an `Iterator` + --> $DIR/filter_methods.rs:13:21 + | +LL | let _: Vec<_> = vec![5_i8; 6] + | _____________________^ +LL | | .into_iter() +LL | | .filter_map(|x| x.checked_mul(2)) +LL | | .flat_map(|x| x.checked_mul(2)) + | |_______________________________________^ + | + = help: this is more succinctly expressed by calling `.flat_map(..)` and filtering by returning `iter::empty()` + +error: called `filter_map(p).map(q)` on an `Iterator` + --> $DIR/filter_methods.rs:19:21 + | +LL | let _: Vec<_> = vec![5_i8; 6] + | _____________________^ +LL | | .into_iter() +LL | | .filter_map(|x| x.checked_mul(2)) +LL | | .map(|x| x.checked_mul(2)) + | |__________________________________^ + | + = help: this is more succinctly expressed by only calling `.filter_map(..)` instead + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/find_map.rs b/src/tools/clippy/tests/ui/find_map.rs new file mode 100644 index 00000000000..c28cca144ca --- /dev/null +++ b/src/tools/clippy/tests/ui/find_map.rs @@ -0,0 +1,32 @@ +#![warn(clippy::all, clippy::pedantic)] + +#[derive(Debug, Copy, Clone)] +enum Flavor { + Chocolate, +} + +#[derive(Debug, Copy, Clone)] +enum Dessert { + Banana, + Pudding, + Cake(Flavor), +} + +fn main() { + let desserts_of_the_week = vec![Dessert::Banana, Dessert::Cake(Flavor::Chocolate), Dessert::Pudding]; + + let a = ["lol", "NaN", "2", "5", "Xunda"]; + + let _: Option<i32> = a.iter().find(|s| s.parse::<i32>().is_ok()).map(|s| s.parse().unwrap()); + + let _: Option<Flavor> = desserts_of_the_week + .iter() + .find(|dessert| match *dessert { + Dessert::Cake(_) => true, + _ => false, + }) + .map(|dessert| match *dessert { + Dessert::Cake(ref flavor) => *flavor, + _ => unreachable!(), + }); +} diff --git a/src/tools/clippy/tests/ui/find_map.stderr b/src/tools/clippy/tests/ui/find_map.stderr new file mode 100644 index 00000000000..92f40fe6f1f --- /dev/null +++ b/src/tools/clippy/tests/ui/find_map.stderr @@ -0,0 +1,26 @@ +error: called `find(p).map(q)` on an `Iterator` + --> $DIR/find_map.rs:20:26 + | +LL | let _: Option<i32> = a.iter().find(|s| s.parse::<i32>().is_ok()).map(|s| s.parse().unwrap()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::find-map` implied by `-D warnings` + = help: this is more succinctly expressed by calling `.find_map(..)` instead + +error: called `find(p).map(q)` on an `Iterator` + --> $DIR/find_map.rs:22:29 + | +LL | let _: Option<Flavor> = desserts_of_the_week + | _____________________________^ +LL | | .iter() +LL | | .find(|dessert| match *dessert { +LL | | Dessert::Cake(_) => true, +... | +LL | | _ => unreachable!(), +LL | | }); + | |__________^ + | + = help: this is more succinctly expressed by calling `.find_map(..)` instead + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/float_arithmetic.rs b/src/tools/clippy/tests/ui/float_arithmetic.rs new file mode 100644 index 00000000000..60fa7569eb9 --- /dev/null +++ b/src/tools/clippy/tests/ui/float_arithmetic.rs @@ -0,0 +1,52 @@ +#![warn(clippy::integer_arithmetic, clippy::float_arithmetic)] +#![allow( + unused, + clippy::shadow_reuse, + clippy::shadow_unrelated, + clippy::no_effect, + clippy::unnecessary_operation, + clippy::op_ref +)] + +#[rustfmt::skip] +fn main() { + let mut f = 1.0f32; + + f * 2.0; + + 1.0 + f; + f * 2.0; + f / 2.0; + f - 2.0 * 4.2; + -f; + + f += 1.0; + f -= 1.0; + f *= 2.0; + f /= 2.0; +} + +// also warn about floating point arith with references involved + +pub fn float_arith_ref() { + 3.1_f32 + &1.2_f32; + &3.4_f32 + 1.5_f32; + &3.5_f32 + &1.3_f32; +} + +pub fn float_foo(f: &f32) -> f32 { + let a = 5.1; + a + f +} + +pub fn float_bar(f1: &f32, f2: &f32) -> f32 { + f1 + f2 +} + +pub fn float_baz(f1: f32, f2: &f32) -> f32 { + f1 + f2 +} + +pub fn float_qux(f1: f32, f2: f32) -> f32 { + (&f1 + &f2) +} diff --git a/src/tools/clippy/tests/ui/float_arithmetic.stderr b/src/tools/clippy/tests/ui/float_arithmetic.stderr new file mode 100644 index 00000000000..1ceffb35bee --- /dev/null +++ b/src/tools/clippy/tests/ui/float_arithmetic.stderr @@ -0,0 +1,106 @@ +error: floating-point arithmetic detected + --> $DIR/float_arithmetic.rs:15:5 + | +LL | f * 2.0; + | ^^^^^^^ + | + = note: `-D clippy::float-arithmetic` implied by `-D warnings` + +error: floating-point arithmetic detected + --> $DIR/float_arithmetic.rs:17:5 + | +LL | 1.0 + f; + | ^^^^^^^ + +error: floating-point arithmetic detected + --> $DIR/float_arithmetic.rs:18:5 + | +LL | f * 2.0; + | ^^^^^^^ + +error: floating-point arithmetic detected + --> $DIR/float_arithmetic.rs:19:5 + | +LL | f / 2.0; + | ^^^^^^^ + +error: floating-point arithmetic detected + --> $DIR/float_arithmetic.rs:20:5 + | +LL | f - 2.0 * 4.2; + | ^^^^^^^^^^^^^ + +error: floating-point arithmetic detected + --> $DIR/float_arithmetic.rs:21:5 + | +LL | -f; + | ^^ + +error: floating-point arithmetic detected + --> $DIR/float_arithmetic.rs:23:5 + | +LL | f += 1.0; + | ^^^^^^^^ + +error: floating-point arithmetic detected + --> $DIR/float_arithmetic.rs:24:5 + | +LL | f -= 1.0; + | ^^^^^^^^ + +error: floating-point arithmetic detected + --> $DIR/float_arithmetic.rs:25:5 + | +LL | f *= 2.0; + | ^^^^^^^^ + +error: floating-point arithmetic detected + --> $DIR/float_arithmetic.rs:26:5 + | +LL | f /= 2.0; + | ^^^^^^^^ + +error: floating-point arithmetic detected + --> $DIR/float_arithmetic.rs:32:5 + | +LL | 3.1_f32 + &1.2_f32; + | ^^^^^^^^^^^^^^^^^^ + +error: floating-point arithmetic detected + --> $DIR/float_arithmetic.rs:33:5 + | +LL | &3.4_f32 + 1.5_f32; + | ^^^^^^^^^^^^^^^^^^ + +error: floating-point arithmetic detected + --> $DIR/float_arithmetic.rs:34:5 + | +LL | &3.5_f32 + &1.3_f32; + | ^^^^^^^^^^^^^^^^^^^ + +error: floating-point arithmetic detected + --> $DIR/float_arithmetic.rs:39:5 + | +LL | a + f + | ^^^^^ + +error: floating-point arithmetic detected + --> $DIR/float_arithmetic.rs:43:5 + | +LL | f1 + f2 + | ^^^^^^^ + +error: floating-point arithmetic detected + --> $DIR/float_arithmetic.rs:47:5 + | +LL | f1 + f2 + | ^^^^^^^ + +error: floating-point arithmetic detected + --> $DIR/float_arithmetic.rs:51:5 + | +LL | (&f1 + &f2) + | ^^^^^^^^^^^ + +error: aborting due to 17 previous errors + diff --git a/src/tools/clippy/tests/ui/float_cmp.rs b/src/tools/clippy/tests/ui/float_cmp.rs new file mode 100644 index 00000000000..9fa0e5f5c07 --- /dev/null +++ b/src/tools/clippy/tests/ui/float_cmp.rs @@ -0,0 +1,119 @@ +#![warn(clippy::float_cmp)] +#![allow( + unused, + clippy::no_effect, + clippy::unnecessary_operation, + clippy::cast_lossless, + clippy::many_single_char_names +)] + +use std::ops::Add; + +const ZERO: f32 = 0.0; +const ONE: f32 = ZERO + 1.0; + +fn twice<T>(x: T) -> T +where + T: Add<T, Output = T> + Copy, +{ + x + x +} + +fn eq_fl(x: f32, y: f32) -> bool { + if x.is_nan() { + y.is_nan() + } else { + x == y + } // no error, inside "eq" fn +} + +fn fl_eq(x: f32, y: f32) -> bool { + if x.is_nan() { + y.is_nan() + } else { + x == y + } // no error, inside "eq" fn +} + +struct X { + val: f32, +} + +impl PartialEq for X { + fn eq(&self, o: &X) -> bool { + if self.val.is_nan() { + o.val.is_nan() + } else { + self.val == o.val // no error, inside "eq" fn + } + } +} + +fn main() { + ZERO == 0f32; //no error, comparison with zero is ok + 1.0f32 != f32::INFINITY; // also comparison with infinity + 1.0f32 != f32::NEG_INFINITY; // and negative infinity + ZERO == 0.0; //no error, comparison with zero is ok + ZERO + ZERO != 1.0; //no error, comparison with zero is ok + + ONE == 1f32; + ONE == 1.0 + 0.0; + ONE + ONE == ZERO + ONE + ONE; + ONE != 2.0; + ONE != 0.0; // no error, comparison with zero is ok + twice(ONE) != ONE; + ONE as f64 != 2.0; + ONE as f64 != 0.0; // no error, comparison with zero is ok + + let x: f64 = 1.0; + + x == 1.0; + x != 0f64; // no error, comparison with zero is ok + + twice(x) != twice(ONE as f64); + + x < 0.0; // no errors, lower or greater comparisons need no fuzzyness + x > 0.0; + x <= 0.0; + x >= 0.0; + + let xs: [f32; 1] = [0.0]; + let a: *const f32 = xs.as_ptr(); + let b: *const f32 = xs.as_ptr(); + + assert_eq!(a, b); // no errors + + const ZERO_ARRAY: [f32; 2] = [0.0, 0.0]; + const NON_ZERO_ARRAY: [f32; 2] = [0.0, 0.1]; + + let i = 0; + let j = 1; + + ZERO_ARRAY[i] == NON_ZERO_ARRAY[j]; // ok, because lhs is zero regardless of i + NON_ZERO_ARRAY[i] == NON_ZERO_ARRAY[j]; + + let a1: [f32; 1] = [0.0]; + let a2: [f32; 1] = [1.1]; + + a1 == a2; + a1[0] == a2[0]; + + // no errors - comparing signums is ok + let x32 = 3.21f32; + 1.23f32.signum() == x32.signum(); + 1.23f32.signum() == -(x32.signum()); + 1.23f32.signum() == 3.21f32.signum(); + + 1.23f32.signum() != x32.signum(); + 1.23f32.signum() != -(x32.signum()); + 1.23f32.signum() != 3.21f32.signum(); + + let x64 = 3.21f64; + 1.23f64.signum() == x64.signum(); + 1.23f64.signum() == -(x64.signum()); + 1.23f64.signum() == 3.21f64.signum(); + + 1.23f64.signum() != x64.signum(); + 1.23f64.signum() != -(x64.signum()); + 1.23f64.signum() != 3.21f64.signum(); +} diff --git a/src/tools/clippy/tests/ui/float_cmp.stderr b/src/tools/clippy/tests/ui/float_cmp.stderr new file mode 100644 index 00000000000..2d454e8e70d --- /dev/null +++ b/src/tools/clippy/tests/ui/float_cmp.stderr @@ -0,0 +1,51 @@ +error: strict comparison of `f32` or `f64` + --> $DIR/float_cmp.rs:65:5 + | +LL | ONE as f64 != 2.0; + | ^^^^^^^^^^^^^^^^^ help: consider comparing them within some error: `(ONE as f64 - 2.0).abs() > error` + | + = note: `-D clippy::float-cmp` implied by `-D warnings` + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + +error: strict comparison of `f32` or `f64` + --> $DIR/float_cmp.rs:70:5 + | +LL | x == 1.0; + | ^^^^^^^^ help: consider comparing them within some error: `(x - 1.0).abs() < error` + | + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + +error: strict comparison of `f32` or `f64` + --> $DIR/float_cmp.rs:73:5 + | +LL | twice(x) != twice(ONE as f64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some error: `(twice(x) - twice(ONE as f64)).abs() > error` + | + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + +error: strict comparison of `f32` or `f64` + --> $DIR/float_cmp.rs:93:5 + | +LL | NON_ZERO_ARRAY[i] == NON_ZERO_ARRAY[j]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some error: `(NON_ZERO_ARRAY[i] - NON_ZERO_ARRAY[j]).abs() < error` + | + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + +error: strict comparison of `f32` or `f64` arrays + --> $DIR/float_cmp.rs:98:5 + | +LL | a1 == a2; + | ^^^^^^^^ + | + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + +error: strict comparison of `f32` or `f64` + --> $DIR/float_cmp.rs:99:5 + | +LL | a1[0] == a2[0]; + | ^^^^^^^^^^^^^^ help: consider comparing them within some error: `(a1[0] - a2[0]).abs() < error` + | + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/float_cmp_const.rs b/src/tools/clippy/tests/ui/float_cmp_const.rs new file mode 100644 index 00000000000..dfc025558a2 --- /dev/null +++ b/src/tools/clippy/tests/ui/float_cmp_const.rs @@ -0,0 +1,62 @@ +// does not test any rustfixable lints + +#![warn(clippy::float_cmp_const)] +#![allow(clippy::float_cmp)] +#![allow(unused, clippy::no_effect, clippy::unnecessary_operation)] + +const ONE: f32 = 1.0; +const TWO: f32 = 2.0; + +fn eq_one(x: f32) -> bool { + if x.is_nan() { + false + } else { + x == ONE + } // no error, inside "eq" fn +} + +fn main() { + // has errors + 1f32 == ONE; + TWO == ONE; + TWO != ONE; + ONE + ONE == TWO; + let x = 1; + x as f32 == ONE; + + let v = 0.9; + v == ONE; + v != ONE; + + // no errors, lower than or greater than comparisons + v < ONE; + v > ONE; + v <= ONE; + v >= ONE; + + // no errors, zero and infinity values + ONE != 0f32; + TWO == 0f32; + ONE != f32::INFINITY; + ONE == f32::NEG_INFINITY; + + // no errors, but will warn clippy::float_cmp if '#![allow(float_cmp)]' above is removed + let w = 1.1; + v == w; + v != w; + v == 1.0; + v != 1.0; + + const ZERO_ARRAY: [f32; 3] = [0.0, 0.0, 0.0]; + const ZERO_INF_ARRAY: [f32; 3] = [0.0, ::std::f32::INFINITY, ::std::f32::NEG_INFINITY]; + const NON_ZERO_ARRAY: [f32; 3] = [0.0, 0.1, 0.2]; + const NON_ZERO_ARRAY2: [f32; 3] = [0.2, 0.1, 0.0]; + + // no errors, zero and infinity values + NON_ZERO_ARRAY[0] == NON_ZERO_ARRAY2[1]; // lhs is 0.0 + ZERO_ARRAY == NON_ZERO_ARRAY; // lhs is all zeros + ZERO_INF_ARRAY == NON_ZERO_ARRAY; // lhs is all zeros or infinities + + // has errors + NON_ZERO_ARRAY == NON_ZERO_ARRAY2; +} diff --git a/src/tools/clippy/tests/ui/float_cmp_const.stderr b/src/tools/clippy/tests/ui/float_cmp_const.stderr new file mode 100644 index 00000000000..19dc4a284b7 --- /dev/null +++ b/src/tools/clippy/tests/ui/float_cmp_const.stderr @@ -0,0 +1,67 @@ +error: strict comparison of `f32` or `f64` constant + --> $DIR/float_cmp_const.rs:20:5 + | +LL | 1f32 == ONE; + | ^^^^^^^^^^^ help: consider comparing them within some error: `(1f32 - ONE).abs() < error` + | + = note: `-D clippy::float-cmp-const` implied by `-D warnings` + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + +error: strict comparison of `f32` or `f64` constant + --> $DIR/float_cmp_const.rs:21:5 + | +LL | TWO == ONE; + | ^^^^^^^^^^ help: consider comparing them within some error: `(TWO - ONE).abs() < error` + | + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + +error: strict comparison of `f32` or `f64` constant + --> $DIR/float_cmp_const.rs:22:5 + | +LL | TWO != ONE; + | ^^^^^^^^^^ help: consider comparing them within some error: `(TWO - ONE).abs() > error` + | + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + +error: strict comparison of `f32` or `f64` constant + --> $DIR/float_cmp_const.rs:23:5 + | +LL | ONE + ONE == TWO; + | ^^^^^^^^^^^^^^^^ help: consider comparing them within some error: `(ONE + ONE - TWO).abs() < error` + | + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + +error: strict comparison of `f32` or `f64` constant + --> $DIR/float_cmp_const.rs:25:5 + | +LL | x as f32 == ONE; + | ^^^^^^^^^^^^^^^ help: consider comparing them within some error: `(x as f32 - ONE).abs() < error` + | + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + +error: strict comparison of `f32` or `f64` constant + --> $DIR/float_cmp_const.rs:28:5 + | +LL | v == ONE; + | ^^^^^^^^ help: consider comparing them within some error: `(v - ONE).abs() < error` + | + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + +error: strict comparison of `f32` or `f64` constant + --> $DIR/float_cmp_const.rs:29:5 + | +LL | v != ONE; + | ^^^^^^^^ help: consider comparing them within some error: `(v - ONE).abs() > error` + | + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + +error: strict comparison of `f32` or `f64` constant arrays + --> $DIR/float_cmp_const.rs:61:5 + | +LL | NON_ZERO_ARRAY == NON_ZERO_ARRAY2; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + +error: aborting due to 8 previous errors + diff --git a/src/tools/clippy/tests/ui/floating_point_abs.fixed b/src/tools/clippy/tests/ui/floating_point_abs.fixed new file mode 100644 index 00000000000..b623e4988e7 --- /dev/null +++ b/src/tools/clippy/tests/ui/floating_point_abs.fixed @@ -0,0 +1,98 @@ +// run-rustfix +#![warn(clippy::suboptimal_flops)] + +struct A { + a: f64, + b: f64, +} + +fn fake_abs1(num: f64) -> f64 { + num.abs() +} + +fn fake_abs2(num: f64) -> f64 { + num.abs() +} + +fn fake_abs3(a: A) -> f64 { + a.a.abs() +} + +fn fake_abs4(num: f64) -> f64 { + num.abs() +} + +fn fake_abs5(a: A) -> f64 { + a.a.abs() +} + +fn fake_nabs1(num: f64) -> f64 { + -num.abs() +} + +fn fake_nabs2(num: f64) -> f64 { + -num.abs() +} + +fn fake_nabs3(a: A) -> A { + A { + a: -a.a.abs(), + b: a.b, + } +} + +fn not_fake_abs1(num: f64) -> f64 { + if num > 0.0 { + num + } else { + -num - 1f64 + } +} + +fn not_fake_abs2(num: f64) -> f64 { + if num > 0.0 { + num + 1.0 + } else { + -(num + 1.0) + } +} + +fn not_fake_abs3(num1: f64, num2: f64) -> f64 { + if num1 > 0.0 { + num2 + } else { + -num2 + } +} + +fn not_fake_abs4(a: A) -> f64 { + if a.a > 0.0 { + a.b + } else { + -a.b + } +} + +fn not_fake_abs5(a: A) -> f64 { + if a.a > 0.0 { + a.a + } else { + -a.b + } +} + +fn main() { + fake_abs1(5.0); + fake_abs2(5.0); + fake_abs3(A { a: 5.0, b: 5.0 }); + fake_abs4(5.0); + fake_abs5(A { a: 5.0, b: 5.0 }); + fake_nabs1(5.0); + fake_nabs2(5.0); + fake_nabs3(A { a: 5.0, b: 5.0 }); + not_fake_abs1(5.0); + not_fake_abs2(5.0); + not_fake_abs3(5.0, 5.0); + not_fake_abs4(A { a: 5.0, b: 5.0 }); + not_fake_abs5(A { a: 5.0, b: 5.0 }); +} diff --git a/src/tools/clippy/tests/ui/floating_point_abs.rs b/src/tools/clippy/tests/ui/floating_point_abs.rs new file mode 100644 index 00000000000..cbf9c94e41e --- /dev/null +++ b/src/tools/clippy/tests/ui/floating_point_abs.rs @@ -0,0 +1,126 @@ +// run-rustfix +#![warn(clippy::suboptimal_flops)] + +struct A { + a: f64, + b: f64, +} + +fn fake_abs1(num: f64) -> f64 { + if num >= 0.0 { + num + } else { + -num + } +} + +fn fake_abs2(num: f64) -> f64 { + if 0.0 < num { + num + } else { + -num + } +} + +fn fake_abs3(a: A) -> f64 { + if a.a > 0.0 { + a.a + } else { + -a.a + } +} + +fn fake_abs4(num: f64) -> f64 { + if 0.0 >= num { + -num + } else { + num + } +} + +fn fake_abs5(a: A) -> f64 { + if a.a < 0.0 { + -a.a + } else { + a.a + } +} + +fn fake_nabs1(num: f64) -> f64 { + if num < 0.0 { + num + } else { + -num + } +} + +fn fake_nabs2(num: f64) -> f64 { + if 0.0 >= num { + num + } else { + -num + } +} + +fn fake_nabs3(a: A) -> A { + A { + a: if a.a >= 0.0 { -a.a } else { a.a }, + b: a.b, + } +} + +fn not_fake_abs1(num: f64) -> f64 { + if num > 0.0 { + num + } else { + -num - 1f64 + } +} + +fn not_fake_abs2(num: f64) -> f64 { + if num > 0.0 { + num + 1.0 + } else { + -(num + 1.0) + } +} + +fn not_fake_abs3(num1: f64, num2: f64) -> f64 { + if num1 > 0.0 { + num2 + } else { + -num2 + } +} + +fn not_fake_abs4(a: A) -> f64 { + if a.a > 0.0 { + a.b + } else { + -a.b + } +} + +fn not_fake_abs5(a: A) -> f64 { + if a.a > 0.0 { + a.a + } else { + -a.b + } +} + +fn main() { + fake_abs1(5.0); + fake_abs2(5.0); + fake_abs3(A { a: 5.0, b: 5.0 }); + fake_abs4(5.0); + fake_abs5(A { a: 5.0, b: 5.0 }); + fake_nabs1(5.0); + fake_nabs2(5.0); + fake_nabs3(A { a: 5.0, b: 5.0 }); + not_fake_abs1(5.0); + not_fake_abs2(5.0); + not_fake_abs3(5.0, 5.0); + not_fake_abs4(A { a: 5.0, b: 5.0 }); + not_fake_abs5(A { a: 5.0, b: 5.0 }); +} diff --git a/src/tools/clippy/tests/ui/floating_point_abs.stderr b/src/tools/clippy/tests/ui/floating_point_abs.stderr new file mode 100644 index 00000000000..74a71f2ca7c --- /dev/null +++ b/src/tools/clippy/tests/ui/floating_point_abs.stderr @@ -0,0 +1,80 @@ +error: manual implementation of `abs` method + --> $DIR/floating_point_abs.rs:10:5 + | +LL | / if num >= 0.0 { +LL | | num +LL | | } else { +LL | | -num +LL | | } + | |_____^ help: try: `num.abs()` + | + = note: `-D clippy::suboptimal-flops` implied by `-D warnings` + +error: manual implementation of `abs` method + --> $DIR/floating_point_abs.rs:18:5 + | +LL | / if 0.0 < num { +LL | | num +LL | | } else { +LL | | -num +LL | | } + | |_____^ help: try: `num.abs()` + +error: manual implementation of `abs` method + --> $DIR/floating_point_abs.rs:26:5 + | +LL | / if a.a > 0.0 { +LL | | a.a +LL | | } else { +LL | | -a.a +LL | | } + | |_____^ help: try: `a.a.abs()` + +error: manual implementation of `abs` method + --> $DIR/floating_point_abs.rs:34:5 + | +LL | / if 0.0 >= num { +LL | | -num +LL | | } else { +LL | | num +LL | | } + | |_____^ help: try: `num.abs()` + +error: manual implementation of `abs` method + --> $DIR/floating_point_abs.rs:42:5 + | +LL | / if a.a < 0.0 { +LL | | -a.a +LL | | } else { +LL | | a.a +LL | | } + | |_____^ help: try: `a.a.abs()` + +error: manual implementation of negation of `abs` method + --> $DIR/floating_point_abs.rs:50:5 + | +LL | / if num < 0.0 { +LL | | num +LL | | } else { +LL | | -num +LL | | } + | |_____^ help: try: `-num.abs()` + +error: manual implementation of negation of `abs` method + --> $DIR/floating_point_abs.rs:58:5 + | +LL | / if 0.0 >= num { +LL | | num +LL | | } else { +LL | | -num +LL | | } + | |_____^ help: try: `-num.abs()` + +error: manual implementation of negation of `abs` method + --> $DIR/floating_point_abs.rs:67:12 + | +LL | a: if a.a >= 0.0 { -a.a } else { a.a }, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `-a.a.abs()` + +error: aborting due to 8 previous errors + diff --git a/src/tools/clippy/tests/ui/floating_point_exp.fixed b/src/tools/clippy/tests/ui/floating_point_exp.fixed new file mode 100644 index 00000000000..ae7805fdf01 --- /dev/null +++ b/src/tools/clippy/tests/ui/floating_point_exp.fixed @@ -0,0 +1,18 @@ +// run-rustfix +#![warn(clippy::imprecise_flops)] + +fn main() { + let x = 2f32; + let _ = x.exp_m1(); + let _ = x.exp_m1() + 2.0; + // Cases where the lint shouldn't be applied + let _ = x.exp() - 2.0; + let _ = x.exp() - 1.0 * 2.0; + + let x = 2f64; + let _ = x.exp_m1(); + let _ = x.exp_m1() + 2.0; + // Cases where the lint shouldn't be applied + let _ = x.exp() - 2.0; + let _ = x.exp() - 1.0 * 2.0; +} diff --git a/src/tools/clippy/tests/ui/floating_point_exp.rs b/src/tools/clippy/tests/ui/floating_point_exp.rs new file mode 100644 index 00000000000..27e0b9bcbc9 --- /dev/null +++ b/src/tools/clippy/tests/ui/floating_point_exp.rs @@ -0,0 +1,18 @@ +// run-rustfix +#![warn(clippy::imprecise_flops)] + +fn main() { + let x = 2f32; + let _ = x.exp() - 1.0; + let _ = x.exp() - 1.0 + 2.0; + // Cases where the lint shouldn't be applied + let _ = x.exp() - 2.0; + let _ = x.exp() - 1.0 * 2.0; + + let x = 2f64; + let _ = x.exp() - 1.0; + let _ = x.exp() - 1.0 + 2.0; + // Cases where the lint shouldn't be applied + let _ = x.exp() - 2.0; + let _ = x.exp() - 1.0 * 2.0; +} diff --git a/src/tools/clippy/tests/ui/floating_point_exp.stderr b/src/tools/clippy/tests/ui/floating_point_exp.stderr new file mode 100644 index 00000000000..5cd999ad47c --- /dev/null +++ b/src/tools/clippy/tests/ui/floating_point_exp.stderr @@ -0,0 +1,28 @@ +error: (e.pow(x) - 1) can be computed more accurately + --> $DIR/floating_point_exp.rs:6:13 + | +LL | let _ = x.exp() - 1.0; + | ^^^^^^^^^^^^^ help: consider using: `x.exp_m1()` + | + = note: `-D clippy::imprecise-flops` implied by `-D warnings` + +error: (e.pow(x) - 1) can be computed more accurately + --> $DIR/floating_point_exp.rs:7:13 + | +LL | let _ = x.exp() - 1.0 + 2.0; + | ^^^^^^^^^^^^^ help: consider using: `x.exp_m1()` + +error: (e.pow(x) - 1) can be computed more accurately + --> $DIR/floating_point_exp.rs:13:13 + | +LL | let _ = x.exp() - 1.0; + | ^^^^^^^^^^^^^ help: consider using: `x.exp_m1()` + +error: (e.pow(x) - 1) can be computed more accurately + --> $DIR/floating_point_exp.rs:14:13 + | +LL | let _ = x.exp() - 1.0 + 2.0; + | ^^^^^^^^^^^^^ help: consider using: `x.exp_m1()` + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/floating_point_log.fixed b/src/tools/clippy/tests/ui/floating_point_log.fixed new file mode 100644 index 00000000000..42c5e5d2bae --- /dev/null +++ b/src/tools/clippy/tests/ui/floating_point_log.fixed @@ -0,0 +1,58 @@ +// run-rustfix +#![allow(dead_code, clippy::double_parens)] +#![warn(clippy::suboptimal_flops, clippy::imprecise_flops)] + +const TWO: f32 = 2.0; +const E: f32 = std::f32::consts::E; + +fn check_log_base() { + let x = 1f32; + let _ = x.log2(); + let _ = x.log10(); + let _ = x.ln(); + let _ = x.log2(); + let _ = x.ln(); + + let x = 1f64; + let _ = x.log2(); + let _ = x.log10(); + let _ = x.ln(); +} + +fn check_ln1p() { + let x = 1f32; + let _ = 2.0f32.ln_1p(); + let _ = 2.0f32.ln_1p(); + let _ = x.ln_1p(); + let _ = (x / 2.0).ln_1p(); + let _ = x.powi(2).ln_1p(); + let _ = (x.powi(2) / 2.0).ln_1p(); + let _ = ((std::f32::consts::E - 1.0)).ln_1p(); + let _ = x.ln_1p(); + let _ = x.powi(2).ln_1p(); + let _ = (x + 2.0).ln_1p(); + let _ = (x / 2.0).ln_1p(); + // Cases where the lint shouldn't be applied + let _ = (1.0 + x + 2.0).ln(); + let _ = (x + 1.0 + 2.0).ln(); + let _ = (x + 1.0 / 2.0).ln(); + let _ = (1.0 + x - 2.0).ln(); + + let x = 1f64; + let _ = 2.0f64.ln_1p(); + let _ = 2.0f64.ln_1p(); + let _ = x.ln_1p(); + let _ = (x / 2.0).ln_1p(); + let _ = x.powi(2).ln_1p(); + let _ = x.ln_1p(); + let _ = x.powi(2).ln_1p(); + let _ = (x + 2.0).ln_1p(); + let _ = (x / 2.0).ln_1p(); + // Cases where the lint shouldn't be applied + let _ = (1.0 + x + 2.0).ln(); + let _ = (x + 1.0 + 2.0).ln(); + let _ = (x + 1.0 / 2.0).ln(); + let _ = (1.0 + x - 2.0).ln(); +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/floating_point_log.rs b/src/tools/clippy/tests/ui/floating_point_log.rs new file mode 100644 index 00000000000..8be0d9ad56f --- /dev/null +++ b/src/tools/clippy/tests/ui/floating_point_log.rs @@ -0,0 +1,58 @@ +// run-rustfix +#![allow(dead_code, clippy::double_parens)] +#![warn(clippy::suboptimal_flops, clippy::imprecise_flops)] + +const TWO: f32 = 2.0; +const E: f32 = std::f32::consts::E; + +fn check_log_base() { + let x = 1f32; + let _ = x.log(2f32); + let _ = x.log(10f32); + let _ = x.log(std::f32::consts::E); + let _ = x.log(TWO); + let _ = x.log(E); + + let x = 1f64; + let _ = x.log(2f64); + let _ = x.log(10f64); + let _ = x.log(std::f64::consts::E); +} + +fn check_ln1p() { + let x = 1f32; + let _ = (1f32 + 2.).ln(); + let _ = (1f32 + 2.0).ln(); + let _ = (1.0 + x).ln(); + let _ = (1.0 + x / 2.0).ln(); + let _ = (1.0 + x.powi(2)).ln(); + let _ = (1.0 + x.powi(2) / 2.0).ln(); + let _ = (1.0 + (std::f32::consts::E - 1.0)).ln(); + let _ = (x + 1.0).ln(); + let _ = (x.powi(2) + 1.0).ln(); + let _ = (x + 2.0 + 1.0).ln(); + let _ = (x / 2.0 + 1.0).ln(); + // Cases where the lint shouldn't be applied + let _ = (1.0 + x + 2.0).ln(); + let _ = (x + 1.0 + 2.0).ln(); + let _ = (x + 1.0 / 2.0).ln(); + let _ = (1.0 + x - 2.0).ln(); + + let x = 1f64; + let _ = (1f64 + 2.).ln(); + let _ = (1f64 + 2.0).ln(); + let _ = (1.0 + x).ln(); + let _ = (1.0 + x / 2.0).ln(); + let _ = (1.0 + x.powi(2)).ln(); + let _ = (x + 1.0).ln(); + let _ = (x.powi(2) + 1.0).ln(); + let _ = (x + 2.0 + 1.0).ln(); + let _ = (x / 2.0 + 1.0).ln(); + // Cases where the lint shouldn't be applied + let _ = (1.0 + x + 2.0).ln(); + let _ = (x + 1.0 + 2.0).ln(); + let _ = (x + 1.0 / 2.0).ln(); + let _ = (1.0 + x - 2.0).ln(); +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/floating_point_log.stderr b/src/tools/clippy/tests/ui/floating_point_log.stderr new file mode 100644 index 00000000000..943fbdb0b83 --- /dev/null +++ b/src/tools/clippy/tests/ui/floating_point_log.stderr @@ -0,0 +1,174 @@ +error: logarithm for bases 2, 10 and e can be computed more accurately + --> $DIR/floating_point_log.rs:10:13 + | +LL | let _ = x.log(2f32); + | ^^^^^^^^^^^ help: consider using: `x.log2()` + | + = note: `-D clippy::suboptimal-flops` implied by `-D warnings` + +error: logarithm for bases 2, 10 and e can be computed more accurately + --> $DIR/floating_point_log.rs:11:13 + | +LL | let _ = x.log(10f32); + | ^^^^^^^^^^^^ help: consider using: `x.log10()` + +error: logarithm for bases 2, 10 and e can be computed more accurately + --> $DIR/floating_point_log.rs:12:13 + | +LL | let _ = x.log(std::f32::consts::E); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.ln()` + +error: logarithm for bases 2, 10 and e can be computed more accurately + --> $DIR/floating_point_log.rs:13:13 + | +LL | let _ = x.log(TWO); + | ^^^^^^^^^^ help: consider using: `x.log2()` + +error: logarithm for bases 2, 10 and e can be computed more accurately + --> $DIR/floating_point_log.rs:14:13 + | +LL | let _ = x.log(E); + | ^^^^^^^^ help: consider using: `x.ln()` + +error: logarithm for bases 2, 10 and e can be computed more accurately + --> $DIR/floating_point_log.rs:17:13 + | +LL | let _ = x.log(2f64); + | ^^^^^^^^^^^ help: consider using: `x.log2()` + +error: logarithm for bases 2, 10 and e can be computed more accurately + --> $DIR/floating_point_log.rs:18:13 + | +LL | let _ = x.log(10f64); + | ^^^^^^^^^^^^ help: consider using: `x.log10()` + +error: logarithm for bases 2, 10 and e can be computed more accurately + --> $DIR/floating_point_log.rs:19:13 + | +LL | let _ = x.log(std::f64::consts::E); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.ln()` + +error: ln(1 + x) can be computed more accurately + --> $DIR/floating_point_log.rs:24:13 + | +LL | let _ = (1f32 + 2.).ln(); + | ^^^^^^^^^^^^^^^^ help: consider using: `2.0f32.ln_1p()` + | + = note: `-D clippy::imprecise-flops` implied by `-D warnings` + +error: ln(1 + x) can be computed more accurately + --> $DIR/floating_point_log.rs:25:13 + | +LL | let _ = (1f32 + 2.0).ln(); + | ^^^^^^^^^^^^^^^^^ help: consider using: `2.0f32.ln_1p()` + +error: ln(1 + x) can be computed more accurately + --> $DIR/floating_point_log.rs:26:13 + | +LL | let _ = (1.0 + x).ln(); + | ^^^^^^^^^^^^^^ help: consider using: `x.ln_1p()` + +error: ln(1 + x) can be computed more accurately + --> $DIR/floating_point_log.rs:27:13 + | +LL | let _ = (1.0 + x / 2.0).ln(); + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x / 2.0).ln_1p()` + +error: ln(1 + x) can be computed more accurately + --> $DIR/floating_point_log.rs:28:13 + | +LL | let _ = (1.0 + x.powi(2)).ln(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(2).ln_1p()` + +error: ln(1 + x) can be computed more accurately + --> $DIR/floating_point_log.rs:29:13 + | +LL | let _ = (1.0 + x.powi(2) / 2.0).ln(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x.powi(2) / 2.0).ln_1p()` + +error: ln(1 + x) can be computed more accurately + --> $DIR/floating_point_log.rs:30:13 + | +LL | let _ = (1.0 + (std::f32::consts::E - 1.0)).ln(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `((std::f32::consts::E - 1.0)).ln_1p()` + +error: ln(1 + x) can be computed more accurately + --> $DIR/floating_point_log.rs:31:13 + | +LL | let _ = (x + 1.0).ln(); + | ^^^^^^^^^^^^^^ help: consider using: `x.ln_1p()` + +error: ln(1 + x) can be computed more accurately + --> $DIR/floating_point_log.rs:32:13 + | +LL | let _ = (x.powi(2) + 1.0).ln(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(2).ln_1p()` + +error: ln(1 + x) can be computed more accurately + --> $DIR/floating_point_log.rs:33:13 + | +LL | let _ = (x + 2.0 + 1.0).ln(); + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x + 2.0).ln_1p()` + +error: ln(1 + x) can be computed more accurately + --> $DIR/floating_point_log.rs:34:13 + | +LL | let _ = (x / 2.0 + 1.0).ln(); + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x / 2.0).ln_1p()` + +error: ln(1 + x) can be computed more accurately + --> $DIR/floating_point_log.rs:42:13 + | +LL | let _ = (1f64 + 2.).ln(); + | ^^^^^^^^^^^^^^^^ help: consider using: `2.0f64.ln_1p()` + +error: ln(1 + x) can be computed more accurately + --> $DIR/floating_point_log.rs:43:13 + | +LL | let _ = (1f64 + 2.0).ln(); + | ^^^^^^^^^^^^^^^^^ help: consider using: `2.0f64.ln_1p()` + +error: ln(1 + x) can be computed more accurately + --> $DIR/floating_point_log.rs:44:13 + | +LL | let _ = (1.0 + x).ln(); + | ^^^^^^^^^^^^^^ help: consider using: `x.ln_1p()` + +error: ln(1 + x) can be computed more accurately + --> $DIR/floating_point_log.rs:45:13 + | +LL | let _ = (1.0 + x / 2.0).ln(); + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x / 2.0).ln_1p()` + +error: ln(1 + x) can be computed more accurately + --> $DIR/floating_point_log.rs:46:13 + | +LL | let _ = (1.0 + x.powi(2)).ln(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(2).ln_1p()` + +error: ln(1 + x) can be computed more accurately + --> $DIR/floating_point_log.rs:47:13 + | +LL | let _ = (x + 1.0).ln(); + | ^^^^^^^^^^^^^^ help: consider using: `x.ln_1p()` + +error: ln(1 + x) can be computed more accurately + --> $DIR/floating_point_log.rs:48:13 + | +LL | let _ = (x.powi(2) + 1.0).ln(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(2).ln_1p()` + +error: ln(1 + x) can be computed more accurately + --> $DIR/floating_point_log.rs:49:13 + | +LL | let _ = (x + 2.0 + 1.0).ln(); + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x + 2.0).ln_1p()` + +error: ln(1 + x) can be computed more accurately + --> $DIR/floating_point_log.rs:50:13 + | +LL | let _ = (x / 2.0 + 1.0).ln(); + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x / 2.0).ln_1p()` + +error: aborting due to 28 previous errors + diff --git a/src/tools/clippy/tests/ui/floating_point_mul_add.fixed b/src/tools/clippy/tests/ui/floating_point_mul_add.fixed new file mode 100644 index 00000000000..e343c37740d --- /dev/null +++ b/src/tools/clippy/tests/ui/floating_point_mul_add.fixed @@ -0,0 +1,21 @@ +// run-rustfix +#![warn(clippy::suboptimal_flops)] + +fn main() { + let a: f64 = 1234.567; + let b: f64 = 45.67834; + let c: f64 = 0.0004; + let d: f64 = 0.0001; + + let _ = a.mul_add(b, c); + let _ = a.mul_add(b, c); + let _ = 2.0f64.mul_add(4.0, a); + let _ = 2.0f64.mul_add(4., a); + + let _ = a.mul_add(b, c); + let _ = a.mul_add(b, c); + let _ = (a * b).mul_add(c, d); + + let _ = a.mul_add(b, c).mul_add(a.mul_add(b, c), a.mul_add(b, c)) + c; + let _ = 1234.567_f64.mul_add(45.67834_f64, 0.0004_f64); +} diff --git a/src/tools/clippy/tests/ui/floating_point_mul_add.rs b/src/tools/clippy/tests/ui/floating_point_mul_add.rs new file mode 100644 index 00000000000..810f929c856 --- /dev/null +++ b/src/tools/clippy/tests/ui/floating_point_mul_add.rs @@ -0,0 +1,21 @@ +// run-rustfix +#![warn(clippy::suboptimal_flops)] + +fn main() { + let a: f64 = 1234.567; + let b: f64 = 45.67834; + let c: f64 = 0.0004; + let d: f64 = 0.0001; + + let _ = a * b + c; + let _ = c + a * b; + let _ = a + 2.0 * 4.0; + let _ = a + 2. * 4.; + + let _ = (a * b) + c; + let _ = c + (a * b); + let _ = a * b * c + d; + + let _ = a.mul_add(b, c) * a.mul_add(b, c) + a.mul_add(b, c) + c; + let _ = 1234.567_f64 * 45.67834_f64 + 0.0004_f64; +} diff --git a/src/tools/clippy/tests/ui/floating_point_mul_add.stderr b/src/tools/clippy/tests/ui/floating_point_mul_add.stderr new file mode 100644 index 00000000000..2dfbf562d15 --- /dev/null +++ b/src/tools/clippy/tests/ui/floating_point_mul_add.stderr @@ -0,0 +1,58 @@ +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_mul_add.rs:10:13 + | +LL | let _ = a * b + c; + | ^^^^^^^^^ help: consider using: `a.mul_add(b, c)` + | + = note: `-D clippy::suboptimal-flops` implied by `-D warnings` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_mul_add.rs:11:13 + | +LL | let _ = c + a * b; + | ^^^^^^^^^ help: consider using: `a.mul_add(b, c)` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_mul_add.rs:12:13 + | +LL | let _ = a + 2.0 * 4.0; + | ^^^^^^^^^^^^^ help: consider using: `2.0f64.mul_add(4.0, a)` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_mul_add.rs:13:13 + | +LL | let _ = a + 2. * 4.; + | ^^^^^^^^^^^ help: consider using: `2.0f64.mul_add(4., a)` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_mul_add.rs:15:13 + | +LL | let _ = (a * b) + c; + | ^^^^^^^^^^^ help: consider using: `a.mul_add(b, c)` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_mul_add.rs:16:13 + | +LL | let _ = c + (a * b); + | ^^^^^^^^^^^ help: consider using: `a.mul_add(b, c)` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_mul_add.rs:17:13 + | +LL | let _ = a * b * c + d; + | ^^^^^^^^^^^^^ help: consider using: `(a * b).mul_add(c, d)` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_mul_add.rs:19:13 + | +LL | let _ = a.mul_add(b, c) * a.mul_add(b, c) + a.mul_add(b, c) + c; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `a.mul_add(b, c).mul_add(a.mul_add(b, c), a.mul_add(b, c))` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_mul_add.rs:20:13 + | +LL | let _ = 1234.567_f64 * 45.67834_f64 + 0.0004_f64; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1234.567_f64.mul_add(45.67834_f64, 0.0004_f64)` + +error: aborting due to 9 previous errors + diff --git a/src/tools/clippy/tests/ui/floating_point_powf.fixed b/src/tools/clippy/tests/ui/floating_point_powf.fixed new file mode 100644 index 00000000000..78a9d44829b --- /dev/null +++ b/src/tools/clippy/tests/ui/floating_point_powf.fixed @@ -0,0 +1,42 @@ +// run-rustfix +#![warn(clippy::suboptimal_flops, clippy::imprecise_flops)] + +fn main() { + let x = 3f32; + let _ = x.exp2(); + let _ = 3.1f32.exp2(); + let _ = (-3.1f32).exp2(); + let _ = x.exp(); + let _ = 3.1f32.exp(); + let _ = (-3.1f32).exp(); + let _ = x.sqrt(); + let _ = x.cbrt(); + let _ = x.powi(2); + let _ = x.powi(-2); + let _ = x.powi(16_777_215); + let _ = x.powi(-16_777_215); + // Cases where the lint shouldn't be applied + let _ = x.powf(2.1); + let _ = x.powf(-2.1); + let _ = x.powf(16_777_216.0); + let _ = x.powf(-16_777_216.0); + + let x = 3f64; + let _ = x.exp2(); + let _ = 3.1f64.exp2(); + let _ = (-3.1f64).exp2(); + let _ = x.exp(); + let _ = 3.1f64.exp(); + let _ = (-3.1f64).exp(); + let _ = x.sqrt(); + let _ = x.cbrt(); + let _ = x.powi(2); + let _ = x.powi(-2); + let _ = x.powi(-2_147_483_648); + let _ = x.powi(2_147_483_647); + // Cases where the lint shouldn't be applied + let _ = x.powf(2.1); + let _ = x.powf(-2.1); + let _ = x.powf(-2_147_483_649.0); + let _ = x.powf(2_147_483_648.0); +} diff --git a/src/tools/clippy/tests/ui/floating_point_powf.rs b/src/tools/clippy/tests/ui/floating_point_powf.rs new file mode 100644 index 00000000000..dbc1cac5cb4 --- /dev/null +++ b/src/tools/clippy/tests/ui/floating_point_powf.rs @@ -0,0 +1,42 @@ +// run-rustfix +#![warn(clippy::suboptimal_flops, clippy::imprecise_flops)] + +fn main() { + let x = 3f32; + let _ = 2f32.powf(x); + let _ = 2f32.powf(3.1); + let _ = 2f32.powf(-3.1); + let _ = std::f32::consts::E.powf(x); + let _ = std::f32::consts::E.powf(3.1); + let _ = std::f32::consts::E.powf(-3.1); + let _ = x.powf(1.0 / 2.0); + let _ = x.powf(1.0 / 3.0); + let _ = x.powf(2.0); + let _ = x.powf(-2.0); + let _ = x.powf(16_777_215.0); + let _ = x.powf(-16_777_215.0); + // Cases where the lint shouldn't be applied + let _ = x.powf(2.1); + let _ = x.powf(-2.1); + let _ = x.powf(16_777_216.0); + let _ = x.powf(-16_777_216.0); + + let x = 3f64; + let _ = 2f64.powf(x); + let _ = 2f64.powf(3.1); + let _ = 2f64.powf(-3.1); + let _ = std::f64::consts::E.powf(x); + let _ = std::f64::consts::E.powf(3.1); + let _ = std::f64::consts::E.powf(-3.1); + let _ = x.powf(1.0 / 2.0); + let _ = x.powf(1.0 / 3.0); + let _ = x.powf(2.0); + let _ = x.powf(-2.0); + let _ = x.powf(-2_147_483_648.0); + let _ = x.powf(2_147_483_647.0); + // Cases where the lint shouldn't be applied + let _ = x.powf(2.1); + let _ = x.powf(-2.1); + let _ = x.powf(-2_147_483_649.0); + let _ = x.powf(2_147_483_648.0); +} diff --git a/src/tools/clippy/tests/ui/floating_point_powf.stderr b/src/tools/clippy/tests/ui/floating_point_powf.stderr new file mode 100644 index 00000000000..ad5163f0079 --- /dev/null +++ b/src/tools/clippy/tests/ui/floating_point_powf.stderr @@ -0,0 +1,150 @@ +error: exponent for bases 2 and e can be computed more accurately + --> $DIR/floating_point_powf.rs:6:13 + | +LL | let _ = 2f32.powf(x); + | ^^^^^^^^^^^^ help: consider using: `x.exp2()` + | + = note: `-D clippy::suboptimal-flops` implied by `-D warnings` + +error: exponent for bases 2 and e can be computed more accurately + --> $DIR/floating_point_powf.rs:7:13 + | +LL | let _ = 2f32.powf(3.1); + | ^^^^^^^^^^^^^^ help: consider using: `3.1f32.exp2()` + +error: exponent for bases 2 and e can be computed more accurately + --> $DIR/floating_point_powf.rs:8:13 + | +LL | let _ = 2f32.powf(-3.1); + | ^^^^^^^^^^^^^^^ help: consider using: `(-3.1f32).exp2()` + +error: exponent for bases 2 and e can be computed more accurately + --> $DIR/floating_point_powf.rs:9:13 + | +LL | let _ = std::f32::consts::E.powf(x); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.exp()` + +error: exponent for bases 2 and e can be computed more accurately + --> $DIR/floating_point_powf.rs:10:13 + | +LL | let _ = std::f32::consts::E.powf(3.1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `3.1f32.exp()` + +error: exponent for bases 2 and e can be computed more accurately + --> $DIR/floating_point_powf.rs:11:13 + | +LL | let _ = std::f32::consts::E.powf(-3.1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(-3.1f32).exp()` + +error: square-root of a number can be computed more efficiently and accurately + --> $DIR/floating_point_powf.rs:12:13 + | +LL | let _ = x.powf(1.0 / 2.0); + | ^^^^^^^^^^^^^^^^^ help: consider using: `x.sqrt()` + +error: cube-root of a number can be computed more accurately + --> $DIR/floating_point_powf.rs:13:13 + | +LL | let _ = x.powf(1.0 / 3.0); + | ^^^^^^^^^^^^^^^^^ help: consider using: `x.cbrt()` + | + = note: `-D clippy::imprecise-flops` implied by `-D warnings` + +error: exponentiation with integer powers can be computed more efficiently + --> $DIR/floating_point_powf.rs:14:13 + | +LL | let _ = x.powf(2.0); + | ^^^^^^^^^^^ help: consider using: `x.powi(2)` + +error: exponentiation with integer powers can be computed more efficiently + --> $DIR/floating_point_powf.rs:15:13 + | +LL | let _ = x.powf(-2.0); + | ^^^^^^^^^^^^ help: consider using: `x.powi(-2)` + +error: exponentiation with integer powers can be computed more efficiently + --> $DIR/floating_point_powf.rs:16:13 + | +LL | let _ = x.powf(16_777_215.0); + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(16_777_215)` + +error: exponentiation with integer powers can be computed more efficiently + --> $DIR/floating_point_powf.rs:17:13 + | +LL | let _ = x.powf(-16_777_215.0); + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(-16_777_215)` + +error: exponent for bases 2 and e can be computed more accurately + --> $DIR/floating_point_powf.rs:25:13 + | +LL | let _ = 2f64.powf(x); + | ^^^^^^^^^^^^ help: consider using: `x.exp2()` + +error: exponent for bases 2 and e can be computed more accurately + --> $DIR/floating_point_powf.rs:26:13 + | +LL | let _ = 2f64.powf(3.1); + | ^^^^^^^^^^^^^^ help: consider using: `3.1f64.exp2()` + +error: exponent for bases 2 and e can be computed more accurately + --> $DIR/floating_point_powf.rs:27:13 + | +LL | let _ = 2f64.powf(-3.1); + | ^^^^^^^^^^^^^^^ help: consider using: `(-3.1f64).exp2()` + +error: exponent for bases 2 and e can be computed more accurately + --> $DIR/floating_point_powf.rs:28:13 + | +LL | let _ = std::f64::consts::E.powf(x); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.exp()` + +error: exponent for bases 2 and e can be computed more accurately + --> $DIR/floating_point_powf.rs:29:13 + | +LL | let _ = std::f64::consts::E.powf(3.1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `3.1f64.exp()` + +error: exponent for bases 2 and e can be computed more accurately + --> $DIR/floating_point_powf.rs:30:13 + | +LL | let _ = std::f64::consts::E.powf(-3.1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(-3.1f64).exp()` + +error: square-root of a number can be computed more efficiently and accurately + --> $DIR/floating_point_powf.rs:31:13 + | +LL | let _ = x.powf(1.0 / 2.0); + | ^^^^^^^^^^^^^^^^^ help: consider using: `x.sqrt()` + +error: cube-root of a number can be computed more accurately + --> $DIR/floating_point_powf.rs:32:13 + | +LL | let _ = x.powf(1.0 / 3.0); + | ^^^^^^^^^^^^^^^^^ help: consider using: `x.cbrt()` + +error: exponentiation with integer powers can be computed more efficiently + --> $DIR/floating_point_powf.rs:33:13 + | +LL | let _ = x.powf(2.0); + | ^^^^^^^^^^^ help: consider using: `x.powi(2)` + +error: exponentiation with integer powers can be computed more efficiently + --> $DIR/floating_point_powf.rs:34:13 + | +LL | let _ = x.powf(-2.0); + | ^^^^^^^^^^^^ help: consider using: `x.powi(-2)` + +error: exponentiation with integer powers can be computed more efficiently + --> $DIR/floating_point_powf.rs:35:13 + | +LL | let _ = x.powf(-2_147_483_648.0); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(-2_147_483_648)` + +error: exponentiation with integer powers can be computed more efficiently + --> $DIR/floating_point_powf.rs:36:13 + | +LL | let _ = x.powf(2_147_483_647.0); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(2_147_483_647)` + +error: aborting due to 24 previous errors + diff --git a/src/tools/clippy/tests/ui/fn_address_comparisons.rs b/src/tools/clippy/tests/ui/fn_address_comparisons.rs new file mode 100644 index 00000000000..362dcb4fd80 --- /dev/null +++ b/src/tools/clippy/tests/ui/fn_address_comparisons.rs @@ -0,0 +1,20 @@ +use std::fmt::Debug; +use std::ptr; +use std::rc::Rc; +use std::sync::Arc; + +fn a() {} + +#[warn(clippy::fn_address_comparisons)] +fn main() { + type F = fn(); + let f: F = a; + let g: F = f; + + // These should fail: + let _ = f == a; + let _ = f != a; + + // These should be fine: + let _ = f == g; +} diff --git a/src/tools/clippy/tests/ui/fn_address_comparisons.stderr b/src/tools/clippy/tests/ui/fn_address_comparisons.stderr new file mode 100644 index 00000000000..9c1b5419a43 --- /dev/null +++ b/src/tools/clippy/tests/ui/fn_address_comparisons.stderr @@ -0,0 +1,16 @@ +error: comparing with a non-unique address of a function item + --> $DIR/fn_address_comparisons.rs:15:13 + | +LL | let _ = f == a; + | ^^^^^^ + | + = note: `-D clippy::fn-address-comparisons` implied by `-D warnings` + +error: comparing with a non-unique address of a function item + --> $DIR/fn_address_comparisons.rs:16:13 + | +LL | let _ = f != a; + | ^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/fn_params_excessive_bools.rs b/src/tools/clippy/tests/ui/fn_params_excessive_bools.rs new file mode 100644 index 00000000000..7d6fd607e65 --- /dev/null +++ b/src/tools/clippy/tests/ui/fn_params_excessive_bools.rs @@ -0,0 +1,44 @@ +#![warn(clippy::fn_params_excessive_bools)] + +extern "C" { + fn f(_: bool, _: bool, _: bool, _: bool); +} + +macro_rules! foo { + () => { + fn fff(_: bool, _: bool, _: bool, _: bool) {} + }; +} + +foo!(); + +#[no_mangle] +extern "C" fn k(_: bool, _: bool, _: bool, _: bool) {} +fn g(_: bool, _: bool, _: bool, _: bool) {} +fn h(_: bool, _: bool, _: bool) {} +fn e(_: S, _: S, _: Box<S>, _: Vec<u32>) {} +fn t(_: S, _: S, _: Box<S>, _: Vec<u32>, _: bool, _: bool, _: bool, _: bool) {} + +struct S {} +trait Trait { + fn f(_: bool, _: bool, _: bool, _: bool); + fn g(_: bool, _: bool, _: bool, _: Vec<u32>); +} + +impl S { + fn f(&self, _: bool, _: bool, _: bool, _: bool) {} + fn g(&self, _: bool, _: bool, _: bool) {} + #[no_mangle] + extern "C" fn h(_: bool, _: bool, _: bool, _: bool) {} +} + +impl Trait for S { + fn f(_: bool, _: bool, _: bool, _: bool) {} + fn g(_: bool, _: bool, _: bool, _: Vec<u32>) {} +} + +fn main() { + fn n(_: bool, _: u32, _: bool, _: Box<u32>, _: bool, _: bool) { + fn nn(_: bool, _: bool, _: bool, _: bool) {} + } +} diff --git a/src/tools/clippy/tests/ui/fn_params_excessive_bools.stderr b/src/tools/clippy/tests/ui/fn_params_excessive_bools.stderr new file mode 100644 index 00000000000..4e5dbc261d6 --- /dev/null +++ b/src/tools/clippy/tests/ui/fn_params_excessive_bools.stderr @@ -0,0 +1,53 @@ +error: more than 3 bools in function parameters + --> $DIR/fn_params_excessive_bools.rs:17:1 + | +LL | fn g(_: bool, _: bool, _: bool, _: bool) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::fn-params-excessive-bools` implied by `-D warnings` + = help: consider refactoring bools into two-variant enums + +error: more than 3 bools in function parameters + --> $DIR/fn_params_excessive_bools.rs:20:1 + | +LL | fn t(_: S, _: S, _: Box<S>, _: Vec<u32>, _: bool, _: bool, _: bool, _: bool) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider refactoring bools into two-variant enums + +error: more than 3 bools in function parameters + --> $DIR/fn_params_excessive_bools.rs:24:5 + | +LL | fn f(_: bool, _: bool, _: bool, _: bool); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider refactoring bools into two-variant enums + +error: more than 3 bools in function parameters + --> $DIR/fn_params_excessive_bools.rs:29:5 + | +LL | fn f(&self, _: bool, _: bool, _: bool, _: bool) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider refactoring bools into two-variant enums + +error: more than 3 bools in function parameters + --> $DIR/fn_params_excessive_bools.rs:41:5 + | +LL | / fn n(_: bool, _: u32, _: bool, _: Box<u32>, _: bool, _: bool) { +LL | | fn nn(_: bool, _: bool, _: bool, _: bool) {} +LL | | } + | |_____^ + | + = help: consider refactoring bools into two-variant enums + +error: more than 3 bools in function parameters + --> $DIR/fn_params_excessive_bools.rs:42:9 + | +LL | fn nn(_: bool, _: bool, _: bool, _: bool) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider refactoring bools into two-variant enums + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/fn_to_numeric_cast.rs b/src/tools/clippy/tests/ui/fn_to_numeric_cast.rs new file mode 100644 index 00000000000..a456c085c87 --- /dev/null +++ b/src/tools/clippy/tests/ui/fn_to_numeric_cast.rs @@ -0,0 +1,55 @@ +// ignore-32bit + +#![warn(clippy::fn_to_numeric_cast, clippy::fn_to_numeric_cast_with_truncation)] + +fn foo() -> String { + String::new() +} + +fn test_function_to_numeric_cast() { + let _ = foo as i8; + let _ = foo as i16; + let _ = foo as i32; + let _ = foo as i64; + let _ = foo as i128; + let _ = foo as isize; + + let _ = foo as u8; + let _ = foo as u16; + let _ = foo as u32; + let _ = foo as u64; + let _ = foo as u128; + + // Casting to usize is OK and should not warn + let _ = foo as usize; + + // Cast `f` (a `FnDef`) to `fn()` should not warn + fn f() {} + let _ = f as fn(); +} + +fn test_function_var_to_numeric_cast() { + let abc: fn() -> String = foo; + + let _ = abc as i8; + let _ = abc as i16; + let _ = abc as i32; + let _ = abc as i64; + let _ = abc as i128; + let _ = abc as isize; + + let _ = abc as u8; + let _ = abc as u16; + let _ = abc as u32; + let _ = abc as u64; + let _ = abc as u128; + + // Casting to usize is OK and should not warn + let _ = abc as usize; +} + +fn fn_with_fn_args(f: fn(i32) -> i32) -> i32 { + f as i32 +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/fn_to_numeric_cast.stderr b/src/tools/clippy/tests/ui/fn_to_numeric_cast.stderr new file mode 100644 index 00000000000..e9549e157cd --- /dev/null +++ b/src/tools/clippy/tests/ui/fn_to_numeric_cast.stderr @@ -0,0 +1,144 @@ +error: casting function pointer `foo` to `i8`, which truncates the value + --> $DIR/fn_to_numeric_cast.rs:10:13 + | +LL | let _ = foo as i8; + | ^^^^^^^^^ help: try: `foo as usize` + | + = note: `-D clippy::fn-to-numeric-cast-with-truncation` implied by `-D warnings` + +error: casting function pointer `foo` to `i16`, which truncates the value + --> $DIR/fn_to_numeric_cast.rs:11:13 + | +LL | let _ = foo as i16; + | ^^^^^^^^^^ help: try: `foo as usize` + +error: casting function pointer `foo` to `i32`, which truncates the value + --> $DIR/fn_to_numeric_cast.rs:12:13 + | +LL | let _ = foo as i32; + | ^^^^^^^^^^ help: try: `foo as usize` + +error: casting function pointer `foo` to `i64` + --> $DIR/fn_to_numeric_cast.rs:13:13 + | +LL | let _ = foo as i64; + | ^^^^^^^^^^ help: try: `foo as usize` + | + = note: `-D clippy::fn-to-numeric-cast` implied by `-D warnings` + +error: casting function pointer `foo` to `i128` + --> $DIR/fn_to_numeric_cast.rs:14:13 + | +LL | let _ = foo as i128; + | ^^^^^^^^^^^ help: try: `foo as usize` + +error: casting function pointer `foo` to `isize` + --> $DIR/fn_to_numeric_cast.rs:15:13 + | +LL | let _ = foo as isize; + | ^^^^^^^^^^^^ help: try: `foo as usize` + +error: casting function pointer `foo` to `u8`, which truncates the value + --> $DIR/fn_to_numeric_cast.rs:17:13 + | +LL | let _ = foo as u8; + | ^^^^^^^^^ help: try: `foo as usize` + +error: casting function pointer `foo` to `u16`, which truncates the value + --> $DIR/fn_to_numeric_cast.rs:18:13 + | +LL | let _ = foo as u16; + | ^^^^^^^^^^ help: try: `foo as usize` + +error: casting function pointer `foo` to `u32`, which truncates the value + --> $DIR/fn_to_numeric_cast.rs:19:13 + | +LL | let _ = foo as u32; + | ^^^^^^^^^^ help: try: `foo as usize` + +error: casting function pointer `foo` to `u64` + --> $DIR/fn_to_numeric_cast.rs:20:13 + | +LL | let _ = foo as u64; + | ^^^^^^^^^^ help: try: `foo as usize` + +error: casting function pointer `foo` to `u128` + --> $DIR/fn_to_numeric_cast.rs:21:13 + | +LL | let _ = foo as u128; + | ^^^^^^^^^^^ help: try: `foo as usize` + +error: casting function pointer `abc` to `i8`, which truncates the value + --> $DIR/fn_to_numeric_cast.rs:34:13 + | +LL | let _ = abc as i8; + | ^^^^^^^^^ help: try: `abc as usize` + +error: casting function pointer `abc` to `i16`, which truncates the value + --> $DIR/fn_to_numeric_cast.rs:35:13 + | +LL | let _ = abc as i16; + | ^^^^^^^^^^ help: try: `abc as usize` + +error: casting function pointer `abc` to `i32`, which truncates the value + --> $DIR/fn_to_numeric_cast.rs:36:13 + | +LL | let _ = abc as i32; + | ^^^^^^^^^^ help: try: `abc as usize` + +error: casting function pointer `abc` to `i64` + --> $DIR/fn_to_numeric_cast.rs:37:13 + | +LL | let _ = abc as i64; + | ^^^^^^^^^^ help: try: `abc as usize` + +error: casting function pointer `abc` to `i128` + --> $DIR/fn_to_numeric_cast.rs:38:13 + | +LL | let _ = abc as i128; + | ^^^^^^^^^^^ help: try: `abc as usize` + +error: casting function pointer `abc` to `isize` + --> $DIR/fn_to_numeric_cast.rs:39:13 + | +LL | let _ = abc as isize; + | ^^^^^^^^^^^^ help: try: `abc as usize` + +error: casting function pointer `abc` to `u8`, which truncates the value + --> $DIR/fn_to_numeric_cast.rs:41:13 + | +LL | let _ = abc as u8; + | ^^^^^^^^^ help: try: `abc as usize` + +error: casting function pointer `abc` to `u16`, which truncates the value + --> $DIR/fn_to_numeric_cast.rs:42:13 + | +LL | let _ = abc as u16; + | ^^^^^^^^^^ help: try: `abc as usize` + +error: casting function pointer `abc` to `u32`, which truncates the value + --> $DIR/fn_to_numeric_cast.rs:43:13 + | +LL | let _ = abc as u32; + | ^^^^^^^^^^ help: try: `abc as usize` + +error: casting function pointer `abc` to `u64` + --> $DIR/fn_to_numeric_cast.rs:44:13 + | +LL | let _ = abc as u64; + | ^^^^^^^^^^ help: try: `abc as usize` + +error: casting function pointer `abc` to `u128` + --> $DIR/fn_to_numeric_cast.rs:45:13 + | +LL | let _ = abc as u128; + | ^^^^^^^^^^^ help: try: `abc as usize` + +error: casting function pointer `f` to `i32`, which truncates the value + --> $DIR/fn_to_numeric_cast.rs:52:5 + | +LL | f as i32 + | ^^^^^^^^ help: try: `f as usize` + +error: aborting due to 23 previous errors + diff --git a/src/tools/clippy/tests/ui/fn_to_numeric_cast_32bit.rs b/src/tools/clippy/tests/ui/fn_to_numeric_cast_32bit.rs new file mode 100644 index 00000000000..04ee985c086 --- /dev/null +++ b/src/tools/clippy/tests/ui/fn_to_numeric_cast_32bit.rs @@ -0,0 +1,55 @@ +// ignore-64bit + +#![warn(clippy::fn_to_numeric_cast, clippy::fn_to_numeric_cast_with_truncation)] + +fn foo() -> String { + String::new() +} + +fn test_function_to_numeric_cast() { + let _ = foo as i8; + let _ = foo as i16; + let _ = foo as i32; + let _ = foo as i64; + let _ = foo as i128; + let _ = foo as isize; + + let _ = foo as u8; + let _ = foo as u16; + let _ = foo as u32; + let _ = foo as u64; + let _ = foo as u128; + + // Casting to usize is OK and should not warn + let _ = foo as usize; + + // Cast `f` (a `FnDef`) to `fn()` should not warn + fn f() {} + let _ = f as fn(); +} + +fn test_function_var_to_numeric_cast() { + let abc: fn() -> String = foo; + + let _ = abc as i8; + let _ = abc as i16; + let _ = abc as i32; + let _ = abc as i64; + let _ = abc as i128; + let _ = abc as isize; + + let _ = abc as u8; + let _ = abc as u16; + let _ = abc as u32; + let _ = abc as u64; + let _ = abc as u128; + + // Casting to usize is OK and should not warn + let _ = abc as usize; +} + +fn fn_with_fn_args(f: fn(i32) -> i32) -> i32 { + f as i32 +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/fn_to_numeric_cast_32bit.stderr b/src/tools/clippy/tests/ui/fn_to_numeric_cast_32bit.stderr new file mode 100644 index 00000000000..08dd611d675 --- /dev/null +++ b/src/tools/clippy/tests/ui/fn_to_numeric_cast_32bit.stderr @@ -0,0 +1,144 @@ +error: casting function pointer `foo` to `i8`, which truncates the value + --> $DIR/fn_to_numeric_cast_32bit.rs:10:13 + | +LL | let _ = foo as i8; + | ^^^^^^^^^ help: try: `foo as usize` + | + = note: `-D clippy::fn-to-numeric-cast-with-truncation` implied by `-D warnings` + +error: casting function pointer `foo` to `i16`, which truncates the value + --> $DIR/fn_to_numeric_cast_32bit.rs:11:13 + | +LL | let _ = foo as i16; + | ^^^^^^^^^^ help: try: `foo as usize` + +error: casting function pointer `foo` to `i32` + --> $DIR/fn_to_numeric_cast_32bit.rs:12:13 + | +LL | let _ = foo as i32; + | ^^^^^^^^^^ help: try: `foo as usize` + | + = note: `-D clippy::fn-to-numeric-cast` implied by `-D warnings` + +error: casting function pointer `foo` to `i64` + --> $DIR/fn_to_numeric_cast_32bit.rs:13:13 + | +LL | let _ = foo as i64; + | ^^^^^^^^^^ help: try: `foo as usize` + +error: casting function pointer `foo` to `i128` + --> $DIR/fn_to_numeric_cast_32bit.rs:14:13 + | +LL | let _ = foo as i128; + | ^^^^^^^^^^^ help: try: `foo as usize` + +error: casting function pointer `foo` to `isize` + --> $DIR/fn_to_numeric_cast_32bit.rs:15:13 + | +LL | let _ = foo as isize; + | ^^^^^^^^^^^^ help: try: `foo as usize` + +error: casting function pointer `foo` to `u8`, which truncates the value + --> $DIR/fn_to_numeric_cast_32bit.rs:17:13 + | +LL | let _ = foo as u8; + | ^^^^^^^^^ help: try: `foo as usize` + +error: casting function pointer `foo` to `u16`, which truncates the value + --> $DIR/fn_to_numeric_cast_32bit.rs:18:13 + | +LL | let _ = foo as u16; + | ^^^^^^^^^^ help: try: `foo as usize` + +error: casting function pointer `foo` to `u32` + --> $DIR/fn_to_numeric_cast_32bit.rs:19:13 + | +LL | let _ = foo as u32; + | ^^^^^^^^^^ help: try: `foo as usize` + +error: casting function pointer `foo` to `u64` + --> $DIR/fn_to_numeric_cast_32bit.rs:20:13 + | +LL | let _ = foo as u64; + | ^^^^^^^^^^ help: try: `foo as usize` + +error: casting function pointer `foo` to `u128` + --> $DIR/fn_to_numeric_cast_32bit.rs:21:13 + | +LL | let _ = foo as u128; + | ^^^^^^^^^^^ help: try: `foo as usize` + +error: casting function pointer `abc` to `i8`, which truncates the value + --> $DIR/fn_to_numeric_cast_32bit.rs:34:13 + | +LL | let _ = abc as i8; + | ^^^^^^^^^ help: try: `abc as usize` + +error: casting function pointer `abc` to `i16`, which truncates the value + --> $DIR/fn_to_numeric_cast_32bit.rs:35:13 + | +LL | let _ = abc as i16; + | ^^^^^^^^^^ help: try: `abc as usize` + +error: casting function pointer `abc` to `i32` + --> $DIR/fn_to_numeric_cast_32bit.rs:36:13 + | +LL | let _ = abc as i32; + | ^^^^^^^^^^ help: try: `abc as usize` + +error: casting function pointer `abc` to `i64` + --> $DIR/fn_to_numeric_cast_32bit.rs:37:13 + | +LL | let _ = abc as i64; + | ^^^^^^^^^^ help: try: `abc as usize` + +error: casting function pointer `abc` to `i128` + --> $DIR/fn_to_numeric_cast_32bit.rs:38:13 + | +LL | let _ = abc as i128; + | ^^^^^^^^^^^ help: try: `abc as usize` + +error: casting function pointer `abc` to `isize` + --> $DIR/fn_to_numeric_cast_32bit.rs:39:13 + | +LL | let _ = abc as isize; + | ^^^^^^^^^^^^ help: try: `abc as usize` + +error: casting function pointer `abc` to `u8`, which truncates the value + --> $DIR/fn_to_numeric_cast_32bit.rs:41:13 + | +LL | let _ = abc as u8; + | ^^^^^^^^^ help: try: `abc as usize` + +error: casting function pointer `abc` to `u16`, which truncates the value + --> $DIR/fn_to_numeric_cast_32bit.rs:42:13 + | +LL | let _ = abc as u16; + | ^^^^^^^^^^ help: try: `abc as usize` + +error: casting function pointer `abc` to `u32` + --> $DIR/fn_to_numeric_cast_32bit.rs:43:13 + | +LL | let _ = abc as u32; + | ^^^^^^^^^^ help: try: `abc as usize` + +error: casting function pointer `abc` to `u64` + --> $DIR/fn_to_numeric_cast_32bit.rs:44:13 + | +LL | let _ = abc as u64; + | ^^^^^^^^^^ help: try: `abc as usize` + +error: casting function pointer `abc` to `u128` + --> $DIR/fn_to_numeric_cast_32bit.rs:45:13 + | +LL | let _ = abc as u128; + | ^^^^^^^^^^^ help: try: `abc as usize` + +error: casting function pointer `f` to `i32` + --> $DIR/fn_to_numeric_cast_32bit.rs:52:5 + | +LL | f as i32 + | ^^^^^^^^ help: try: `f as usize` + +error: aborting due to 23 previous errors + diff --git a/src/tools/clippy/tests/ui/for_kv_map.rs b/src/tools/clippy/tests/ui/for_kv_map.rs new file mode 100644 index 00000000000..39a8d960a7e --- /dev/null +++ b/src/tools/clippy/tests/ui/for_kv_map.rs @@ -0,0 +1,50 @@ +#![warn(clippy::for_kv_map)] +#![allow(clippy::used_underscore_binding)] + +use std::collections::*; +use std::rc::Rc; + +fn main() { + let m: HashMap<u64, u64> = HashMap::new(); + for (_, v) in &m { + let _v = v; + } + + let m: Rc<HashMap<u64, u64>> = Rc::new(HashMap::new()); + for (_, v) in &*m { + let _v = v; + // Here the `*` is not actually necessary, but the test tests that we don't + // suggest + // `in *m.values()` as we used to + } + + let mut m: HashMap<u64, u64> = HashMap::new(); + for (_, v) in &mut m { + let _v = v; + } + + let m: &mut HashMap<u64, u64> = &mut HashMap::new(); + for (_, v) in &mut *m { + let _v = v; + } + + let m: HashMap<u64, u64> = HashMap::new(); + let rm = &m; + for (k, _value) in rm { + let _k = k; + } + + // The following should not produce warnings. + + let m: HashMap<u64, u64> = HashMap::new(); + // No error, _value is actually used + for (k, _value) in &m { + let _ = _value; + let _k = k; + } + + let m: HashMap<u64, String> = Default::default(); + for (_, v) in m { + let _v = v; + } +} diff --git a/src/tools/clippy/tests/ui/for_kv_map.stderr b/src/tools/clippy/tests/ui/for_kv_map.stderr new file mode 100644 index 00000000000..241758c542c --- /dev/null +++ b/src/tools/clippy/tests/ui/for_kv_map.stderr @@ -0,0 +1,58 @@ +error: you seem to want to iterate on a map's values + --> $DIR/for_kv_map.rs:9:19 + | +LL | for (_, v) in &m { + | ^^ + | + = note: `-D clippy::for-kv-map` implied by `-D warnings` +help: use the corresponding method + | +LL | for v in m.values() { + | ^ ^^^^^^^^^^ + +error: you seem to want to iterate on a map's values + --> $DIR/for_kv_map.rs:14:19 + | +LL | for (_, v) in &*m { + | ^^^ + | +help: use the corresponding method + | +LL | for v in (*m).values() { + | ^ ^^^^^^^^^^^^^ + +error: you seem to want to iterate on a map's values + --> $DIR/for_kv_map.rs:22:19 + | +LL | for (_, v) in &mut m { + | ^^^^^^ + | +help: use the corresponding method + | +LL | for v in m.values_mut() { + | ^ ^^^^^^^^^^^^^^ + +error: you seem to want to iterate on a map's values + --> $DIR/for_kv_map.rs:27:19 + | +LL | for (_, v) in &mut *m { + | ^^^^^^^ + | +help: use the corresponding method + | +LL | for v in (*m).values_mut() { + | ^ ^^^^^^^^^^^^^^^^^ + +error: you seem to want to iterate on a map's keys + --> $DIR/for_kv_map.rs:33:24 + | +LL | for (k, _value) in rm { + | ^^ + | +help: use the corresponding method + | +LL | for k in rm.keys() { + | ^ ^^^^^^^^^ + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/for_loop_fixable.fixed b/src/tools/clippy/tests/ui/for_loop_fixable.fixed new file mode 100644 index 00000000000..5fc84ada9ef --- /dev/null +++ b/src/tools/clippy/tests/ui/for_loop_fixable.fixed @@ -0,0 +1,337 @@ +// run-rustfix + +#![allow(dead_code, unused)] + +use std::collections::*; + +#[warn(clippy::all)] +struct Unrelated(Vec<u8>); +impl Unrelated { + fn next(&self) -> std::slice::Iter<u8> { + self.0.iter() + } + + fn iter(&self) -> std::slice::Iter<u8> { + self.0.iter() + } +} + +#[warn( + clippy::needless_range_loop, + clippy::explicit_iter_loop, + clippy::explicit_into_iter_loop, + clippy::iter_next_loop, + clippy::reverse_range_loop, + clippy::for_kv_map +)] +#[allow( + clippy::linkedlist, + clippy::shadow_unrelated, + clippy::unnecessary_mut_passed, + clippy::similar_names +)] +#[allow(clippy::many_single_char_names, unused_variables)] +fn main() { + const MAX_LEN: usize = 42; + let mut vec = vec![1, 2, 3, 4]; + + for i in (0..10).rev() { + println!("{}", i); + } + + for i in (0..=10).rev() { + println!("{}", i); + } + + for i in (0..MAX_LEN).rev() { + println!("{}", i); + } + + for i in 5..=5 { + // not an error, this is the range with only one element “5” + println!("{}", i); + } + + for i in 0..10 { + // not an error, the start index is less than the end index + println!("{}", i); + } + + for i in -10..0 { + // not an error + println!("{}", i); + } + + for i in (10..0).map(|x| x * 2) { + // not an error, it can't be known what arbitrary methods do to a range + println!("{}", i); + } + + // testing that the empty range lint folds constants + for i in (5 + 4..10).rev() { + println!("{}", i); + } + + for i in ((3 - 1)..(5 + 2)).rev() { + println!("{}", i); + } + + for i in (2 * 2)..(2 * 3) { + // no error, 4..6 is fine + println!("{}", i); + } + + let x = 42; + for i in x..10 { + // no error, not constant-foldable + println!("{}", i); + } + + // See #601 + for i in 0..10 { + // no error, id_col does not exist outside the loop + let mut id_col = vec![0f64; 10]; + id_col[i] = 1f64; + } + + for _v in &vec {} + + for _v in &mut vec {} + + let out_vec = vec![1, 2, 3]; + for _v in out_vec {} + + for _v in &vec {} // these are fine + for _v in &mut vec {} // these are fine + + for _v in &[1, 2, 3] {} + + for _v in (&mut [1, 2, 3]).iter() {} // no error + + for _v in &[0; 32] {} + + for _v in [0; 33].iter() {} // no error + + let ll: LinkedList<()> = LinkedList::new(); + for _v in &ll {} + + let vd: VecDeque<()> = VecDeque::new(); + for _v in &vd {} + + let bh: BinaryHeap<()> = BinaryHeap::new(); + for _v in &bh {} + + let hm: HashMap<(), ()> = HashMap::new(); + for _v in &hm {} + + let bt: BTreeMap<(), ()> = BTreeMap::new(); + for _v in &bt {} + + let hs: HashSet<()> = HashSet::new(); + for _v in &hs {} + + let bs: BTreeSet<()> = BTreeSet::new(); + for _v in &bs {} + + let u = Unrelated(vec![]); + for _v in u.next() {} // no error + for _v in u.iter() {} // no error + + let mut out = vec![]; + vec.iter().cloned().map(|x| out.push(x)).collect::<Vec<_>>(); + let _y = vec.iter().cloned().map(|x| out.push(x)).collect::<Vec<_>>(); // this is fine + + // Loop with explicit counter variable + + // Potential false positives + let mut _index = 0; + _index = 1; + for _v in &vec { + _index += 1 + } + + let mut _index = 0; + _index += 1; + for _v in &vec { + _index += 1 + } + + let mut _index = 0; + if true { + _index = 1 + } + for _v in &vec { + _index += 1 + } + + let mut _index = 0; + let mut _index = 1; + for _v in &vec { + _index += 1 + } + + let mut _index = 0; + for _v in &vec { + _index += 1; + _index += 1 + } + + let mut _index = 0; + for _v in &vec { + _index *= 2; + _index += 1 + } + + let mut _index = 0; + for _v in &vec { + _index = 1; + _index += 1 + } + + let mut _index = 0; + + for _v in &vec { + let mut _index = 0; + _index += 1 + } + + let mut _index = 0; + for _v in &vec { + _index += 1; + _index = 0; + } + + let mut _index = 0; + for _v in &vec { + for _x in 0..1 { + _index += 1; + } + _index += 1 + } + + let mut _index = 0; + for x in &vec { + if *x == 1 { + _index += 1 + } + } + + let mut _index = 0; + if true { + _index = 1 + }; + for _v in &vec { + _index += 1 + } + + let mut _index = 1; + if false { + _index = 0 + }; + for _v in &vec { + _index += 1 + } + + let mut index = 0; + { + let mut _x = &mut index; + } + for _v in &vec { + _index += 1 + } + + let mut index = 0; + for _v in &vec { + index += 1 + } + println!("index: {}", index); + + fn f<T>(_: &T, _: &T) -> bool { + unimplemented!() + } + fn g<T>(_: &mut [T], _: usize, _: usize) { + unimplemented!() + } + for i in 1..vec.len() { + if f(&vec[i - 1], &vec[i]) { + g(&mut vec, i - 1, i); + } + } + + for mid in 1..vec.len() { + let (_, _) = vec.split_at(mid); + } +} + +fn partition<T: PartialOrd + Send>(v: &mut [T]) -> usize { + let pivot = v.len() - 1; + let mut i = 0; + for j in 0..pivot { + if v[j] <= v[pivot] { + v.swap(i, j); + i += 1; + } + } + v.swap(i, pivot); + i +} + +#[warn(clippy::needless_range_loop)] +pub fn manual_copy_same_destination(dst: &mut [i32], d: usize, s: usize) { + // Same source and destination - don't trigger lint + for i in 0..dst.len() { + dst[d + i] = dst[s + i]; + } +} + +mod issue_2496 { + pub trait Handle { + fn new_for_index(index: usize) -> Self; + fn index(&self) -> usize; + } + + pub fn test<H: Handle>() -> H { + for x in 0..5 { + let next_handle = H::new_for_index(x); + println!("{}", next_handle.index()); + } + unimplemented!() + } +} + +// explicit_into_iter_loop bad suggestions +#[warn(clippy::explicit_into_iter_loop, clippy::explicit_iter_loop)] +mod issue_4958 { + fn takes_iterator<T>(iterator: &T) + where + for<'a> &'a T: IntoIterator<Item = &'a String>, + { + for i in iterator { + println!("{}", i); + } + } + + struct T; + impl IntoIterator for &T { + type Item = (); + type IntoIter = std::vec::IntoIter<Self::Item>; + fn into_iter(self) -> Self::IntoIter { + vec![].into_iter() + } + } + + fn more_tests() { + let t = T; + let r = &t; + let rr = &&t; + + // This case is handled by `explicit_iter_loop`. No idea why. + for _ in &t {} + + for _ in r {} + + // No suggestion for this. + // We'd have to suggest `for _ in *rr {}` which is less clear. + for _ in rr.into_iter() {} + } +} diff --git a/src/tools/clippy/tests/ui/for_loop_fixable.rs b/src/tools/clippy/tests/ui/for_loop_fixable.rs new file mode 100644 index 00000000000..4165b0dc004 --- /dev/null +++ b/src/tools/clippy/tests/ui/for_loop_fixable.rs @@ -0,0 +1,337 @@ +// run-rustfix + +#![allow(dead_code, unused)] + +use std::collections::*; + +#[warn(clippy::all)] +struct Unrelated(Vec<u8>); +impl Unrelated { + fn next(&self) -> std::slice::Iter<u8> { + self.0.iter() + } + + fn iter(&self) -> std::slice::Iter<u8> { + self.0.iter() + } +} + +#[warn( + clippy::needless_range_loop, + clippy::explicit_iter_loop, + clippy::explicit_into_iter_loop, + clippy::iter_next_loop, + clippy::reverse_range_loop, + clippy::for_kv_map +)] +#[allow( + clippy::linkedlist, + clippy::shadow_unrelated, + clippy::unnecessary_mut_passed, + clippy::similar_names +)] +#[allow(clippy::many_single_char_names, unused_variables)] +fn main() { + const MAX_LEN: usize = 42; + let mut vec = vec![1, 2, 3, 4]; + + for i in 10..0 { + println!("{}", i); + } + + for i in 10..=0 { + println!("{}", i); + } + + for i in MAX_LEN..0 { + println!("{}", i); + } + + for i in 5..=5 { + // not an error, this is the range with only one element “5” + println!("{}", i); + } + + for i in 0..10 { + // not an error, the start index is less than the end index + println!("{}", i); + } + + for i in -10..0 { + // not an error + println!("{}", i); + } + + for i in (10..0).map(|x| x * 2) { + // not an error, it can't be known what arbitrary methods do to a range + println!("{}", i); + } + + // testing that the empty range lint folds constants + for i in 10..5 + 4 { + println!("{}", i); + } + + for i in (5 + 2)..(3 - 1) { + println!("{}", i); + } + + for i in (2 * 2)..(2 * 3) { + // no error, 4..6 is fine + println!("{}", i); + } + + let x = 42; + for i in x..10 { + // no error, not constant-foldable + println!("{}", i); + } + + // See #601 + for i in 0..10 { + // no error, id_col does not exist outside the loop + let mut id_col = vec![0f64; 10]; + id_col[i] = 1f64; + } + + for _v in vec.iter() {} + + for _v in vec.iter_mut() {} + + let out_vec = vec![1, 2, 3]; + for _v in out_vec.into_iter() {} + + for _v in &vec {} // these are fine + for _v in &mut vec {} // these are fine + + for _v in [1, 2, 3].iter() {} + + for _v in (&mut [1, 2, 3]).iter() {} // no error + + for _v in [0; 32].iter() {} + + for _v in [0; 33].iter() {} // no error + + let ll: LinkedList<()> = LinkedList::new(); + for _v in ll.iter() {} + + let vd: VecDeque<()> = VecDeque::new(); + for _v in vd.iter() {} + + let bh: BinaryHeap<()> = BinaryHeap::new(); + for _v in bh.iter() {} + + let hm: HashMap<(), ()> = HashMap::new(); + for _v in hm.iter() {} + + let bt: BTreeMap<(), ()> = BTreeMap::new(); + for _v in bt.iter() {} + + let hs: HashSet<()> = HashSet::new(); + for _v in hs.iter() {} + + let bs: BTreeSet<()> = BTreeSet::new(); + for _v in bs.iter() {} + + let u = Unrelated(vec![]); + for _v in u.next() {} // no error + for _v in u.iter() {} // no error + + let mut out = vec![]; + vec.iter().cloned().map(|x| out.push(x)).collect::<Vec<_>>(); + let _y = vec.iter().cloned().map(|x| out.push(x)).collect::<Vec<_>>(); // this is fine + + // Loop with explicit counter variable + + // Potential false positives + let mut _index = 0; + _index = 1; + for _v in &vec { + _index += 1 + } + + let mut _index = 0; + _index += 1; + for _v in &vec { + _index += 1 + } + + let mut _index = 0; + if true { + _index = 1 + } + for _v in &vec { + _index += 1 + } + + let mut _index = 0; + let mut _index = 1; + for _v in &vec { + _index += 1 + } + + let mut _index = 0; + for _v in &vec { + _index += 1; + _index += 1 + } + + let mut _index = 0; + for _v in &vec { + _index *= 2; + _index += 1 + } + + let mut _index = 0; + for _v in &vec { + _index = 1; + _index += 1 + } + + let mut _index = 0; + + for _v in &vec { + let mut _index = 0; + _index += 1 + } + + let mut _index = 0; + for _v in &vec { + _index += 1; + _index = 0; + } + + let mut _index = 0; + for _v in &vec { + for _x in 0..1 { + _index += 1; + } + _index += 1 + } + + let mut _index = 0; + for x in &vec { + if *x == 1 { + _index += 1 + } + } + + let mut _index = 0; + if true { + _index = 1 + }; + for _v in &vec { + _index += 1 + } + + let mut _index = 1; + if false { + _index = 0 + }; + for _v in &vec { + _index += 1 + } + + let mut index = 0; + { + let mut _x = &mut index; + } + for _v in &vec { + _index += 1 + } + + let mut index = 0; + for _v in &vec { + index += 1 + } + println!("index: {}", index); + + fn f<T>(_: &T, _: &T) -> bool { + unimplemented!() + } + fn g<T>(_: &mut [T], _: usize, _: usize) { + unimplemented!() + } + for i in 1..vec.len() { + if f(&vec[i - 1], &vec[i]) { + g(&mut vec, i - 1, i); + } + } + + for mid in 1..vec.len() { + let (_, _) = vec.split_at(mid); + } +} + +fn partition<T: PartialOrd + Send>(v: &mut [T]) -> usize { + let pivot = v.len() - 1; + let mut i = 0; + for j in 0..pivot { + if v[j] <= v[pivot] { + v.swap(i, j); + i += 1; + } + } + v.swap(i, pivot); + i +} + +#[warn(clippy::needless_range_loop)] +pub fn manual_copy_same_destination(dst: &mut [i32], d: usize, s: usize) { + // Same source and destination - don't trigger lint + for i in 0..dst.len() { + dst[d + i] = dst[s + i]; + } +} + +mod issue_2496 { + pub trait Handle { + fn new_for_index(index: usize) -> Self; + fn index(&self) -> usize; + } + + pub fn test<H: Handle>() -> H { + for x in 0..5 { + let next_handle = H::new_for_index(x); + println!("{}", next_handle.index()); + } + unimplemented!() + } +} + +// explicit_into_iter_loop bad suggestions +#[warn(clippy::explicit_into_iter_loop, clippy::explicit_iter_loop)] +mod issue_4958 { + fn takes_iterator<T>(iterator: &T) + where + for<'a> &'a T: IntoIterator<Item = &'a String>, + { + for i in iterator.into_iter() { + println!("{}", i); + } + } + + struct T; + impl IntoIterator for &T { + type Item = (); + type IntoIter = std::vec::IntoIter<Self::Item>; + fn into_iter(self) -> Self::IntoIter { + vec![].into_iter() + } + } + + fn more_tests() { + let t = T; + let r = &t; + let rr = &&t; + + // This case is handled by `explicit_iter_loop`. No idea why. + for _ in t.into_iter() {} + + for _ in r.into_iter() {} + + // No suggestion for this. + // We'd have to suggest `for _ in *rr {}` which is less clear. + for _ in rr.into_iter() {} + } +} diff --git a/src/tools/clippy/tests/ui/for_loop_fixable.stderr b/src/tools/clippy/tests/ui/for_loop_fixable.stderr new file mode 100644 index 00000000000..cffb4b9f0a9 --- /dev/null +++ b/src/tools/clippy/tests/ui/for_loop_fixable.stderr @@ -0,0 +1,152 @@ +error: this range is empty so this for loop will never run + --> $DIR/for_loop_fixable.rs:38:14 + | +LL | for i in 10..0 { + | ^^^^^ + | + = note: `-D clippy::reverse-range-loop` implied by `-D warnings` +help: consider using the following if you are attempting to iterate over this range in reverse + | +LL | for i in (0..10).rev() { + | ^^^^^^^^^^^^^ + +error: this range is empty so this for loop will never run + --> $DIR/for_loop_fixable.rs:42:14 + | +LL | for i in 10..=0 { + | ^^^^^^ + | +help: consider using the following if you are attempting to iterate over this range in reverse + | +LL | for i in (0..=10).rev() { + | ^^^^^^^^^^^^^^ + +error: this range is empty so this for loop will never run + --> $DIR/for_loop_fixable.rs:46:14 + | +LL | for i in MAX_LEN..0 { + | ^^^^^^^^^^ + | +help: consider using the following if you are attempting to iterate over this range in reverse + | +LL | for i in (0..MAX_LEN).rev() { + | ^^^^^^^^^^^^^^^^^^ + +error: this range is empty so this for loop will never run + --> $DIR/for_loop_fixable.rs:71:14 + | +LL | for i in 10..5 + 4 { + | ^^^^^^^^^ + | +help: consider using the following if you are attempting to iterate over this range in reverse + | +LL | for i in (5 + 4..10).rev() { + | ^^^^^^^^^^^^^^^^^ + +error: this range is empty so this for loop will never run + --> $DIR/for_loop_fixable.rs:75:14 + | +LL | for i in (5 + 2)..(3 - 1) { + | ^^^^^^^^^^^^^^^^ + | +help: consider using the following if you are attempting to iterate over this range in reverse + | +LL | for i in ((3 - 1)..(5 + 2)).rev() { + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: it is more concise to loop over references to containers instead of using explicit iteration methods + --> $DIR/for_loop_fixable.rs:97:15 + | +LL | for _v in vec.iter() {} + | ^^^^^^^^^^ help: to write this more concisely, try: `&vec` + | + = note: `-D clippy::explicit-iter-loop` implied by `-D warnings` + +error: it is more concise to loop over references to containers instead of using explicit iteration methods + --> $DIR/for_loop_fixable.rs:99:15 + | +LL | for _v in vec.iter_mut() {} + | ^^^^^^^^^^^^^^ help: to write this more concisely, try: `&mut vec` + +error: it is more concise to loop over containers instead of using explicit iteration methods + --> $DIR/for_loop_fixable.rs:102:15 + | +LL | for _v in out_vec.into_iter() {} + | ^^^^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `out_vec` + | + = note: `-D clippy::explicit-into-iter-loop` implied by `-D warnings` + +error: it is more concise to loop over references to containers instead of using explicit iteration methods + --> $DIR/for_loop_fixable.rs:107:15 + | +LL | for _v in [1, 2, 3].iter() {} + | ^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `&[1, 2, 3]` + +error: it is more concise to loop over references to containers instead of using explicit iteration methods + --> $DIR/for_loop_fixable.rs:111:15 + | +LL | for _v in [0; 32].iter() {} + | ^^^^^^^^^^^^^^ help: to write this more concisely, try: `&[0; 32]` + +error: it is more concise to loop over references to containers instead of using explicit iteration methods + --> $DIR/for_loop_fixable.rs:116:15 + | +LL | for _v in ll.iter() {} + | ^^^^^^^^^ help: to write this more concisely, try: `&ll` + +error: it is more concise to loop over references to containers instead of using explicit iteration methods + --> $DIR/for_loop_fixable.rs:119:15 + | +LL | for _v in vd.iter() {} + | ^^^^^^^^^ help: to write this more concisely, try: `&vd` + +error: it is more concise to loop over references to containers instead of using explicit iteration methods + --> $DIR/for_loop_fixable.rs:122:15 + | +LL | for _v in bh.iter() {} + | ^^^^^^^^^ help: to write this more concisely, try: `&bh` + +error: it is more concise to loop over references to containers instead of using explicit iteration methods + --> $DIR/for_loop_fixable.rs:125:15 + | +LL | for _v in hm.iter() {} + | ^^^^^^^^^ help: to write this more concisely, try: `&hm` + +error: it is more concise to loop over references to containers instead of using explicit iteration methods + --> $DIR/for_loop_fixable.rs:128:15 + | +LL | for _v in bt.iter() {} + | ^^^^^^^^^ help: to write this more concisely, try: `&bt` + +error: it is more concise to loop over references to containers instead of using explicit iteration methods + --> $DIR/for_loop_fixable.rs:131:15 + | +LL | for _v in hs.iter() {} + | ^^^^^^^^^ help: to write this more concisely, try: `&hs` + +error: it is more concise to loop over references to containers instead of using explicit iteration methods + --> $DIR/for_loop_fixable.rs:134:15 + | +LL | for _v in bs.iter() {} + | ^^^^^^^^^ help: to write this more concisely, try: `&bs` + +error: it is more concise to loop over containers instead of using explicit iteration methods + --> $DIR/for_loop_fixable.rs:309:18 + | +LL | for i in iterator.into_iter() { + | ^^^^^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `iterator` + +error: it is more concise to loop over references to containers instead of using explicit iteration methods + --> $DIR/for_loop_fixable.rs:329:18 + | +LL | for _ in t.into_iter() {} + | ^^^^^^^^^^^^^ help: to write this more concisely, try: `&t` + +error: it is more concise to loop over containers instead of using explicit iteration methods + --> $DIR/for_loop_fixable.rs:331:18 + | +LL | for _ in r.into_iter() {} + | ^^^^^^^^^^^^^ help: to write this more concisely, try: `r` + +error: aborting due to 20 previous errors + diff --git a/src/tools/clippy/tests/ui/for_loop_over_option_result.rs b/src/tools/clippy/tests/ui/for_loop_over_option_result.rs new file mode 100644 index 00000000000..6b207b26b6b --- /dev/null +++ b/src/tools/clippy/tests/ui/for_loop_over_option_result.rs @@ -0,0 +1,59 @@ +#![warn(clippy::for_loop_over_option, clippy::for_loop_over_result)] + +/// Tests for_loop_over_result and for_loop_over_option + +fn for_loop_over_option_and_result() { + let option = Some(1); + let result = option.ok_or("x not found"); + let v = vec![0, 1, 2]; + + // check FOR_LOOP_OVER_OPTION lint + for x in option { + println!("{}", x); + } + + // check FOR_LOOP_OVER_RESULT lint + for x in result { + println!("{}", x); + } + + for x in option.ok_or("x not found") { + println!("{}", x); + } + + // make sure LOOP_OVER_NEXT lint takes clippy::precedence when next() is the last call + // in the chain + for x in v.iter().next() { + println!("{}", x); + } + + // make sure we lint when next() is not the last call in the chain + for x in v.iter().next().and(Some(0)) { + println!("{}", x); + } + + for x in v.iter().next().ok_or("x not found") { + println!("{}", x); + } + + // check for false positives + + // for loop false positive + for x in v { + println!("{}", x); + } + + // while let false positive for Option + while let Some(x) = option { + println!("{}", x); + break; + } + + // while let false positive for Result + while let Ok(x) = result { + println!("{}", x); + break; + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/for_loop_over_option_result.stderr b/src/tools/clippy/tests/ui/for_loop_over_option_result.stderr new file mode 100644 index 00000000000..194a0bfec5b --- /dev/null +++ b/src/tools/clippy/tests/ui/for_loop_over_option_result.stderr @@ -0,0 +1,72 @@ +error: for loop over `option`, which is an `Option`. This is more readably written as an `if let` statement. + --> $DIR/for_loop_over_option_result.rs:11:14 + | +LL | for x in option { + | ^^^^^^ + | + = note: `-D clippy::for-loop-over-option` implied by `-D warnings` + = help: consider replacing `for x in option` with `if let Some(x) = option` + +error: for loop over `result`, which is a `Result`. This is more readably written as an `if let` statement. + --> $DIR/for_loop_over_option_result.rs:16:14 + | +LL | for x in result { + | ^^^^^^ + | + = note: `-D clippy::for-loop-over-result` implied by `-D warnings` + = help: consider replacing `for x in result` with `if let Ok(x) = result` + +error: for loop over `option.ok_or("x not found")`, which is a `Result`. This is more readably written as an `if let` statement. + --> $DIR/for_loop_over_option_result.rs:20:14 + | +LL | for x in option.ok_or("x not found") { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider replacing `for x in option.ok_or("x not found")` with `if let Ok(x) = option.ok_or("x not found")` + +error: you are iterating over `Iterator::next()` which is an Option; this will compile but is probably not what you want + --> $DIR/for_loop_over_option_result.rs:26:14 + | +LL | for x in v.iter().next() { + | ^^^^^^^^^^^^^^^ + | + = note: `#[deny(clippy::iter_next_loop)]` on by default + +error: for loop over `v.iter().next().and(Some(0))`, which is an `Option`. This is more readably written as an `if let` statement. + --> $DIR/for_loop_over_option_result.rs:31:14 + | +LL | for x in v.iter().next().and(Some(0)) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider replacing `for x in v.iter().next().and(Some(0))` with `if let Some(x) = v.iter().next().and(Some(0))` + +error: for loop over `v.iter().next().ok_or("x not found")`, which is a `Result`. This is more readably written as an `if let` statement. + --> $DIR/for_loop_over_option_result.rs:35:14 + | +LL | for x in v.iter().next().ok_or("x not found") { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider replacing `for x in v.iter().next().ok_or("x not found")` with `if let Ok(x) = v.iter().next().ok_or("x not found")` + +error: this loop never actually loops + --> $DIR/for_loop_over_option_result.rs:47:5 + | +LL | / while let Some(x) = option { +LL | | println!("{}", x); +LL | | break; +LL | | } + | |_____^ + | + = note: `#[deny(clippy::never_loop)]` on by default + +error: this loop never actually loops + --> $DIR/for_loop_over_option_result.rs:53:5 + | +LL | / while let Ok(x) = result { +LL | | println!("{}", x); +LL | | break; +LL | | } + | |_____^ + +error: aborting due to 8 previous errors + diff --git a/src/tools/clippy/tests/ui/for_loop_unfixable.rs b/src/tools/clippy/tests/ui/for_loop_unfixable.rs new file mode 100644 index 00000000000..179b255e08c --- /dev/null +++ b/src/tools/clippy/tests/ui/for_loop_unfixable.rs @@ -0,0 +1,40 @@ +// Tests from for_loop.rs that don't have suggestions + +#[warn( + clippy::needless_range_loop, + clippy::explicit_iter_loop, + clippy::explicit_into_iter_loop, + clippy::iter_next_loop, + clippy::reverse_range_loop, + clippy::for_kv_map +)] +#[allow( + clippy::linkedlist, + clippy::shadow_unrelated, + clippy::unnecessary_mut_passed, + clippy::similar_names, + unused, + dead_code +)] +#[allow(clippy::many_single_char_names, unused_variables)] +fn main() { + for i in 5..5 { + println!("{}", i); + } + + let vec = vec![1, 2, 3, 4]; + + for _v in vec.iter().next() {} + + for i in (5 + 2)..(8 - 1) { + println!("{}", i); + } + + const ZERO: usize = 0; + + for i in ZERO..vec.len() { + if f(&vec[i], &vec[i]) { + panic!("at the disco"); + } + } +} diff --git a/src/tools/clippy/tests/ui/for_loop_unfixable.stderr b/src/tools/clippy/tests/ui/for_loop_unfixable.stderr new file mode 100644 index 00000000000..1da8e0f3588 --- /dev/null +++ b/src/tools/clippy/tests/ui/for_loop_unfixable.stderr @@ -0,0 +1,9 @@ +error[E0425]: cannot find function `f` in this scope + --> $DIR/for_loop_unfixable.rs:36:12 + | +LL | if f(&vec[i], &vec[i]) { + | ^ help: a local variable with a similar name exists: `i` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0425`. diff --git a/src/tools/clippy/tests/ui/forget_ref.rs b/src/tools/clippy/tests/ui/forget_ref.rs new file mode 100644 index 00000000000..447fdbe1fac --- /dev/null +++ b/src/tools/clippy/tests/ui/forget_ref.rs @@ -0,0 +1,48 @@ +#![warn(clippy::forget_ref)] +#![allow(clippy::toplevel_ref_arg)] + +use std::mem::forget; + +struct SomeStruct; + +fn main() { + forget(&SomeStruct); + + let mut owned = SomeStruct; + forget(&owned); + forget(&&owned); + forget(&mut owned); + forget(owned); //OK + + let reference1 = &SomeStruct; + forget(&*reference1); + + let reference2 = &mut SomeStruct; + forget(reference2); + + let ref reference3 = SomeStruct; + forget(reference3); +} + +#[allow(dead_code)] +fn test_generic_fn_forget<T>(val: T) { + forget(&val); + forget(val); //OK +} + +#[allow(dead_code)] +fn test_similarly_named_function() { + fn forget<T>(_val: T) {} + forget(&SomeStruct); //OK; call to unrelated function which happens to have the same name + std::mem::forget(&SomeStruct); +} + +#[derive(Copy, Clone)] +pub struct Error; +fn produce_half_owl_error() -> Result<(), Error> { + Ok(()) +} + +fn produce_half_owl_ok() -> Result<bool, ()> { + Ok(true) +} diff --git a/src/tools/clippy/tests/ui/forget_ref.stderr b/src/tools/clippy/tests/ui/forget_ref.stderr new file mode 100644 index 00000000000..f90bcc2762c --- /dev/null +++ b/src/tools/clippy/tests/ui/forget_ref.stderr @@ -0,0 +1,111 @@ +error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing. + --> $DIR/forget_ref.rs:9:5 + | +LL | forget(&SomeStruct); + | ^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::forget-ref` implied by `-D warnings` +note: argument has type `&SomeStruct` + --> $DIR/forget_ref.rs:9:12 + | +LL | forget(&SomeStruct); + | ^^^^^^^^^^^ + +error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing. + --> $DIR/forget_ref.rs:12:5 + | +LL | forget(&owned); + | ^^^^^^^^^^^^^^ + | +note: argument has type `&SomeStruct` + --> $DIR/forget_ref.rs:12:12 + | +LL | forget(&owned); + | ^^^^^^ + +error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing. + --> $DIR/forget_ref.rs:13:5 + | +LL | forget(&&owned); + | ^^^^^^^^^^^^^^^ + | +note: argument has type `&&SomeStruct` + --> $DIR/forget_ref.rs:13:12 + | +LL | forget(&&owned); + | ^^^^^^^ + +error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing. + --> $DIR/forget_ref.rs:14:5 + | +LL | forget(&mut owned); + | ^^^^^^^^^^^^^^^^^^ + | +note: argument has type `&mut SomeStruct` + --> $DIR/forget_ref.rs:14:12 + | +LL | forget(&mut owned); + | ^^^^^^^^^^ + +error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing. + --> $DIR/forget_ref.rs:18:5 + | +LL | forget(&*reference1); + | ^^^^^^^^^^^^^^^^^^^^ + | +note: argument has type `&SomeStruct` + --> $DIR/forget_ref.rs:18:12 + | +LL | forget(&*reference1); + | ^^^^^^^^^^^^ + +error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing. + --> $DIR/forget_ref.rs:21:5 + | +LL | forget(reference2); + | ^^^^^^^^^^^^^^^^^^ + | +note: argument has type `&mut SomeStruct` + --> $DIR/forget_ref.rs:21:12 + | +LL | forget(reference2); + | ^^^^^^^^^^ + +error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing. + --> $DIR/forget_ref.rs:24:5 + | +LL | forget(reference3); + | ^^^^^^^^^^^^^^^^^^ + | +note: argument has type `&SomeStruct` + --> $DIR/forget_ref.rs:24:12 + | +LL | forget(reference3); + | ^^^^^^^^^^ + +error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing. + --> $DIR/forget_ref.rs:29:5 + | +LL | forget(&val); + | ^^^^^^^^^^^^ + | +note: argument has type `&T` + --> $DIR/forget_ref.rs:29:12 + | +LL | forget(&val); + | ^^^^ + +error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing. + --> $DIR/forget_ref.rs:37:5 + | +LL | std::mem::forget(&SomeStruct); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: argument has type `&SomeStruct` + --> $DIR/forget_ref.rs:37:22 + | +LL | std::mem::forget(&SomeStruct); + | ^^^^^^^^^^^ + +error: aborting due to 9 previous errors + diff --git a/src/tools/clippy/tests/ui/format.fixed b/src/tools/clippy/tests/ui/format.fixed new file mode 100644 index 00000000000..30651476999 --- /dev/null +++ b/src/tools/clippy/tests/ui/format.fixed @@ -0,0 +1,67 @@ +// run-rustfix + +#![allow(clippy::print_literal, clippy::redundant_clone)] +#![warn(clippy::useless_format)] + +struct Foo(pub String); + +macro_rules! foo { + ($($t:tt)*) => (Foo(format!($($t)*))) +} + +fn main() { + "foo".to_string(); + "{}".to_string(); + "{} abc {}".to_string(); + "foo {}\n\" bar".to_string(); + + "foo".to_string(); + format!("{:?}", "foo"); // Don't warn about `Debug`. + format!("{:8}", "foo"); + format!("{:width$}", "foo", width = 8); + "foo".to_string(); // Warn when the format makes no difference. + "foo".to_string(); // Warn when the format makes no difference. + format!("foo {}", "bar"); + format!("{} bar", "foo"); + + let arg: String = "".to_owned(); + arg.to_string(); + format!("{:?}", arg); // Don't warn about debug. + format!("{:8}", arg); + format!("{:width$}", arg, width = 8); + arg.to_string(); // Warn when the format makes no difference. + arg.to_string(); // Warn when the format makes no difference. + format!("foo {}", arg); + format!("{} bar", arg); + + // We don’t want to warn for non-string args; see issue #697. + format!("{}", 42); + format!("{:?}", 42); + format!("{:+}", 42); + format!("foo {}", 42); + format!("{} bar", 42); + + // We only want to warn about `format!` itself. + println!("foo"); + println!("{}", "foo"); + println!("foo {}", "foo"); + println!("{}", 42); + println!("foo {}", 42); + + // A `format!` inside a macro should not trigger a warning. + foo!("should not warn"); + + // Precision on string means slicing without panicking on size. + format!("{:.1}", "foo"); // Could be `"foo"[..1]` + format!("{:.10}", "foo"); // Could not be `"foo"[..10]` + format!("{:.prec$}", "foo", prec = 1); + format!("{:.prec$}", "foo", prec = 10); + + 42.to_string(); + let x = std::path::PathBuf::from("/bar/foo/qux"); + x.display().to_string(); + + // False positive + let a = "foo".to_string(); + let _ = Some(a + "bar"); +} diff --git a/src/tools/clippy/tests/ui/format.rs b/src/tools/clippy/tests/ui/format.rs new file mode 100644 index 00000000000..b604d79cca3 --- /dev/null +++ b/src/tools/clippy/tests/ui/format.rs @@ -0,0 +1,70 @@ +// run-rustfix + +#![allow(clippy::print_literal, clippy::redundant_clone)] +#![warn(clippy::useless_format)] + +struct Foo(pub String); + +macro_rules! foo { + ($($t:tt)*) => (Foo(format!($($t)*))) +} + +fn main() { + format!("foo"); + format!("{{}}"); + format!("{{}} abc {{}}"); + format!( + r##"foo {{}} +" bar"## + ); + + format!("{}", "foo"); + format!("{:?}", "foo"); // Don't warn about `Debug`. + format!("{:8}", "foo"); + format!("{:width$}", "foo", width = 8); + format!("{:+}", "foo"); // Warn when the format makes no difference. + format!("{:<}", "foo"); // Warn when the format makes no difference. + format!("foo {}", "bar"); + format!("{} bar", "foo"); + + let arg: String = "".to_owned(); + format!("{}", arg); + format!("{:?}", arg); // Don't warn about debug. + format!("{:8}", arg); + format!("{:width$}", arg, width = 8); + format!("{:+}", arg); // Warn when the format makes no difference. + format!("{:<}", arg); // Warn when the format makes no difference. + format!("foo {}", arg); + format!("{} bar", arg); + + // We don’t want to warn for non-string args; see issue #697. + format!("{}", 42); + format!("{:?}", 42); + format!("{:+}", 42); + format!("foo {}", 42); + format!("{} bar", 42); + + // We only want to warn about `format!` itself. + println!("foo"); + println!("{}", "foo"); + println!("foo {}", "foo"); + println!("{}", 42); + println!("foo {}", 42); + + // A `format!` inside a macro should not trigger a warning. + foo!("should not warn"); + + // Precision on string means slicing without panicking on size. + format!("{:.1}", "foo"); // Could be `"foo"[..1]` + format!("{:.10}", "foo"); // Could not be `"foo"[..10]` + format!("{:.prec$}", "foo", prec = 1); + format!("{:.prec$}", "foo", prec = 10); + + format!("{}", 42.to_string()); + let x = std::path::PathBuf::from("/bar/foo/qux"); + format!("{}", x.display().to_string()); + + // False positive + let a = "foo".to_string(); + let _ = Some(format!("{}", a + "bar")); +} diff --git a/src/tools/clippy/tests/ui/format.stderr b/src/tools/clippy/tests/ui/format.stderr new file mode 100644 index 00000000000..9734492154e --- /dev/null +++ b/src/tools/clippy/tests/ui/format.stderr @@ -0,0 +1,85 @@ +error: useless use of `format!` + --> $DIR/format.rs:13:5 + | +LL | format!("foo"); + | ^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string();` + | + = note: `-D clippy::useless-format` implied by `-D warnings` + +error: useless use of `format!` + --> $DIR/format.rs:14:5 + | +LL | format!("{{}}"); + | ^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"{}".to_string();` + +error: useless use of `format!` + --> $DIR/format.rs:15:5 + | +LL | format!("{{}} abc {{}}"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"{} abc {}".to_string();` + +error: useless use of `format!` + --> $DIR/format.rs:16:5 + | +LL | / format!( +LL | | r##"foo {{}} +LL | | " bar"## +LL | | ); + | |______^ help: consider using `.to_string()`: `"foo {}/n/" bar".to_string();` + +error: useless use of `format!` + --> $DIR/format.rs:21:5 + | +LL | format!("{}", "foo"); + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string();` + +error: useless use of `format!` + --> $DIR/format.rs:25:5 + | +LL | format!("{:+}", "foo"); // Warn when the format makes no difference. + | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string();` + +error: useless use of `format!` + --> $DIR/format.rs:26:5 + | +LL | format!("{:<}", "foo"); // Warn when the format makes no difference. + | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string();` + +error: useless use of `format!` + --> $DIR/format.rs:31:5 + | +LL | format!("{}", arg); + | ^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `arg.to_string();` + +error: useless use of `format!` + --> $DIR/format.rs:35:5 + | +LL | format!("{:+}", arg); // Warn when the format makes no difference. + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `arg.to_string();` + +error: useless use of `format!` + --> $DIR/format.rs:36:5 + | +LL | format!("{:<}", arg); // Warn when the format makes no difference. + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `arg.to_string();` + +error: useless use of `format!` + --> $DIR/format.rs:63:5 + | +LL | format!("{}", 42.to_string()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `42.to_string();` + +error: useless use of `format!` + --> $DIR/format.rs:65:5 + | +LL | format!("{}", x.display().to_string()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.display().to_string();` + +error: useless use of `format!` + --> $DIR/format.rs:69:18 + | +LL | let _ = Some(format!("{}", a + "bar")); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `a + "bar"` + +error: aborting due to 13 previous errors + diff --git a/src/tools/clippy/tests/ui/formatting.rs b/src/tools/clippy/tests/ui/formatting.rs new file mode 100644 index 00000000000..078811b8d88 --- /dev/null +++ b/src/tools/clippy/tests/ui/formatting.rs @@ -0,0 +1,157 @@ +#![warn(clippy::all)] +#![allow(unused_variables)] +#![allow(unused_assignments)] +#![allow(clippy::if_same_then_else)] +#![allow(clippy::deref_addrof)] + +fn foo() -> bool { + true +} + +#[rustfmt::skip] +fn main() { + // weird `else` formatting: + if foo() { + } { + } + + if foo() { + } if foo() { + } + + let _ = { // if as the last expression + let _ = 0; + + if foo() { + } if foo() { + } + else { + } + }; + + let _ = { // if in the middle of a block + if foo() { + } if foo() { + } + else { + } + + let _ = 0; + }; + + if foo() { + } else + { + } + + if foo() { + } + else + { + } + + if foo() { + } else + if foo() { // the span of the above error should continue here + } + + if foo() { + } + else + if foo() { // the span of the above error should continue here + } + + // those are ok: + if foo() { + } + { + } + + if foo() { + } else { + } + + if foo() { + } + else { + } + + if foo() { + } + if foo() { + } + + if foo() { + } else if foo() { + } + + if foo() { + } + else if foo() { + } + + if foo() { + } + else if + foo() {} + + // weird op_eq formatting: + let mut a = 42; + a =- 35; + a =* &191; + + let mut b = true; + b =! false; + + // those are ok: + a = -35; + a = *&191; + b = !false; + + // possible missing comma in an array + let _ = &[ + -1, -2, -3 // <= no comma here + -4, -5, -6 + ]; + let _ = &[ + -1, -2, -3 // <= no comma here + *4, -5, -6 + ]; + + // those are ok: + let _ = &[ + -1, -2, -3, + -4, -5, -6 + ]; + let _ = &[ + -1, -2, -3, + -4, -5, -6, + ]; + let _ = &[ + 1 + 2, 3 + + 4, 5 + 6, + ]; + + // don't lint for bin op without unary equiv + // issue 3244 + vec![ + 1 + / 2, + ]; + // issue 3396 + vec![ + true + | false, + ]; + + // don't lint if the indentation suggests not to + let _ = &[ + 1 + 2, 3 + - 4, 5 + ]; + // lint if it doesnt + let _ = &[ + -1 + -4, + ]; +} diff --git a/src/tools/clippy/tests/ui/formatting.stderr b/src/tools/clippy/tests/ui/formatting.stderr new file mode 100644 index 00000000000..e2095cc125b --- /dev/null +++ b/src/tools/clippy/tests/ui/formatting.stderr @@ -0,0 +1,127 @@ +error: this looks like an `else {..}` but the `else` is missing + --> $DIR/formatting.rs:15:6 + | +LL | } { + | ^ + | + = note: `-D clippy::suspicious-else-formatting` implied by `-D warnings` + = note: to remove this lint, add the missing `else` or add a new line before the next block + +error: this looks like an `else if` but the `else` is missing + --> $DIR/formatting.rs:19:6 + | +LL | } if foo() { + | ^ + | + = note: to remove this lint, add the missing `else` or add a new line before the second `if` + +error: this looks like an `else if` but the `else` is missing + --> $DIR/formatting.rs:26:10 + | +LL | } if foo() { + | ^ + | + = note: to remove this lint, add the missing `else` or add a new line before the second `if` + +error: this looks like an `else if` but the `else` is missing + --> $DIR/formatting.rs:34:10 + | +LL | } if foo() { + | ^ + | + = note: to remove this lint, add the missing `else` or add a new line before the second `if` + +error: this is an `else {..}` but the formatting might hide it + --> $DIR/formatting.rs:43:6 + | +LL | } else + | ______^ +LL | | { + | |____^ + | + = note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}` + +error: this is an `else {..}` but the formatting might hide it + --> $DIR/formatting.rs:48:6 + | +LL | } + | ______^ +LL | | else +LL | | { + | |____^ + | + = note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}` + +error: this is an `else if` but the formatting might hide it + --> $DIR/formatting.rs:54:6 + | +LL | } else + | ______^ +LL | | if foo() { // the span of the above error should continue here + | |____^ + | + = note: to remove this lint, remove the `else` or remove the new line between `else` and `if` + +error: this is an `else if` but the formatting might hide it + --> $DIR/formatting.rs:59:6 + | +LL | } + | ______^ +LL | | else +LL | | if foo() { // the span of the above error should continue here + | |____^ + | + = note: to remove this lint, remove the `else` or remove the new line between `else` and `if` + +error: this looks like you are trying to use `.. -= ..`, but you really are doing `.. = (- ..)` + --> $DIR/formatting.rs:100:6 + | +LL | a =- 35; + | ^^^^ + | + = note: `-D clippy::suspicious-assignment-formatting` implied by `-D warnings` + = note: to remove this lint, use either `-=` or `= -` + +error: this looks like you are trying to use `.. *= ..`, but you really are doing `.. = (* ..)` + --> $DIR/formatting.rs:101:6 + | +LL | a =* &191; + | ^^^^ + | + = note: to remove this lint, use either `*=` or `= *` + +error: this looks like you are trying to use `.. != ..`, but you really are doing `.. = (! ..)` + --> $DIR/formatting.rs:104:6 + | +LL | b =! false; + | ^^^^ + | + = note: to remove this lint, use either `!=` or `= !` + +error: possibly missing a comma here + --> $DIR/formatting.rs:113:19 + | +LL | -1, -2, -3 // <= no comma here + | ^ + | + = note: `-D clippy::possible-missing-comma` implied by `-D warnings` + = note: to remove this lint, add a comma or write the expr in a single line + +error: possibly missing a comma here + --> $DIR/formatting.rs:117:19 + | +LL | -1, -2, -3 // <= no comma here + | ^ + | + = note: to remove this lint, add a comma or write the expr in a single line + +error: possibly missing a comma here + --> $DIR/formatting.rs:154:11 + | +LL | -1 + | ^ + | + = note: to remove this lint, add a comma or write the expr in a single line + +error: aborting due to 14 previous errors + diff --git a/src/tools/clippy/tests/ui/functions.rs b/src/tools/clippy/tests/ui/functions.rs new file mode 100644 index 00000000000..271754cb06e --- /dev/null +++ b/src/tools/clippy/tests/ui/functions.rs @@ -0,0 +1,104 @@ +#![warn(clippy::all)] +#![allow(dead_code)] +#![allow(unused_unsafe, clippy::missing_safety_doc)] + +// TOO_MANY_ARGUMENTS +fn good(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool) {} + +fn bad(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool, _eight: ()) {} + +#[rustfmt::skip] +fn bad_multiline( + one: u32, + two: u32, + three: &str, + four: bool, + five: f32, + six: f32, + seven: bool, + eight: () +) { + let _one = one; + let _two = two; + let _three = three; + let _four = four; + let _five = five; + let _six = six; + let _seven = seven; +} + +// don't lint extern fns +extern "C" fn extern_fn( + _one: u32, + _two: u32, + _three: *const u8, + _four: bool, + _five: f32, + _six: f32, + _seven: bool, + _eight: *const std::ffi::c_void, +) { +} + +pub trait Foo { + fn good(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool); + fn bad(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool, _eight: ()); + + fn ptr(p: *const u8); +} + +pub struct Bar; + +impl Bar { + fn good_method(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool) {} + fn bad_method(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool, _eight: ()) {} +} + +// ok, we don’t want to warn implementations +impl Foo for Bar { + fn good(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool) {} + fn bad(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool, _eight: ()) {} + + fn ptr(p: *const u8) { + println!("{}", unsafe { *p }); + println!("{:?}", unsafe { p.as_ref() }); + unsafe { std::ptr::read(p) }; + } +} + +// NOT_UNSAFE_PTR_ARG_DEREF + +fn private(p: *const u8) { + println!("{}", unsafe { *p }); +} + +pub fn public(p: *const u8) { + println!("{}", unsafe { *p }); + println!("{:?}", unsafe { p.as_ref() }); + unsafe { std::ptr::read(p) }; +} + +impl Bar { + fn private(self, p: *const u8) { + println!("{}", unsafe { *p }); + } + + pub fn public(self, p: *const u8) { + println!("{}", unsafe { *p }); + println!("{:?}", unsafe { p.as_ref() }); + unsafe { std::ptr::read(p) }; + } + + pub fn public_ok(self, p: *const u8) { + if !p.is_null() { + println!("{:p}", p); + } + } + + pub unsafe fn public_unsafe(self, p: *const u8) { + println!("{}", unsafe { *p }); + println!("{:?}", unsafe { p.as_ref() }); + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/functions.stderr b/src/tools/clippy/tests/ui/functions.stderr new file mode 100644 index 00000000000..0a86568b18d --- /dev/null +++ b/src/tools/clippy/tests/ui/functions.stderr @@ -0,0 +1,90 @@ +error: this function has too many arguments (8/7) + --> $DIR/functions.rs:8:1 + | +LL | fn bad(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool, _eight: ()) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::too-many-arguments` implied by `-D warnings` + +error: this function has too many arguments (8/7) + --> $DIR/functions.rs:11:1 + | +LL | / fn bad_multiline( +LL | | one: u32, +LL | | two: u32, +LL | | three: &str, +... | +LL | | eight: () +LL | | ) { + | |__^ + +error: this function has too many arguments (8/7) + --> $DIR/functions.rs:45:5 + | +LL | fn bad(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool, _eight: ()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this function has too many arguments (8/7) + --> $DIR/functions.rs:54:5 + | +LL | fn bad_method(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool, _eight: ()) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this public function dereferences a raw pointer but is not marked `unsafe` + --> $DIR/functions.rs:63:34 + | +LL | println!("{}", unsafe { *p }); + | ^ + | + = note: `-D clippy::not-unsafe-ptr-arg-deref` implied by `-D warnings` + +error: this public function dereferences a raw pointer but is not marked `unsafe` + --> $DIR/functions.rs:64:35 + | +LL | println!("{:?}", unsafe { p.as_ref() }); + | ^ + +error: this public function dereferences a raw pointer but is not marked `unsafe` + --> $DIR/functions.rs:65:33 + | +LL | unsafe { std::ptr::read(p) }; + | ^ + +error: this public function dereferences a raw pointer but is not marked `unsafe` + --> $DIR/functions.rs:76:30 + | +LL | println!("{}", unsafe { *p }); + | ^ + +error: this public function dereferences a raw pointer but is not marked `unsafe` + --> $DIR/functions.rs:77:31 + | +LL | println!("{:?}", unsafe { p.as_ref() }); + | ^ + +error: this public function dereferences a raw pointer but is not marked `unsafe` + --> $DIR/functions.rs:78:29 + | +LL | unsafe { std::ptr::read(p) }; + | ^ + +error: this public function dereferences a raw pointer but is not marked `unsafe` + --> $DIR/functions.rs:87:34 + | +LL | println!("{}", unsafe { *p }); + | ^ + +error: this public function dereferences a raw pointer but is not marked `unsafe` + --> $DIR/functions.rs:88:35 + | +LL | println!("{:?}", unsafe { p.as_ref() }); + | ^ + +error: this public function dereferences a raw pointer but is not marked `unsafe` + --> $DIR/functions.rs:89:33 + | +LL | unsafe { std::ptr::read(p) }; + | ^ + +error: aborting due to 13 previous errors + diff --git a/src/tools/clippy/tests/ui/functions_maxlines.rs b/src/tools/clippy/tests/ui/functions_maxlines.rs new file mode 100644 index 00000000000..5e1ee55e010 --- /dev/null +++ b/src/tools/clippy/tests/ui/functions_maxlines.rs @@ -0,0 +1,163 @@ +#![warn(clippy::too_many_lines)] + +fn good_lines() { + /* println!("This is good."); */ + // println!("This is good."); + /* */ // println!("This is good."); + /* */ // println!("This is good."); + /* */ // println!("This is good."); + /* */ // println!("This is good."); + /* println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); */ + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); + println!("This is good."); +} + +fn bad_lines() { + println!("Dont get confused by braces: {{}}"); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); + println!("This is bad."); +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/functions_maxlines.stderr b/src/tools/clippy/tests/ui/functions_maxlines.stderr new file mode 100644 index 00000000000..9b0e7550cc3 --- /dev/null +++ b/src/tools/clippy/tests/ui/functions_maxlines.stderr @@ -0,0 +1,16 @@ +error: This function has a large number of lines. + --> $DIR/functions_maxlines.rs:58:1 + | +LL | / fn bad_lines() { +LL | | println!("Dont get confused by braces: {{}}"); +LL | | println!("This is bad."); +LL | | println!("This is bad."); +... | +LL | | println!("This is bad."); +LL | | } + | |_^ + | + = note: `-D clippy::too-many-lines` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/future_not_send.rs b/src/tools/clippy/tests/ui/future_not_send.rs new file mode 100644 index 00000000000..6d09d71a630 --- /dev/null +++ b/src/tools/clippy/tests/ui/future_not_send.rs @@ -0,0 +1,79 @@ +// edition:2018 +#![warn(clippy::future_not_send)] + +use std::cell::Cell; +use std::rc::Rc; +use std::sync::Arc; + +async fn private_future(rc: Rc<[u8]>, cell: &Cell<usize>) -> bool { + async { true }.await +} + +pub async fn public_future(rc: Rc<[u8]>) { + async { true }.await; +} + +pub async fn public_send(arc: Arc<[u8]>) -> bool { + async { false }.await +} + +async fn private_future2(rc: Rc<[u8]>, cell: &Cell<usize>) -> bool { + true +} + +pub async fn public_future2(rc: Rc<[u8]>) {} + +pub async fn public_send2(arc: Arc<[u8]>) -> bool { + false +} + +struct Dummy { + rc: Rc<[u8]>, +} + +impl Dummy { + async fn private_future(&self) -> usize { + async { true }.await; + self.rc.len() + } + + pub async fn public_future(&self) { + self.private_future().await; + } + + pub fn public_send(&self) -> impl std::future::Future<Output = bool> { + async { false } + } +} + +async fn generic_future<T>(t: T) -> T +where + T: Send, +{ + let rt = &t; + async { true }.await; + t +} + +async fn generic_future_send<T>(t: T) +where + T: Send, +{ + async { true }.await; +} + +async fn unclear_future<T>(t: T) {} + +fn main() { + let rc = Rc::new([1, 2, 3]); + private_future(rc.clone(), &Cell::new(42)); + public_future(rc.clone()); + let arc = Arc::new([4, 5, 6]); + public_send(arc); + generic_future(42); + generic_future_send(42); + + let dummy = Dummy { rc }; + dummy.public_future(); + dummy.public_send(); +} diff --git a/src/tools/clippy/tests/ui/future_not_send.stderr b/src/tools/clippy/tests/ui/future_not_send.stderr new file mode 100644 index 00000000000..3b4968ef0a6 --- /dev/null +++ b/src/tools/clippy/tests/ui/future_not_send.stderr @@ -0,0 +1,125 @@ +error: future cannot be sent between threads safely + --> $DIR/future_not_send.rs:8:62 + | +LL | async fn private_future(rc: Rc<[u8]>, cell: &Cell<usize>) -> bool { + | ^^^^ future returned by `private_future` is not `Send` + | + = note: `-D clippy::future-not-send` implied by `-D warnings` +note: future is not `Send` as this value is used across an await + --> $DIR/future_not_send.rs:9:5 + | +LL | async fn private_future(rc: Rc<[u8]>, cell: &Cell<usize>) -> bool { + | -- has type `std::rc::Rc<[u8]>` which is not `Send` +LL | async { true }.await + | ^^^^^^^^^^^^^^^^^^^^ await occurs here, with `rc` maybe used later +LL | } + | - `rc` is later dropped here + = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Send` +note: future is not `Send` as this value is used across an await + --> $DIR/future_not_send.rs:9:5 + | +LL | async fn private_future(rc: Rc<[u8]>, cell: &Cell<usize>) -> bool { + | ---- has type `&std::cell::Cell<usize>` which is not `Send` +LL | async { true }.await + | ^^^^^^^^^^^^^^^^^^^^ await occurs here, with `cell` maybe used later +LL | } + | - `cell` is later dropped here + = note: `std::cell::Cell<usize>` doesn't implement `std::marker::Sync` + +error: future cannot be sent between threads safely + --> $DIR/future_not_send.rs:12:42 + | +LL | pub async fn public_future(rc: Rc<[u8]>) { + | ^ future returned by `public_future` is not `Send` + | +note: future is not `Send` as this value is used across an await + --> $DIR/future_not_send.rs:13:5 + | +LL | pub async fn public_future(rc: Rc<[u8]>) { + | -- has type `std::rc::Rc<[u8]>` which is not `Send` +LL | async { true }.await; + | ^^^^^^^^^^^^^^^^^^^^ await occurs here, with `rc` maybe used later +LL | } + | - `rc` is later dropped here + = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Send` + +error: future cannot be sent between threads safely + --> $DIR/future_not_send.rs:20:63 + | +LL | async fn private_future2(rc: Rc<[u8]>, cell: &Cell<usize>) -> bool { + | ^^^^ + | + = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Send` + = note: `std::cell::Cell<usize>` doesn't implement `std::marker::Sync` + +error: future cannot be sent between threads safely + --> $DIR/future_not_send.rs:24:43 + | +LL | pub async fn public_future2(rc: Rc<[u8]>) {} + | ^ + | + = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Send` + +error: future cannot be sent between threads safely + --> $DIR/future_not_send.rs:35:39 + | +LL | async fn private_future(&self) -> usize { + | ^^^^^ future returned by `private_future` is not `Send` + | +note: future is not `Send` as this value is used across an await + --> $DIR/future_not_send.rs:36:9 + | +LL | async fn private_future(&self) -> usize { + | ----- has type `&Dummy` which is not `Send` +LL | async { true }.await; + | ^^^^^^^^^^^^^^^^^^^^ await occurs here, with `&self` maybe used later +LL | self.rc.len() +LL | } + | - `&self` is later dropped here + = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Sync` + +error: future cannot be sent between threads safely + --> $DIR/future_not_send.rs:40:39 + | +LL | pub async fn public_future(&self) { + | ^ future returned by `public_future` is not `Send` + | +note: future is not `Send` as this value is used across an await + --> $DIR/future_not_send.rs:41:9 + | +LL | pub async fn public_future(&self) { + | ----- has type `&Dummy` which is not `Send` +LL | self.private_future().await; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ await occurs here, with `&self` maybe used later +LL | } + | - `&self` is later dropped here + = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Sync` + +error: future cannot be sent between threads safely + --> $DIR/future_not_send.rs:49:37 + | +LL | async fn generic_future<T>(t: T) -> T + | ^ future returned by `generic_future` is not `Send` + | +note: future is not `Send` as this value is used across an await + --> $DIR/future_not_send.rs:54:5 + | +LL | let rt = &t; + | -- has type `&T` which is not `Send` +LL | async { true }.await; + | ^^^^^^^^^^^^^^^^^^^^ await occurs here, with `rt` maybe used later +LL | t +LL | } + | - `rt` is later dropped here + = note: `T` doesn't implement `std::marker::Sync` + +error: future cannot be sent between threads safely + --> $DIR/future_not_send.rs:65:34 + | +LL | async fn unclear_future<T>(t: T) {} + | ^ + | + = note: `T` doesn't implement `std::marker::Send` + +error: aborting due to 8 previous errors + diff --git a/src/tools/clippy/tests/ui/get_last_with_len.fixed b/src/tools/clippy/tests/ui/get_last_with_len.fixed new file mode 100644 index 00000000000..c8b363f9c38 --- /dev/null +++ b/src/tools/clippy/tests/ui/get_last_with_len.fixed @@ -0,0 +1,31 @@ +// run-rustfix + +#![warn(clippy::get_last_with_len)] + +fn dont_use_last() { + let x = vec![2, 3, 5]; + let _ = x.last(); // ~ERROR Use x.last() +} + +fn indexing_two_from_end() { + let x = vec![2, 3, 5]; + let _ = x.get(x.len() - 2); +} + +fn index_into_last() { + let x = vec![2, 3, 5]; + let _ = x[x.len() - 1]; +} + +fn use_last_with_different_vec_length() { + let x = vec![2, 3, 5]; + let y = vec!['a', 'b', 'c']; + let _ = x.get(y.len() - 1); +} + +fn main() { + dont_use_last(); + indexing_two_from_end(); + index_into_last(); + use_last_with_different_vec_length(); +} diff --git a/src/tools/clippy/tests/ui/get_last_with_len.rs b/src/tools/clippy/tests/ui/get_last_with_len.rs new file mode 100644 index 00000000000..bf9cb2d7e0c --- /dev/null +++ b/src/tools/clippy/tests/ui/get_last_with_len.rs @@ -0,0 +1,31 @@ +// run-rustfix + +#![warn(clippy::get_last_with_len)] + +fn dont_use_last() { + let x = vec![2, 3, 5]; + let _ = x.get(x.len() - 1); // ~ERROR Use x.last() +} + +fn indexing_two_from_end() { + let x = vec![2, 3, 5]; + let _ = x.get(x.len() - 2); +} + +fn index_into_last() { + let x = vec![2, 3, 5]; + let _ = x[x.len() - 1]; +} + +fn use_last_with_different_vec_length() { + let x = vec![2, 3, 5]; + let y = vec!['a', 'b', 'c']; + let _ = x.get(y.len() - 1); +} + +fn main() { + dont_use_last(); + indexing_two_from_end(); + index_into_last(); + use_last_with_different_vec_length(); +} diff --git a/src/tools/clippy/tests/ui/get_last_with_len.stderr b/src/tools/clippy/tests/ui/get_last_with_len.stderr new file mode 100644 index 00000000000..55baf87384a --- /dev/null +++ b/src/tools/clippy/tests/ui/get_last_with_len.stderr @@ -0,0 +1,10 @@ +error: accessing last element with `x.get(x.len() - 1)` + --> $DIR/get_last_with_len.rs:7:13 + | +LL | let _ = x.get(x.len() - 1); // ~ERROR Use x.last() + | ^^^^^^^^^^^^^^^^^^ help: try: `x.last()` + | + = note: `-D clippy::get-last-with-len` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/get_unwrap.fixed b/src/tools/clippy/tests/ui/get_unwrap.fixed new file mode 100644 index 00000000000..97e6b20f471 --- /dev/null +++ b/src/tools/clippy/tests/ui/get_unwrap.fixed @@ -0,0 +1,62 @@ +// run-rustfix +#![allow(unused_mut)] +#![deny(clippy::get_unwrap)] + +use std::collections::BTreeMap; +use std::collections::HashMap; +use std::collections::VecDeque; +use std::iter::FromIterator; + +struct GetFalsePositive { + arr: [u32; 3], +} + +impl GetFalsePositive { + fn get(&self, pos: usize) -> Option<&u32> { + self.arr.get(pos) + } + fn get_mut(&mut self, pos: usize) -> Option<&mut u32> { + self.arr.get_mut(pos) + } +} + +fn main() { + let mut boxed_slice: Box<[u8]> = Box::new([0, 1, 2, 3]); + let mut some_slice = &mut [0, 1, 2, 3]; + let mut some_vec = vec![0, 1, 2, 3]; + let mut some_vecdeque: VecDeque<_> = some_vec.iter().cloned().collect(); + let mut some_hashmap: HashMap<u8, char> = HashMap::from_iter(vec![(1, 'a'), (2, 'b')]); + let mut some_btreemap: BTreeMap<u8, char> = BTreeMap::from_iter(vec![(1, 'a'), (2, 'b')]); + let mut false_positive = GetFalsePositive { arr: [0, 1, 2] }; + + { + // Test `get().unwrap()` + let _ = &boxed_slice[1]; + let _ = &some_slice[0]; + let _ = &some_vec[0]; + let _ = &some_vecdeque[0]; + let _ = &some_hashmap[&1]; + let _ = &some_btreemap[&1]; + let _ = false_positive.get(0).unwrap(); + // Test with deref + let _: u8 = boxed_slice[1]; + } + + { + // Test `get_mut().unwrap()` + boxed_slice[0] = 1; + some_slice[0] = 1; + some_vec[0] = 1; + some_vecdeque[0] = 1; + // Check false positives + *some_hashmap.get_mut(&1).unwrap() = 'b'; + *some_btreemap.get_mut(&1).unwrap() = 'b'; + *false_positive.get_mut(0).unwrap() = 1; + } + + { + // Test `get().unwrap().foo()` and `get_mut().unwrap().bar()` + let _ = some_vec[0..1].to_vec(); + let _ = some_vec[0..1].to_vec(); + } +} diff --git a/src/tools/clippy/tests/ui/get_unwrap.rs b/src/tools/clippy/tests/ui/get_unwrap.rs new file mode 100644 index 00000000000..1c9a71c0969 --- /dev/null +++ b/src/tools/clippy/tests/ui/get_unwrap.rs @@ -0,0 +1,62 @@ +// run-rustfix +#![allow(unused_mut)] +#![deny(clippy::get_unwrap)] + +use std::collections::BTreeMap; +use std::collections::HashMap; +use std::collections::VecDeque; +use std::iter::FromIterator; + +struct GetFalsePositive { + arr: [u32; 3], +} + +impl GetFalsePositive { + fn get(&self, pos: usize) -> Option<&u32> { + self.arr.get(pos) + } + fn get_mut(&mut self, pos: usize) -> Option<&mut u32> { + self.arr.get_mut(pos) + } +} + +fn main() { + let mut boxed_slice: Box<[u8]> = Box::new([0, 1, 2, 3]); + let mut some_slice = &mut [0, 1, 2, 3]; + let mut some_vec = vec![0, 1, 2, 3]; + let mut some_vecdeque: VecDeque<_> = some_vec.iter().cloned().collect(); + let mut some_hashmap: HashMap<u8, char> = HashMap::from_iter(vec![(1, 'a'), (2, 'b')]); + let mut some_btreemap: BTreeMap<u8, char> = BTreeMap::from_iter(vec![(1, 'a'), (2, 'b')]); + let mut false_positive = GetFalsePositive { arr: [0, 1, 2] }; + + { + // Test `get().unwrap()` + let _ = boxed_slice.get(1).unwrap(); + let _ = some_slice.get(0).unwrap(); + let _ = some_vec.get(0).unwrap(); + let _ = some_vecdeque.get(0).unwrap(); + let _ = some_hashmap.get(&1).unwrap(); + let _ = some_btreemap.get(&1).unwrap(); + let _ = false_positive.get(0).unwrap(); + // Test with deref + let _: u8 = *boxed_slice.get(1).unwrap(); + } + + { + // Test `get_mut().unwrap()` + *boxed_slice.get_mut(0).unwrap() = 1; + *some_slice.get_mut(0).unwrap() = 1; + *some_vec.get_mut(0).unwrap() = 1; + *some_vecdeque.get_mut(0).unwrap() = 1; + // Check false positives + *some_hashmap.get_mut(&1).unwrap() = 'b'; + *some_btreemap.get_mut(&1).unwrap() = 'b'; + *false_positive.get_mut(0).unwrap() = 1; + } + + { + // Test `get().unwrap().foo()` and `get_mut().unwrap().bar()` + let _ = some_vec.get(0..1).unwrap().to_vec(); + let _ = some_vec.get_mut(0..1).unwrap().to_vec(); + } +} diff --git a/src/tools/clippy/tests/ui/get_unwrap.stderr b/src/tools/clippy/tests/ui/get_unwrap.stderr new file mode 100644 index 00000000000..76a098df82a --- /dev/null +++ b/src/tools/clippy/tests/ui/get_unwrap.stderr @@ -0,0 +1,86 @@ +error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise + --> $DIR/get_unwrap.rs:34:17 + | +LL | let _ = boxed_slice.get(1).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&boxed_slice[1]` + | +note: the lint level is defined here + --> $DIR/get_unwrap.rs:3:9 + | +LL | #![deny(clippy::get_unwrap)] + | ^^^^^^^^^^^^^^^^^^ + +error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise + --> $DIR/get_unwrap.rs:35:17 + | +LL | let _ = some_slice.get(0).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_slice[0]` + +error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more concise + --> $DIR/get_unwrap.rs:36:17 + | +LL | let _ = some_vec.get(0).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_vec[0]` + +error: called `.get().unwrap()` on a VecDeque. Using `[]` is more clear and more concise + --> $DIR/get_unwrap.rs:37:17 + | +LL | let _ = some_vecdeque.get(0).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_vecdeque[0]` + +error: called `.get().unwrap()` on a HashMap. Using `[]` is more clear and more concise + --> $DIR/get_unwrap.rs:38:17 + | +LL | let _ = some_hashmap.get(&1).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_hashmap[&1]` + +error: called `.get().unwrap()` on a BTreeMap. Using `[]` is more clear and more concise + --> $DIR/get_unwrap.rs:39:17 + | +LL | let _ = some_btreemap.get(&1).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_btreemap[&1]` + +error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise + --> $DIR/get_unwrap.rs:42:21 + | +LL | let _: u8 = *boxed_slice.get(1).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `boxed_slice[1]` + +error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and more concise + --> $DIR/get_unwrap.rs:47:9 + | +LL | *boxed_slice.get_mut(0).unwrap() = 1; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `boxed_slice[0]` + +error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and more concise + --> $DIR/get_unwrap.rs:48:9 + | +LL | *some_slice.get_mut(0).unwrap() = 1; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_slice[0]` + +error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more concise + --> $DIR/get_unwrap.rs:49:9 + | +LL | *some_vec.get_mut(0).unwrap() = 1; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0]` + +error: called `.get_mut().unwrap()` on a VecDeque. Using `[]` is more clear and more concise + --> $DIR/get_unwrap.rs:50:9 + | +LL | *some_vecdeque.get_mut(0).unwrap() = 1; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vecdeque[0]` + +error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more concise + --> $DIR/get_unwrap.rs:59:17 + | +LL | let _ = some_vec.get(0..1).unwrap().to_vec(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0..1]` + +error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more concise + --> $DIR/get_unwrap.rs:60:17 + | +LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0..1]` + +error: aborting due to 13 previous errors + diff --git a/src/tools/clippy/tests/ui/identity_conversion.fixed b/src/tools/clippy/tests/ui/identity_conversion.fixed new file mode 100644 index 00000000000..dd3fc56e98b --- /dev/null +++ b/src/tools/clippy/tests/ui/identity_conversion.fixed @@ -0,0 +1,58 @@ +// run-rustfix + +#![deny(clippy::identity_conversion)] + +fn test_generic<T: Copy>(val: T) -> T { + let _ = val; + val +} + +fn test_generic2<T: Copy + Into<i32> + Into<U>, U: From<T>>(val: T) { + // ok + let _: i32 = val.into(); + let _: U = val.into(); + let _ = U::from(val); +} + +fn test_questionmark() -> Result<(), ()> { + { + let _: i32 = 0i32; + Ok(Ok(())) + }??; + Ok(()) +} + +fn test_issue_3913() -> Result<(), std::io::Error> { + use std::fs; + use std::path::Path; + + let path = Path::new("."); + for _ in fs::read_dir(path)? {} + + Ok(()) +} + +fn main() { + test_generic(10i32); + test_generic2::<i32, i32>(10i32); + test_questionmark().unwrap(); + test_issue_3913().unwrap(); + + let _: String = "foo".into(); + let _: String = From::from("foo"); + let _ = String::from("foo"); + #[allow(clippy::identity_conversion)] + { + let _: String = "foo".into(); + let _ = String::from("foo"); + let _ = "".lines().into_iter(); + } + + let _: String = "foo".to_string(); + let _: String = "foo".to_string(); + let _ = "foo".to_string(); + let _ = format!("A: {:04}", 123); + let _ = "".lines(); + let _ = vec![1, 2, 3].into_iter(); + let _: String = format!("Hello {}", "world"); +} diff --git a/src/tools/clippy/tests/ui/identity_conversion.rs b/src/tools/clippy/tests/ui/identity_conversion.rs new file mode 100644 index 00000000000..875ed7db373 --- /dev/null +++ b/src/tools/clippy/tests/ui/identity_conversion.rs @@ -0,0 +1,58 @@ +// run-rustfix + +#![deny(clippy::identity_conversion)] + +fn test_generic<T: Copy>(val: T) -> T { + let _ = T::from(val); + val.into() +} + +fn test_generic2<T: Copy + Into<i32> + Into<U>, U: From<T>>(val: T) { + // ok + let _: i32 = val.into(); + let _: U = val.into(); + let _ = U::from(val); +} + +fn test_questionmark() -> Result<(), ()> { + { + let _: i32 = 0i32.into(); + Ok(Ok(())) + }??; + Ok(()) +} + +fn test_issue_3913() -> Result<(), std::io::Error> { + use std::fs; + use std::path::Path; + + let path = Path::new("."); + for _ in fs::read_dir(path)? {} + + Ok(()) +} + +fn main() { + test_generic(10i32); + test_generic2::<i32, i32>(10i32); + test_questionmark().unwrap(); + test_issue_3913().unwrap(); + + let _: String = "foo".into(); + let _: String = From::from("foo"); + let _ = String::from("foo"); + #[allow(clippy::identity_conversion)] + { + let _: String = "foo".into(); + let _ = String::from("foo"); + let _ = "".lines().into_iter(); + } + + let _: String = "foo".to_string().into(); + let _: String = From::from("foo".to_string()); + let _ = String::from("foo".to_string()); + let _ = String::from(format!("A: {:04}", 123)); + let _ = "".lines().into_iter(); + let _ = vec![1, 2, 3].into_iter().into_iter(); + let _: String = format!("Hello {}", "world").into(); +} diff --git a/src/tools/clippy/tests/ui/identity_conversion.stderr b/src/tools/clippy/tests/ui/identity_conversion.stderr new file mode 100644 index 00000000000..57626b23795 --- /dev/null +++ b/src/tools/clippy/tests/ui/identity_conversion.stderr @@ -0,0 +1,68 @@ +error: identical conversion + --> $DIR/identity_conversion.rs:6:13 + | +LL | let _ = T::from(val); + | ^^^^^^^^^^^^ help: consider removing `T::from()`: `val` + | +note: the lint level is defined here + --> $DIR/identity_conversion.rs:3:9 + | +LL | #![deny(clippy::identity_conversion)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: identical conversion + --> $DIR/identity_conversion.rs:7:5 + | +LL | val.into() + | ^^^^^^^^^^ help: consider removing `.into()`: `val` + +error: identical conversion + --> $DIR/identity_conversion.rs:19:22 + | +LL | let _: i32 = 0i32.into(); + | ^^^^^^^^^^^ help: consider removing `.into()`: `0i32` + +error: identical conversion + --> $DIR/identity_conversion.rs:51:21 + | +LL | let _: String = "foo".to_string().into(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `"foo".to_string()` + +error: identical conversion + --> $DIR/identity_conversion.rs:52:21 + | +LL | let _: String = From::from("foo".to_string()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `From::from()`: `"foo".to_string()` + +error: identical conversion + --> $DIR/identity_conversion.rs:53:13 + | +LL | let _ = String::from("foo".to_string()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `"foo".to_string()` + +error: identical conversion + --> $DIR/identity_conversion.rs:54:13 + | +LL | let _ = String::from(format!("A: {:04}", 123)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `format!("A: {:04}", 123)` + +error: identical conversion + --> $DIR/identity_conversion.rs:55:13 + | +LL | let _ = "".lines().into_iter(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `"".lines()` + +error: identical conversion + --> $DIR/identity_conversion.rs:56:13 + | +LL | let _ = vec![1, 2, 3].into_iter().into_iter(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `vec![1, 2, 3].into_iter()` + +error: identical conversion + --> $DIR/identity_conversion.rs:57:21 + | +LL | let _: String = format!("Hello {}", "world").into(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `format!("Hello {}", "world")` + +error: aborting due to 10 previous errors + diff --git a/src/tools/clippy/tests/ui/identity_op.rs b/src/tools/clippy/tests/ui/identity_op.rs new file mode 100644 index 00000000000..ae2815d345a --- /dev/null +++ b/src/tools/clippy/tests/ui/identity_op.rs @@ -0,0 +1,36 @@ +const ONE: i64 = 1; +const NEG_ONE: i64 = -1; +const ZERO: i64 = 0; + +#[allow( + clippy::eq_op, + clippy::no_effect, + clippy::unnecessary_operation, + clippy::double_parens +)] +#[warn(clippy::identity_op)] +#[rustfmt::skip] +fn main() { + let x = 0; + + x + 0; + x + (1 - 1); + x + 1; + 0 + x; + 1 + x; + x - ZERO; //no error, as we skip lookups (for now) + x | (0); + ((ZERO)) | x; //no error, as we skip lookups (for now) + + x * 1; + 1 * x; + x / ONE; //no error, as we skip lookups (for now) + + x / 2; //no false positive + + x & NEG_ONE; //no error, as we skip lookups (for now) + -1 & x; + + let u: u8 = 0; + u & 255; +} diff --git a/src/tools/clippy/tests/ui/identity_op.stderr b/src/tools/clippy/tests/ui/identity_op.stderr new file mode 100644 index 00000000000..4742877706a --- /dev/null +++ b/src/tools/clippy/tests/ui/identity_op.stderr @@ -0,0 +1,52 @@ +error: the operation is ineffective. Consider reducing it to `x` + --> $DIR/identity_op.rs:16:5 + | +LL | x + 0; + | ^^^^^ + | + = note: `-D clippy::identity-op` implied by `-D warnings` + +error: the operation is ineffective. Consider reducing it to `x` + --> $DIR/identity_op.rs:17:5 + | +LL | x + (1 - 1); + | ^^^^^^^^^^^ + +error: the operation is ineffective. Consider reducing it to `x` + --> $DIR/identity_op.rs:19:5 + | +LL | 0 + x; + | ^^^^^ + +error: the operation is ineffective. Consider reducing it to `x` + --> $DIR/identity_op.rs:22:5 + | +LL | x | (0); + | ^^^^^^^ + +error: the operation is ineffective. Consider reducing it to `x` + --> $DIR/identity_op.rs:25:5 + | +LL | x * 1; + | ^^^^^ + +error: the operation is ineffective. Consider reducing it to `x` + --> $DIR/identity_op.rs:26:5 + | +LL | 1 * x; + | ^^^^^ + +error: the operation is ineffective. Consider reducing it to `x` + --> $DIR/identity_op.rs:32:5 + | +LL | -1 & x; + | ^^^^^^ + +error: the operation is ineffective. Consider reducing it to `u` + --> $DIR/identity_op.rs:35:5 + | +LL | u & 255; + | ^^^^^^^ + +error: aborting due to 8 previous errors + diff --git a/src/tools/clippy/tests/ui/if_let_mutex.rs b/src/tools/clippy/tests/ui/if_let_mutex.rs new file mode 100644 index 00000000000..58feae422a3 --- /dev/null +++ b/src/tools/clippy/tests/ui/if_let_mutex.rs @@ -0,0 +1,42 @@ +#![warn(clippy::if_let_mutex)] + +use std::ops::Deref; +use std::sync::Mutex; + +fn do_stuff<T>(_: T) {} + +fn if_let() { + let m = Mutex::new(1_u8); + if let Err(locked) = m.lock() { + do_stuff(locked); + } else { + let lock = m.lock().unwrap(); + do_stuff(lock); + }; +} + +// This is the most common case as the above case is pretty +// contrived. +fn if_let_option() { + let m = Mutex::new(Some(0_u8)); + if let Some(locked) = m.lock().unwrap().deref() { + do_stuff(locked); + } else { + let lock = m.lock().unwrap(); + do_stuff(lock); + }; +} + +// When mutexs are different don't warn +fn if_let_different_mutex() { + let m = Mutex::new(Some(0_u8)); + let other = Mutex::new(None::<u8>); + if let Some(locked) = m.lock().unwrap().deref() { + do_stuff(locked); + } else { + let lock = other.lock().unwrap(); + do_stuff(lock); + }; +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/if_let_mutex.stderr b/src/tools/clippy/tests/ui/if_let_mutex.stderr new file mode 100644 index 00000000000..e9c4d916332 --- /dev/null +++ b/src/tools/clippy/tests/ui/if_let_mutex.stderr @@ -0,0 +1,29 @@ +error: calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock + --> $DIR/if_let_mutex.rs:10:5 + | +LL | / if let Err(locked) = m.lock() { +LL | | do_stuff(locked); +LL | | } else { +LL | | let lock = m.lock().unwrap(); +LL | | do_stuff(lock); +LL | | }; + | |_____^ + | + = note: `-D clippy::if-let-mutex` implied by `-D warnings` + = help: move the lock call outside of the `if let ...` expression + +error: calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock + --> $DIR/if_let_mutex.rs:22:5 + | +LL | / if let Some(locked) = m.lock().unwrap().deref() { +LL | | do_stuff(locked); +LL | | } else { +LL | | let lock = m.lock().unwrap(); +LL | | do_stuff(lock); +LL | | }; + | |_____^ + | + = help: move the lock call outside of the `if let ...` expression + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/if_let_some_result.fixed b/src/tools/clippy/tests/ui/if_let_some_result.fixed new file mode 100644 index 00000000000..80505fd997f --- /dev/null +++ b/src/tools/clippy/tests/ui/if_let_some_result.fixed @@ -0,0 +1,35 @@ +// run-rustfix + +#![warn(clippy::if_let_some_result)] + +fn str_to_int(x: &str) -> i32 { + if let Ok(y) = x.parse() { + y + } else { + 0 + } +} + +fn str_to_int_ok(x: &str) -> i32 { + if let Ok(y) = x.parse() { + y + } else { + 0 + } +} + +#[rustfmt::skip] +fn strange_some_no_else(x: &str) -> i32 { + { + if let Ok(y) = x . parse() { + return y; + }; + 0 + } +} + +fn main() { + let _ = str_to_int("1"); + let _ = str_to_int_ok("2"); + let _ = strange_some_no_else("3"); +} diff --git a/src/tools/clippy/tests/ui/if_let_some_result.rs b/src/tools/clippy/tests/ui/if_let_some_result.rs new file mode 100644 index 00000000000..ecac1357445 --- /dev/null +++ b/src/tools/clippy/tests/ui/if_let_some_result.rs @@ -0,0 +1,35 @@ +// run-rustfix + +#![warn(clippy::if_let_some_result)] + +fn str_to_int(x: &str) -> i32 { + if let Some(y) = x.parse().ok() { + y + } else { + 0 + } +} + +fn str_to_int_ok(x: &str) -> i32 { + if let Ok(y) = x.parse() { + y + } else { + 0 + } +} + +#[rustfmt::skip] +fn strange_some_no_else(x: &str) -> i32 { + { + if let Some(y) = x . parse() . ok () { + return y; + }; + 0 + } +} + +fn main() { + let _ = str_to_int("1"); + let _ = str_to_int_ok("2"); + let _ = strange_some_no_else("3"); +} diff --git a/src/tools/clippy/tests/ui/if_let_some_result.stderr b/src/tools/clippy/tests/ui/if_let_some_result.stderr new file mode 100644 index 00000000000..334ccb01016 --- /dev/null +++ b/src/tools/clippy/tests/ui/if_let_some_result.stderr @@ -0,0 +1,25 @@ +error: Matching on `Some` with `ok()` is redundant + --> $DIR/if_let_some_result.rs:6:5 + | +LL | if let Some(y) = x.parse().ok() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::if-let-some-result` implied by `-D warnings` +help: Consider matching on `Ok(y)` and removing the call to `ok` instead + | +LL | if let Ok(y) = x.parse() { + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: Matching on `Some` with `ok()` is redundant + --> $DIR/if_let_some_result.rs:24:9 + | +LL | if let Some(y) = x . parse() . ok () { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: Consider matching on `Ok(y)` and removing the call to `ok` instead + | +LL | if let Ok(y) = x . parse() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/if_not_else.rs b/src/tools/clippy/tests/ui/if_not_else.rs new file mode 100644 index 00000000000..dc3fb1ceac9 --- /dev/null +++ b/src/tools/clippy/tests/ui/if_not_else.rs @@ -0,0 +1,19 @@ +#![warn(clippy::all)] +#![warn(clippy::if_not_else)] + +fn bla() -> bool { + unimplemented!() +} + +fn main() { + if !bla() { + println!("Bugs"); + } else { + println!("Bunny"); + } + if 4 != 5 { + println!("Bugs"); + } else { + println!("Bunny"); + } +} diff --git a/src/tools/clippy/tests/ui/if_not_else.stderr b/src/tools/clippy/tests/ui/if_not_else.stderr new file mode 100644 index 00000000000..78bc4d4bd20 --- /dev/null +++ b/src/tools/clippy/tests/ui/if_not_else.stderr @@ -0,0 +1,27 @@ +error: Unnecessary boolean `not` operation + --> $DIR/if_not_else.rs:9:5 + | +LL | / if !bla() { +LL | | println!("Bugs"); +LL | | } else { +LL | | println!("Bunny"); +LL | | } + | |_____^ + | + = note: `-D clippy::if-not-else` implied by `-D warnings` + = help: remove the `!` and swap the blocks of the `if`/`else` + +error: Unnecessary `!=` operation + --> $DIR/if_not_else.rs:14:5 + | +LL | / if 4 != 5 { +LL | | println!("Bugs"); +LL | | } else { +LL | | println!("Bunny"); +LL | | } + | |_____^ + | + = help: change to `==` and swap the blocks of the `if`/`else` + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/if_same_then_else.rs b/src/tools/clippy/tests/ui/if_same_then_else.rs new file mode 100644 index 00000000000..6bbf79edfcf --- /dev/null +++ b/src/tools/clippy/tests/ui/if_same_then_else.rs @@ -0,0 +1,145 @@ +#![warn(clippy::if_same_then_else)] +#![allow( + clippy::blacklisted_name, + clippy::eq_op, + clippy::never_loop, + clippy::no_effect, + clippy::unused_unit, + clippy::zero_divided_by_zero +)] + +struct Foo { + bar: u8, +} + +fn foo() -> bool { + unimplemented!() +} + +fn if_same_then_else() { + if true { + Foo { bar: 42 }; + 0..10; + ..; + 0..; + ..10; + 0..=10; + foo(); + } else { + //~ ERROR same body as `if` block + Foo { bar: 42 }; + 0..10; + ..; + 0..; + ..10; + 0..=10; + foo(); + } + + if true { + Foo { bar: 42 }; + } else { + Foo { bar: 43 }; + } + + if true { + (); + } else { + () + } + + if true { + 0..10; + } else { + 0..=10; + } + + if true { + foo(); + foo(); + } else { + foo(); + } + + let _ = if true { + 0.0 + } else { + //~ ERROR same body as `if` block + 0.0 + }; + + let _ = if true { + -0.0 + } else { + //~ ERROR same body as `if` block + -0.0 + }; + + let _ = if true { 0.0 } else { -0.0 }; + + // Different NaNs + let _ = if true { 0.0 / 0.0 } else { f32::NAN }; + + if true { + foo(); + } + + let _ = if true { + 42 + } else { + //~ ERROR same body as `if` block + 42 + }; + + if true { + let bar = if true { 42 } else { 43 }; + + while foo() { + break; + } + bar + 1; + } else { + //~ ERROR same body as `if` block + let bar = if true { 42 } else { 43 }; + + while foo() { + break; + } + bar + 1; + } + + if true { + let _ = match 42 { + 42 => 1, + a if a > 0 => 2, + 10..=15 => 3, + _ => 4, + }; + } else if false { + foo(); + } else if foo() { + let _ = match 42 { + 42 => 1, + a if a > 0 => 2, + 10..=15 => 3, + _ => 4, + }; + } +} + +// Issue #2423. This was causing an ICE. +fn func() { + if true { + f(&[0; 62]); + f(&[0; 4]); + f(&[0; 3]); + } else { + f(&[0; 62]); + f(&[0; 6]); + f(&[0; 6]); + } +} + +fn f(val: &[u8]) {} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/if_same_then_else.stderr b/src/tools/clippy/tests/ui/if_same_then_else.stderr new file mode 100644 index 00000000000..d9fdf06fa8b --- /dev/null +++ b/src/tools/clippy/tests/ui/if_same_then_else.stderr @@ -0,0 +1,112 @@ +error: this `if` has identical blocks + --> $DIR/if_same_then_else.rs:28:12 + | +LL | } else { + | ____________^ +LL | | //~ ERROR same body as `if` block +LL | | Foo { bar: 42 }; +LL | | 0..10; +... | +LL | | foo(); +LL | | } + | |_____^ + | + = note: `-D clippy::if-same-then-else` implied by `-D warnings` +note: same as this + --> $DIR/if_same_then_else.rs:20:13 + | +LL | if true { + | _____________^ +LL | | Foo { bar: 42 }; +LL | | 0..10; +LL | | ..; +... | +LL | | foo(); +LL | | } else { + | |_____^ + +error: this `if` has identical blocks + --> $DIR/if_same_then_else.rs:66:12 + | +LL | } else { + | ____________^ +LL | | //~ ERROR same body as `if` block +LL | | 0.0 +LL | | }; + | |_____^ + | +note: same as this + --> $DIR/if_same_then_else.rs:64:21 + | +LL | let _ = if true { + | _____________________^ +LL | | 0.0 +LL | | } else { + | |_____^ + +error: this `if` has identical blocks + --> $DIR/if_same_then_else.rs:73:12 + | +LL | } else { + | ____________^ +LL | | //~ ERROR same body as `if` block +LL | | -0.0 +LL | | }; + | |_____^ + | +note: same as this + --> $DIR/if_same_then_else.rs:71:21 + | +LL | let _ = if true { + | _____________________^ +LL | | -0.0 +LL | | } else { + | |_____^ + +error: this `if` has identical blocks + --> $DIR/if_same_then_else.rs:89:12 + | +LL | } else { + | ____________^ +LL | | //~ ERROR same body as `if` block +LL | | 42 +LL | | }; + | |_____^ + | +note: same as this + --> $DIR/if_same_then_else.rs:87:21 + | +LL | let _ = if true { + | _____________________^ +LL | | 42 +LL | | } else { + | |_____^ + +error: this `if` has identical blocks + --> $DIR/if_same_then_else.rs:101:12 + | +LL | } else { + | ____________^ +LL | | //~ ERROR same body as `if` block +LL | | let bar = if true { 42 } else { 43 }; +LL | | +... | +LL | | bar + 1; +LL | | } + | |_____^ + | +note: same as this + --> $DIR/if_same_then_else.rs:94:13 + | +LL | if true { + | _____________^ +LL | | let bar = if true { 42 } else { 43 }; +LL | | +LL | | while foo() { +... | +LL | | bar + 1; +LL | | } else { + | |_____^ + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/if_same_then_else2.rs b/src/tools/clippy/tests/ui/if_same_then_else2.rs new file mode 100644 index 00000000000..3cc21809264 --- /dev/null +++ b/src/tools/clippy/tests/ui/if_same_then_else2.rs @@ -0,0 +1,139 @@ +#![warn(clippy::if_same_then_else)] +#![allow( + clippy::blacklisted_name, + clippy::collapsible_if, + clippy::ifs_same_cond, + clippy::needless_return +)] + +fn if_same_then_else2() -> Result<&'static str, ()> { + if true { + for _ in &[42] { + let foo: &Option<_> = &Some::<u8>(42); + if true { + break; + } else { + continue; + } + } + } else { + //~ ERROR same body as `if` block + for _ in &[42] { + let foo: &Option<_> = &Some::<u8>(42); + if true { + break; + } else { + continue; + } + } + } + + if true { + if let Some(a) = Some(42) {} + } else { + //~ ERROR same body as `if` block + if let Some(a) = Some(42) {} + } + + if true { + if let (1, .., 3) = (1, 2, 3) {} + } else { + //~ ERROR same body as `if` block + if let (1, .., 3) = (1, 2, 3) {} + } + + if true { + if let (1, .., 3) = (1, 2, 3) {} + } else { + if let (.., 3) = (1, 2, 3) {} + } + + if true { + if let (1, .., 3) = (1, 2, 3) {} + } else { + if let (.., 4) = (1, 2, 3) {} + } + + if true { + if let (1, .., 3) = (1, 2, 3) {} + } else { + if let (.., 1, 3) = (1, 2, 3) {} + } + + if true { + if let Some(42) = None {} + } else { + if let Option::Some(42) = None {} + } + + if true { + if let Some(42) = None::<u8> {} + } else { + if let Some(42) = None {} + } + + if true { + if let Some(42) = None::<u8> {} + } else { + if let Some(42) = None::<u32> {} + } + + if true { + if let Some(a) = Some(42) {} + } else { + if let Some(a) = Some(43) {} + } + + // Same NaNs + let _ = if true { + f32::NAN + } else { + //~ ERROR same body as `if` block + f32::NAN + }; + + if true { + Ok("foo")?; + } else { + //~ ERROR same body as `if` block + Ok("foo")?; + } + + if true { + let foo = ""; + return Ok(&foo[0..]); + } else if false { + let foo = "bar"; + return Ok(&foo[0..]); + } else { + let foo = ""; + return Ok(&foo[0..]); + } + + if true { + let foo = ""; + return Ok(&foo[0..]); + } else if false { + let foo = "bar"; + return Ok(&foo[0..]); + } else if true { + let foo = ""; + return Ok(&foo[0..]); + } else { + let foo = ""; + return Ok(&foo[0..]); + } + + // False positive `if_same_then_else`: `let (x, y)` vs. `let (y, x)`; see issue #3559. + if true { + let foo = ""; + let (x, y) = (1, 2); + return Ok(&foo[x..y]); + } else { + let foo = ""; + let (y, x) = (1, 2); + return Ok(&foo[x..y]); + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/if_same_then_else2.stderr b/src/tools/clippy/tests/ui/if_same_then_else2.stderr new file mode 100644 index 00000000000..f5d087fe128 --- /dev/null +++ b/src/tools/clippy/tests/ui/if_same_then_else2.stderr @@ -0,0 +1,125 @@ +error: this `if` has identical blocks + --> $DIR/if_same_then_else2.rs:19:12 + | +LL | } else { + | ____________^ +LL | | //~ ERROR same body as `if` block +LL | | for _ in &[42] { +LL | | let foo: &Option<_> = &Some::<u8>(42); +... | +LL | | } +LL | | } + | |_____^ + | + = note: `-D clippy::if-same-then-else` implied by `-D warnings` +note: same as this + --> $DIR/if_same_then_else2.rs:10:13 + | +LL | if true { + | _____________^ +LL | | for _ in &[42] { +LL | | let foo: &Option<_> = &Some::<u8>(42); +LL | | if true { +... | +LL | | } +LL | | } else { + | |_____^ + +error: this `if` has identical blocks + --> $DIR/if_same_then_else2.rs:33:12 + | +LL | } else { + | ____________^ +LL | | //~ ERROR same body as `if` block +LL | | if let Some(a) = Some(42) {} +LL | | } + | |_____^ + | +note: same as this + --> $DIR/if_same_then_else2.rs:31:13 + | +LL | if true { + | _____________^ +LL | | if let Some(a) = Some(42) {} +LL | | } else { + | |_____^ + +error: this `if` has identical blocks + --> $DIR/if_same_then_else2.rs:40:12 + | +LL | } else { + | ____________^ +LL | | //~ ERROR same body as `if` block +LL | | if let (1, .., 3) = (1, 2, 3) {} +LL | | } + | |_____^ + | +note: same as this + --> $DIR/if_same_then_else2.rs:38:13 + | +LL | if true { + | _____________^ +LL | | if let (1, .., 3) = (1, 2, 3) {} +LL | | } else { + | |_____^ + +error: this `if` has identical blocks + --> $DIR/if_same_then_else2.rs:90:12 + | +LL | } else { + | ____________^ +LL | | //~ ERROR same body as `if` block +LL | | f32::NAN +LL | | }; + | |_____^ + | +note: same as this + --> $DIR/if_same_then_else2.rs:88:21 + | +LL | let _ = if true { + | _____________________^ +LL | | f32::NAN +LL | | } else { + | |_____^ + +error: this `if` has identical blocks + --> $DIR/if_same_then_else2.rs:97:12 + | +LL | } else { + | ____________^ +LL | | //~ ERROR same body as `if` block +LL | | Ok("foo")?; +LL | | } + | |_____^ + | +note: same as this + --> $DIR/if_same_then_else2.rs:95:13 + | +LL | if true { + | _____________^ +LL | | Ok("foo")?; +LL | | } else { + | |_____^ + +error: this `if` has identical blocks + --> $DIR/if_same_then_else2.rs:122:12 + | +LL | } else { + | ____________^ +LL | | let foo = ""; +LL | | return Ok(&foo[0..]); +LL | | } + | |_____^ + | +note: same as this + --> $DIR/if_same_then_else2.rs:119:20 + | +LL | } else if true { + | ____________________^ +LL | | let foo = ""; +LL | | return Ok(&foo[0..]); +LL | | } else { + | |_____^ + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/ifs_same_cond.rs b/src/tools/clippy/tests/ui/ifs_same_cond.rs new file mode 100644 index 00000000000..80e9839ff40 --- /dev/null +++ b/src/tools/clippy/tests/ui/ifs_same_cond.rs @@ -0,0 +1,46 @@ +#![warn(clippy::ifs_same_cond)] +#![allow(clippy::if_same_then_else, clippy::comparison_chain)] // all empty blocks + +fn ifs_same_cond() { + let a = 0; + let b = false; + + if b { + } else if b { + //~ ERROR ifs same condition + } + + if a == 1 { + } else if a == 1 { + //~ ERROR ifs same condition + } + + if 2 * a == 1 { + } else if 2 * a == 2 { + } else if 2 * a == 1 { + //~ ERROR ifs same condition + } else if a == 1 { + } + + // See #659 + if cfg!(feature = "feature1-659") { + 1 + } else if cfg!(feature = "feature2-659") { + 2 + } else { + 3 + }; + + let mut v = vec![1]; + if v.pop() == None { + // ok, functions + } else if v.pop() == None { + } + + if v.len() == 42 { + // ok, functions + } else if v.len() == 42 { + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/ifs_same_cond.stderr b/src/tools/clippy/tests/ui/ifs_same_cond.stderr new file mode 100644 index 00000000000..0c8f49b8687 --- /dev/null +++ b/src/tools/clippy/tests/ui/ifs_same_cond.stderr @@ -0,0 +1,39 @@ +error: this `if` has the same condition as a previous `if` + --> $DIR/ifs_same_cond.rs:9:15 + | +LL | } else if b { + | ^ + | + = note: `-D clippy::ifs-same-cond` implied by `-D warnings` +note: same as this + --> $DIR/ifs_same_cond.rs:8:8 + | +LL | if b { + | ^ + +error: this `if` has the same condition as a previous `if` + --> $DIR/ifs_same_cond.rs:14:15 + | +LL | } else if a == 1 { + | ^^^^^^ + | +note: same as this + --> $DIR/ifs_same_cond.rs:13:8 + | +LL | if a == 1 { + | ^^^^^^ + +error: this `if` has the same condition as a previous `if` + --> $DIR/ifs_same_cond.rs:20:15 + | +LL | } else if 2 * a == 1 { + | ^^^^^^^^^^ + | +note: same as this + --> $DIR/ifs_same_cond.rs:18:8 + | +LL | if 2 * a == 1 { + | ^^^^^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/impl.rs b/src/tools/clippy/tests/ui/impl.rs new file mode 100644 index 00000000000..1c46e3a5337 --- /dev/null +++ b/src/tools/clippy/tests/ui/impl.rs @@ -0,0 +1,36 @@ +#![allow(dead_code)] +#![warn(clippy::multiple_inherent_impl)] + +struct MyStruct; + +impl MyStruct { + fn first() {} +} + +impl MyStruct { + fn second() {} +} + +impl<'a> MyStruct { + fn lifetimed() {} +} + +mod submod { + struct MyStruct; + impl MyStruct { + fn other() {} + } + + impl super::MyStruct { + fn third() {} + } +} + +use std::fmt; +impl fmt::Debug for MyStruct { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "MyStruct {{ }}") + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/impl.stderr b/src/tools/clippy/tests/ui/impl.stderr new file mode 100644 index 00000000000..585d32845d2 --- /dev/null +++ b/src/tools/clippy/tests/ui/impl.stderr @@ -0,0 +1,35 @@ +error: Multiple implementations of this structure + --> $DIR/impl.rs:10:1 + | +LL | / impl MyStruct { +LL | | fn second() {} +LL | | } + | |_^ + | + = note: `-D clippy::multiple-inherent-impl` implied by `-D warnings` +note: First implementation here + --> $DIR/impl.rs:6:1 + | +LL | / impl MyStruct { +LL | | fn first() {} +LL | | } + | |_^ + +error: Multiple implementations of this structure + --> $DIR/impl.rs:24:5 + | +LL | / impl super::MyStruct { +LL | | fn third() {} +LL | | } + | |_____^ + | +note: First implementation here + --> $DIR/impl.rs:6:1 + | +LL | / impl MyStruct { +LL | | fn first() {} +LL | | } + | |_^ + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/implicit_hasher.rs b/src/tools/clippy/tests/ui/implicit_hasher.rs new file mode 100644 index 00000000000..fdcc9a33f55 --- /dev/null +++ b/src/tools/clippy/tests/ui/implicit_hasher.rs @@ -0,0 +1,99 @@ +// aux-build:implicit_hasher_macros.rs +#![deny(clippy::implicit_hasher)] +#![allow(unused)] + +#[macro_use] +extern crate implicit_hasher_macros; + +use std::cmp::Eq; +use std::collections::{HashMap, HashSet}; +use std::hash::{BuildHasher, Hash}; + +pub trait Foo<T>: Sized { + fn make() -> (Self, Self); +} + +impl<K: Hash + Eq, V> Foo<i8> for HashMap<K, V> { + fn make() -> (Self, Self) { + // OK, don't suggest to modify these + let _: HashMap<i32, i32> = HashMap::new(); + let _: HashSet<i32> = HashSet::new(); + + (HashMap::new(), HashMap::with_capacity(10)) + } +} +impl<K: Hash + Eq, V> Foo<i8> for (HashMap<K, V>,) { + fn make() -> (Self, Self) { + ((HashMap::new(),), (HashMap::with_capacity(10),)) + } +} +impl Foo<i16> for HashMap<String, String> { + fn make() -> (Self, Self) { + (HashMap::new(), HashMap::with_capacity(10)) + } +} + +impl<K: Hash + Eq, V, S: BuildHasher + Default> Foo<i32> for HashMap<K, V, S> { + fn make() -> (Self, Self) { + (HashMap::default(), HashMap::with_capacity_and_hasher(10, S::default())) + } +} +impl<S: BuildHasher + Default> Foo<i64> for HashMap<String, String, S> { + fn make() -> (Self, Self) { + (HashMap::default(), HashMap::with_capacity_and_hasher(10, S::default())) + } +} + +impl<T: Hash + Eq> Foo<i8> for HashSet<T> { + fn make() -> (Self, Self) { + (HashSet::new(), HashSet::with_capacity(10)) + } +} +impl Foo<i16> for HashSet<String> { + fn make() -> (Self, Self) { + (HashSet::new(), HashSet::with_capacity(10)) + } +} + +impl<T: Hash + Eq, S: BuildHasher + Default> Foo<i32> for HashSet<T, S> { + fn make() -> (Self, Self) { + (HashSet::default(), HashSet::with_capacity_and_hasher(10, S::default())) + } +} +impl<S: BuildHasher + Default> Foo<i64> for HashSet<String, S> { + fn make() -> (Self, Self) { + (HashSet::default(), HashSet::with_capacity_and_hasher(10, S::default())) + } +} + +pub fn foo(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32>) {} + +macro_rules! gen { + (impl) => { + impl<K: Hash + Eq, V> Foo<u8> for HashMap<K, V> { + fn make() -> (Self, Self) { + (HashMap::new(), HashMap::with_capacity(10)) + } + } + }; + + (fn $name:ident) => { + pub fn $name(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32>) {} + }; +} +#[rustfmt::skip] +gen!(impl); +gen!(fn bar); + +// When the macro is in a different file, the suggestion spans can't be combined properly +// and should not cause an ICE +// See #2707 +#[macro_use] +#[path = "../auxiliary/test_macro.rs"] +pub mod test_macro; +__implicit_hasher_test_macro!(impl<K, V> for HashMap<K, V> where V: test_macro::A); + +// #4260 +implicit_hasher_fn!(); + +fn main() {} diff --git a/src/tools/clippy/tests/ui/implicit_hasher.stderr b/src/tools/clippy/tests/ui/implicit_hasher.stderr new file mode 100644 index 00000000000..2b06d661772 --- /dev/null +++ b/src/tools/clippy/tests/ui/implicit_hasher.stderr @@ -0,0 +1,153 @@ +error: impl for `HashMap` should be generalized over different hashers + --> $DIR/implicit_hasher.rs:16:35 + | +LL | impl<K: Hash + Eq, V> Foo<i8> for HashMap<K, V> { + | ^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/implicit_hasher.rs:2:9 + | +LL | #![deny(clippy::implicit_hasher)] + | ^^^^^^^^^^^^^^^^^^^^^^^ +help: consider adding a type parameter + | +LL | impl<K: Hash + Eq, V, S: ::std::hash::BuildHasher + Default> Foo<i8> for HashMap<K, V, S> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^ +help: ...and use generic constructor + | +LL | (HashMap::default(), HashMap::with_capacity_and_hasher(10, Default::default())) + | ^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: impl for `HashMap` should be generalized over different hashers + --> $DIR/implicit_hasher.rs:25:36 + | +LL | impl<K: Hash + Eq, V> Foo<i8> for (HashMap<K, V>,) { + | ^^^^^^^^^^^^^ + | +help: consider adding a type parameter + | +LL | impl<K: Hash + Eq, V, S: ::std::hash::BuildHasher + Default> Foo<i8> for (HashMap<K, V, S>,) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^ +help: ...and use generic constructor + | +LL | ((HashMap::default(),), (HashMap::with_capacity_and_hasher(10, Default::default()),)) + | ^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: impl for `HashMap` should be generalized over different hashers + --> $DIR/implicit_hasher.rs:30:19 + | +LL | impl Foo<i16> for HashMap<String, String> { + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider adding a type parameter + | +LL | impl<S: ::std::hash::BuildHasher + Default> Foo<i16> for HashMap<String, String, S> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: ...and use generic constructor + | +LL | (HashMap::default(), HashMap::with_capacity_and_hasher(10, Default::default())) + | ^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: impl for `HashSet` should be generalized over different hashers + --> $DIR/implicit_hasher.rs:47:32 + | +LL | impl<T: Hash + Eq> Foo<i8> for HashSet<T> { + | ^^^^^^^^^^ + | +help: consider adding a type parameter + | +LL | impl<T: Hash + Eq, S: ::std::hash::BuildHasher + Default> Foo<i8> for HashSet<T, S> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^ +help: ...and use generic constructor + | +LL | (HashSet::default(), HashSet::with_capacity_and_hasher(10, Default::default())) + | ^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: impl for `HashSet` should be generalized over different hashers + --> $DIR/implicit_hasher.rs:52:19 + | +LL | impl Foo<i16> for HashSet<String> { + | ^^^^^^^^^^^^^^^ + | +help: consider adding a type parameter + | +LL | impl<S: ::std::hash::BuildHasher + Default> Foo<i16> for HashSet<String, S> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^ +help: ...and use generic constructor + | +LL | (HashSet::default(), HashSet::with_capacity_and_hasher(10, Default::default())) + | ^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: parameter of type `HashMap` should be generalized over different hashers + --> $DIR/implicit_hasher.rs:69:23 + | +LL | pub fn foo(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32>) {} + | ^^^^^^^^^^^^^^^^^ + | +help: consider adding a type parameter + | +LL | pub fn foo<S: ::std::hash::BuildHasher>(_map: &mut HashMap<i32, i32, S>, _set: &mut HashSet<i32>) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^ + +error: parameter of type `HashSet` should be generalized over different hashers + --> $DIR/implicit_hasher.rs:69:53 + | +LL | pub fn foo(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32>) {} + | ^^^^^^^^^^^^ + | +help: consider adding a type parameter + | +LL | pub fn foo<S: ::std::hash::BuildHasher>(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32, S>) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^ + +error: impl for `HashMap` should be generalized over different hashers + --> $DIR/implicit_hasher.rs:73:43 + | +LL | impl<K: Hash + Eq, V> Foo<u8> for HashMap<K, V> { + | ^^^^^^^^^^^^^ +... +LL | gen!(impl); + | ----------- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider adding a type parameter + | +LL | impl<K: Hash + Eq, V, S: ::std::hash::BuildHasher + Default> Foo<u8> for HashMap<K, V, S> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^ +help: ...and use generic constructor + | +LL | (HashMap::default(), HashMap::with_capacity_and_hasher(10, Default::default())) + | ^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: parameter of type `HashMap` should be generalized over different hashers + --> $DIR/implicit_hasher.rs:81:33 + | +LL | pub fn $name(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32>) {} + | ^^^^^^^^^^^^^^^^^ +... +LL | gen!(fn bar); + | ------------- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider adding a type parameter + | +LL | pub fn $name<S: ::std::hash::BuildHasher>(_map: &mut HashMap<i32, i32, S>, _set: &mut HashSet<i32>) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^ + +error: parameter of type `HashSet` should be generalized over different hashers + --> $DIR/implicit_hasher.rs:81:63 + | +LL | pub fn $name(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32>) {} + | ^^^^^^^^^^^^ +... +LL | gen!(fn bar); + | ------------- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider adding a type parameter + | +LL | pub fn $name<S: ::std::hash::BuildHasher>(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32, S>) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^ + +error: aborting due to 10 previous errors + diff --git a/src/tools/clippy/tests/ui/implicit_return.fixed b/src/tools/clippy/tests/ui/implicit_return.fixed new file mode 100644 index 00000000000..9066dc3fedf --- /dev/null +++ b/src/tools/clippy/tests/ui/implicit_return.fixed @@ -0,0 +1,101 @@ +// run-rustfix + +#![warn(clippy::implicit_return)] +#![allow(clippy::needless_return, unused)] + +fn test_end_of_fn() -> bool { + if true { + // no error! + return true; + } + + return true +} + +#[allow(clippy::needless_bool)] +fn test_if_block() -> bool { + if true { + return true + } else { + return false + } +} + +#[rustfmt::skip] +fn test_match(x: bool) -> bool { + match x { + true => return false, + false => { return true }, + } +} + +#[allow(clippy::needless_return)] +fn test_match_with_unreachable(x: bool) -> bool { + match x { + true => return false, + false => unreachable!(), + } +} + +#[allow(clippy::never_loop)] +fn test_loop() -> bool { + loop { + return true; + } +} + +#[allow(clippy::never_loop)] +fn test_loop_with_block() -> bool { + loop { + { + return true; + } + } +} + +#[allow(clippy::never_loop)] +fn test_loop_with_nests() -> bool { + loop { + if true { + return true; + } else { + let _ = true; + } + } +} + +#[allow(clippy::redundant_pattern_matching)] +fn test_loop_with_if_let() -> bool { + loop { + if let Some(x) = Some(true) { + return x; + } + } +} + +fn test_closure() { + #[rustfmt::skip] + let _ = || { return true }; + let _ = || return true; +} + +fn test_panic() -> bool { + panic!() +} + +fn test_return_macro() -> String { + return format!("test {}", "test") +} + +fn main() { + let _ = test_end_of_fn(); + let _ = test_if_block(); + let _ = test_match(true); + let _ = test_match_with_unreachable(true); + let _ = test_loop(); + let _ = test_loop_with_block(); + let _ = test_loop_with_nests(); + let _ = test_loop_with_if_let(); + test_closure(); + let _ = test_return_macro(); +} diff --git a/src/tools/clippy/tests/ui/implicit_return.rs b/src/tools/clippy/tests/ui/implicit_return.rs new file mode 100644 index 00000000000..c0d70ecf502 --- /dev/null +++ b/src/tools/clippy/tests/ui/implicit_return.rs @@ -0,0 +1,101 @@ +// run-rustfix + +#![warn(clippy::implicit_return)] +#![allow(clippy::needless_return, unused)] + +fn test_end_of_fn() -> bool { + if true { + // no error! + return true; + } + + true +} + +#[allow(clippy::needless_bool)] +fn test_if_block() -> bool { + if true { + true + } else { + false + } +} + +#[rustfmt::skip] +fn test_match(x: bool) -> bool { + match x { + true => false, + false => { true }, + } +} + +#[allow(clippy::needless_return)] +fn test_match_with_unreachable(x: bool) -> bool { + match x { + true => return false, + false => unreachable!(), + } +} + +#[allow(clippy::never_loop)] +fn test_loop() -> bool { + loop { + break true; + } +} + +#[allow(clippy::never_loop)] +fn test_loop_with_block() -> bool { + loop { + { + break true; + } + } +} + +#[allow(clippy::never_loop)] +fn test_loop_with_nests() -> bool { + loop { + if true { + break true; + } else { + let _ = true; + } + } +} + +#[allow(clippy::redundant_pattern_matching)] +fn test_loop_with_if_let() -> bool { + loop { + if let Some(x) = Some(true) { + return x; + } + } +} + +fn test_closure() { + #[rustfmt::skip] + let _ = || { true }; + let _ = || true; +} + +fn test_panic() -> bool { + panic!() +} + +fn test_return_macro() -> String { + format!("test {}", "test") +} + +fn main() { + let _ = test_end_of_fn(); + let _ = test_if_block(); + let _ = test_match(true); + let _ = test_match_with_unreachable(true); + let _ = test_loop(); + let _ = test_loop_with_block(); + let _ = test_loop_with_nests(); + let _ = test_loop_with_if_let(); + test_closure(); + let _ = test_return_macro(); +} diff --git a/src/tools/clippy/tests/ui/implicit_return.stderr b/src/tools/clippy/tests/ui/implicit_return.stderr new file mode 100644 index 00000000000..fb2ec902764 --- /dev/null +++ b/src/tools/clippy/tests/ui/implicit_return.stderr @@ -0,0 +1,70 @@ +error: missing `return` statement + --> $DIR/implicit_return.rs:12:5 + | +LL | true + | ^^^^ help: add `return` as shown: `return true` + | + = note: `-D clippy::implicit-return` implied by `-D warnings` + +error: missing `return` statement + --> $DIR/implicit_return.rs:18:9 + | +LL | true + | ^^^^ help: add `return` as shown: `return true` + +error: missing `return` statement + --> $DIR/implicit_return.rs:20:9 + | +LL | false + | ^^^^^ help: add `return` as shown: `return false` + +error: missing `return` statement + --> $DIR/implicit_return.rs:27:17 + | +LL | true => false, + | ^^^^^ help: add `return` as shown: `return false` + +error: missing `return` statement + --> $DIR/implicit_return.rs:28:20 + | +LL | false => { true }, + | ^^^^ help: add `return` as shown: `return true` + +error: missing `return` statement + --> $DIR/implicit_return.rs:43:9 + | +LL | break true; + | ^^^^^^^^^^ help: change `break` to `return` as shown: `return true` + +error: missing `return` statement + --> $DIR/implicit_return.rs:51:13 + | +LL | break true; + | ^^^^^^^^^^ help: change `break` to `return` as shown: `return true` + +error: missing `return` statement + --> $DIR/implicit_return.rs:60:13 + | +LL | break true; + | ^^^^^^^^^^ help: change `break` to `return` as shown: `return true` + +error: missing `return` statement + --> $DIR/implicit_return.rs:78:18 + | +LL | let _ = || { true }; + | ^^^^ help: add `return` as shown: `return true` + +error: missing `return` statement + --> $DIR/implicit_return.rs:79:16 + | +LL | let _ = || true; + | ^^^^ help: add `return` as shown: `return true` + +error: missing `return` statement + --> $DIR/implicit_return.rs:87:5 + | +LL | format!("test {}", "test") + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add `return` as shown: `return format!("test {}", "test")` + +error: aborting due to 11 previous errors + diff --git a/src/tools/clippy/tests/ui/implicit_saturating_sub.fixed b/src/tools/clippy/tests/ui/implicit_saturating_sub.fixed new file mode 100644 index 00000000000..e0b5b31a00c --- /dev/null +++ b/src/tools/clippy/tests/ui/implicit_saturating_sub.fixed @@ -0,0 +1,160 @@ +// run-rustfix +#![allow(unused_assignments, unused_mut, clippy::assign_op_pattern)] +#![warn(clippy::implicit_saturating_sub)] + +fn main() { + // Tests for unsigned integers + + let end_8: u8 = 10; + let start_8: u8 = 5; + let mut u_8: u8 = end_8 - start_8; + + // Lint + u_8 = u_8.saturating_sub(1); + + match end_8 { + 10 => { + // Lint + u_8 = u_8.saturating_sub(1); + }, + 11 => u_8 += 1, + _ => u_8 = 0, + } + + let end_16: u16 = 40; + let start_16: u16 = 35; + + let mut u_16: u16 = end_16 - start_16; + + // Lint + u_16 = u_16.saturating_sub(1); + + let mut end_32: u32 = 7000; + let mut start_32: u32 = 7010; + + let mut u_32: u32 = end_32 - start_32; + + // Lint + u_32 = u_32.saturating_sub(1); + + // No Lint + if u_32 > 0 { + u_16 += 1; + } + + // No Lint + if u_32 != 0 { + end_32 -= 1; + start_32 += 1; + } + + let mut end_64: u64 = 75001; + let mut start_64: u64 = 75000; + + let mut u_64: u64 = end_64 - start_64; + + // Lint + u_64 = u_64.saturating_sub(1); + + // Lint + u_64 = u_64.saturating_sub(1); + + // Lint + u_64 = u_64.saturating_sub(1); + + // No Lint + if u_64 >= 1 { + u_64 -= 1; + } + + // No Lint + if u_64 > 0 { + end_64 -= 1; + } + + // Tests for usize + let end_usize: usize = 8054; + let start_usize: usize = 8050; + + let mut u_usize: usize = end_usize - start_usize; + + // Lint + u_usize = u_usize.saturating_sub(1); + + // Tests for signed integers + + let endi_8: i8 = 10; + let starti_8: i8 = 50; + + let mut i_8: i8 = endi_8 - starti_8; + + // Lint + i_8 = i_8.saturating_sub(1); + + // Lint + i_8 = i_8.saturating_sub(1); + + // Lint + i_8 = i_8.saturating_sub(1); + + // Lint + i_8 = i_8.saturating_sub(1); + + let endi_16: i16 = 45; + let starti_16: i16 = 44; + + let mut i_16: i16 = endi_16 - starti_16; + + // Lint + i_16 = i_16.saturating_sub(1); + + // Lint + i_16 = i_16.saturating_sub(1); + + // Lint + i_16 = i_16.saturating_sub(1); + + // Lint + i_16 = i_16.saturating_sub(1); + + let endi_32: i32 = 45; + let starti_32: i32 = 44; + + let mut i_32: i32 = endi_32 - starti_32; + + // Lint + i_32 = i_32.saturating_sub(1); + + // Lint + i_32 = i_32.saturating_sub(1); + + // Lint + i_32 = i_32.saturating_sub(1); + + // Lint + i_32 = i_32.saturating_sub(1); + + let endi_64: i64 = 45; + let starti_64: i64 = 44; + + let mut i_64: i64 = endi_64 - starti_64; + + // Lint + i_64 = i_64.saturating_sub(1); + + // Lint + i_64 = i_64.saturating_sub(1); + + // Lint + i_64 = i_64.saturating_sub(1); + + // No Lint + if i_64 > 0 { + i_64 -= 1; + } + + // No Lint + if i_64 != 0 { + i_64 -= 1; + } +} diff --git a/src/tools/clippy/tests/ui/implicit_saturating_sub.rs b/src/tools/clippy/tests/ui/implicit_saturating_sub.rs new file mode 100644 index 00000000000..39d81608922 --- /dev/null +++ b/src/tools/clippy/tests/ui/implicit_saturating_sub.rs @@ -0,0 +1,206 @@ +// run-rustfix +#![allow(unused_assignments, unused_mut, clippy::assign_op_pattern)] +#![warn(clippy::implicit_saturating_sub)] + +fn main() { + // Tests for unsigned integers + + let end_8: u8 = 10; + let start_8: u8 = 5; + let mut u_8: u8 = end_8 - start_8; + + // Lint + if u_8 > 0 { + u_8 = u_8 - 1; + } + + match end_8 { + 10 => { + // Lint + if u_8 > 0 { + u_8 -= 1; + } + }, + 11 => u_8 += 1, + _ => u_8 = 0, + } + + let end_16: u16 = 40; + let start_16: u16 = 35; + + let mut u_16: u16 = end_16 - start_16; + + // Lint + if u_16 > 0 { + u_16 -= 1; + } + + let mut end_32: u32 = 7000; + let mut start_32: u32 = 7010; + + let mut u_32: u32 = end_32 - start_32; + + // Lint + if u_32 != 0 { + u_32 -= 1; + } + + // No Lint + if u_32 > 0 { + u_16 += 1; + } + + // No Lint + if u_32 != 0 { + end_32 -= 1; + start_32 += 1; + } + + let mut end_64: u64 = 75001; + let mut start_64: u64 = 75000; + + let mut u_64: u64 = end_64 - start_64; + + // Lint + if u_64 > 0 { + u_64 -= 1; + } + + // Lint + if 0 < u_64 { + u_64 -= 1; + } + + // Lint + if 0 != u_64 { + u_64 -= 1; + } + + // No Lint + if u_64 >= 1 { + u_64 -= 1; + } + + // No Lint + if u_64 > 0 { + end_64 -= 1; + } + + // Tests for usize + let end_usize: usize = 8054; + let start_usize: usize = 8050; + + let mut u_usize: usize = end_usize - start_usize; + + // Lint + if u_usize > 0 { + u_usize -= 1; + } + + // Tests for signed integers + + let endi_8: i8 = 10; + let starti_8: i8 = 50; + + let mut i_8: i8 = endi_8 - starti_8; + + // Lint + if i_8 > i8::MIN { + i_8 -= 1; + } + + // Lint + if i_8 > i8::min_value() { + i_8 -= 1; + } + + // Lint + if i_8 != i8::MIN { + i_8 -= 1; + } + + // Lint + if i_8 != i8::min_value() { + i_8 -= 1; + } + + let endi_16: i16 = 45; + let starti_16: i16 = 44; + + let mut i_16: i16 = endi_16 - starti_16; + + // Lint + if i_16 > i16::MIN { + i_16 -= 1; + } + + // Lint + if i_16 > i16::min_value() { + i_16 -= 1; + } + + // Lint + if i_16 != i16::MIN { + i_16 -= 1; + } + + // Lint + if i_16 != i16::min_value() { + i_16 -= 1; + } + + let endi_32: i32 = 45; + let starti_32: i32 = 44; + + let mut i_32: i32 = endi_32 - starti_32; + + // Lint + if i_32 > i32::MIN { + i_32 -= 1; + } + + // Lint + if i_32 > i32::min_value() { + i_32 -= 1; + } + + // Lint + if i_32 != i32::MIN { + i_32 -= 1; + } + + // Lint + if i_32 != i32::min_value() { + i_32 -= 1; + } + + let endi_64: i64 = 45; + let starti_64: i64 = 44; + + let mut i_64: i64 = endi_64 - starti_64; + + // Lint + if i64::min_value() < i_64 { + i_64 -= 1; + } + + // Lint + if i64::MIN != i_64 { + i_64 -= 1; + } + + // Lint + if i64::MIN < i_64 { + i_64 -= 1; + } + + // No Lint + if i_64 > 0 { + i_64 -= 1; + } + + // No Lint + if i_64 != 0 { + i_64 -= 1; + } +} diff --git a/src/tools/clippy/tests/ui/implicit_saturating_sub.stderr b/src/tools/clippy/tests/ui/implicit_saturating_sub.stderr new file mode 100644 index 00000000000..a8ba870b1dd --- /dev/null +++ b/src/tools/clippy/tests/ui/implicit_saturating_sub.stderr @@ -0,0 +1,188 @@ +error: Implicitly performing saturating subtraction + --> $DIR/implicit_saturating_sub.rs:13:5 + | +LL | / if u_8 > 0 { +LL | | u_8 = u_8 - 1; +LL | | } + | |_____^ help: try: `u_8 = u_8.saturating_sub(1);` + | + = note: `-D clippy::implicit-saturating-sub` implied by `-D warnings` + +error: Implicitly performing saturating subtraction + --> $DIR/implicit_saturating_sub.rs:20:13 + | +LL | / if u_8 > 0 { +LL | | u_8 -= 1; +LL | | } + | |_____________^ help: try: `u_8 = u_8.saturating_sub(1);` + +error: Implicitly performing saturating subtraction + --> $DIR/implicit_saturating_sub.rs:34:5 + | +LL | / if u_16 > 0 { +LL | | u_16 -= 1; +LL | | } + | |_____^ help: try: `u_16 = u_16.saturating_sub(1);` + +error: Implicitly performing saturating subtraction + --> $DIR/implicit_saturating_sub.rs:44:5 + | +LL | / if u_32 != 0 { +LL | | u_32 -= 1; +LL | | } + | |_____^ help: try: `u_32 = u_32.saturating_sub(1);` + +error: Implicitly performing saturating subtraction + --> $DIR/implicit_saturating_sub.rs:65:5 + | +LL | / if u_64 > 0 { +LL | | u_64 -= 1; +LL | | } + | |_____^ help: try: `u_64 = u_64.saturating_sub(1);` + +error: Implicitly performing saturating subtraction + --> $DIR/implicit_saturating_sub.rs:70:5 + | +LL | / if 0 < u_64 { +LL | | u_64 -= 1; +LL | | } + | |_____^ help: try: `u_64 = u_64.saturating_sub(1);` + +error: Implicitly performing saturating subtraction + --> $DIR/implicit_saturating_sub.rs:75:5 + | +LL | / if 0 != u_64 { +LL | | u_64 -= 1; +LL | | } + | |_____^ help: try: `u_64 = u_64.saturating_sub(1);` + +error: Implicitly performing saturating subtraction + --> $DIR/implicit_saturating_sub.rs:96:5 + | +LL | / if u_usize > 0 { +LL | | u_usize -= 1; +LL | | } + | |_____^ help: try: `u_usize = u_usize.saturating_sub(1);` + +error: Implicitly performing saturating subtraction + --> $DIR/implicit_saturating_sub.rs:108:5 + | +LL | / if i_8 > i8::MIN { +LL | | i_8 -= 1; +LL | | } + | |_____^ help: try: `i_8 = i_8.saturating_sub(1);` + +error: Implicitly performing saturating subtraction + --> $DIR/implicit_saturating_sub.rs:113:5 + | +LL | / if i_8 > i8::min_value() { +LL | | i_8 -= 1; +LL | | } + | |_____^ help: try: `i_8 = i_8.saturating_sub(1);` + +error: Implicitly performing saturating subtraction + --> $DIR/implicit_saturating_sub.rs:118:5 + | +LL | / if i_8 != i8::MIN { +LL | | i_8 -= 1; +LL | | } + | |_____^ help: try: `i_8 = i_8.saturating_sub(1);` + +error: Implicitly performing saturating subtraction + --> $DIR/implicit_saturating_sub.rs:123:5 + | +LL | / if i_8 != i8::min_value() { +LL | | i_8 -= 1; +LL | | } + | |_____^ help: try: `i_8 = i_8.saturating_sub(1);` + +error: Implicitly performing saturating subtraction + --> $DIR/implicit_saturating_sub.rs:133:5 + | +LL | / if i_16 > i16::MIN { +LL | | i_16 -= 1; +LL | | } + | |_____^ help: try: `i_16 = i_16.saturating_sub(1);` + +error: Implicitly performing saturating subtraction + --> $DIR/implicit_saturating_sub.rs:138:5 + | +LL | / if i_16 > i16::min_value() { +LL | | i_16 -= 1; +LL | | } + | |_____^ help: try: `i_16 = i_16.saturating_sub(1);` + +error: Implicitly performing saturating subtraction + --> $DIR/implicit_saturating_sub.rs:143:5 + | +LL | / if i_16 != i16::MIN { +LL | | i_16 -= 1; +LL | | } + | |_____^ help: try: `i_16 = i_16.saturating_sub(1);` + +error: Implicitly performing saturating subtraction + --> $DIR/implicit_saturating_sub.rs:148:5 + | +LL | / if i_16 != i16::min_value() { +LL | | i_16 -= 1; +LL | | } + | |_____^ help: try: `i_16 = i_16.saturating_sub(1);` + +error: Implicitly performing saturating subtraction + --> $DIR/implicit_saturating_sub.rs:158:5 + | +LL | / if i_32 > i32::MIN { +LL | | i_32 -= 1; +LL | | } + | |_____^ help: try: `i_32 = i_32.saturating_sub(1);` + +error: Implicitly performing saturating subtraction + --> $DIR/implicit_saturating_sub.rs:163:5 + | +LL | / if i_32 > i32::min_value() { +LL | | i_32 -= 1; +LL | | } + | |_____^ help: try: `i_32 = i_32.saturating_sub(1);` + +error: Implicitly performing saturating subtraction + --> $DIR/implicit_saturating_sub.rs:168:5 + | +LL | / if i_32 != i32::MIN { +LL | | i_32 -= 1; +LL | | } + | |_____^ help: try: `i_32 = i_32.saturating_sub(1);` + +error: Implicitly performing saturating subtraction + --> $DIR/implicit_saturating_sub.rs:173:5 + | +LL | / if i_32 != i32::min_value() { +LL | | i_32 -= 1; +LL | | } + | |_____^ help: try: `i_32 = i_32.saturating_sub(1);` + +error: Implicitly performing saturating subtraction + --> $DIR/implicit_saturating_sub.rs:183:5 + | +LL | / if i64::min_value() < i_64 { +LL | | i_64 -= 1; +LL | | } + | |_____^ help: try: `i_64 = i_64.saturating_sub(1);` + +error: Implicitly performing saturating subtraction + --> $DIR/implicit_saturating_sub.rs:188:5 + | +LL | / if i64::MIN != i_64 { +LL | | i_64 -= 1; +LL | | } + | |_____^ help: try: `i_64 = i_64.saturating_sub(1);` + +error: Implicitly performing saturating subtraction + --> $DIR/implicit_saturating_sub.rs:193:5 + | +LL | / if i64::MIN < i_64 { +LL | | i_64 -= 1; +LL | | } + | |_____^ help: try: `i_64 = i_64.saturating_sub(1);` + +error: aborting due to 23 previous errors + diff --git a/src/tools/clippy/tests/ui/inconsistent_digit_grouping.fixed b/src/tools/clippy/tests/ui/inconsistent_digit_grouping.fixed new file mode 100644 index 00000000000..b75f10917df --- /dev/null +++ b/src/tools/clippy/tests/ui/inconsistent_digit_grouping.fixed @@ -0,0 +1,43 @@ +// run-rustfix +#[warn(clippy::inconsistent_digit_grouping)] +#[deny(clippy::unreadable_literal)] +#[allow(unused_variables, clippy::excessive_precision)] +fn main() { + macro_rules! mac1 { + () => { + 1_23_456 + }; + } + macro_rules! mac2 { + () => { + 1_234.5678_f32 + }; + } + + let good = ( + 123, + 1_234, + 1_2345_6789, + 123_f32, + 1_234.12_f32, + 1_234.123_4_f32, + 1.123_456_7_f32, + ); + let bad = (123_456, 12_345_678, 1_234_567, 1_234.567_8_f32, 1.234_567_8_f32); + + // Test padding + let _ = 0x0010_0000; + let _ = 0x0100_0000; + let _ = 0x1000_0000; + let _ = 0x0001_0000_0000_u64; + + // Test suggestion when fraction has no digits + let _: f32 = 123_456.; + + // Test UUID formatted literal + let _: u128 = 0x12345678_1234_1234_1234_123456789012; + + // Ignore literals in macros + let _ = mac1!(); + let _ = mac2!(); +} diff --git a/src/tools/clippy/tests/ui/inconsistent_digit_grouping.rs b/src/tools/clippy/tests/ui/inconsistent_digit_grouping.rs new file mode 100644 index 00000000000..79ce38be19b --- /dev/null +++ b/src/tools/clippy/tests/ui/inconsistent_digit_grouping.rs @@ -0,0 +1,43 @@ +// run-rustfix +#[warn(clippy::inconsistent_digit_grouping)] +#[deny(clippy::unreadable_literal)] +#[allow(unused_variables, clippy::excessive_precision)] +fn main() { + macro_rules! mac1 { + () => { + 1_23_456 + }; + } + macro_rules! mac2 { + () => { + 1_234.5678_f32 + }; + } + + let good = ( + 123, + 1_234, + 1_2345_6789, + 123_f32, + 1_234.12_f32, + 1_234.123_4_f32, + 1.123_456_7_f32, + ); + let bad = (1_23_456, 1_234_5678, 1234_567, 1_234.5678_f32, 1.234_5678_f32); + + // Test padding + let _ = 0x100000; + let _ = 0x1000000; + let _ = 0x10000000; + let _ = 0x100000000_u64; + + // Test suggestion when fraction has no digits + let _: f32 = 1_23_456.; + + // Test UUID formatted literal + let _: u128 = 0x12345678_1234_1234_1234_123456789012; + + // Ignore literals in macros + let _ = mac1!(); + let _ = mac2!(); +} diff --git a/src/tools/clippy/tests/ui/inconsistent_digit_grouping.stderr b/src/tools/clippy/tests/ui/inconsistent_digit_grouping.stderr new file mode 100644 index 00000000000..b8ac9155462 --- /dev/null +++ b/src/tools/clippy/tests/ui/inconsistent_digit_grouping.stderr @@ -0,0 +1,70 @@ +error: digits grouped inconsistently by underscores + --> $DIR/inconsistent_digit_grouping.rs:26:16 + | +LL | let bad = (1_23_456, 1_234_5678, 1234_567, 1_234.5678_f32, 1.234_5678_f32); + | ^^^^^^^^ help: consider: `123_456` + | + = note: `-D clippy::inconsistent-digit-grouping` implied by `-D warnings` + +error: digits grouped inconsistently by underscores + --> $DIR/inconsistent_digit_grouping.rs:26:26 + | +LL | let bad = (1_23_456, 1_234_5678, 1234_567, 1_234.5678_f32, 1.234_5678_f32); + | ^^^^^^^^^^ help: consider: `12_345_678` + +error: digits grouped inconsistently by underscores + --> $DIR/inconsistent_digit_grouping.rs:26:38 + | +LL | let bad = (1_23_456, 1_234_5678, 1234_567, 1_234.5678_f32, 1.234_5678_f32); + | ^^^^^^^^ help: consider: `1_234_567` + +error: digits grouped inconsistently by underscores + --> $DIR/inconsistent_digit_grouping.rs:26:48 + | +LL | let bad = (1_23_456, 1_234_5678, 1234_567, 1_234.5678_f32, 1.234_5678_f32); + | ^^^^^^^^^^^^^^ help: consider: `1_234.567_8_f32` + +error: digits grouped inconsistently by underscores + --> $DIR/inconsistent_digit_grouping.rs:26:64 + | +LL | let bad = (1_23_456, 1_234_5678, 1234_567, 1_234.5678_f32, 1.234_5678_f32); + | ^^^^^^^^^^^^^^ help: consider: `1.234_567_8_f32` + +error: long literal lacking separators + --> $DIR/inconsistent_digit_grouping.rs:29:13 + | +LL | let _ = 0x100000; + | ^^^^^^^^ help: consider: `0x0010_0000` + | +note: the lint level is defined here + --> $DIR/inconsistent_digit_grouping.rs:3:8 + | +LL | #[deny(clippy::unreadable_literal)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: long literal lacking separators + --> $DIR/inconsistent_digit_grouping.rs:30:13 + | +LL | let _ = 0x1000000; + | ^^^^^^^^^ help: consider: `0x0100_0000` + +error: long literal lacking separators + --> $DIR/inconsistent_digit_grouping.rs:31:13 + | +LL | let _ = 0x10000000; + | ^^^^^^^^^^ help: consider: `0x1000_0000` + +error: long literal lacking separators + --> $DIR/inconsistent_digit_grouping.rs:32:13 + | +LL | let _ = 0x100000000_u64; + | ^^^^^^^^^^^^^^^ help: consider: `0x0001_0000_0000_u64` + +error: digits grouped inconsistently by underscores + --> $DIR/inconsistent_digit_grouping.rs:35:18 + | +LL | let _: f32 = 1_23_456.; + | ^^^^^^^^^ help: consider: `123_456.` + +error: aborting due to 10 previous errors + diff --git a/src/tools/clippy/tests/ui/indexing_slicing_index.rs b/src/tools/clippy/tests/ui/indexing_slicing_index.rs new file mode 100644 index 00000000000..000d5269930 --- /dev/null +++ b/src/tools/clippy/tests/ui/indexing_slicing_index.rs @@ -0,0 +1,31 @@ +#![warn(clippy::indexing_slicing)] +// We also check the out_of_bounds_indexing lint here, because it lints similar things and +// we want to avoid false positives. +#![warn(clippy::out_of_bounds_indexing)] +#![allow(clippy::no_effect, clippy::unnecessary_operation)] + +fn main() { + let x = [1, 2, 3, 4]; + let index: usize = 1; + x[index]; + x[4]; // Ok, let rustc's `const_err` lint handle `usize` indexing on arrays. + x[1 << 3]; // Ok, let rustc's `const_err` lint handle `usize` indexing on arrays. + + x[0]; // Ok, should not produce stderr. + x[3]; // Ok, should not produce stderr. + + let y = &x; + y[0]; + + let v = vec![0; 5]; + v[0]; + v[10]; + v[1 << 3]; + + const N: usize = 15; // Out of bounds + const M: usize = 3; // In bounds + x[N]; // Ok, let rustc's `const_err` lint handle `usize` indexing on arrays. + x[M]; // Ok, should not produce stderr. + v[N]; + v[M]; +} diff --git a/src/tools/clippy/tests/ui/indexing_slicing_index.stderr b/src/tools/clippy/tests/ui/indexing_slicing_index.stderr new file mode 100644 index 00000000000..ac5f0d0a39e --- /dev/null +++ b/src/tools/clippy/tests/ui/indexing_slicing_index.stderr @@ -0,0 +1,79 @@ +error: this operation will panic at runtime + --> $DIR/indexing_slicing_index.rs:11:5 + | +LL | x[4]; // Ok, let rustc's `const_err` lint handle `usize` indexing on arrays. + | ^^^^ index out of bounds: the len is 4 but the index is 4 + | + = note: `#[deny(unconditional_panic)]` on by default + +error: this operation will panic at runtime + --> $DIR/indexing_slicing_index.rs:12:5 + | +LL | x[1 << 3]; // Ok, let rustc's `const_err` lint handle `usize` indexing on arrays. + | ^^^^^^^^^ index out of bounds: the len is 4 but the index is 8 + +error: this operation will panic at runtime + --> $DIR/indexing_slicing_index.rs:27:5 + | +LL | x[N]; // Ok, let rustc's `const_err` lint handle `usize` indexing on arrays. + | ^^^^ index out of bounds: the len is 4 but the index is 15 + +error: indexing may panic. + --> $DIR/indexing_slicing_index.rs:10:5 + | +LL | x[index]; + | ^^^^^^^^ + | + = note: `-D clippy::indexing-slicing` implied by `-D warnings` + = help: Consider using `.get(n)` or `.get_mut(n)` instead + +error: indexing may panic. + --> $DIR/indexing_slicing_index.rs:18:5 + | +LL | y[0]; + | ^^^^ + | + = help: Consider using `.get(n)` or `.get_mut(n)` instead + +error: indexing may panic. + --> $DIR/indexing_slicing_index.rs:21:5 + | +LL | v[0]; + | ^^^^ + | + = help: Consider using `.get(n)` or `.get_mut(n)` instead + +error: indexing may panic. + --> $DIR/indexing_slicing_index.rs:22:5 + | +LL | v[10]; + | ^^^^^ + | + = help: Consider using `.get(n)` or `.get_mut(n)` instead + +error: indexing may panic. + --> $DIR/indexing_slicing_index.rs:23:5 + | +LL | v[1 << 3]; + | ^^^^^^^^^ + | + = help: Consider using `.get(n)` or `.get_mut(n)` instead + +error: indexing may panic. + --> $DIR/indexing_slicing_index.rs:29:5 + | +LL | v[N]; + | ^^^^ + | + = help: Consider using `.get(n)` or `.get_mut(n)` instead + +error: indexing may panic. + --> $DIR/indexing_slicing_index.rs:30:5 + | +LL | v[M]; + | ^^^^ + | + = help: Consider using `.get(n)` or `.get_mut(n)` instead + +error: aborting due to 10 previous errors + diff --git a/src/tools/clippy/tests/ui/indexing_slicing_slice.rs b/src/tools/clippy/tests/ui/indexing_slicing_slice.rs new file mode 100644 index 00000000000..7b107db39f0 --- /dev/null +++ b/src/tools/clippy/tests/ui/indexing_slicing_slice.rs @@ -0,0 +1,37 @@ +#![warn(clippy::indexing_slicing)] +// We also check the out_of_bounds_indexing lint here, because it lints similar things and +// we want to avoid false positives. +#![warn(clippy::out_of_bounds_indexing)] +#![allow(clippy::no_effect, clippy::unnecessary_operation)] + +fn main() { + let x = [1, 2, 3, 4]; + let index: usize = 1; + let index_from: usize = 2; + let index_to: usize = 3; + &x[index..]; + &x[..index]; + &x[index_from..index_to]; + &x[index_from..][..index_to]; // Two lint reports, one for [index_from..] and another for [..index_to]. + &x[5..][..10]; // Two lint reports, one for out of bounds [5..] and another for slicing [..10]. + &x[0..][..3]; + &x[1..][..5]; + + &x[0..].get(..3); // Ok, should not produce stderr. + &x[0..3]; // Ok, should not produce stderr. + + let y = &x; + &y[1..2]; + &y[0..=4]; + &y[..=4]; + + &y[..]; // Ok, should not produce stderr. + + let v = vec![0; 5]; + &v[10..100]; + &x[10..][..100]; // Two lint reports, one for [10..] and another for [..100]. + &v[10..]; + &v[..100]; + + &v[..]; // Ok, should not produce stderr. +} diff --git a/src/tools/clippy/tests/ui/indexing_slicing_slice.stderr b/src/tools/clippy/tests/ui/indexing_slicing_slice.stderr new file mode 100644 index 00000000000..ec6c157ac1a --- /dev/null +++ b/src/tools/clippy/tests/ui/indexing_slicing_slice.stderr @@ -0,0 +1,137 @@ +error: slicing may panic. + --> $DIR/indexing_slicing_slice.rs:12:6 + | +LL | &x[index..]; + | ^^^^^^^^^^ + | + = note: `-D clippy::indexing-slicing` implied by `-D warnings` + = help: Consider using `.get(n..)` or .get_mut(n..)` instead + +error: slicing may panic. + --> $DIR/indexing_slicing_slice.rs:13:6 + | +LL | &x[..index]; + | ^^^^^^^^^^ + | + = help: Consider using `.get(..n)`or `.get_mut(..n)` instead + +error: slicing may panic. + --> $DIR/indexing_slicing_slice.rs:14:6 + | +LL | &x[index_from..index_to]; + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: Consider using `.get(n..m)` or `.get_mut(n..m)` instead + +error: slicing may panic. + --> $DIR/indexing_slicing_slice.rs:15:6 + | +LL | &x[index_from..][..index_to]; // Two lint reports, one for [index_from..] and another for [..index_to]. + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: Consider using `.get(..n)`or `.get_mut(..n)` instead + +error: slicing may panic. + --> $DIR/indexing_slicing_slice.rs:15:6 + | +LL | &x[index_from..][..index_to]; // Two lint reports, one for [index_from..] and another for [..index_to]. + | ^^^^^^^^^^^^^^^ + | + = help: Consider using `.get(n..)` or .get_mut(n..)` instead + +error: slicing may panic. + --> $DIR/indexing_slicing_slice.rs:16:6 + | +LL | &x[5..][..10]; // Two lint reports, one for out of bounds [5..] and another for slicing [..10]. + | ^^^^^^^^^^^^ + | + = help: Consider using `.get(..n)`or `.get_mut(..n)` instead + +error: range is out of bounds + --> $DIR/indexing_slicing_slice.rs:16:8 + | +LL | &x[5..][..10]; // Two lint reports, one for out of bounds [5..] and another for slicing [..10]. + | ^ + | + = note: `-D clippy::out-of-bounds-indexing` implied by `-D warnings` + +error: slicing may panic. + --> $DIR/indexing_slicing_slice.rs:17:6 + | +LL | &x[0..][..3]; + | ^^^^^^^^^^^ + | + = help: Consider using `.get(..n)`or `.get_mut(..n)` instead + +error: slicing may panic. + --> $DIR/indexing_slicing_slice.rs:18:6 + | +LL | &x[1..][..5]; + | ^^^^^^^^^^^ + | + = help: Consider using `.get(..n)`or `.get_mut(..n)` instead + +error: slicing may panic. + --> $DIR/indexing_slicing_slice.rs:24:6 + | +LL | &y[1..2]; + | ^^^^^^^ + | + = help: Consider using `.get(n..m)` or `.get_mut(n..m)` instead + +error: slicing may panic. + --> $DIR/indexing_slicing_slice.rs:25:6 + | +LL | &y[0..=4]; + | ^^^^^^^^ + | + = help: Consider using `.get(n..m)` or `.get_mut(n..m)` instead + +error: slicing may panic. + --> $DIR/indexing_slicing_slice.rs:26:6 + | +LL | &y[..=4]; + | ^^^^^^^ + | + = help: Consider using `.get(..n)`or `.get_mut(..n)` instead + +error: slicing may panic. + --> $DIR/indexing_slicing_slice.rs:31:6 + | +LL | &v[10..100]; + | ^^^^^^^^^^ + | + = help: Consider using `.get(n..m)` or `.get_mut(n..m)` instead + +error: slicing may panic. + --> $DIR/indexing_slicing_slice.rs:32:6 + | +LL | &x[10..][..100]; // Two lint reports, one for [10..] and another for [..100]. + | ^^^^^^^^^^^^^^ + | + = help: Consider using `.get(..n)`or `.get_mut(..n)` instead + +error: range is out of bounds + --> $DIR/indexing_slicing_slice.rs:32:8 + | +LL | &x[10..][..100]; // Two lint reports, one for [10..] and another for [..100]. + | ^^ + +error: slicing may panic. + --> $DIR/indexing_slicing_slice.rs:33:6 + | +LL | &v[10..]; + | ^^^^^^^ + | + = help: Consider using `.get(n..)` or .get_mut(n..)` instead + +error: slicing may panic. + --> $DIR/indexing_slicing_slice.rs:34:6 + | +LL | &v[..100]; + | ^^^^^^^^ + | + = help: Consider using `.get(..n)`or `.get_mut(..n)` instead + +error: aborting due to 17 previous errors + diff --git a/src/tools/clippy/tests/ui/inefficient_to_string.fixed b/src/tools/clippy/tests/ui/inefficient_to_string.fixed new file mode 100644 index 00000000000..c972b9419ef --- /dev/null +++ b/src/tools/clippy/tests/ui/inefficient_to_string.fixed @@ -0,0 +1,31 @@ +// run-rustfix +#![deny(clippy::inefficient_to_string)] + +use std::borrow::Cow; + +fn main() { + let rstr: &str = "hello"; + let rrstr: &&str = &rstr; + let rrrstr: &&&str = &rrstr; + let _: String = rstr.to_string(); + let _: String = (*rrstr).to_string(); + let _: String = (**rrrstr).to_string(); + + let string: String = String::from("hello"); + let rstring: &String = &string; + let rrstring: &&String = &rstring; + let rrrstring: &&&String = &rrstring; + let _: String = string.to_string(); + let _: String = rstring.to_string(); + let _: String = (*rrstring).to_string(); + let _: String = (**rrrstring).to_string(); + + let cow: Cow<'_, str> = Cow::Borrowed("hello"); + let rcow: &Cow<'_, str> = &cow; + let rrcow: &&Cow<'_, str> = &rcow; + let rrrcow: &&&Cow<'_, str> = &rrcow; + let _: String = cow.to_string(); + let _: String = rcow.to_string(); + let _: String = (*rrcow).to_string(); + let _: String = (**rrrcow).to_string(); +} diff --git a/src/tools/clippy/tests/ui/inefficient_to_string.rs b/src/tools/clippy/tests/ui/inefficient_to_string.rs new file mode 100644 index 00000000000..acdc55aa0d6 --- /dev/null +++ b/src/tools/clippy/tests/ui/inefficient_to_string.rs @@ -0,0 +1,31 @@ +// run-rustfix +#![deny(clippy::inefficient_to_string)] + +use std::borrow::Cow; + +fn main() { + let rstr: &str = "hello"; + let rrstr: &&str = &rstr; + let rrrstr: &&&str = &rrstr; + let _: String = rstr.to_string(); + let _: String = rrstr.to_string(); + let _: String = rrrstr.to_string(); + + let string: String = String::from("hello"); + let rstring: &String = &string; + let rrstring: &&String = &rstring; + let rrrstring: &&&String = &rrstring; + let _: String = string.to_string(); + let _: String = rstring.to_string(); + let _: String = rrstring.to_string(); + let _: String = rrrstring.to_string(); + + let cow: Cow<'_, str> = Cow::Borrowed("hello"); + let rcow: &Cow<'_, str> = &cow; + let rrcow: &&Cow<'_, str> = &rcow; + let rrrcow: &&&Cow<'_, str> = &rrcow; + let _: String = cow.to_string(); + let _: String = rcow.to_string(); + let _: String = rrcow.to_string(); + let _: String = rrrcow.to_string(); +} diff --git a/src/tools/clippy/tests/ui/inefficient_to_string.stderr b/src/tools/clippy/tests/ui/inefficient_to_string.stderr new file mode 100644 index 00000000000..4be46161e8b --- /dev/null +++ b/src/tools/clippy/tests/ui/inefficient_to_string.stderr @@ -0,0 +1,55 @@ +error: calling `to_string` on `&&str` + --> $DIR/inefficient_to_string.rs:11:21 + | +LL | let _: String = rrstr.to_string(); + | ^^^^^^^^^^^^^^^^^ help: try dereferencing the receiver: `(*rrstr).to_string()` + | +note: the lint level is defined here + --> $DIR/inefficient_to_string.rs:2:9 + | +LL | #![deny(clippy::inefficient_to_string)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: `&str` implements `ToString` through a slower blanket impl, but `str` has a fast specialization of `ToString` + +error: calling `to_string` on `&&&str` + --> $DIR/inefficient_to_string.rs:12:21 + | +LL | let _: String = rrrstr.to_string(); + | ^^^^^^^^^^^^^^^^^^ help: try dereferencing the receiver: `(**rrrstr).to_string()` + | + = help: `&&str` implements `ToString` through a slower blanket impl, but `str` has a fast specialization of `ToString` + +error: calling `to_string` on `&&std::string::String` + --> $DIR/inefficient_to_string.rs:20:21 + | +LL | let _: String = rrstring.to_string(); + | ^^^^^^^^^^^^^^^^^^^^ help: try dereferencing the receiver: `(*rrstring).to_string()` + | + = help: `&std::string::String` implements `ToString` through a slower blanket impl, but `std::string::String` has a fast specialization of `ToString` + +error: calling `to_string` on `&&&std::string::String` + --> $DIR/inefficient_to_string.rs:21:21 + | +LL | let _: String = rrrstring.to_string(); + | ^^^^^^^^^^^^^^^^^^^^^ help: try dereferencing the receiver: `(**rrrstring).to_string()` + | + = help: `&&std::string::String` implements `ToString` through a slower blanket impl, but `std::string::String` has a fast specialization of `ToString` + +error: calling `to_string` on `&&std::borrow::Cow<str>` + --> $DIR/inefficient_to_string.rs:29:21 + | +LL | let _: String = rrcow.to_string(); + | ^^^^^^^^^^^^^^^^^ help: try dereferencing the receiver: `(*rrcow).to_string()` + | + = help: `&std::borrow::Cow<str>` implements `ToString` through a slower blanket impl, but `std::borrow::Cow<str>` has a fast specialization of `ToString` + +error: calling `to_string` on `&&&std::borrow::Cow<str>` + --> $DIR/inefficient_to_string.rs:30:21 + | +LL | let _: String = rrrcow.to_string(); + | ^^^^^^^^^^^^^^^^^^ help: try dereferencing the receiver: `(**rrrcow).to_string()` + | + = help: `&&std::borrow::Cow<str>` implements `ToString` through a slower blanket impl, but `std::borrow::Cow<str>` has a fast specialization of `ToString` + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/infallible_destructuring_match.fixed b/src/tools/clippy/tests/ui/infallible_destructuring_match.fixed new file mode 100644 index 00000000000..b8e40d99553 --- /dev/null +++ b/src/tools/clippy/tests/ui/infallible_destructuring_match.fixed @@ -0,0 +1,112 @@ +// run-rustfix +#![feature(exhaustive_patterns, never_type)] +#![allow(dead_code, unreachable_code, unused_variables)] +#![allow(clippy::let_and_return)] + +enum SingleVariantEnum { + Variant(i32), +} + +struct TupleStruct(i32); + +enum EmptyEnum {} + +macro_rules! match_enum { + ($param:expr) => { + let data = match $param { + SingleVariantEnum::Variant(i) => i, + }; + }; +} + +fn infallible_destructuring_match_enum() { + let wrapper = SingleVariantEnum::Variant(0); + + // This should lint! + let SingleVariantEnum::Variant(data) = wrapper; + + // This shouldn't (inside macro) + match_enum!(wrapper); + + // This shouldn't! + let data = match wrapper { + SingleVariantEnum::Variant(_) => -1, + }; + + // Neither should this! + let data = match wrapper { + SingleVariantEnum::Variant(i) => -1, + }; + + let SingleVariantEnum::Variant(data) = wrapper; +} + +macro_rules! match_struct { + ($param:expr) => { + let data = match $param { + TupleStruct(i) => i, + }; + }; +} + +fn infallible_destructuring_match_struct() { + let wrapper = TupleStruct(0); + + // This should lint! + let TupleStruct(data) = wrapper; + + // This shouldn't (inside macro) + match_struct!(wrapper); + + // This shouldn't! + let data = match wrapper { + TupleStruct(_) => -1, + }; + + // Neither should this! + let data = match wrapper { + TupleStruct(i) => -1, + }; + + let TupleStruct(data) = wrapper; +} + +macro_rules! match_never_enum { + ($param:expr) => { + let data = match $param { + Ok(i) => i, + }; + }; +} + +fn never_enum() { + let wrapper: Result<i32, !> = Ok(23); + + // This should lint! + let Ok(data) = wrapper; + + // This shouldn't (inside macro) + match_never_enum!(wrapper); + + // This shouldn't! + let data = match wrapper { + Ok(_) => -1, + }; + + // Neither should this! + let data = match wrapper { + Ok(i) => -1, + }; + + let Ok(data) = wrapper; +} + +impl EmptyEnum { + fn match_on(&self) -> ! { + // The lint shouldn't pick this up, as `let` won't work here! + let data = match *self {}; + data + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/infallible_destructuring_match.rs b/src/tools/clippy/tests/ui/infallible_destructuring_match.rs new file mode 100644 index 00000000000..106cd438b90 --- /dev/null +++ b/src/tools/clippy/tests/ui/infallible_destructuring_match.rs @@ -0,0 +1,118 @@ +// run-rustfix +#![feature(exhaustive_patterns, never_type)] +#![allow(dead_code, unreachable_code, unused_variables)] +#![allow(clippy::let_and_return)] + +enum SingleVariantEnum { + Variant(i32), +} + +struct TupleStruct(i32); + +enum EmptyEnum {} + +macro_rules! match_enum { + ($param:expr) => { + let data = match $param { + SingleVariantEnum::Variant(i) => i, + }; + }; +} + +fn infallible_destructuring_match_enum() { + let wrapper = SingleVariantEnum::Variant(0); + + // This should lint! + let data = match wrapper { + SingleVariantEnum::Variant(i) => i, + }; + + // This shouldn't (inside macro) + match_enum!(wrapper); + + // This shouldn't! + let data = match wrapper { + SingleVariantEnum::Variant(_) => -1, + }; + + // Neither should this! + let data = match wrapper { + SingleVariantEnum::Variant(i) => -1, + }; + + let SingleVariantEnum::Variant(data) = wrapper; +} + +macro_rules! match_struct { + ($param:expr) => { + let data = match $param { + TupleStruct(i) => i, + }; + }; +} + +fn infallible_destructuring_match_struct() { + let wrapper = TupleStruct(0); + + // This should lint! + let data = match wrapper { + TupleStruct(i) => i, + }; + + // This shouldn't (inside macro) + match_struct!(wrapper); + + // This shouldn't! + let data = match wrapper { + TupleStruct(_) => -1, + }; + + // Neither should this! + let data = match wrapper { + TupleStruct(i) => -1, + }; + + let TupleStruct(data) = wrapper; +} + +macro_rules! match_never_enum { + ($param:expr) => { + let data = match $param { + Ok(i) => i, + }; + }; +} + +fn never_enum() { + let wrapper: Result<i32, !> = Ok(23); + + // This should lint! + let data = match wrapper { + Ok(i) => i, + }; + + // This shouldn't (inside macro) + match_never_enum!(wrapper); + + // This shouldn't! + let data = match wrapper { + Ok(_) => -1, + }; + + // Neither should this! + let data = match wrapper { + Ok(i) => -1, + }; + + let Ok(data) = wrapper; +} + +impl EmptyEnum { + fn match_on(&self) -> ! { + // The lint shouldn't pick this up, as `let` won't work here! + let data = match *self {}; + data + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/infallible_destructuring_match.stderr b/src/tools/clippy/tests/ui/infallible_destructuring_match.stderr new file mode 100644 index 00000000000..1b78db42014 --- /dev/null +++ b/src/tools/clippy/tests/ui/infallible_destructuring_match.stderr @@ -0,0 +1,28 @@ +error: you seem to be trying to use `match` to destructure a single infallible pattern. Consider using `let` + --> $DIR/infallible_destructuring_match.rs:26:5 + | +LL | / let data = match wrapper { +LL | | SingleVariantEnum::Variant(i) => i, +LL | | }; + | |______^ help: try this: `let SingleVariantEnum::Variant(data) = wrapper;` + | + = note: `-D clippy::infallible-destructuring-match` implied by `-D warnings` + +error: you seem to be trying to use `match` to destructure a single infallible pattern. Consider using `let` + --> $DIR/infallible_destructuring_match.rs:58:5 + | +LL | / let data = match wrapper { +LL | | TupleStruct(i) => i, +LL | | }; + | |______^ help: try this: `let TupleStruct(data) = wrapper;` + +error: you seem to be trying to use `match` to destructure a single infallible pattern. Consider using `let` + --> $DIR/infallible_destructuring_match.rs:90:5 + | +LL | / let data = match wrapper { +LL | | Ok(i) => i, +LL | | }; + | |______^ help: try this: `let Ok(data) = wrapper;` + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/infinite_iter.rs b/src/tools/clippy/tests/ui/infinite_iter.rs new file mode 100644 index 00000000000..1fe68897765 --- /dev/null +++ b/src/tools/clippy/tests/ui/infinite_iter.rs @@ -0,0 +1,69 @@ +use std::iter::repeat; +fn square_is_lower_64(x: &u32) -> bool { + x * x < 64 +} + +#[allow(clippy::maybe_infinite_iter)] +#[deny(clippy::infinite_iter)] +fn infinite_iters() { + repeat(0_u8).collect::<Vec<_>>(); // infinite iter + (0..8_u32).take_while(square_is_lower_64).cycle().count(); // infinite iter + (0..8_u64).chain(0..).max(); // infinite iter + (0_usize..) + .chain([0usize, 1, 2].iter().cloned()) + .skip_while(|x| *x != 42) + .min(); // infinite iter + (0..8_u32) + .rev() + .cycle() + .map(|x| x + 1_u32) + .for_each(|x| println!("{}", x)); // infinite iter + (0..3_u32).flat_map(|x| x..).sum::<u32>(); // infinite iter + (0_usize..).flat_map(|x| 0..x).product::<usize>(); // infinite iter + (0_u64..).filter(|x| x % 2 == 0).last(); // infinite iter + (0..42_u64).by_ref().last(); // not an infinite, because ranges are double-ended + (0..).next(); // iterator is not exhausted +} + +#[deny(clippy::maybe_infinite_iter)] +fn potential_infinite_iters() { + (0..).zip((0..).take_while(square_is_lower_64)).count(); // maybe infinite iter + repeat(42).take_while(|x| *x == 42).chain(0..42).max(); // maybe infinite iter + (1..) + .scan(0, |state, x| { + *state += x; + Some(*state) + }) + .min(); // maybe infinite iter + (0..).find(|x| *x == 24); // maybe infinite iter + (0..).position(|x| x == 24); // maybe infinite iter + (0..).any(|x| x == 24); // maybe infinite iter + (0..).all(|x| x == 24); // maybe infinite iter + + (0..).zip(0..42).take_while(|&(x, _)| x != 42).count(); // not infinite + repeat(42).take_while(|x| *x == 42).next(); // iterator is not exhausted +} + +fn main() { + infinite_iters(); + potential_infinite_iters(); +} + +mod finite_collect { + use std::collections::HashSet; + use std::iter::FromIterator; + + struct C; + impl FromIterator<i32> for C { + fn from_iter<I: IntoIterator<Item = i32>>(iter: I) -> Self { + C + } + } + + fn check_collect() { + let _: HashSet<i32> = (0..).collect(); // Infinite iter + + // Some data structures don't collect infinitely, such as `ArrayVec` + let _: C = (0..).collect(); + } +} diff --git a/src/tools/clippy/tests/ui/infinite_iter.stderr b/src/tools/clippy/tests/ui/infinite_iter.stderr new file mode 100644 index 00000000000..5f5e7ac9f25 --- /dev/null +++ b/src/tools/clippy/tests/ui/infinite_iter.stderr @@ -0,0 +1,109 @@ +error: infinite iteration detected + --> $DIR/infinite_iter.rs:9:5 + | +LL | repeat(0_u8).collect::<Vec<_>>(); // infinite iter + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/infinite_iter.rs:7:8 + | +LL | #[deny(clippy::infinite_iter)] + | ^^^^^^^^^^^^^^^^^^^^^ + +error: infinite iteration detected + --> $DIR/infinite_iter.rs:10:5 + | +LL | (0..8_u32).take_while(square_is_lower_64).cycle().count(); // infinite iter + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: infinite iteration detected + --> $DIR/infinite_iter.rs:11:5 + | +LL | (0..8_u64).chain(0..).max(); // infinite iter + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: infinite iteration detected + --> $DIR/infinite_iter.rs:16:5 + | +LL | / (0..8_u32) +LL | | .rev() +LL | | .cycle() +LL | | .map(|x| x + 1_u32) +LL | | .for_each(|x| println!("{}", x)); // infinite iter + | |________________________________________^ + +error: infinite iteration detected + --> $DIR/infinite_iter.rs:22:5 + | +LL | (0_usize..).flat_map(|x| 0..x).product::<usize>(); // infinite iter + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: infinite iteration detected + --> $DIR/infinite_iter.rs:23:5 + | +LL | (0_u64..).filter(|x| x % 2 == 0).last(); // infinite iter + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: possible infinite iteration detected + --> $DIR/infinite_iter.rs:30:5 + | +LL | (0..).zip((0..).take_while(square_is_lower_64)).count(); // maybe infinite iter + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/infinite_iter.rs:28:8 + | +LL | #[deny(clippy::maybe_infinite_iter)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: possible infinite iteration detected + --> $DIR/infinite_iter.rs:31:5 + | +LL | repeat(42).take_while(|x| *x == 42).chain(0..42).max(); // maybe infinite iter + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: possible infinite iteration detected + --> $DIR/infinite_iter.rs:32:5 + | +LL | / (1..) +LL | | .scan(0, |state, x| { +LL | | *state += x; +LL | | Some(*state) +LL | | }) +LL | | .min(); // maybe infinite iter + | |______________^ + +error: possible infinite iteration detected + --> $DIR/infinite_iter.rs:38:5 + | +LL | (0..).find(|x| *x == 24); // maybe infinite iter + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: possible infinite iteration detected + --> $DIR/infinite_iter.rs:39:5 + | +LL | (0..).position(|x| x == 24); // maybe infinite iter + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: possible infinite iteration detected + --> $DIR/infinite_iter.rs:40:5 + | +LL | (0..).any(|x| x == 24); // maybe infinite iter + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: possible infinite iteration detected + --> $DIR/infinite_iter.rs:41:5 + | +LL | (0..).all(|x| x == 24); // maybe infinite iter + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: infinite iteration detected + --> $DIR/infinite_iter.rs:64:31 + | +LL | let _: HashSet<i32> = (0..).collect(); // Infinite iter + | ^^^^^^^^^^^^^^^ + | + = note: `#[deny(clippy::infinite_iter)]` on by default + +error: aborting due to 14 previous errors + diff --git a/src/tools/clippy/tests/ui/infinite_loop.rs b/src/tools/clippy/tests/ui/infinite_loop.rs new file mode 100644 index 00000000000..72591f12baf --- /dev/null +++ b/src/tools/clippy/tests/ui/infinite_loop.rs @@ -0,0 +1,206 @@ +fn fn_val(i: i32) -> i32 { + unimplemented!() +} +fn fn_constref(i: &i32) -> i32 { + unimplemented!() +} +fn fn_mutref(i: &mut i32) { + unimplemented!() +} +fn fooi() -> i32 { + unimplemented!() +} +fn foob() -> bool { + unimplemented!() +} + +#[allow(clippy::many_single_char_names)] +fn immutable_condition() { + // Should warn when all vars mentioned are immutable + let y = 0; + while y < 10 { + println!("KO - y is immutable"); + } + + let x = 0; + while y < 10 && x < 3 { + let mut k = 1; + k += 2; + println!("KO - x and y immutable"); + } + + let cond = false; + while !cond { + println!("KO - cond immutable"); + } + + let mut i = 0; + while y < 10 && i < 3 { + i += 1; + println!("OK - i is mutable"); + } + + let mut mut_cond = false; + while !mut_cond || cond { + mut_cond = true; + println!("OK - mut_cond is mutable"); + } + + while fooi() < x { + println!("OK - Fn call results may vary"); + } + + while foob() { + println!("OK - Fn call results may vary"); + } + + let mut a = 0; + let mut c = move || { + while a < 5 { + a += 1; + println!("OK - a is mutable"); + } + }; + c(); + + let mut tup = (0, 0); + while tup.0 < 5 { + tup.0 += 1; + println!("OK - tup.0 gets mutated") + } +} + +fn unused_var() { + // Should warn when a (mutable) var is not used in while body + let (mut i, mut j) = (0, 0); + + while i < 3 { + j = 3; + println!("KO - i not mentioned"); + } + + while i < 3 && j > 0 { + println!("KO - i and j not mentioned"); + } + + while i < 3 { + let mut i = 5; + fn_mutref(&mut i); + println!("KO - shadowed"); + } + + while i < 3 && j > 0 { + i = 5; + println!("OK - i in cond and mentioned"); + } +} + +fn used_immutable() { + let mut i = 0; + + while i < 3 { + fn_constref(&i); + println!("KO - const reference"); + } + + while i < 3 { + fn_val(i); + println!("KO - passed by value"); + } + + while i < 3 { + println!("OK - passed by mutable reference"); + fn_mutref(&mut i) + } + + while i < 3 { + fn_mutref(&mut i); + println!("OK - passed by mutable reference"); + } +} + +const N: i32 = 5; +const B: bool = false; + +fn consts() { + while false { + println!("Constants are not linted"); + } + + while B { + println!("Constants are not linted"); + } + + while N > 0 { + println!("Constants are not linted"); + } +} + +use std::cell::Cell; + +fn maybe_i_mutate(i: &Cell<bool>) { + unimplemented!() +} + +fn internally_mutable() { + let b = Cell::new(true); + + while b.get() { + // b cannot be silently coerced to `bool` + maybe_i_mutate(&b); + println!("OK - Method call within condition"); + } +} + +struct Counter { + count: usize, +} + +impl Counter { + fn inc(&mut self) { + self.count += 1; + } + + fn inc_n(&mut self, n: usize) { + while self.count < n { + self.inc(); + } + println!("OK - self borrowed mutably"); + } + + fn print_n(&self, n: usize) { + while self.count < n { + println!("KO - {} is not mutated", self.count); + } + } +} + +fn while_loop_with_break_and_return() { + let y = 0; + while y < 10 { + if y == 0 { + break; + } + println!("KO - loop contains break"); + } + + while y < 10 { + if y == 0 { + return; + } + println!("KO - loop contains return"); + } +} + +fn main() { + immutable_condition(); + unused_var(); + used_immutable(); + internally_mutable(); + + let mut c = Counter { count: 0 }; + c.inc_n(5); + c.print_n(2); + + while_loop_with_break_and_return(); +} diff --git a/src/tools/clippy/tests/ui/infinite_loop.stderr b/src/tools/clippy/tests/ui/infinite_loop.stderr new file mode 100644 index 00000000000..1fcb29eff18 --- /dev/null +++ b/src/tools/clippy/tests/ui/infinite_loop.stderr @@ -0,0 +1,95 @@ +error: variables in the condition are not mutated in the loop body + --> $DIR/infinite_loop.rs:21:11 + | +LL | while y < 10 { + | ^^^^^^ + | + = note: `#[deny(clippy::while_immutable_condition)]` on by default + = note: this may lead to an infinite or to a never running loop + +error: variables in the condition are not mutated in the loop body + --> $DIR/infinite_loop.rs:26:11 + | +LL | while y < 10 && x < 3 { + | ^^^^^^^^^^^^^^^ + | + = note: this may lead to an infinite or to a never running loop + +error: variables in the condition are not mutated in the loop body + --> $DIR/infinite_loop.rs:33:11 + | +LL | while !cond { + | ^^^^^ + | + = note: this may lead to an infinite or to a never running loop + +error: variables in the condition are not mutated in the loop body + --> $DIR/infinite_loop.rs:77:11 + | +LL | while i < 3 { + | ^^^^^ + | + = note: this may lead to an infinite or to a never running loop + +error: variables in the condition are not mutated in the loop body + --> $DIR/infinite_loop.rs:82:11 + | +LL | while i < 3 && j > 0 { + | ^^^^^^^^^^^^^^ + | + = note: this may lead to an infinite or to a never running loop + +error: variables in the condition are not mutated in the loop body + --> $DIR/infinite_loop.rs:86:11 + | +LL | while i < 3 { + | ^^^^^ + | + = note: this may lead to an infinite or to a never running loop + +error: variables in the condition are not mutated in the loop body + --> $DIR/infinite_loop.rs:101:11 + | +LL | while i < 3 { + | ^^^^^ + | + = note: this may lead to an infinite or to a never running loop + +error: variables in the condition are not mutated in the loop body + --> $DIR/infinite_loop.rs:106:11 + | +LL | while i < 3 { + | ^^^^^ + | + = note: this may lead to an infinite or to a never running loop + +error: variables in the condition are not mutated in the loop body + --> $DIR/infinite_loop.rs:172:15 + | +LL | while self.count < n { + | ^^^^^^^^^^^^^^ + | + = note: this may lead to an infinite or to a never running loop + +error: variables in the condition are not mutated in the loop body + --> $DIR/infinite_loop.rs:180:11 + | +LL | while y < 10 { + | ^^^^^^ + | + = note: this may lead to an infinite or to a never running loop + = note: this loop contains `return`s or `break`s + = help: rewrite it as `if cond { loop { } }` + +error: variables in the condition are not mutated in the loop body + --> $DIR/infinite_loop.rs:187:11 + | +LL | while y < 10 { + | ^^^^^^ + | + = note: this may lead to an infinite or to a never running loop + = note: this loop contains `return`s or `break`s + = help: rewrite it as `if cond { loop { } }` + +error: aborting due to 11 previous errors + diff --git a/src/tools/clippy/tests/ui/inherent_to_string.rs b/src/tools/clippy/tests/ui/inherent_to_string.rs new file mode 100644 index 00000000000..e6cf337d1bb --- /dev/null +++ b/src/tools/clippy/tests/ui/inherent_to_string.rs @@ -0,0 +1,96 @@ +#![warn(clippy::inherent_to_string)] +#![deny(clippy::inherent_to_string_shadow_display)] +#![allow(clippy::many_single_char_names)] + +use std::fmt; + +trait FalsePositive { + fn to_string(&self) -> String; +} + +struct A; +struct B; +struct C; +struct D; +struct E; +struct F; + +impl A { + // Should be detected; emit warning + fn to_string(&self) -> String { + "A.to_string()".to_string() + } + + // Should not be detected as it does not match the function signature + fn to_str(&self) -> String { + "A.to_str()".to_string() + } +} + +// Should not be detected as it is a free function +fn to_string() -> String { + "free to_string()".to_string() +} + +impl B { + // Should not be detected, wrong return type + fn to_string(&self) -> i32 { + 42 + } +} + +impl C { + // Should be detected and emit error as C also implements Display + fn to_string(&self) -> String { + "C.to_string()".to_string() + } +} + +impl fmt::Display for C { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "impl Display for C") + } +} + +impl FalsePositive for D { + // Should not be detected, as it is a trait function + fn to_string(&self) -> String { + "impl FalsePositive for D".to_string() + } +} + +impl E { + // Should not be detected, as it is not bound to an instance + fn to_string() -> String { + "E::to_string()".to_string() + } +} + +impl F { + // Should not be detected, as it does not match the function signature + fn to_string(&self, _i: i32) -> String { + "F.to_string()".to_string() + } +} + +fn main() { + let a = A; + a.to_string(); + a.to_str(); + + to_string(); + + let b = B; + b.to_string(); + + let c = C; + C.to_string(); + + let d = D; + d.to_string(); + + E::to_string(); + + let f = F; + f.to_string(1); +} diff --git a/src/tools/clippy/tests/ui/inherent_to_string.stderr b/src/tools/clippy/tests/ui/inherent_to_string.stderr new file mode 100644 index 00000000000..4f331f5bec9 --- /dev/null +++ b/src/tools/clippy/tests/ui/inherent_to_string.stderr @@ -0,0 +1,28 @@ +error: implementation of inherent method `to_string(&self) -> String` for type `A` + --> $DIR/inherent_to_string.rs:20:5 + | +LL | / fn to_string(&self) -> String { +LL | | "A.to_string()".to_string() +LL | | } + | |_____^ + | + = note: `-D clippy::inherent-to-string` implied by `-D warnings` + = help: implement trait `Display` for type `A` instead + +error: type `C` implements inherent method `to_string(&self) -> String` which shadows the implementation of `Display` + --> $DIR/inherent_to_string.rs:44:5 + | +LL | / fn to_string(&self) -> String { +LL | | "C.to_string()".to_string() +LL | | } + | |_____^ + | +note: the lint level is defined here + --> $DIR/inherent_to_string.rs:2:9 + | +LL | #![deny(clippy::inherent_to_string_shadow_display)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: remove the inherent method from type `C` + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/inline_fn_without_body.fixed b/src/tools/clippy/tests/ui/inline_fn_without_body.fixed new file mode 100644 index 00000000000..fe21a71a42c --- /dev/null +++ b/src/tools/clippy/tests/ui/inline_fn_without_body.fixed @@ -0,0 +1,17 @@ +// run-rustfix + +#![warn(clippy::inline_fn_without_body)] +#![allow(clippy::inline_always)] + +trait Foo { + fn default_inline(); + + fn always_inline(); + + fn never_inline(); + + #[inline] + fn has_body() {} +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/inline_fn_without_body.rs b/src/tools/clippy/tests/ui/inline_fn_without_body.rs new file mode 100644 index 00000000000..50746989466 --- /dev/null +++ b/src/tools/clippy/tests/ui/inline_fn_without_body.rs @@ -0,0 +1,20 @@ +// run-rustfix + +#![warn(clippy::inline_fn_without_body)] +#![allow(clippy::inline_always)] + +trait Foo { + #[inline] + fn default_inline(); + + #[inline(always)] + fn always_inline(); + + #[inline(never)] + fn never_inline(); + + #[inline] + fn has_body() {} +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/inline_fn_without_body.stderr b/src/tools/clippy/tests/ui/inline_fn_without_body.stderr new file mode 100644 index 00000000000..32d35e209b0 --- /dev/null +++ b/src/tools/clippy/tests/ui/inline_fn_without_body.stderr @@ -0,0 +1,28 @@ +error: use of `#[inline]` on trait method `default_inline` which has no body + --> $DIR/inline_fn_without_body.rs:7:5 + | +LL | #[inline] + | _____-^^^^^^^^ +LL | | fn default_inline(); + | |____- help: remove + | + = note: `-D clippy::inline-fn-without-body` implied by `-D warnings` + +error: use of `#[inline]` on trait method `always_inline` which has no body + --> $DIR/inline_fn_without_body.rs:10:5 + | +LL | #[inline(always)] + | _____-^^^^^^^^^^^^^^^^ +LL | | fn always_inline(); + | |____- help: remove + +error: use of `#[inline]` on trait method `never_inline` which has no body + --> $DIR/inline_fn_without_body.rs:13:5 + | +LL | #[inline(never)] + | _____-^^^^^^^^^^^^^^^ +LL | | fn never_inline(); + | |____- help: remove + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/int_plus_one.fixed b/src/tools/clippy/tests/ui/int_plus_one.fixed new file mode 100644 index 00000000000..642830f24f5 --- /dev/null +++ b/src/tools/clippy/tests/ui/int_plus_one.fixed @@ -0,0 +1,17 @@ +// run-rustfix + +#[allow(clippy::no_effect, clippy::unnecessary_operation)] +#[warn(clippy::int_plus_one)] +fn main() { + let x = 1i32; + let y = 0i32; + + let _ = x > y; + let _ = y < x; + + let _ = x > y; + let _ = y < x; + + let _ = x > y; // should be ok + let _ = y < x; // should be ok +} diff --git a/src/tools/clippy/tests/ui/int_plus_one.rs b/src/tools/clippy/tests/ui/int_plus_one.rs new file mode 100644 index 00000000000..0755a0c79d2 --- /dev/null +++ b/src/tools/clippy/tests/ui/int_plus_one.rs @@ -0,0 +1,17 @@ +// run-rustfix + +#[allow(clippy::no_effect, clippy::unnecessary_operation)] +#[warn(clippy::int_plus_one)] +fn main() { + let x = 1i32; + let y = 0i32; + + let _ = x >= y + 1; + let _ = y + 1 <= x; + + let _ = x - 1 >= y; + let _ = y <= x - 1; + + let _ = x > y; // should be ok + let _ = y < x; // should be ok +} diff --git a/src/tools/clippy/tests/ui/int_plus_one.stderr b/src/tools/clippy/tests/ui/int_plus_one.stderr new file mode 100644 index 00000000000..29a6914761c --- /dev/null +++ b/src/tools/clippy/tests/ui/int_plus_one.stderr @@ -0,0 +1,28 @@ +error: Unnecessary `>= y + 1` or `x - 1 >=` + --> $DIR/int_plus_one.rs:9:13 + | +LL | let _ = x >= y + 1; + | ^^^^^^^^^^ help: change it to: `x > y` + | + = note: `-D clippy::int-plus-one` implied by `-D warnings` + +error: Unnecessary `>= y + 1` or `x - 1 >=` + --> $DIR/int_plus_one.rs:10:13 + | +LL | let _ = y + 1 <= x; + | ^^^^^^^^^^ help: change it to: `y < x` + +error: Unnecessary `>= y + 1` or `x - 1 >=` + --> $DIR/int_plus_one.rs:12:13 + | +LL | let _ = x - 1 >= y; + | ^^^^^^^^^^ help: change it to: `x > y` + +error: Unnecessary `>= y + 1` or `x - 1 >=` + --> $DIR/int_plus_one.rs:13:13 + | +LL | let _ = y <= x - 1; + | ^^^^^^^^^^ help: change it to: `y < x` + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/integer_arithmetic.rs b/src/tools/clippy/tests/ui/integer_arithmetic.rs new file mode 100644 index 00000000000..7b1b64f390a --- /dev/null +++ b/src/tools/clippy/tests/ui/integer_arithmetic.rs @@ -0,0 +1,99 @@ +#![warn(clippy::integer_arithmetic, clippy::float_arithmetic)] +#![allow( + unused, + clippy::shadow_reuse, + clippy::shadow_unrelated, + clippy::no_effect, + clippy::unnecessary_operation, + clippy::op_ref +)] + +#[rustfmt::skip] +fn main() { + let mut i = 1i32; + 1 + i; + i * 2; + 1 % + i / 2; // no error, this is part of the expression in the preceding line + i - 2 + 2 - i; + -i; + i >> 1; + i << 1; + + // no error, overflows are checked by `overflowing_literals` + -1; + -(-1); + + i & 1; // no wrapping + i | 1; + i ^ 1; + + i += 1; + i -= 1; + i *= 2; + i /= 2; + i %= 2; + i <<= 3; + i >>= 2; + + // no errors + i |= 1; + i &= 1; + i ^= i; + + // No errors for the following items because they are constant expressions + enum Foo { + Bar = -2, + } + struct Baz([i32; 1 + 1]); + union Qux { + field: [i32; 1 + 1], + } + type Alias = [i32; 1 + 1]; + + const FOO: i32 = -2; + static BAR: i32 = -2; + + let _: [i32; 1 + 1] = [0, 0]; + + let _: [i32; 1 + 1] = { + let a: [i32; 1 + 1] = [0, 0]; + a + }; + + trait Trait { + const ASSOC: i32 = 1 + 1; + } + + impl Trait for Foo { + const ASSOC: i32 = { + let _: [i32; 1 + 1]; + fn foo() {} + 1 + 1 + }; + } +} + +// warn on references as well! (#5328) +pub fn int_arith_ref() { + 3 + &1; + &3 + 1; + &3 + &1; +} + +pub fn foo(x: &i32) -> i32 { + let a = 5; + a + x +} + +pub fn bar(x: &i32, y: &i32) -> i32 { + x + y +} + +pub fn baz(x: i32, y: &i32) -> i32 { + x + y +} + +pub fn qux(x: i32, y: i32) -> i32 { + (&x + &y) +} diff --git a/src/tools/clippy/tests/ui/integer_arithmetic.stderr b/src/tools/clippy/tests/ui/integer_arithmetic.stderr new file mode 100644 index 00000000000..83e8a9cde3f --- /dev/null +++ b/src/tools/clippy/tests/ui/integer_arithmetic.stderr @@ -0,0 +1,131 @@ +error: integer arithmetic detected + --> $DIR/integer_arithmetic.rs:14:5 + | +LL | 1 + i; + | ^^^^^ + | + = note: `-D clippy::integer-arithmetic` implied by `-D warnings` + +error: integer arithmetic detected + --> $DIR/integer_arithmetic.rs:15:5 + | +LL | i * 2; + | ^^^^^ + +error: integer arithmetic detected + --> $DIR/integer_arithmetic.rs:16:5 + | +LL | / 1 % +LL | | i / 2; // no error, this is part of the expression in the preceding line + | |_________^ + +error: integer arithmetic detected + --> $DIR/integer_arithmetic.rs:18:5 + | +LL | i - 2 + 2 - i; + | ^^^^^^^^^^^^^ + +error: integer arithmetic detected + --> $DIR/integer_arithmetic.rs:19:5 + | +LL | -i; + | ^^ + +error: integer arithmetic detected + --> $DIR/integer_arithmetic.rs:20:5 + | +LL | i >> 1; + | ^^^^^^ + +error: integer arithmetic detected + --> $DIR/integer_arithmetic.rs:21:5 + | +LL | i << 1; + | ^^^^^^ + +error: integer arithmetic detected + --> $DIR/integer_arithmetic.rs:31:5 + | +LL | i += 1; + | ^^^^^^ + +error: integer arithmetic detected + --> $DIR/integer_arithmetic.rs:32:5 + | +LL | i -= 1; + | ^^^^^^ + +error: integer arithmetic detected + --> $DIR/integer_arithmetic.rs:33:5 + | +LL | i *= 2; + | ^^^^^^ + +error: integer arithmetic detected + --> $DIR/integer_arithmetic.rs:34:5 + | +LL | i /= 2; + | ^^^^^^ + +error: integer arithmetic detected + --> $DIR/integer_arithmetic.rs:35:5 + | +LL | i %= 2; + | ^^^^^^ + +error: integer arithmetic detected + --> $DIR/integer_arithmetic.rs:36:5 + | +LL | i <<= 3; + | ^^^^^^^ + +error: integer arithmetic detected + --> $DIR/integer_arithmetic.rs:37:5 + | +LL | i >>= 2; + | ^^^^^^^ + +error: integer arithmetic detected + --> $DIR/integer_arithmetic.rs:79:5 + | +LL | 3 + &1; + | ^^^^^^ + +error: integer arithmetic detected + --> $DIR/integer_arithmetic.rs:80:5 + | +LL | &3 + 1; + | ^^^^^^ + +error: integer arithmetic detected + --> $DIR/integer_arithmetic.rs:81:5 + | +LL | &3 + &1; + | ^^^^^^^ + +error: integer arithmetic detected + --> $DIR/integer_arithmetic.rs:86:5 + | +LL | a + x + | ^^^^^ + +error: integer arithmetic detected + --> $DIR/integer_arithmetic.rs:90:5 + | +LL | x + y + | ^^^^^ + +error: integer arithmetic detected + --> $DIR/integer_arithmetic.rs:94:5 + | +LL | x + y + | ^^^^^ + +error: integer arithmetic detected + --> $DIR/integer_arithmetic.rs:98:5 + | +LL | (&x + &y) + | ^^^^^^^^^ + +error: aborting due to 21 previous errors + diff --git a/src/tools/clippy/tests/ui/integer_division.rs b/src/tools/clippy/tests/ui/integer_division.rs new file mode 100644 index 00000000000..800c7525752 --- /dev/null +++ b/src/tools/clippy/tests/ui/integer_division.rs @@ -0,0 +1,9 @@ +#![warn(clippy::integer_division)] + +fn main() { + let two = 2; + let n = 1 / 2; + let o = 1 / two; + let p = two / 4; + let x = 1. / 2.0; +} diff --git a/src/tools/clippy/tests/ui/integer_division.stderr b/src/tools/clippy/tests/ui/integer_division.stderr new file mode 100644 index 00000000000..72a232ef3d7 --- /dev/null +++ b/src/tools/clippy/tests/ui/integer_division.stderr @@ -0,0 +1,27 @@ +error: integer division + --> $DIR/integer_division.rs:5:13 + | +LL | let n = 1 / 2; + | ^^^^^ + | + = note: `-D clippy::integer-division` implied by `-D warnings` + = help: division of integers may cause loss of precision. consider using floats. + +error: integer division + --> $DIR/integer_division.rs:6:13 + | +LL | let o = 1 / two; + | ^^^^^^^ + | + = help: division of integers may cause loss of precision. consider using floats. + +error: integer division + --> $DIR/integer_division.rs:7:13 + | +LL | let p = two / 4; + | ^^^^^^^ + | + = help: division of integers may cause loss of precision. consider using floats. + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/into_iter_on_ref.fixed b/src/tools/clippy/tests/ui/into_iter_on_ref.fixed new file mode 100644 index 00000000000..c30d23de3f8 --- /dev/null +++ b/src/tools/clippy/tests/ui/into_iter_on_ref.fixed @@ -0,0 +1,43 @@ +// run-rustfix +#![allow(clippy::useless_vec)] +#![warn(clippy::into_iter_on_ref)] + +struct X; +use std::collections::*; + +fn main() { + for _ in &[1, 2, 3] {} + for _ in vec![X, X] {} + for _ in &vec![X, X] {} + + let _ = vec![1, 2, 3].into_iter(); + let _ = (&vec![1, 2, 3]).iter(); //~ WARN equivalent to .iter() + let _ = vec![1, 2, 3].into_boxed_slice().iter(); //~ WARN equivalent to .iter() + let _ = std::rc::Rc::from(&[X][..]).iter(); //~ WARN equivalent to .iter() + let _ = std::sync::Arc::from(&[X][..]).iter(); //~ WARN equivalent to .iter() + + let _ = (&&&&&&&[1, 2, 3]).iter(); //~ ERROR equivalent to .iter() + let _ = (&&&&mut &&&[1, 2, 3]).iter(); //~ ERROR equivalent to .iter() + let _ = (&mut &mut &mut [1, 2, 3]).iter_mut(); //~ ERROR equivalent to .iter_mut() + + let _ = (&Some(4)).iter(); //~ WARN equivalent to .iter() + let _ = (&mut Some(5)).iter_mut(); //~ WARN equivalent to .iter_mut() + let _ = (&Ok::<_, i32>(6)).iter(); //~ WARN equivalent to .iter() + let _ = (&mut Err::<i32, _>(7)).iter_mut(); //~ WARN equivalent to .iter_mut() + let _ = (&Vec::<i32>::new()).iter(); //~ WARN equivalent to .iter() + let _ = (&mut Vec::<i32>::new()).iter_mut(); //~ WARN equivalent to .iter_mut() + let _ = (&BTreeMap::<i32, u64>::new()).iter(); //~ WARN equivalent to .iter() + let _ = (&mut BTreeMap::<i32, u64>::new()).iter_mut(); //~ WARN equivalent to .iter_mut() + let _ = (&VecDeque::<i32>::new()).iter(); //~ WARN equivalent to .iter() + let _ = (&mut VecDeque::<i32>::new()).iter_mut(); //~ WARN equivalent to .iter_mut() + let _ = (&LinkedList::<i32>::new()).iter(); //~ WARN equivalent to .iter() + let _ = (&mut LinkedList::<i32>::new()).iter_mut(); //~ WARN equivalent to .iter_mut() + let _ = (&HashMap::<i32, u64>::new()).iter(); //~ WARN equivalent to .iter() + let _ = (&mut HashMap::<i32, u64>::new()).iter_mut(); //~ WARN equivalent to .iter_mut() + + let _ = (&BTreeSet::<i32>::new()).iter(); //~ WARN equivalent to .iter() + let _ = (&BinaryHeap::<i32>::new()).iter(); //~ WARN equivalent to .iter() + let _ = (&HashSet::<i32>::new()).iter(); //~ WARN equivalent to .iter() + let _ = std::path::Path::new("12/34").iter(); //~ WARN equivalent to .iter() + let _ = std::path::PathBuf::from("12/34").iter(); //~ ERROR equivalent to .iter() +} diff --git a/src/tools/clippy/tests/ui/into_iter_on_ref.rs b/src/tools/clippy/tests/ui/into_iter_on_ref.rs new file mode 100644 index 00000000000..94bc1689619 --- /dev/null +++ b/src/tools/clippy/tests/ui/into_iter_on_ref.rs @@ -0,0 +1,43 @@ +// run-rustfix +#![allow(clippy::useless_vec)] +#![warn(clippy::into_iter_on_ref)] + +struct X; +use std::collections::*; + +fn main() { + for _ in &[1, 2, 3] {} + for _ in vec![X, X] {} + for _ in &vec![X, X] {} + + let _ = vec![1, 2, 3].into_iter(); + let _ = (&vec![1, 2, 3]).into_iter(); //~ WARN equivalent to .iter() + let _ = vec![1, 2, 3].into_boxed_slice().into_iter(); //~ WARN equivalent to .iter() + let _ = std::rc::Rc::from(&[X][..]).into_iter(); //~ WARN equivalent to .iter() + let _ = std::sync::Arc::from(&[X][..]).into_iter(); //~ WARN equivalent to .iter() + + let _ = (&&&&&&&[1, 2, 3]).into_iter(); //~ ERROR equivalent to .iter() + let _ = (&&&&mut &&&[1, 2, 3]).into_iter(); //~ ERROR equivalent to .iter() + let _ = (&mut &mut &mut [1, 2, 3]).into_iter(); //~ ERROR equivalent to .iter_mut() + + let _ = (&Some(4)).into_iter(); //~ WARN equivalent to .iter() + let _ = (&mut Some(5)).into_iter(); //~ WARN equivalent to .iter_mut() + let _ = (&Ok::<_, i32>(6)).into_iter(); //~ WARN equivalent to .iter() + let _ = (&mut Err::<i32, _>(7)).into_iter(); //~ WARN equivalent to .iter_mut() + let _ = (&Vec::<i32>::new()).into_iter(); //~ WARN equivalent to .iter() + let _ = (&mut Vec::<i32>::new()).into_iter(); //~ WARN equivalent to .iter_mut() + let _ = (&BTreeMap::<i32, u64>::new()).into_iter(); //~ WARN equivalent to .iter() + let _ = (&mut BTreeMap::<i32, u64>::new()).into_iter(); //~ WARN equivalent to .iter_mut() + let _ = (&VecDeque::<i32>::new()).into_iter(); //~ WARN equivalent to .iter() + let _ = (&mut VecDeque::<i32>::new()).into_iter(); //~ WARN equivalent to .iter_mut() + let _ = (&LinkedList::<i32>::new()).into_iter(); //~ WARN equivalent to .iter() + let _ = (&mut LinkedList::<i32>::new()).into_iter(); //~ WARN equivalent to .iter_mut() + let _ = (&HashMap::<i32, u64>::new()).into_iter(); //~ WARN equivalent to .iter() + let _ = (&mut HashMap::<i32, u64>::new()).into_iter(); //~ WARN equivalent to .iter_mut() + + let _ = (&BTreeSet::<i32>::new()).into_iter(); //~ WARN equivalent to .iter() + let _ = (&BinaryHeap::<i32>::new()).into_iter(); //~ WARN equivalent to .iter() + let _ = (&HashSet::<i32>::new()).into_iter(); //~ WARN equivalent to .iter() + let _ = std::path::Path::new("12/34").into_iter(); //~ WARN equivalent to .iter() + let _ = std::path::PathBuf::from("12/34").into_iter(); //~ ERROR equivalent to .iter() +} diff --git a/src/tools/clippy/tests/ui/into_iter_on_ref.stderr b/src/tools/clippy/tests/ui/into_iter_on_ref.stderr new file mode 100644 index 00000000000..80e2d104f82 --- /dev/null +++ b/src/tools/clippy/tests/ui/into_iter_on_ref.stderr @@ -0,0 +1,160 @@ +error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `Vec` + --> $DIR/into_iter_on_ref.rs:14:30 + | +LL | let _ = (&vec![1, 2, 3]).into_iter(); //~ WARN equivalent to .iter() + | ^^^^^^^^^ help: call directly: `iter` + | + = note: `-D clippy::into-iter-on-ref` implied by `-D warnings` + +error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `slice` + --> $DIR/into_iter_on_ref.rs:15:46 + | +LL | let _ = vec![1, 2, 3].into_boxed_slice().into_iter(); //~ WARN equivalent to .iter() + | ^^^^^^^^^ help: call directly: `iter` + +error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `slice` + --> $DIR/into_iter_on_ref.rs:16:41 + | +LL | let _ = std::rc::Rc::from(&[X][..]).into_iter(); //~ WARN equivalent to .iter() + | ^^^^^^^^^ help: call directly: `iter` + +error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `slice` + --> $DIR/into_iter_on_ref.rs:17:44 + | +LL | let _ = std::sync::Arc::from(&[X][..]).into_iter(); //~ WARN equivalent to .iter() + | ^^^^^^^^^ help: call directly: `iter` + +error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `array` + --> $DIR/into_iter_on_ref.rs:19:32 + | +LL | let _ = (&&&&&&&[1, 2, 3]).into_iter(); //~ ERROR equivalent to .iter() + | ^^^^^^^^^ help: call directly: `iter` + +error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `array` + --> $DIR/into_iter_on_ref.rs:20:36 + | +LL | let _ = (&&&&mut &&&[1, 2, 3]).into_iter(); //~ ERROR equivalent to .iter() + | ^^^^^^^^^ help: call directly: `iter` + +error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `array` + --> $DIR/into_iter_on_ref.rs:21:40 + | +LL | let _ = (&mut &mut &mut [1, 2, 3]).into_iter(); //~ ERROR equivalent to .iter_mut() + | ^^^^^^^^^ help: call directly: `iter_mut` + +error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `Option` + --> $DIR/into_iter_on_ref.rs:23:24 + | +LL | let _ = (&Some(4)).into_iter(); //~ WARN equivalent to .iter() + | ^^^^^^^^^ help: call directly: `iter` + +error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `Option` + --> $DIR/into_iter_on_ref.rs:24:28 + | +LL | let _ = (&mut Some(5)).into_iter(); //~ WARN equivalent to .iter_mut() + | ^^^^^^^^^ help: call directly: `iter_mut` + +error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `Result` + --> $DIR/into_iter_on_ref.rs:25:32 + | +LL | let _ = (&Ok::<_, i32>(6)).into_iter(); //~ WARN equivalent to .iter() + | ^^^^^^^^^ help: call directly: `iter` + +error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `Result` + --> $DIR/into_iter_on_ref.rs:26:37 + | +LL | let _ = (&mut Err::<i32, _>(7)).into_iter(); //~ WARN equivalent to .iter_mut() + | ^^^^^^^^^ help: call directly: `iter_mut` + +error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `Vec` + --> $DIR/into_iter_on_ref.rs:27:34 + | +LL | let _ = (&Vec::<i32>::new()).into_iter(); //~ WARN equivalent to .iter() + | ^^^^^^^^^ help: call directly: `iter` + +error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `Vec` + --> $DIR/into_iter_on_ref.rs:28:38 + | +LL | let _ = (&mut Vec::<i32>::new()).into_iter(); //~ WARN equivalent to .iter_mut() + | ^^^^^^^^^ help: call directly: `iter_mut` + +error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `BTreeMap` + --> $DIR/into_iter_on_ref.rs:29:44 + | +LL | let _ = (&BTreeMap::<i32, u64>::new()).into_iter(); //~ WARN equivalent to .iter() + | ^^^^^^^^^ help: call directly: `iter` + +error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `BTreeMap` + --> $DIR/into_iter_on_ref.rs:30:48 + | +LL | let _ = (&mut BTreeMap::<i32, u64>::new()).into_iter(); //~ WARN equivalent to .iter_mut() + | ^^^^^^^^^ help: call directly: `iter_mut` + +error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `VecDeque` + --> $DIR/into_iter_on_ref.rs:31:39 + | +LL | let _ = (&VecDeque::<i32>::new()).into_iter(); //~ WARN equivalent to .iter() + | ^^^^^^^^^ help: call directly: `iter` + +error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `VecDeque` + --> $DIR/into_iter_on_ref.rs:32:43 + | +LL | let _ = (&mut VecDeque::<i32>::new()).into_iter(); //~ WARN equivalent to .iter_mut() + | ^^^^^^^^^ help: call directly: `iter_mut` + +error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `LinkedList` + --> $DIR/into_iter_on_ref.rs:33:41 + | +LL | let _ = (&LinkedList::<i32>::new()).into_iter(); //~ WARN equivalent to .iter() + | ^^^^^^^^^ help: call directly: `iter` + +error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `LinkedList` + --> $DIR/into_iter_on_ref.rs:34:45 + | +LL | let _ = (&mut LinkedList::<i32>::new()).into_iter(); //~ WARN equivalent to .iter_mut() + | ^^^^^^^^^ help: call directly: `iter_mut` + +error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `HashMap` + --> $DIR/into_iter_on_ref.rs:35:43 + | +LL | let _ = (&HashMap::<i32, u64>::new()).into_iter(); //~ WARN equivalent to .iter() + | ^^^^^^^^^ help: call directly: `iter` + +error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `HashMap` + --> $DIR/into_iter_on_ref.rs:36:47 + | +LL | let _ = (&mut HashMap::<i32, u64>::new()).into_iter(); //~ WARN equivalent to .iter_mut() + | ^^^^^^^^^ help: call directly: `iter_mut` + +error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `BTreeSet` + --> $DIR/into_iter_on_ref.rs:38:39 + | +LL | let _ = (&BTreeSet::<i32>::new()).into_iter(); //~ WARN equivalent to .iter() + | ^^^^^^^^^ help: call directly: `iter` + +error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `BinaryHeap` + --> $DIR/into_iter_on_ref.rs:39:41 + | +LL | let _ = (&BinaryHeap::<i32>::new()).into_iter(); //~ WARN equivalent to .iter() + | ^^^^^^^^^ help: call directly: `iter` + +error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `HashSet` + --> $DIR/into_iter_on_ref.rs:40:38 + | +LL | let _ = (&HashSet::<i32>::new()).into_iter(); //~ WARN equivalent to .iter() + | ^^^^^^^^^ help: call directly: `iter` + +error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `Path` + --> $DIR/into_iter_on_ref.rs:41:43 + | +LL | let _ = std::path::Path::new("12/34").into_iter(); //~ WARN equivalent to .iter() + | ^^^^^^^^^ help: call directly: `iter` + +error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `PathBuf` + --> $DIR/into_iter_on_ref.rs:42:47 + | +LL | let _ = std::path::PathBuf::from("12/34").into_iter(); //~ ERROR equivalent to .iter() + | ^^^^^^^^^ help: call directly: `iter` + +error: aborting due to 26 previous errors + diff --git a/src/tools/clippy/tests/ui/invalid_upcast_comparisons.rs b/src/tools/clippy/tests/ui/invalid_upcast_comparisons.rs new file mode 100644 index 00000000000..697416dcee8 --- /dev/null +++ b/src/tools/clippy/tests/ui/invalid_upcast_comparisons.rs @@ -0,0 +1,85 @@ +#![warn(clippy::invalid_upcast_comparisons)] +#![allow( + unused, + clippy::eq_op, + clippy::no_effect, + clippy::unnecessary_operation, + clippy::cast_lossless +)] + +fn mk_value<T>() -> T { + unimplemented!() +} + +fn main() { + let u32: u32 = mk_value(); + let u8: u8 = mk_value(); + let i32: i32 = mk_value(); + let i8: i8 = mk_value(); + + // always false, since no u8 can be > 300 + (u8 as u32) > 300; + (u8 as i32) > 300; + (u8 as u32) == 300; + (u8 as i32) == 300; + 300 < (u8 as u32); + 300 < (u8 as i32); + 300 == (u8 as u32); + 300 == (u8 as i32); + // inverted of the above + (u8 as u32) <= 300; + (u8 as i32) <= 300; + (u8 as u32) != 300; + (u8 as i32) != 300; + 300 >= (u8 as u32); + 300 >= (u8 as i32); + 300 != (u8 as u32); + 300 != (u8 as i32); + + // always false, since u8 -> i32 doesn't wrap + (u8 as i32) < 0; + -5 != (u8 as i32); + // inverted of the above + (u8 as i32) >= 0; + -5 == (u8 as i32); + + // always false, since no u8 can be 1337 + 1337 == (u8 as i32); + 1337 == (u8 as u32); + // inverted of the above + 1337 != (u8 as i32); + 1337 != (u8 as u32); + + // Those are Ok: + (u8 as u32) > 20; + 42 == (u8 as i32); + 42 != (u8 as i32); + 42 > (u8 as i32); + (u8 as i32) == 42; + (u8 as i32) != 42; + (u8 as i32) > 42; + (u8 as i32) < 42; + + (u8 as i8) == -1; + (u8 as i8) != -1; + (u8 as i32) > -1; + (u8 as i32) < -1; + (u32 as i32) < -5; + (u32 as i32) < 10; + + (i8 as u8) == 1; + (i8 as u8) != 1; + (i8 as u8) < 1; + (i8 as u8) > 1; + (i32 as u32) < 5; + (i32 as u32) < 10; + + -5 < (u32 as i32); + 0 <= (u32 as i32); + 0 < (u32 as i32); + + -5 > (u32 as i32); + -5 >= (u8 as i32); + + -5 == (u32 as i32); +} diff --git a/src/tools/clippy/tests/ui/invalid_upcast_comparisons.stderr b/src/tools/clippy/tests/ui/invalid_upcast_comparisons.stderr new file mode 100644 index 00000000000..03c3fb80aaa --- /dev/null +++ b/src/tools/clippy/tests/ui/invalid_upcast_comparisons.stderr @@ -0,0 +1,166 @@ +error: because of the numeric bounds on `u8` prior to casting, this expression is always false + --> $DIR/invalid_upcast_comparisons.rs:21:5 + | +LL | (u8 as u32) > 300; + | ^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::invalid-upcast-comparisons` implied by `-D warnings` + +error: because of the numeric bounds on `u8` prior to casting, this expression is always false + --> $DIR/invalid_upcast_comparisons.rs:22:5 + | +LL | (u8 as i32) > 300; + | ^^^^^^^^^^^^^^^^^ + +error: because of the numeric bounds on `u8` prior to casting, this expression is always false + --> $DIR/invalid_upcast_comparisons.rs:23:5 + | +LL | (u8 as u32) == 300; + | ^^^^^^^^^^^^^^^^^^ + +error: because of the numeric bounds on `u8` prior to casting, this expression is always false + --> $DIR/invalid_upcast_comparisons.rs:24:5 + | +LL | (u8 as i32) == 300; + | ^^^^^^^^^^^^^^^^^^ + +error: because of the numeric bounds on `u8` prior to casting, this expression is always false + --> $DIR/invalid_upcast_comparisons.rs:25:5 + | +LL | 300 < (u8 as u32); + | ^^^^^^^^^^^^^^^^^ + +error: because of the numeric bounds on `u8` prior to casting, this expression is always false + --> $DIR/invalid_upcast_comparisons.rs:26:5 + | +LL | 300 < (u8 as i32); + | ^^^^^^^^^^^^^^^^^ + +error: because of the numeric bounds on `u8` prior to casting, this expression is always false + --> $DIR/invalid_upcast_comparisons.rs:27:5 + | +LL | 300 == (u8 as u32); + | ^^^^^^^^^^^^^^^^^^ + +error: because of the numeric bounds on `u8` prior to casting, this expression is always false + --> $DIR/invalid_upcast_comparisons.rs:28:5 + | +LL | 300 == (u8 as i32); + | ^^^^^^^^^^^^^^^^^^ + +error: because of the numeric bounds on `u8` prior to casting, this expression is always true + --> $DIR/invalid_upcast_comparisons.rs:30:5 + | +LL | (u8 as u32) <= 300; + | ^^^^^^^^^^^^^^^^^^ + +error: because of the numeric bounds on `u8` prior to casting, this expression is always true + --> $DIR/invalid_upcast_comparisons.rs:31:5 + | +LL | (u8 as i32) <= 300; + | ^^^^^^^^^^^^^^^^^^ + +error: because of the numeric bounds on `u8` prior to casting, this expression is always true + --> $DIR/invalid_upcast_comparisons.rs:32:5 + | +LL | (u8 as u32) != 300; + | ^^^^^^^^^^^^^^^^^^ + +error: because of the numeric bounds on `u8` prior to casting, this expression is always true + --> $DIR/invalid_upcast_comparisons.rs:33:5 + | +LL | (u8 as i32) != 300; + | ^^^^^^^^^^^^^^^^^^ + +error: because of the numeric bounds on `u8` prior to casting, this expression is always true + --> $DIR/invalid_upcast_comparisons.rs:34:5 + | +LL | 300 >= (u8 as u32); + | ^^^^^^^^^^^^^^^^^^ + +error: because of the numeric bounds on `u8` prior to casting, this expression is always true + --> $DIR/invalid_upcast_comparisons.rs:35:5 + | +LL | 300 >= (u8 as i32); + | ^^^^^^^^^^^^^^^^^^ + +error: because of the numeric bounds on `u8` prior to casting, this expression is always true + --> $DIR/invalid_upcast_comparisons.rs:36:5 + | +LL | 300 != (u8 as u32); + | ^^^^^^^^^^^^^^^^^^ + +error: because of the numeric bounds on `u8` prior to casting, this expression is always true + --> $DIR/invalid_upcast_comparisons.rs:37:5 + | +LL | 300 != (u8 as i32); + | ^^^^^^^^^^^^^^^^^^ + +error: because of the numeric bounds on `u8` prior to casting, this expression is always false + --> $DIR/invalid_upcast_comparisons.rs:40:5 + | +LL | (u8 as i32) < 0; + | ^^^^^^^^^^^^^^^ + +error: because of the numeric bounds on `u8` prior to casting, this expression is always true + --> $DIR/invalid_upcast_comparisons.rs:41:5 + | +LL | -5 != (u8 as i32); + | ^^^^^^^^^^^^^^^^^ + +error: because of the numeric bounds on `u8` prior to casting, this expression is always true + --> $DIR/invalid_upcast_comparisons.rs:43:5 + | +LL | (u8 as i32) >= 0; + | ^^^^^^^^^^^^^^^^ + +error: because of the numeric bounds on `u8` prior to casting, this expression is always false + --> $DIR/invalid_upcast_comparisons.rs:44:5 + | +LL | -5 == (u8 as i32); + | ^^^^^^^^^^^^^^^^^ + +error: because of the numeric bounds on `u8` prior to casting, this expression is always false + --> $DIR/invalid_upcast_comparisons.rs:47:5 + | +LL | 1337 == (u8 as i32); + | ^^^^^^^^^^^^^^^^^^^ + +error: because of the numeric bounds on `u8` prior to casting, this expression is always false + --> $DIR/invalid_upcast_comparisons.rs:48:5 + | +LL | 1337 == (u8 as u32); + | ^^^^^^^^^^^^^^^^^^^ + +error: because of the numeric bounds on `u8` prior to casting, this expression is always true + --> $DIR/invalid_upcast_comparisons.rs:50:5 + | +LL | 1337 != (u8 as i32); + | ^^^^^^^^^^^^^^^^^^^ + +error: because of the numeric bounds on `u8` prior to casting, this expression is always true + --> $DIR/invalid_upcast_comparisons.rs:51:5 + | +LL | 1337 != (u8 as u32); + | ^^^^^^^^^^^^^^^^^^^ + +error: because of the numeric bounds on `u8` prior to casting, this expression is always true + --> $DIR/invalid_upcast_comparisons.rs:65:5 + | +LL | (u8 as i32) > -1; + | ^^^^^^^^^^^^^^^^ + +error: because of the numeric bounds on `u8` prior to casting, this expression is always false + --> $DIR/invalid_upcast_comparisons.rs:66:5 + | +LL | (u8 as i32) < -1; + | ^^^^^^^^^^^^^^^^ + +error: because of the numeric bounds on `u8` prior to casting, this expression is always false + --> $DIR/invalid_upcast_comparisons.rs:82:5 + | +LL | -5 >= (u8 as i32); + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to 27 previous errors + diff --git a/src/tools/clippy/tests/ui/issue-3145.rs b/src/tools/clippy/tests/ui/issue-3145.rs new file mode 100644 index 00000000000..f497d5550af --- /dev/null +++ b/src/tools/clippy/tests/ui/issue-3145.rs @@ -0,0 +1,3 @@ +fn main() { + println!("{}" a); //~ERROR expected token: `,` +} diff --git a/src/tools/clippy/tests/ui/issue-3145.stderr b/src/tools/clippy/tests/ui/issue-3145.stderr new file mode 100644 index 00000000000..cb0d95f5e26 --- /dev/null +++ b/src/tools/clippy/tests/ui/issue-3145.stderr @@ -0,0 +1,8 @@ +error: expected token: `,` + --> $DIR/issue-3145.rs:2:19 + | +LL | println!("{}" a); //~ERROR expected token: `,` + | ^ expected `,` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/issue-3746.rs b/src/tools/clippy/tests/ui/issue-3746.rs new file mode 100644 index 00000000000..879d1d5d916 --- /dev/null +++ b/src/tools/clippy/tests/ui/issue-3746.rs @@ -0,0 +1,22 @@ +// ignore-macos +// ignore-windows + +#![warn(clippy::empty_loop)] +#![feature(lang_items, link_args, start, libc)] +#![link_args = "-nostartfiles"] +#![no_std] + +use core::panic::PanicInfo; + +#[start] +fn main(argc: isize, argv: *const *const u8) -> isize { + loop {} +} + +#[panic_handler] +fn panic(_info: &PanicInfo) -> ! { + loop {} +} + +#[lang = "eh_personality"] +extern "C" fn eh_personality() {} diff --git a/src/tools/clippy/tests/ui/issue_2356.rs b/src/tools/clippy/tests/ui/issue_2356.rs new file mode 100644 index 00000000000..da580a1839a --- /dev/null +++ b/src/tools/clippy/tests/ui/issue_2356.rs @@ -0,0 +1,24 @@ +#![deny(clippy::while_let_on_iterator)] + +use std::iter::Iterator; + +struct Foo; + +impl Foo { + fn foo1<I: Iterator<Item = usize>>(mut it: I) { + while let Some(_) = it.next() { + println!("{:?}", it.size_hint()); + } + } + + fn foo2<I: Iterator<Item = usize>>(mut it: I) { + while let Some(e) = it.next() { + println!("{:?}", e); + } + } +} + +fn main() { + Foo::foo1(vec![].into_iter()); + Foo::foo2(vec![].into_iter()); +} diff --git a/src/tools/clippy/tests/ui/issue_2356.stderr b/src/tools/clippy/tests/ui/issue_2356.stderr new file mode 100644 index 00000000000..51b872e21c0 --- /dev/null +++ b/src/tools/clippy/tests/ui/issue_2356.stderr @@ -0,0 +1,14 @@ +error: this loop could be written as a `for` loop + --> $DIR/issue_2356.rs:15:9 + | +LL | while let Some(e) = it.next() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for e in it` + | +note: the lint level is defined here + --> $DIR/issue_2356.rs:1:9 + | +LL | #![deny(clippy::while_let_on_iterator)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/issue_4266.rs b/src/tools/clippy/tests/ui/issue_4266.rs new file mode 100644 index 00000000000..8a9d5a3d1d5 --- /dev/null +++ b/src/tools/clippy/tests/ui/issue_4266.rs @@ -0,0 +1,37 @@ +// edition:2018 +#![allow(dead_code)] + +async fn sink1<'a>(_: &'a str) {} // lint +async fn sink1_elided(_: &str) {} // ok + +// lint +async fn one_to_one<'a>(s: &'a str) -> &'a str { + s +} + +// ok +async fn one_to_one_elided(s: &str) -> &str { + s +} + +// ok +async fn all_to_one<'a>(a: &'a str, _b: &'a str) -> &'a str { + a +} + +// async fn unrelated(_: &str, _: &str) {} // Not allowed in async fn + +// #3988 +struct Foo; +impl Foo { + // ok + pub async fn foo(&mut self) {} +} + +// rust-lang/rust#61115 +// ok +async fn print(s: &str) { + println!("{}", s); +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/issue_4266.stderr b/src/tools/clippy/tests/ui/issue_4266.stderr new file mode 100644 index 00000000000..0426508e622 --- /dev/null +++ b/src/tools/clippy/tests/ui/issue_4266.stderr @@ -0,0 +1,16 @@ +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/issue_4266.rs:4:1 + | +LL | async fn sink1<'a>(_: &'a str) {} // lint + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::needless-lifetimes` implied by `-D warnings` + +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/issue_4266.rs:8:1 + | +LL | async fn one_to_one<'a>(s: &'a str) -> &'a str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/item_after_statement.rs b/src/tools/clippy/tests/ui/item_after_statement.rs new file mode 100644 index 00000000000..c17a7cbc8d9 --- /dev/null +++ b/src/tools/clippy/tests/ui/item_after_statement.rs @@ -0,0 +1,36 @@ +#![warn(clippy::items_after_statements)] + +fn ok() { + fn foo() { + println!("foo"); + } + foo(); +} + +fn last() { + foo(); + fn foo() { + println!("foo"); + } +} + +fn main() { + foo(); + fn foo() { + println!("foo"); + } + foo(); +} + +fn mac() { + let mut a = 5; + println!("{}", a); + // do not lint this, because it needs to be after `a` + macro_rules! b { + () => {{ + a = 6 + }}; + } + b!(); + println!("{}", a); +} diff --git a/src/tools/clippy/tests/ui/item_after_statement.stderr b/src/tools/clippy/tests/ui/item_after_statement.stderr new file mode 100644 index 00000000000..f8f010b5e5c --- /dev/null +++ b/src/tools/clippy/tests/ui/item_after_statement.stderr @@ -0,0 +1,20 @@ +error: adding items after statements is confusing, since items exist from the start of the scope + --> $DIR/item_after_statement.rs:12:5 + | +LL | / fn foo() { +LL | | println!("foo"); +LL | | } + | |_____^ + | + = note: `-D clippy::items-after-statements` implied by `-D warnings` + +error: adding items after statements is confusing, since items exist from the start of the scope + --> $DIR/item_after_statement.rs:19:5 + | +LL | / fn foo() { +LL | | println!("foo"); +LL | | } + | |_____^ + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/iter_cloned_collect.fixed b/src/tools/clippy/tests/ui/iter_cloned_collect.fixed new file mode 100644 index 00000000000..2773227e26b --- /dev/null +++ b/src/tools/clippy/tests/ui/iter_cloned_collect.fixed @@ -0,0 +1,22 @@ +// run-rustfix + +#![allow(unused)] + +use std::collections::HashSet; +use std::collections::VecDeque; + +fn main() { + let v = [1, 2, 3, 4, 5]; + let v2: Vec<isize> = v.to_vec(); + let v3: HashSet<isize> = v.iter().cloned().collect(); + let v4: VecDeque<isize> = v.iter().cloned().collect(); + + // Handle macro expansion in suggestion + let _: Vec<isize> = vec![1, 2, 3].to_vec(); + + // Issue #3704 + unsafe { + let _: Vec<u8> = std::ffi::CStr::from_ptr(std::ptr::null()) + .to_bytes().to_vec(); + } +} diff --git a/src/tools/clippy/tests/ui/iter_cloned_collect.rs b/src/tools/clippy/tests/ui/iter_cloned_collect.rs new file mode 100644 index 00000000000..60a4eac23c7 --- /dev/null +++ b/src/tools/clippy/tests/ui/iter_cloned_collect.rs @@ -0,0 +1,25 @@ +// run-rustfix + +#![allow(unused)] + +use std::collections::HashSet; +use std::collections::VecDeque; + +fn main() { + let v = [1, 2, 3, 4, 5]; + let v2: Vec<isize> = v.iter().cloned().collect(); + let v3: HashSet<isize> = v.iter().cloned().collect(); + let v4: VecDeque<isize> = v.iter().cloned().collect(); + + // Handle macro expansion in suggestion + let _: Vec<isize> = vec![1, 2, 3].iter().cloned().collect(); + + // Issue #3704 + unsafe { + let _: Vec<u8> = std::ffi::CStr::from_ptr(std::ptr::null()) + .to_bytes() + .iter() + .cloned() + .collect(); + } +} diff --git a/src/tools/clippy/tests/ui/iter_cloned_collect.stderr b/src/tools/clippy/tests/ui/iter_cloned_collect.stderr new file mode 100644 index 00000000000..b90a1e6c919 --- /dev/null +++ b/src/tools/clippy/tests/ui/iter_cloned_collect.stderr @@ -0,0 +1,26 @@ +error: called `iter().cloned().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and more readable + --> $DIR/iter_cloned_collect.rs:10:27 + | +LL | let v2: Vec<isize> = v.iter().cloned().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `.to_vec()` + | + = note: `-D clippy::iter-cloned-collect` implied by `-D warnings` + +error: called `iter().cloned().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and more readable + --> $DIR/iter_cloned_collect.rs:15:38 + | +LL | let _: Vec<isize> = vec![1, 2, 3].iter().cloned().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `.to_vec()` + +error: called `iter().cloned().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and more readable + --> $DIR/iter_cloned_collect.rs:20:24 + | +LL | .to_bytes() + | ________________________^ +LL | | .iter() +LL | | .cloned() +LL | | .collect(); + | |______________________^ help: try: `.to_vec()` + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/iter_nth.rs b/src/tools/clippy/tests/ui/iter_nth.rs new file mode 100644 index 00000000000..9c21dd82ee4 --- /dev/null +++ b/src/tools/clippy/tests/ui/iter_nth.rs @@ -0,0 +1,56 @@ +// aux-build:option_helpers.rs + +#![warn(clippy::iter_nth)] + +#[macro_use] +extern crate option_helpers; + +use option_helpers::IteratorFalsePositives; +use std::collections::VecDeque; + +/// Struct to generate false positives for things with `.iter()`. +#[derive(Copy, Clone)] +struct HasIter; + +impl HasIter { + fn iter(self) -> IteratorFalsePositives { + IteratorFalsePositives { foo: 0 } + } + + fn iter_mut(self) -> IteratorFalsePositives { + IteratorFalsePositives { foo: 0 } + } +} + +/// Checks implementation of `ITER_NTH` lint. +fn iter_nth() { + let mut some_vec = vec![0, 1, 2, 3]; + let mut boxed_slice: Box<[u8]> = Box::new([0, 1, 2, 3]); + let mut some_vec_deque: VecDeque<_> = some_vec.iter().cloned().collect(); + + { + // Make sure we lint `.iter()` for relevant types. + let bad_vec = some_vec.iter().nth(3); + let bad_slice = &some_vec[..].iter().nth(3); + let bad_boxed_slice = boxed_slice.iter().nth(3); + let bad_vec_deque = some_vec_deque.iter().nth(3); + } + + { + // Make sure we lint `.iter_mut()` for relevant types. + let bad_vec = some_vec.iter_mut().nth(3); + } + { + let bad_slice = &some_vec[..].iter_mut().nth(3); + } + { + let bad_vec_deque = some_vec_deque.iter_mut().nth(3); + } + + // Make sure we don't lint for non-relevant types. + let false_positive = HasIter; + let ok = false_positive.iter().nth(3); + let ok_mut = false_positive.iter_mut().nth(3); +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/iter_nth.stderr b/src/tools/clippy/tests/ui/iter_nth.stderr new file mode 100644 index 00000000000..d00b2fb672b --- /dev/null +++ b/src/tools/clippy/tests/ui/iter_nth.stderr @@ -0,0 +1,59 @@ +error: called `.iter().nth()` on a Vec + --> $DIR/iter_nth.rs:33:23 + | +LL | let bad_vec = some_vec.iter().nth(3); + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::iter-nth` implied by `-D warnings` + = help: calling `.get()` is both faster and more readable + +error: called `.iter().nth()` on a slice + --> $DIR/iter_nth.rs:34:26 + | +LL | let bad_slice = &some_vec[..].iter().nth(3); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: calling `.get()` is both faster and more readable + +error: called `.iter().nth()` on a slice + --> $DIR/iter_nth.rs:35:31 + | +LL | let bad_boxed_slice = boxed_slice.iter().nth(3); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: calling `.get()` is both faster and more readable + +error: called `.iter().nth()` on a VecDeque + --> $DIR/iter_nth.rs:36:29 + | +LL | let bad_vec_deque = some_vec_deque.iter().nth(3); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: calling `.get()` is both faster and more readable + +error: called `.iter_mut().nth()` on a Vec + --> $DIR/iter_nth.rs:41:23 + | +LL | let bad_vec = some_vec.iter_mut().nth(3); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: calling `.get_mut()` is both faster and more readable + +error: called `.iter_mut().nth()` on a slice + --> $DIR/iter_nth.rs:44:26 + | +LL | let bad_slice = &some_vec[..].iter_mut().nth(3); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: calling `.get_mut()` is both faster and more readable + +error: called `.iter_mut().nth()` on a VecDeque + --> $DIR/iter_nth.rs:47:29 + | +LL | let bad_vec_deque = some_vec_deque.iter_mut().nth(3); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: calling `.get_mut()` is both faster and more readable + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/tests/ui/iter_nth_zero.fixed b/src/tools/clippy/tests/ui/iter_nth_zero.fixed new file mode 100644 index 00000000000..b54147c94d1 --- /dev/null +++ b/src/tools/clippy/tests/ui/iter_nth_zero.fixed @@ -0,0 +1,31 @@ +// run-rustfix + +#![warn(clippy::iter_nth_zero)] +use std::collections::HashSet; + +struct Foo {} + +impl Foo { + fn nth(&self, index: usize) -> usize { + index + 1 + } +} + +fn main() { + let f = Foo {}; + f.nth(0); // lint does not apply here + + let mut s = HashSet::new(); + s.insert(1); + let _x = s.iter().next(); + + let mut s2 = HashSet::new(); + s2.insert(2); + let mut iter = s2.iter(); + let _y = iter.next(); + + let mut s3 = HashSet::new(); + s3.insert(3); + let mut iter2 = s3.iter(); + let _unwrapped = iter2.next().unwrap(); +} diff --git a/src/tools/clippy/tests/ui/iter_nth_zero.rs b/src/tools/clippy/tests/ui/iter_nth_zero.rs new file mode 100644 index 00000000000..b92c7d18adb --- /dev/null +++ b/src/tools/clippy/tests/ui/iter_nth_zero.rs @@ -0,0 +1,31 @@ +// run-rustfix + +#![warn(clippy::iter_nth_zero)] +use std::collections::HashSet; + +struct Foo {} + +impl Foo { + fn nth(&self, index: usize) -> usize { + index + 1 + } +} + +fn main() { + let f = Foo {}; + f.nth(0); // lint does not apply here + + let mut s = HashSet::new(); + s.insert(1); + let _x = s.iter().nth(0); + + let mut s2 = HashSet::new(); + s2.insert(2); + let mut iter = s2.iter(); + let _y = iter.nth(0); + + let mut s3 = HashSet::new(); + s3.insert(3); + let mut iter2 = s3.iter(); + let _unwrapped = iter2.nth(0).unwrap(); +} diff --git a/src/tools/clippy/tests/ui/iter_nth_zero.stderr b/src/tools/clippy/tests/ui/iter_nth_zero.stderr new file mode 100644 index 00000000000..2b20a4ceb4a --- /dev/null +++ b/src/tools/clippy/tests/ui/iter_nth_zero.stderr @@ -0,0 +1,22 @@ +error: called `.nth(0)` on a `std::iter::Iterator` + --> $DIR/iter_nth_zero.rs:20:14 + | +LL | let _x = s.iter().nth(0); + | ^^^^^^^^^^^^^^^ help: try calling: `s.iter().next()` + | + = note: `-D clippy::iter-nth-zero` implied by `-D warnings` + +error: called `.nth(0)` on a `std::iter::Iterator` + --> $DIR/iter_nth_zero.rs:25:14 + | +LL | let _y = iter.nth(0); + | ^^^^^^^^^^^ help: try calling: `iter.next()` + +error: called `.nth(0)` on a `std::iter::Iterator` + --> $DIR/iter_nth_zero.rs:30:22 + | +LL | let _unwrapped = iter2.nth(0).unwrap(); + | ^^^^^^^^^^^^ help: try calling: `iter2.next()` + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/iter_skip_next.rs b/src/tools/clippy/tests/ui/iter_skip_next.rs new file mode 100644 index 00000000000..a65ca3bbb13 --- /dev/null +++ b/src/tools/clippy/tests/ui/iter_skip_next.rs @@ -0,0 +1,22 @@ +// aux-build:option_helpers.rs + +#![warn(clippy::iter_skip_next)] +#![allow(clippy::blacklisted_name)] + +extern crate option_helpers; + +use option_helpers::IteratorFalsePositives; + +/// Checks implementation of `ITER_SKIP_NEXT` lint +fn iter_skip_next() { + let mut some_vec = vec![0, 1, 2, 3]; + let _ = some_vec.iter().skip(42).next(); + let _ = some_vec.iter().cycle().skip(42).next(); + let _ = (1..10).skip(10).next(); + let _ = &some_vec[..].iter().skip(3).next(); + let foo = IteratorFalsePositives { foo: 0 }; + let _ = foo.skip(42).next(); + let _ = foo.filter().skip(42).next(); +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/iter_skip_next.stderr b/src/tools/clippy/tests/ui/iter_skip_next.stderr new file mode 100644 index 00000000000..5709f335529 --- /dev/null +++ b/src/tools/clippy/tests/ui/iter_skip_next.stderr @@ -0,0 +1,35 @@ +error: called `skip(x).next()` on an iterator + --> $DIR/iter_skip_next.rs:13:13 + | +LL | let _ = some_vec.iter().skip(42).next(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::iter-skip-next` implied by `-D warnings` + = help: this is more succinctly expressed by calling `nth(x)` + +error: called `skip(x).next()` on an iterator + --> $DIR/iter_skip_next.rs:14:13 + | +LL | let _ = some_vec.iter().cycle().skip(42).next(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: this is more succinctly expressed by calling `nth(x)` + +error: called `skip(x).next()` on an iterator + --> $DIR/iter_skip_next.rs:15:13 + | +LL | let _ = (1..10).skip(10).next(); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: this is more succinctly expressed by calling `nth(x)` + +error: called `skip(x).next()` on an iterator + --> $DIR/iter_skip_next.rs:16:14 + | +LL | let _ = &some_vec[..].iter().skip(3).next(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: this is more succinctly expressed by calling `nth(x)` + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/iterator_step_by_zero.rs b/src/tools/clippy/tests/ui/iterator_step_by_zero.rs new file mode 100644 index 00000000000..13d1cfd4281 --- /dev/null +++ b/src/tools/clippy/tests/ui/iterator_step_by_zero.rs @@ -0,0 +1,28 @@ +#[warn(clippy::iterator_step_by_zero)] +fn main() { + let _ = vec!["A", "B", "B"].iter().step_by(0); + let _ = "XXX".chars().step_by(0); + let _ = (0..1).step_by(0); + + // No error, not an iterator. + let y = NotIterator; + y.step_by(0); + + // No warning for non-zero step + let _ = (0..1).step_by(1); + + let _ = (1..).step_by(0); + let _ = (1..=2).step_by(0); + + let x = 0..1; + let _ = x.step_by(0); + + // check const eval + let v1 = vec![1, 2, 3]; + let _ = v1.iter().step_by(2 / 3); +} + +struct NotIterator; +impl NotIterator { + fn step_by(&self, _: u32) {} +} diff --git a/src/tools/clippy/tests/ui/iterator_step_by_zero.stderr b/src/tools/clippy/tests/ui/iterator_step_by_zero.stderr new file mode 100644 index 00000000000..c2c6803b3e6 --- /dev/null +++ b/src/tools/clippy/tests/ui/iterator_step_by_zero.stderr @@ -0,0 +1,46 @@ +error: Iterator::step_by(0) will panic at runtime + --> $DIR/iterator_step_by_zero.rs:3:13 + | +LL | let _ = vec!["A", "B", "B"].iter().step_by(0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::iterator-step-by-zero` implied by `-D warnings` + +error: Iterator::step_by(0) will panic at runtime + --> $DIR/iterator_step_by_zero.rs:4:13 + | +LL | let _ = "XXX".chars().step_by(0); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: Iterator::step_by(0) will panic at runtime + --> $DIR/iterator_step_by_zero.rs:5:13 + | +LL | let _ = (0..1).step_by(0); + | ^^^^^^^^^^^^^^^^^ + +error: Iterator::step_by(0) will panic at runtime + --> $DIR/iterator_step_by_zero.rs:14:13 + | +LL | let _ = (1..).step_by(0); + | ^^^^^^^^^^^^^^^^ + +error: Iterator::step_by(0) will panic at runtime + --> $DIR/iterator_step_by_zero.rs:15:13 + | +LL | let _ = (1..=2).step_by(0); + | ^^^^^^^^^^^^^^^^^^ + +error: Iterator::step_by(0) will panic at runtime + --> $DIR/iterator_step_by_zero.rs:18:13 + | +LL | let _ = x.step_by(0); + | ^^^^^^^^^^^^ + +error: Iterator::step_by(0) will panic at runtime + --> $DIR/iterator_step_by_zero.rs:22:13 + | +LL | let _ = v1.iter().step_by(2 / 3); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/tests/ui/large_const_arrays.fixed b/src/tools/clippy/tests/ui/large_const_arrays.fixed new file mode 100644 index 00000000000..c5af07c8a17 --- /dev/null +++ b/src/tools/clippy/tests/ui/large_const_arrays.fixed @@ -0,0 +1,37 @@ +// run-rustfix + +#![warn(clippy::large_const_arrays)] +#![allow(dead_code)] + +#[derive(Clone, Copy)] +pub struct S { + pub data: [u64; 32], +} + +// Should lint +pub(crate) static FOO_PUB_CRATE: [u32; 1_000_000] = [0u32; 1_000_000]; +pub static FOO_PUB: [u32; 1_000_000] = [0u32; 1_000_000]; +static FOO: [u32; 1_000_000] = [0u32; 1_000_000]; + +// Good +pub(crate) const G_FOO_PUB_CRATE: [u32; 1_000] = [0u32; 1_000]; +pub const G_FOO_PUB: [u32; 1_000] = [0u32; 1_000]; +const G_FOO: [u32; 1_000] = [0u32; 1_000]; + +fn main() { + // Should lint + pub static BAR_PUB: [u32; 1_000_000] = [0u32; 1_000_000]; + static BAR: [u32; 1_000_000] = [0u32; 1_000_000]; + pub static BAR_STRUCT_PUB: [S; 5_000] = [S { data: [0; 32] }; 5_000]; + static BAR_STRUCT: [S; 5_000] = [S { data: [0; 32] }; 5_000]; + pub static BAR_S_PUB: [Option<&str>; 200_000] = [Some("str"); 200_000]; + static BAR_S: [Option<&str>; 200_000] = [Some("str"); 200_000]; + + // Good + pub const G_BAR_PUB: [u32; 1_000] = [0u32; 1_000]; + const G_BAR: [u32; 1_000] = [0u32; 1_000]; + pub const G_BAR_STRUCT_PUB: [S; 500] = [S { data: [0; 32] }; 500]; + const G_BAR_STRUCT: [S; 500] = [S { data: [0; 32] }; 500]; + pub const G_BAR_S_PUB: [Option<&str>; 200] = [Some("str"); 200]; + const G_BAR_S: [Option<&str>; 200] = [Some("str"); 200]; +} diff --git a/src/tools/clippy/tests/ui/large_const_arrays.rs b/src/tools/clippy/tests/ui/large_const_arrays.rs new file mode 100644 index 00000000000..a160b9f8ad5 --- /dev/null +++ b/src/tools/clippy/tests/ui/large_const_arrays.rs @@ -0,0 +1,37 @@ +// run-rustfix + +#![warn(clippy::large_const_arrays)] +#![allow(dead_code)] + +#[derive(Clone, Copy)] +pub struct S { + pub data: [u64; 32], +} + +// Should lint +pub(crate) const FOO_PUB_CRATE: [u32; 1_000_000] = [0u32; 1_000_000]; +pub const FOO_PUB: [u32; 1_000_000] = [0u32; 1_000_000]; +const FOO: [u32; 1_000_000] = [0u32; 1_000_000]; + +// Good +pub(crate) const G_FOO_PUB_CRATE: [u32; 1_000] = [0u32; 1_000]; +pub const G_FOO_PUB: [u32; 1_000] = [0u32; 1_000]; +const G_FOO: [u32; 1_000] = [0u32; 1_000]; + +fn main() { + // Should lint + pub const BAR_PUB: [u32; 1_000_000] = [0u32; 1_000_000]; + const BAR: [u32; 1_000_000] = [0u32; 1_000_000]; + pub const BAR_STRUCT_PUB: [S; 5_000] = [S { data: [0; 32] }; 5_000]; + const BAR_STRUCT: [S; 5_000] = [S { data: [0; 32] }; 5_000]; + pub const BAR_S_PUB: [Option<&str>; 200_000] = [Some("str"); 200_000]; + const BAR_S: [Option<&str>; 200_000] = [Some("str"); 200_000]; + + // Good + pub const G_BAR_PUB: [u32; 1_000] = [0u32; 1_000]; + const G_BAR: [u32; 1_000] = [0u32; 1_000]; + pub const G_BAR_STRUCT_PUB: [S; 500] = [S { data: [0; 32] }; 500]; + const G_BAR_STRUCT: [S; 500] = [S { data: [0; 32] }; 500]; + pub const G_BAR_S_PUB: [Option<&str>; 200] = [Some("str"); 200]; + const G_BAR_S: [Option<&str>; 200] = [Some("str"); 200]; +} diff --git a/src/tools/clippy/tests/ui/large_const_arrays.stderr b/src/tools/clippy/tests/ui/large_const_arrays.stderr new file mode 100644 index 00000000000..3fb0acbca67 --- /dev/null +++ b/src/tools/clippy/tests/ui/large_const_arrays.stderr @@ -0,0 +1,76 @@ +error: large array defined as const + --> $DIR/large_const_arrays.rs:12:1 + | +LL | pub(crate) const FOO_PUB_CRATE: [u32; 1_000_000] = [0u32; 1_000_000]; + | ^^^^^^^^^^^-----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | help: make this a static item: `static` + | + = note: `-D clippy::large-const-arrays` implied by `-D warnings` + +error: large array defined as const + --> $DIR/large_const_arrays.rs:13:1 + | +LL | pub const FOO_PUB: [u32; 1_000_000] = [0u32; 1_000_000]; + | ^^^^-----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | help: make this a static item: `static` + +error: large array defined as const + --> $DIR/large_const_arrays.rs:14:1 + | +LL | const FOO: [u32; 1_000_000] = [0u32; 1_000_000]; + | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | help: make this a static item: `static` + +error: large array defined as const + --> $DIR/large_const_arrays.rs:23:5 + | +LL | pub const BAR_PUB: [u32; 1_000_000] = [0u32; 1_000_000]; + | ^^^^-----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | help: make this a static item: `static` + +error: large array defined as const + --> $DIR/large_const_arrays.rs:24:5 + | +LL | const BAR: [u32; 1_000_000] = [0u32; 1_000_000]; + | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | help: make this a static item: `static` + +error: large array defined as const + --> $DIR/large_const_arrays.rs:25:5 + | +LL | pub const BAR_STRUCT_PUB: [S; 5_000] = [S { data: [0; 32] }; 5_000]; + | ^^^^-----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | help: make this a static item: `static` + +error: large array defined as const + --> $DIR/large_const_arrays.rs:26:5 + | +LL | const BAR_STRUCT: [S; 5_000] = [S { data: [0; 32] }; 5_000]; + | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | help: make this a static item: `static` + +error: large array defined as const + --> $DIR/large_const_arrays.rs:27:5 + | +LL | pub const BAR_S_PUB: [Option<&str>; 200_000] = [Some("str"); 200_000]; + | ^^^^-----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | help: make this a static item: `static` + +error: large array defined as const + --> $DIR/large_const_arrays.rs:28:5 + | +LL | const BAR_S: [Option<&str>; 200_000] = [Some("str"); 200_000]; + | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | help: make this a static item: `static` + +error: aborting due to 9 previous errors + diff --git a/src/tools/clippy/tests/ui/large_digit_groups.fixed b/src/tools/clippy/tests/ui/large_digit_groups.fixed new file mode 100644 index 00000000000..859fad2f54d --- /dev/null +++ b/src/tools/clippy/tests/ui/large_digit_groups.fixed @@ -0,0 +1,31 @@ +// run-rustfix +#![warn(clippy::large_digit_groups)] + +fn main() { + macro_rules! mac { + () => { + 0b1_10110_i64 + }; + } + + let _good = ( + 0b1011_i64, + 0o1_234_u32, + 0x1_234_567, + 1_2345_6789, + 1234_f32, + 1_234.12_f32, + 1_234.123_f32, + 1.123_4_f32, + ); + let _bad = ( + 0b11_0110_i64, + 0xdead_beef_usize, + 123_456_f32, + 123_456.12_f32, + 123_456.123_45_f64, + 123_456.123_456_f64, + ); + // Ignore literals in macros + let _ = mac!(); +} diff --git a/src/tools/clippy/tests/ui/large_digit_groups.rs b/src/tools/clippy/tests/ui/large_digit_groups.rs new file mode 100644 index 00000000000..ac116d5dbda --- /dev/null +++ b/src/tools/clippy/tests/ui/large_digit_groups.rs @@ -0,0 +1,31 @@ +// run-rustfix +#![warn(clippy::large_digit_groups)] + +fn main() { + macro_rules! mac { + () => { + 0b1_10110_i64 + }; + } + + let _good = ( + 0b1011_i64, + 0o1_234_u32, + 0x1_234_567, + 1_2345_6789, + 1234_f32, + 1_234.12_f32, + 1_234.123_f32, + 1.123_4_f32, + ); + let _bad = ( + 0b1_10110_i64, + 0xd_e_adbee_f_usize, + 1_23456_f32, + 1_23456.12_f32, + 1_23456.12345_f64, + 1_23456.12345_6_f64, + ); + // Ignore literals in macros + let _ = mac!(); +} diff --git a/src/tools/clippy/tests/ui/large_digit_groups.stderr b/src/tools/clippy/tests/ui/large_digit_groups.stderr new file mode 100644 index 00000000000..b6d9672a78e --- /dev/null +++ b/src/tools/clippy/tests/ui/large_digit_groups.stderr @@ -0,0 +1,42 @@ +error: digit groups should be smaller + --> $DIR/large_digit_groups.rs:22:9 + | +LL | 0b1_10110_i64, + | ^^^^^^^^^^^^^ help: consider: `0b11_0110_i64` + | + = note: `-D clippy::large-digit-groups` implied by `-D warnings` + +error: digits grouped inconsistently by underscores + --> $DIR/large_digit_groups.rs:23:9 + | +LL | 0xd_e_adbee_f_usize, + | ^^^^^^^^^^^^^^^^^^^ help: consider: `0xdead_beef_usize` + | + = note: `-D clippy::inconsistent-digit-grouping` implied by `-D warnings` + +error: digit groups should be smaller + --> $DIR/large_digit_groups.rs:24:9 + | +LL | 1_23456_f32, + | ^^^^^^^^^^^ help: consider: `123_456_f32` + +error: digit groups should be smaller + --> $DIR/large_digit_groups.rs:25:9 + | +LL | 1_23456.12_f32, + | ^^^^^^^^^^^^^^ help: consider: `123_456.12_f32` + +error: digit groups should be smaller + --> $DIR/large_digit_groups.rs:26:9 + | +LL | 1_23456.12345_f64, + | ^^^^^^^^^^^^^^^^^ help: consider: `123_456.123_45_f64` + +error: digit groups should be smaller + --> $DIR/large_digit_groups.rs:27:9 + | +LL | 1_23456.12345_6_f64, + | ^^^^^^^^^^^^^^^^^^^ help: consider: `123_456.123_456_f64` + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/large_enum_variant.rs b/src/tools/clippy/tests/ui/large_enum_variant.rs new file mode 100644 index 00000000000..852ef5fec0e --- /dev/null +++ b/src/tools/clippy/tests/ui/large_enum_variant.rs @@ -0,0 +1,54 @@ +#![allow(dead_code)] +#![allow(unused_variables)] +#![warn(clippy::large_enum_variant)] + +enum LargeEnum { + A(i32), + B([i32; 8000]), +} + +enum GenericEnumOk<T> { + A(i32), + B([T; 8000]), +} + +enum GenericEnum2<T> { + A(i32), + B([i32; 8000]), + C(T, [i32; 8000]), +} + +trait SomeTrait { + type Item; +} + +enum LargeEnumGeneric<A: SomeTrait> { + Var(A::Item), +} + +enum LargeEnum2 { + VariantOk(i32, u32), + ContainingLargeEnum(LargeEnum), +} +enum LargeEnum3 { + ContainingMoreThanOneField(i32, [i32; 8000], [i32; 9500]), + VoidVariant, + StructLikeLittle { x: i32, y: i32 }, +} + +enum LargeEnum4 { + VariantOk(i32, u32), + StructLikeLarge { x: [i32; 8000], y: i32 }, +} + +enum LargeEnum5 { + VariantOk(i32, u32), + StructLikeLarge2 { x: [i32; 8000] }, +} + +enum LargeEnumOk { + LargeA([i32; 8000]), + LargeB([i32; 8001]), +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/large_enum_variant.stderr b/src/tools/clippy/tests/ui/large_enum_variant.stderr new file mode 100644 index 00000000000..8ce641a81f2 --- /dev/null +++ b/src/tools/clippy/tests/ui/large_enum_variant.stderr @@ -0,0 +1,68 @@ +error: large size difference between variants + --> $DIR/large_enum_variant.rs:7:5 + | +LL | B([i32; 8000]), + | ^^^^^^^^^^^^^^ this variant is 32000 bytes + | + = note: `-D clippy::large-enum-variant` implied by `-D warnings` +note: and the second-largest variant is 4 bytes: + --> $DIR/large_enum_variant.rs:6:5 + | +LL | A(i32), + | ^^^^^^ +help: consider boxing the large fields to reduce the total size of the enum + | +LL | B(Box<[i32; 8000]>), + | ^^^^^^^^^^^^^^^^ + +error: large size difference between variants + --> $DIR/large_enum_variant.rs:31:5 + | +LL | ContainingLargeEnum(LargeEnum), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this variant is 32004 bytes + | +note: and the second-largest variant is 8 bytes: + --> $DIR/large_enum_variant.rs:30:5 + | +LL | VariantOk(i32, u32), + | ^^^^^^^^^^^^^^^^^^^ +help: consider boxing the large fields to reduce the total size of the enum + | +LL | ContainingLargeEnum(Box<LargeEnum>), + | ^^^^^^^^^^^^^^ + +error: large size difference between variants + --> $DIR/large_enum_variant.rs:41:5 + | +LL | StructLikeLarge { x: [i32; 8000], y: i32 }, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this variant is 32004 bytes + | +note: and the second-largest variant is 8 bytes: + --> $DIR/large_enum_variant.rs:40:5 + | +LL | VariantOk(i32, u32), + | ^^^^^^^^^^^^^^^^^^^ +help: consider boxing the large fields to reduce the total size of the enum + --> $DIR/large_enum_variant.rs:41:5 + | +LL | StructLikeLarge { x: [i32; 8000], y: i32 }, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: large size difference between variants + --> $DIR/large_enum_variant.rs:46:5 + | +LL | StructLikeLarge2 { x: [i32; 8000] }, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this variant is 32000 bytes + | +note: and the second-largest variant is 8 bytes: + --> $DIR/large_enum_variant.rs:45:5 + | +LL | VariantOk(i32, u32), + | ^^^^^^^^^^^^^^^^^^^ +help: consider boxing the large fields to reduce the total size of the enum + | +LL | StructLikeLarge2 { x: Box<[i32; 8000]> }, + | ^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/large_stack_arrays.rs b/src/tools/clippy/tests/ui/large_stack_arrays.rs new file mode 100644 index 00000000000..d9161bfcf15 --- /dev/null +++ b/src/tools/clippy/tests/ui/large_stack_arrays.rs @@ -0,0 +1,30 @@ +#![warn(clippy::large_stack_arrays)] +#![allow(clippy::large_enum_variant)] + +#[derive(Clone, Copy)] +struct S { + pub data: [u64; 32], +} + +#[derive(Clone, Copy)] +enum E { + S(S), + T(u32), +} + +fn main() { + let bad = ( + [0u32; 20_000_000], + [S { data: [0; 32] }; 5000], + [Some(""); 20_000_000], + [E::T(0); 5000], + ); + + let good = ( + [0u32; 1000], + [S { data: [0; 32] }; 1000], + [Some(""); 1000], + [E::T(0); 1000], + [(); 20_000_000], + ); +} diff --git a/src/tools/clippy/tests/ui/large_stack_arrays.stderr b/src/tools/clippy/tests/ui/large_stack_arrays.stderr new file mode 100644 index 00000000000..58c0a77c1c8 --- /dev/null +++ b/src/tools/clippy/tests/ui/large_stack_arrays.stderr @@ -0,0 +1,35 @@ +error: allocating a local array larger than 512000 bytes + --> $DIR/large_stack_arrays.rs:17:9 + | +LL | [0u32; 20_000_000], + | ^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::large-stack-arrays` implied by `-D warnings` + = help: consider allocating on the heap with `vec![0u32; 20_000_000].into_boxed_slice()` + +error: allocating a local array larger than 512000 bytes + --> $DIR/large_stack_arrays.rs:18:9 + | +LL | [S { data: [0; 32] }; 5000], + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider allocating on the heap with `vec![S { data: [0; 32] }; 5000].into_boxed_slice()` + +error: allocating a local array larger than 512000 bytes + --> $DIR/large_stack_arrays.rs:19:9 + | +LL | [Some(""); 20_000_000], + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider allocating on the heap with `vec![Some(""); 20_000_000].into_boxed_slice()` + +error: allocating a local array larger than 512000 bytes + --> $DIR/large_stack_arrays.rs:20:9 + | +LL | [E::T(0); 5000], + | ^^^^^^^^^^^^^^^ + | + = help: consider allocating on the heap with `vec![E::T(0); 5000].into_boxed_slice()` + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/len_without_is_empty.rs b/src/tools/clippy/tests/ui/len_without_is_empty.rs new file mode 100644 index 00000000000..3ef29dd6388 --- /dev/null +++ b/src/tools/clippy/tests/ui/len_without_is_empty.rs @@ -0,0 +1,145 @@ +#![warn(clippy::len_without_is_empty)] +#![allow(dead_code, unused)] + +pub struct PubOne; + +impl PubOne { + pub fn len(self: &Self) -> isize { + 1 + } +} + +impl PubOne { + // A second impl for this struct -- the error span shouldn't mention this. + pub fn irrelevant(self: &Self) -> bool { + false + } +} + +// Identical to `PubOne`, but with an `allow` attribute on the impl complaining `len`. +pub struct PubAllowed; + +#[allow(clippy::len_without_is_empty)] +impl PubAllowed { + pub fn len(self: &Self) -> isize { + 1 + } +} + +// No `allow` attribute on this impl block, but that doesn't matter -- we only require one on the +// impl containing `len`. +impl PubAllowed { + pub fn irrelevant(self: &Self) -> bool { + false + } +} + +pub trait PubTraitsToo { + fn len(self: &Self) -> isize; +} + +impl PubTraitsToo for One { + fn len(self: &Self) -> isize { + 0 + } +} + +pub struct HasIsEmpty; + +impl HasIsEmpty { + pub fn len(self: &Self) -> isize { + 1 + } + + fn is_empty(self: &Self) -> bool { + false + } +} + +pub struct HasWrongIsEmpty; + +impl HasWrongIsEmpty { + pub fn len(self: &Self) -> isize { + 1 + } + + pub fn is_empty(self: &Self, x: u32) -> bool { + false + } +} + +struct NotPubOne; + +impl NotPubOne { + pub fn len(self: &Self) -> isize { + // No error; `len` is pub but `NotPubOne` is not exported anyway. + 1 + } +} + +struct One; + +impl One { + fn len(self: &Self) -> isize { + // No error; `len` is private; see issue #1085. + 1 + } +} + +trait TraitsToo { + fn len(self: &Self) -> isize; + // No error; `len` is private; see issue #1085. +} + +impl TraitsToo for One { + fn len(self: &Self) -> isize { + 0 + } +} + +struct HasPrivateIsEmpty; + +impl HasPrivateIsEmpty { + pub fn len(self: &Self) -> isize { + 1 + } + + fn is_empty(self: &Self) -> bool { + false + } +} + +struct Wither; + +pub trait WithIsEmpty { + fn len(self: &Self) -> isize; + fn is_empty(self: &Self) -> bool; +} + +impl WithIsEmpty for Wither { + fn len(self: &Self) -> isize { + 1 + } + + fn is_empty(self: &Self) -> bool { + false + } +} + +pub trait Empty { + fn is_empty(&self) -> bool; +} + +pub trait InheritingEmpty: Empty { + // Must not trigger `LEN_WITHOUT_IS_EMPTY`. + fn len(&self) -> isize; +} + +// This used to ICE. +pub trait Foo: Sized {} + +pub trait DependsOnFoo: Foo { + fn len(&mut self) -> usize; +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/len_without_is_empty.stderr b/src/tools/clippy/tests/ui/len_without_is_empty.stderr new file mode 100644 index 00000000000..4493b17a4b4 --- /dev/null +++ b/src/tools/clippy/tests/ui/len_without_is_empty.stderr @@ -0,0 +1,54 @@ +error: item `PubOne` has a public `len` method but no corresponding `is_empty` method + --> $DIR/len_without_is_empty.rs:6:1 + | +LL | / impl PubOne { +LL | | pub fn len(self: &Self) -> isize { +LL | | 1 +LL | | } +LL | | } + | |_^ + | + = note: `-D clippy::len-without-is-empty` implied by `-D warnings` + +error: trait `PubTraitsToo` has a `len` method but no (possibly inherited) `is_empty` method + --> $DIR/len_without_is_empty.rs:37:1 + | +LL | / pub trait PubTraitsToo { +LL | | fn len(self: &Self) -> isize; +LL | | } + | |_^ + +error: item `HasIsEmpty` has a public `len` method but a private `is_empty` method + --> $DIR/len_without_is_empty.rs:49:1 + | +LL | / impl HasIsEmpty { +LL | | pub fn len(self: &Self) -> isize { +LL | | 1 +LL | | } +... | +LL | | } +LL | | } + | |_^ + +error: item `HasWrongIsEmpty` has a public `len` method but no corresponding `is_empty` method + --> $DIR/len_without_is_empty.rs:61:1 + | +LL | / impl HasWrongIsEmpty { +LL | | pub fn len(self: &Self) -> isize { +LL | | 1 +LL | | } +... | +LL | | } +LL | | } + | |_^ + +error: trait `DependsOnFoo` has a `len` method but no (possibly inherited) `is_empty` method + --> $DIR/len_without_is_empty.rs:141:1 + | +LL | / pub trait DependsOnFoo: Foo { +LL | | fn len(&mut self) -> usize; +LL | | } + | |_^ + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/len_zero.fixed b/src/tools/clippy/tests/ui/len_zero.fixed new file mode 100644 index 00000000000..624e5ef8fcf --- /dev/null +++ b/src/tools/clippy/tests/ui/len_zero.fixed @@ -0,0 +1,143 @@ +// run-rustfix + +#![warn(clippy::len_zero)] +#![allow(dead_code, unused, clippy::len_without_is_empty)] + +pub struct One; +struct Wither; + +trait TraitsToo { + fn len(self: &Self) -> isize; + // No error; `len` is private; see issue #1085. +} + +impl TraitsToo for One { + fn len(self: &Self) -> isize { + 0 + } +} + +pub struct HasIsEmpty; + +impl HasIsEmpty { + pub fn len(self: &Self) -> isize { + 1 + } + + fn is_empty(self: &Self) -> bool { + false + } +} + +pub struct HasWrongIsEmpty; + +impl HasWrongIsEmpty { + pub fn len(self: &Self) -> isize { + 1 + } + + pub fn is_empty(self: &Self, x: u32) -> bool { + false + } +} + +pub trait WithIsEmpty { + fn len(self: &Self) -> isize; + fn is_empty(self: &Self) -> bool; +} + +impl WithIsEmpty for Wither { + fn len(self: &Self) -> isize { + 1 + } + + fn is_empty(self: &Self) -> bool { + false + } +} + +fn main() { + let x = [1, 2]; + if x.is_empty() { + println!("This should not happen!"); + } + + if "".is_empty() {} + + let y = One; + if y.len() == 0 { + // No error; `One` does not have `.is_empty()`. + println!("This should not happen either!"); + } + + let z: &dyn TraitsToo = &y; + if z.len() > 0 { + // No error; `TraitsToo` has no `.is_empty()` method. + println!("Nor should this!"); + } + + let has_is_empty = HasIsEmpty; + if has_is_empty.is_empty() { + println!("Or this!"); + } + if !has_is_empty.is_empty() { + println!("Or this!"); + } + if !has_is_empty.is_empty() { + println!("Or this!"); + } + if has_is_empty.is_empty() { + println!("Or this!"); + } + if !has_is_empty.is_empty() { + println!("Or this!"); + } + if has_is_empty.len() > 1 { + // No error. + println!("This can happen."); + } + if has_is_empty.len() <= 1 { + // No error. + println!("This can happen."); + } + if has_is_empty.is_empty() { + println!("Or this!"); + } + if !has_is_empty.is_empty() { + println!("Or this!"); + } + if !has_is_empty.is_empty() { + println!("Or this!"); + } + if !has_is_empty.is_empty() { + println!("Or this!"); + } + if has_is_empty.is_empty() { + println!("Or this!"); + } + if 1 < has_is_empty.len() { + // No error. + println!("This can happen."); + } + if 1 >= has_is_empty.len() { + // No error. + println!("This can happen."); + } + assert!(!has_is_empty.is_empty()); + + let with_is_empty: &dyn WithIsEmpty = &Wither; + if with_is_empty.is_empty() { + println!("Or this!"); + } + assert!(!with_is_empty.is_empty()); + + let has_wrong_is_empty = HasWrongIsEmpty; + if has_wrong_is_empty.len() == 0 { + // No error; `HasWrongIsEmpty` does not have `.is_empty()`. + println!("Or this!"); + } +} + +fn test_slice(b: &[u8]) { + if !b.is_empty() {} +} diff --git a/src/tools/clippy/tests/ui/len_zero.rs b/src/tools/clippy/tests/ui/len_zero.rs new file mode 100644 index 00000000000..7fba971cfd8 --- /dev/null +++ b/src/tools/clippy/tests/ui/len_zero.rs @@ -0,0 +1,143 @@ +// run-rustfix + +#![warn(clippy::len_zero)] +#![allow(dead_code, unused, clippy::len_without_is_empty)] + +pub struct One; +struct Wither; + +trait TraitsToo { + fn len(self: &Self) -> isize; + // No error; `len` is private; see issue #1085. +} + +impl TraitsToo for One { + fn len(self: &Self) -> isize { + 0 + } +} + +pub struct HasIsEmpty; + +impl HasIsEmpty { + pub fn len(self: &Self) -> isize { + 1 + } + + fn is_empty(self: &Self) -> bool { + false + } +} + +pub struct HasWrongIsEmpty; + +impl HasWrongIsEmpty { + pub fn len(self: &Self) -> isize { + 1 + } + + pub fn is_empty(self: &Self, x: u32) -> bool { + false + } +} + +pub trait WithIsEmpty { + fn len(self: &Self) -> isize; + fn is_empty(self: &Self) -> bool; +} + +impl WithIsEmpty for Wither { + fn len(self: &Self) -> isize { + 1 + } + + fn is_empty(self: &Self) -> bool { + false + } +} + +fn main() { + let x = [1, 2]; + if x.len() == 0 { + println!("This should not happen!"); + } + + if "".len() == 0 {} + + let y = One; + if y.len() == 0 { + // No error; `One` does not have `.is_empty()`. + println!("This should not happen either!"); + } + + let z: &dyn TraitsToo = &y; + if z.len() > 0 { + // No error; `TraitsToo` has no `.is_empty()` method. + println!("Nor should this!"); + } + + let has_is_empty = HasIsEmpty; + if has_is_empty.len() == 0 { + println!("Or this!"); + } + if has_is_empty.len() != 0 { + println!("Or this!"); + } + if has_is_empty.len() > 0 { + println!("Or this!"); + } + if has_is_empty.len() < 1 { + println!("Or this!"); + } + if has_is_empty.len() >= 1 { + println!("Or this!"); + } + if has_is_empty.len() > 1 { + // No error. + println!("This can happen."); + } + if has_is_empty.len() <= 1 { + // No error. + println!("This can happen."); + } + if 0 == has_is_empty.len() { + println!("Or this!"); + } + if 0 != has_is_empty.len() { + println!("Or this!"); + } + if 0 < has_is_empty.len() { + println!("Or this!"); + } + if 1 <= has_is_empty.len() { + println!("Or this!"); + } + if 1 > has_is_empty.len() { + println!("Or this!"); + } + if 1 < has_is_empty.len() { + // No error. + println!("This can happen."); + } + if 1 >= has_is_empty.len() { + // No error. + println!("This can happen."); + } + assert!(!has_is_empty.is_empty()); + + let with_is_empty: &dyn WithIsEmpty = &Wither; + if with_is_empty.len() == 0 { + println!("Or this!"); + } + assert!(!with_is_empty.is_empty()); + + let has_wrong_is_empty = HasWrongIsEmpty; + if has_wrong_is_empty.len() == 0 { + // No error; `HasWrongIsEmpty` does not have `.is_empty()`. + println!("Or this!"); + } +} + +fn test_slice(b: &[u8]) { + if b.len() != 0 {} +} diff --git a/src/tools/clippy/tests/ui/len_zero.stderr b/src/tools/clippy/tests/ui/len_zero.stderr new file mode 100644 index 00000000000..6c71f1beeac --- /dev/null +++ b/src/tools/clippy/tests/ui/len_zero.stderr @@ -0,0 +1,88 @@ +error: length comparison to zero + --> $DIR/len_zero.rs:61:8 + | +LL | if x.len() == 0 { + | ^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `x.is_empty()` + | + = note: `-D clippy::len-zero` implied by `-D warnings` + +error: length comparison to zero + --> $DIR/len_zero.rs:65:8 + | +LL | if "".len() == 0 {} + | ^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `"".is_empty()` + +error: length comparison to zero + --> $DIR/len_zero.rs:80:8 + | +LL | if has_is_empty.len() == 0 { + | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()` + +error: length comparison to zero + --> $DIR/len_zero.rs:83:8 + | +LL | if has_is_empty.len() != 0 { + | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` + +error: length comparison to zero + --> $DIR/len_zero.rs:86:8 + | +LL | if has_is_empty.len() > 0 { + | ^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` + +error: length comparison to one + --> $DIR/len_zero.rs:89:8 + | +LL | if has_is_empty.len() < 1 { + | ^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()` + +error: length comparison to one + --> $DIR/len_zero.rs:92:8 + | +LL | if has_is_empty.len() >= 1 { + | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` + +error: length comparison to zero + --> $DIR/len_zero.rs:103:8 + | +LL | if 0 == has_is_empty.len() { + | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()` + +error: length comparison to zero + --> $DIR/len_zero.rs:106:8 + | +LL | if 0 != has_is_empty.len() { + | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` + +error: length comparison to zero + --> $DIR/len_zero.rs:109:8 + | +LL | if 0 < has_is_empty.len() { + | ^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` + +error: length comparison to one + --> $DIR/len_zero.rs:112:8 + | +LL | if 1 <= has_is_empty.len() { + | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` + +error: length comparison to one + --> $DIR/len_zero.rs:115:8 + | +LL | if 1 > has_is_empty.len() { + | ^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()` + +error: length comparison to zero + --> $DIR/len_zero.rs:129:8 + | +LL | if with_is_empty.len() == 0 { + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `with_is_empty.is_empty()` + +error: length comparison to zero + --> $DIR/len_zero.rs:142:8 + | +LL | if b.len() != 0 {} + | ^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!b.is_empty()` + +error: aborting due to 14 previous errors + diff --git a/src/tools/clippy/tests/ui/let_if_seq.rs b/src/tools/clippy/tests/ui/let_if_seq.rs new file mode 100644 index 00000000000..802beeb4be6 --- /dev/null +++ b/src/tools/clippy/tests/ui/let_if_seq.rs @@ -0,0 +1,119 @@ +#![allow( + unused_variables, + unused_assignments, + clippy::similar_names, + clippy::blacklisted_name +)] +#![warn(clippy::useless_let_if_seq)] + +fn f() -> bool { + true +} +fn g(x: i32) -> i32 { + x + 1 +} + +fn issue985() -> i32 { + let mut x = 42; + if f() { + x = g(x); + } + + x +} + +fn issue985_alt() -> i32 { + let mut x = 42; + if f() { + f(); + } else { + x = g(x); + } + + x +} + +fn issue975() -> String { + let mut udn = "dummy".to_string(); + if udn.starts_with("uuid:") { + udn = String::from(&udn[5..]); + } + udn +} + +fn early_return() -> u8 { + // FIXME: we could extend the lint to include such cases: + let foo; + + if f() { + return 42; + } else { + foo = 0; + } + + foo +} + +fn main() { + early_return(); + issue975(); + issue985(); + issue985_alt(); + + let mut foo = 0; + if f() { + foo = 42; + } + + let mut bar = 0; + if f() { + f(); + bar = 42; + } else { + f(); + } + + let quz; + if f() { + quz = 42; + } else { + quz = 0; + } + + // `toto` is used several times + let mut toto; + if f() { + toto = 42; + } else { + for i in &[1, 2] { + toto = *i; + } + + toto = 2; + } + + // found in libcore, the inner if is not a statement but the block's expr + let mut ch = b'x'; + if f() { + ch = b'*'; + if f() { + ch = b'?'; + } + } + + // baz needs to be mut + let mut baz = 0; + if f() { + baz = 42; + } + + baz = 1337; + + // issue 3043 - types with interior mutability should not trigger this lint + use std::cell::Cell; + let mut val = Cell::new(1); + if true { + val = Cell::new(2); + } + println!("{}", val.get()); +} diff --git a/src/tools/clippy/tests/ui/let_if_seq.stderr b/src/tools/clippy/tests/ui/let_if_seq.stderr new file mode 100644 index 00000000000..c53a63a541b --- /dev/null +++ b/src/tools/clippy/tests/ui/let_if_seq.stderr @@ -0,0 +1,50 @@ +error: `if _ { .. } else { .. }` is an expression + --> $DIR/let_if_seq.rs:63:5 + | +LL | / let mut foo = 0; +LL | | if f() { +LL | | foo = 42; +LL | | } + | |_____^ help: it is more idiomatic to write: `let <mut> foo = if f() { 42 } else { 0 };` + | + = note: `-D clippy::useless-let-if-seq` implied by `-D warnings` + = note: you might not need `mut` at all + +error: `if _ { .. } else { .. }` is an expression + --> $DIR/let_if_seq.rs:68:5 + | +LL | / let mut bar = 0; +LL | | if f() { +LL | | f(); +LL | | bar = 42; +LL | | } else { +LL | | f(); +LL | | } + | |_____^ help: it is more idiomatic to write: `let <mut> bar = if f() { ..; 42 } else { ..; 0 };` + | + = note: you might not need `mut` at all + +error: `if _ { .. } else { .. }` is an expression + --> $DIR/let_if_seq.rs:76:5 + | +LL | / let quz; +LL | | if f() { +LL | | quz = 42; +LL | | } else { +LL | | quz = 0; +LL | | } + | |_____^ help: it is more idiomatic to write: `let quz = if f() { 42 } else { 0 };` + +error: `if _ { .. } else { .. }` is an expression + --> $DIR/let_if_seq.rs:105:5 + | +LL | / let mut baz = 0; +LL | | if f() { +LL | | baz = 42; +LL | | } + | |_____^ help: it is more idiomatic to write: `let <mut> baz = if f() { 42 } else { 0 };` + | + = note: you might not need `mut` at all + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/let_return.rs b/src/tools/clippy/tests/ui/let_return.rs new file mode 100644 index 00000000000..23645d48fe7 --- /dev/null +++ b/src/tools/clippy/tests/ui/let_return.rs @@ -0,0 +1,70 @@ +#![allow(unused)] +#![warn(clippy::let_and_return)] + +fn test() -> i32 { + let _y = 0; // no warning + let x = 5; + x +} + +fn test_inner() -> i32 { + if true { + let x = 5; + x + } else { + 0 + } +} + +fn test_nowarn_1() -> i32 { + let mut x = 5; + x += 1; + x +} + +fn test_nowarn_2() -> i32 { + let x = 5; + x + 1 +} + +fn test_nowarn_3() -> (i32, i32) { + // this should technically warn, but we do not compare complex patterns + let (x, y) = (5, 9); + (x, y) +} + +fn test_nowarn_4() -> i32 { + // this should technically warn, but not b/c of clippy::let_and_return, but b/c of useless type + let x: i32 = 5; + x +} + +fn test_nowarn_5(x: i16) -> u16 { + #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] + let x = x as u16; + x +} + +// False positive example +trait Decode { + fn decode<D: std::io::Read>(d: D) -> Result<Self, ()> + where + Self: Sized; +} + +macro_rules! tuple_encode { + ($($x:ident),*) => ( + impl<$($x: Decode),*> Decode for ($($x),*) { + #[inline] + #[allow(non_snake_case)] + fn decode<D: std::io::Read>(mut d: D) -> Result<Self, ()> { + // Shouldn't trigger lint + Ok(($({let $x = Decode::decode(&mut d)?; $x }),*)) + } + } + ); +} + +tuple_encode!(T0, T1, T2, T3, T4, T5, T6, T7); + +fn main() {} diff --git a/src/tools/clippy/tests/ui/let_return.stderr b/src/tools/clippy/tests/ui/let_return.stderr new file mode 100644 index 00000000000..128a22c86e3 --- /dev/null +++ b/src/tools/clippy/tests/ui/let_return.stderr @@ -0,0 +1,31 @@ +error: returning the result of a `let` binding from a block + --> $DIR/let_return.rs:7:5 + | +LL | let x = 5; + | ---------- unnecessary `let` binding +LL | x + | ^ + | + = note: `-D clippy::let-and-return` implied by `-D warnings` +help: return the expression directly + | +LL | +LL | 5 + | + +error: returning the result of a `let` binding from a block + --> $DIR/let_return.rs:13:9 + | +LL | let x = 5; + | ---------- unnecessary `let` binding +LL | x + | ^ + | +help: return the expression directly + | +LL | +LL | 5 + | + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/let_underscore_lock.rs b/src/tools/clippy/tests/ui/let_underscore_lock.rs new file mode 100644 index 00000000000..88fb216a743 --- /dev/null +++ b/src/tools/clippy/tests/ui/let_underscore_lock.rs @@ -0,0 +1,13 @@ +#![warn(clippy::let_underscore_lock)] + +fn main() { + let m = std::sync::Mutex::new(()); + let rw = std::sync::RwLock::new(()); + + let _ = m.lock(); + let _ = rw.read(); + let _ = rw.write(); + let _ = m.try_lock(); + let _ = rw.try_read(); + let _ = rw.try_write(); +} diff --git a/src/tools/clippy/tests/ui/let_underscore_lock.stderr b/src/tools/clippy/tests/ui/let_underscore_lock.stderr new file mode 100644 index 00000000000..5d5f6059ef1 --- /dev/null +++ b/src/tools/clippy/tests/ui/let_underscore_lock.stderr @@ -0,0 +1,51 @@ +error: non-binding let on a synchronization lock + --> $DIR/let_underscore_lock.rs:7:5 + | +LL | let _ = m.lock(); + | ^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::let-underscore-lock` implied by `-D warnings` + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + +error: non-binding let on a synchronization lock + --> $DIR/let_underscore_lock.rs:8:5 + | +LL | let _ = rw.read(); + | ^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + +error: non-binding let on a synchronization lock + --> $DIR/let_underscore_lock.rs:9:5 + | +LL | let _ = rw.write(); + | ^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + +error: non-binding let on a synchronization lock + --> $DIR/let_underscore_lock.rs:10:5 + | +LL | let _ = m.try_lock(); + | ^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + +error: non-binding let on a synchronization lock + --> $DIR/let_underscore_lock.rs:11:5 + | +LL | let _ = rw.try_read(); + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + +error: non-binding let on a synchronization lock + --> $DIR/let_underscore_lock.rs:12:5 + | +LL | let _ = rw.try_write(); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/let_underscore_must_use.rs b/src/tools/clippy/tests/ui/let_underscore_must_use.rs new file mode 100644 index 00000000000..27dda606067 --- /dev/null +++ b/src/tools/clippy/tests/ui/let_underscore_must_use.rs @@ -0,0 +1,94 @@ +#![warn(clippy::let_underscore_must_use)] + +// Debug implementations can fire this lint, +// so we shouldn't lint external macros +#[derive(Debug)] +struct Foo { + field: i32, +} + +#[must_use] +fn f() -> u32 { + 0 +} + +fn g() -> Result<u32, u32> { + Ok(0) +} + +#[must_use] +fn l<T>(x: T) -> T { + x +} + +fn h() -> u32 { + 0 +} + +struct S {} + +impl S { + #[must_use] + pub fn f(&self) -> u32 { + 0 + } + + pub fn g(&self) -> Result<u32, u32> { + Ok(0) + } + + fn k(&self) -> u32 { + 0 + } + + #[must_use] + fn h() -> u32 { + 0 + } + + fn p() -> Result<u32, u32> { + Ok(0) + } +} + +trait Trait { + #[must_use] + fn a() -> u32; +} + +impl Trait for S { + fn a() -> u32 { + 0 + } +} + +fn main() { + let _ = f(); + let _ = g(); + let _ = h(); + let _ = l(0_u32); + + let s = S {}; + + let _ = s.f(); + let _ = s.g(); + let _ = s.k(); + + let _ = S::h(); + let _ = S::p(); + + let _ = S::a(); + + let _ = if true { Ok(()) } else { Err(()) }; + + let a = Result::<(), ()>::Ok(()); + + let _ = a.is_ok(); + + let _ = a.map(|_| ()); + + let _ = a; + + #[allow(clippy::let_underscore_must_use)] + let _ = a; +} diff --git a/src/tools/clippy/tests/ui/let_underscore_must_use.stderr b/src/tools/clippy/tests/ui/let_underscore_must_use.stderr new file mode 100644 index 00000000000..447f2419e3b --- /dev/null +++ b/src/tools/clippy/tests/ui/let_underscore_must_use.stderr @@ -0,0 +1,99 @@ +error: non-binding let on a result of a `#[must_use]` function + --> $DIR/let_underscore_must_use.rs:66:5 + | +LL | let _ = f(); + | ^^^^^^^^^^^^ + | + = note: `-D clippy::let-underscore-must-use` implied by `-D warnings` + = help: consider explicitly using function result + +error: non-binding let on an expression with `#[must_use]` type + --> $DIR/let_underscore_must_use.rs:67:5 + | +LL | let _ = g(); + | ^^^^^^^^^^^^ + | + = help: consider explicitly using expression value + +error: non-binding let on a result of a `#[must_use]` function + --> $DIR/let_underscore_must_use.rs:69:5 + | +LL | let _ = l(0_u32); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider explicitly using function result + +error: non-binding let on a result of a `#[must_use]` function + --> $DIR/let_underscore_must_use.rs:73:5 + | +LL | let _ = s.f(); + | ^^^^^^^^^^^^^^ + | + = help: consider explicitly using function result + +error: non-binding let on an expression with `#[must_use]` type + --> $DIR/let_underscore_must_use.rs:74:5 + | +LL | let _ = s.g(); + | ^^^^^^^^^^^^^^ + | + = help: consider explicitly using expression value + +error: non-binding let on a result of a `#[must_use]` function + --> $DIR/let_underscore_must_use.rs:77:5 + | +LL | let _ = S::h(); + | ^^^^^^^^^^^^^^^ + | + = help: consider explicitly using function result + +error: non-binding let on an expression with `#[must_use]` type + --> $DIR/let_underscore_must_use.rs:78:5 + | +LL | let _ = S::p(); + | ^^^^^^^^^^^^^^^ + | + = help: consider explicitly using expression value + +error: non-binding let on a result of a `#[must_use]` function + --> $DIR/let_underscore_must_use.rs:80:5 + | +LL | let _ = S::a(); + | ^^^^^^^^^^^^^^^ + | + = help: consider explicitly using function result + +error: non-binding let on an expression with `#[must_use]` type + --> $DIR/let_underscore_must_use.rs:82:5 + | +LL | let _ = if true { Ok(()) } else { Err(()) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider explicitly using expression value + +error: non-binding let on a result of a `#[must_use]` function + --> $DIR/let_underscore_must_use.rs:86:5 + | +LL | let _ = a.is_ok(); + | ^^^^^^^^^^^^^^^^^^ + | + = help: consider explicitly using function result + +error: non-binding let on an expression with `#[must_use]` type + --> $DIR/let_underscore_must_use.rs:88:5 + | +LL | let _ = a.map(|_| ()); + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider explicitly using expression value + +error: non-binding let on an expression with `#[must_use]` type + --> $DIR/let_underscore_must_use.rs:90:5 + | +LL | let _ = a; + | ^^^^^^^^^^ + | + = help: consider explicitly using expression value + +error: aborting due to 12 previous errors + diff --git a/src/tools/clippy/tests/ui/let_unit.fixed b/src/tools/clippy/tests/ui/let_unit.fixed new file mode 100644 index 00000000000..f398edc23cb --- /dev/null +++ b/src/tools/clippy/tests/ui/let_unit.fixed @@ -0,0 +1,63 @@ +// run-rustfix + +#![warn(clippy::let_unit_value)] +#![allow(clippy::no_effect)] +#![allow(unused_variables)] + +macro_rules! let_and_return { + ($n:expr) => {{ + let ret = $n; + }}; +} + +fn main() { + println!("x"); + let _y = 1; // this is fine + let _z = ((), 1); // this as well + if true { + (); + } + + consume_units_with_for_loop(); // should be fine as well + + multiline_sugg(); + + let_and_return!(()) // should be fine +} + +// Related to issue #1964 +fn consume_units_with_for_loop() { + // `for_let_unit` lint should not be triggered by consuming them using for loop. + let v = vec![(), (), ()]; + let mut count = 0; + for _ in v { + count += 1; + } + assert_eq!(count, 3); + + // Same for consuming from some other Iterator<Item = ()>. + let (tx, rx) = ::std::sync::mpsc::channel(); + tx.send(()).unwrap(); + drop(tx); + + count = 0; + for _ in rx.iter() { + count += 1; + } + assert_eq!(count, 1); +} + +fn multiline_sugg() { + let v: Vec<u8> = vec![2]; + + v + .into_iter() + .map(|i| i * 2) + .filter(|i| i % 2 == 0) + .map(|_| ()) + .next() + .unwrap(); +} + +#[derive(Copy, Clone)] +pub struct ContainsUnit(()); // should be fine diff --git a/src/tools/clippy/tests/ui/let_unit.rs b/src/tools/clippy/tests/ui/let_unit.rs new file mode 100644 index 00000000000..af5b1fb2ac7 --- /dev/null +++ b/src/tools/clippy/tests/ui/let_unit.rs @@ -0,0 +1,63 @@ +// run-rustfix + +#![warn(clippy::let_unit_value)] +#![allow(clippy::no_effect)] +#![allow(unused_variables)] + +macro_rules! let_and_return { + ($n:expr) => {{ + let ret = $n; + }}; +} + +fn main() { + let _x = println!("x"); + let _y = 1; // this is fine + let _z = ((), 1); // this as well + if true { + let _a = (); + } + + consume_units_with_for_loop(); // should be fine as well + + multiline_sugg(); + + let_and_return!(()) // should be fine +} + +// Related to issue #1964 +fn consume_units_with_for_loop() { + // `for_let_unit` lint should not be triggered by consuming them using for loop. + let v = vec![(), (), ()]; + let mut count = 0; + for _ in v { + count += 1; + } + assert_eq!(count, 3); + + // Same for consuming from some other Iterator<Item = ()>. + let (tx, rx) = ::std::sync::mpsc::channel(); + tx.send(()).unwrap(); + drop(tx); + + count = 0; + for _ in rx.iter() { + count += 1; + } + assert_eq!(count, 1); +} + +fn multiline_sugg() { + let v: Vec<u8> = vec![2]; + + let _ = v + .into_iter() + .map(|i| i * 2) + .filter(|i| i % 2 == 0) + .map(|_| ()) + .next() + .unwrap(); +} + +#[derive(Copy, Clone)] +pub struct ContainsUnit(()); // should be fine diff --git a/src/tools/clippy/tests/ui/let_unit.stderr b/src/tools/clippy/tests/ui/let_unit.stderr new file mode 100644 index 00000000000..eb8482087bc --- /dev/null +++ b/src/tools/clippy/tests/ui/let_unit.stderr @@ -0,0 +1,38 @@ +error: this let-binding has unit value + --> $DIR/let_unit.rs:14:5 + | +LL | let _x = println!("x"); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `println!("x");` + | + = note: `-D clippy::let-unit-value` implied by `-D warnings` + +error: this let-binding has unit value + --> $DIR/let_unit.rs:18:9 + | +LL | let _a = (); + | ^^^^^^^^^^^^ help: omit the `let` binding: `();` + +error: this let-binding has unit value + --> $DIR/let_unit.rs:53:5 + | +LL | / let _ = v +LL | | .into_iter() +LL | | .map(|i| i * 2) +LL | | .filter(|i| i % 2 == 0) +LL | | .map(|_| ()) +LL | | .next() +LL | | .unwrap(); + | |__________________^ + | +help: omit the `let` binding + | +LL | v +LL | .into_iter() +LL | .map(|i| i * 2) +LL | .filter(|i| i % 2 == 0) +LL | .map(|_| ()) +LL | .next() + ... + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/lint_without_lint_pass.rs b/src/tools/clippy/tests/ui/lint_without_lint_pass.rs new file mode 100644 index 00000000000..beaef79a340 --- /dev/null +++ b/src/tools/clippy/tests/ui/lint_without_lint_pass.rs @@ -0,0 +1,44 @@ +#![deny(clippy::internal)] +#![feature(rustc_private)] + +#[macro_use] +extern crate rustc_middle; +#[macro_use] +extern crate rustc_session; +extern crate rustc_lint; +use rustc_lint::LintPass; + +declare_tool_lint! { + pub clippy::TEST_LINT, + Warn, + "", + report_in_external_macro: true +} + +declare_tool_lint! { + pub clippy::TEST_LINT_REGISTERED, + Warn, + "", + report_in_external_macro: true +} + +declare_tool_lint! { + pub clippy::TEST_LINT_REGISTERED_ONLY_IMPL, + Warn, + "", + report_in_external_macro: true +} + +pub struct Pass; +impl LintPass for Pass { + fn name(&self) -> &'static str { + "TEST_LINT" + } +} + +declare_lint_pass!(Pass2 => [TEST_LINT_REGISTERED]); + +pub struct Pass3; +impl_lint_pass!(Pass3 => [TEST_LINT_REGISTERED_ONLY_IMPL]); + +fn main() {} diff --git a/src/tools/clippy/tests/ui/lint_without_lint_pass.stderr b/src/tools/clippy/tests/ui/lint_without_lint_pass.stderr new file mode 100644 index 00000000000..1257dae96d7 --- /dev/null +++ b/src/tools/clippy/tests/ui/lint_without_lint_pass.stderr @@ -0,0 +1,21 @@ +error: the lint `TEST_LINT` is not added to any `LintPass` + --> $DIR/lint_without_lint_pass.rs:11:1 + | +LL | / declare_tool_lint! { +LL | | pub clippy::TEST_LINT, +LL | | Warn, +LL | | "", +LL | | report_in_external_macro: true +LL | | } + | |_^ + | +note: the lint level is defined here + --> $DIR/lint_without_lint_pass.rs:1:9 + | +LL | #![deny(clippy::internal)] + | ^^^^^^^^^^^^^^^^ + = note: `#[deny(clippy::lint_without_lint_pass)]` implied by `#[deny(clippy::internal)]` + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/literals.rs b/src/tools/clippy/tests/ui/literals.rs new file mode 100644 index 00000000000..c299b16c8ce --- /dev/null +++ b/src/tools/clippy/tests/ui/literals.rs @@ -0,0 +1,36 @@ +// does not test any rustfixable lints + +#![warn(clippy::mixed_case_hex_literals)] +#![warn(clippy::zero_prefixed_literal)] +#![allow(clippy::unseparated_literal_suffix)] +#![allow(dead_code)] + +fn main() { + let ok1 = 0xABCD; + let ok3 = 0xab_cd; + let ok4 = 0xab_cd_i32; + let ok5 = 0xAB_CD_u32; + let ok5 = 0xAB_CD_isize; + let fail1 = 0xabCD; + let fail2 = 0xabCD_u32; + let fail2 = 0xabCD_isize; + let fail_multi_zero = 000_123usize; + + let ok9 = 0; + let ok10 = 0_i64; + let fail8 = 0123; + + let ok11 = 0o123; + let ok12 = 0b10_1010; + + let ok13 = 0xab_abcd; + let ok14 = 0xBAFE_BAFE; + let ok15 = 0xab_cabc_abca_bcab_cabc; + let ok16 = 0xFE_BAFE_ABAB_ABCD; + let ok17 = 0x123_4567_8901_usize; + let ok18 = 0xF; + + let fail19 = 12_3456_21; + let fail22 = 3__4___23; + let fail23 = 3__16___23; +} diff --git a/src/tools/clippy/tests/ui/literals.stderr b/src/tools/clippy/tests/ui/literals.stderr new file mode 100644 index 00000000000..0b3af2d8bc3 --- /dev/null +++ b/src/tools/clippy/tests/ui/literals.stderr @@ -0,0 +1,73 @@ +error: inconsistent casing in hexadecimal literal + --> $DIR/literals.rs:14:17 + | +LL | let fail1 = 0xabCD; + | ^^^^^^ + | + = note: `-D clippy::mixed-case-hex-literals` implied by `-D warnings` + +error: inconsistent casing in hexadecimal literal + --> $DIR/literals.rs:15:17 + | +LL | let fail2 = 0xabCD_u32; + | ^^^^^^^^^^ + +error: inconsistent casing in hexadecimal literal + --> $DIR/literals.rs:16:17 + | +LL | let fail2 = 0xabCD_isize; + | ^^^^^^^^^^^^ + +error: this is a decimal constant + --> $DIR/literals.rs:17:27 + | +LL | let fail_multi_zero = 000_123usize; + | ^^^^^^^^^^^^ + | + = note: `-D clippy::zero-prefixed-literal` implied by `-D warnings` +help: if you mean to use a decimal constant, remove the `0` to avoid confusion + | +LL | let fail_multi_zero = 123usize; + | ^^^^^^^^ +help: if you mean to use an octal constant, use `0o` + | +LL | let fail_multi_zero = 0o123usize; + | ^^^^^^^^^^ + +error: this is a decimal constant + --> $DIR/literals.rs:21:17 + | +LL | let fail8 = 0123; + | ^^^^ + | +help: if you mean to use a decimal constant, remove the `0` to avoid confusion + | +LL | let fail8 = 123; + | ^^^ +help: if you mean to use an octal constant, use `0o` + | +LL | let fail8 = 0o123; + | ^^^^^ + +error: digits grouped inconsistently by underscores + --> $DIR/literals.rs:33:18 + | +LL | let fail19 = 12_3456_21; + | ^^^^^^^^^^ help: consider: `12_345_621` + | + = note: `-D clippy::inconsistent-digit-grouping` implied by `-D warnings` + +error: digits grouped inconsistently by underscores + --> $DIR/literals.rs:34:18 + | +LL | let fail22 = 3__4___23; + | ^^^^^^^^^ help: consider: `3_423` + +error: digits grouped inconsistently by underscores + --> $DIR/literals.rs:35:18 + | +LL | let fail23 = 3__16___23; + | ^^^^^^^^^^ help: consider: `31_623` + +error: aborting due to 8 previous errors + diff --git a/src/tools/clippy/tests/ui/logic_bug.rs b/src/tools/clippy/tests/ui/logic_bug.rs new file mode 100644 index 00000000000..b4163d776e7 --- /dev/null +++ b/src/tools/clippy/tests/ui/logic_bug.rs @@ -0,0 +1,26 @@ +#![allow(unused, clippy::many_single_char_names)] +#![warn(clippy::logic_bug)] + +fn main() { + let a: bool = unimplemented!(); + let b: bool = unimplemented!(); + let c: bool = unimplemented!(); + let d: bool = unimplemented!(); + let e: bool = unimplemented!(); + let _ = a && b || a; + let _ = !(a && b); + let _ = false && a; + // don't lint on cfgs + let _ = cfg!(you_shall_not_not_pass) && a; + let _ = a || !b || !c || !d || !e; + let _ = !(a && b || c); +} + +fn equality_stuff() { + let a: i32 = unimplemented!(); + let b: i32 = unimplemented!(); + let _ = a == b && a != b; + let _ = a < b && a >= b; + let _ = a > b && a <= b; + let _ = a > b && a == b; +} diff --git a/src/tools/clippy/tests/ui/logic_bug.stderr b/src/tools/clippy/tests/ui/logic_bug.stderr new file mode 100644 index 00000000000..8f55e1c8ad8 --- /dev/null +++ b/src/tools/clippy/tests/ui/logic_bug.stderr @@ -0,0 +1,63 @@ +error: this boolean expression contains a logic bug + --> $DIR/logic_bug.rs:10:13 + | +LL | let _ = a && b || a; + | ^^^^^^^^^^^ help: it would look like the following: `a` + | + = note: `-D clippy::logic-bug` implied by `-D warnings` +help: this expression can be optimized out by applying boolean operations to the outer expression + --> $DIR/logic_bug.rs:10:18 + | +LL | let _ = a && b || a; + | ^ + +error: this boolean expression contains a logic bug + --> $DIR/logic_bug.rs:12:13 + | +LL | let _ = false && a; + | ^^^^^^^^^^ help: it would look like the following: `false` + | +help: this expression can be optimized out by applying boolean operations to the outer expression + --> $DIR/logic_bug.rs:12:22 + | +LL | let _ = false && a; + | ^ + +error: this boolean expression contains a logic bug + --> $DIR/logic_bug.rs:22:13 + | +LL | let _ = a == b && a != b; + | ^^^^^^^^^^^^^^^^ help: it would look like the following: `false` + | +help: this expression can be optimized out by applying boolean operations to the outer expression + --> $DIR/logic_bug.rs:22:13 + | +LL | let _ = a == b && a != b; + | ^^^^^^ + +error: this boolean expression contains a logic bug + --> $DIR/logic_bug.rs:23:13 + | +LL | let _ = a < b && a >= b; + | ^^^^^^^^^^^^^^^ help: it would look like the following: `false` + | +help: this expression can be optimized out by applying boolean operations to the outer expression + --> $DIR/logic_bug.rs:23:13 + | +LL | let _ = a < b && a >= b; + | ^^^^^ + +error: this boolean expression contains a logic bug + --> $DIR/logic_bug.rs:24:13 + | +LL | let _ = a > b && a <= b; + | ^^^^^^^^^^^^^^^ help: it would look like the following: `false` + | +help: this expression can be optimized out by applying boolean operations to the outer expression + --> $DIR/logic_bug.rs:24:13 + | +LL | let _ = a > b && a <= b; + | ^^^^^ + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/lossy_float_literal.fixed b/src/tools/clippy/tests/ui/lossy_float_literal.fixed new file mode 100644 index 00000000000..24e372354fc --- /dev/null +++ b/src/tools/clippy/tests/ui/lossy_float_literal.fixed @@ -0,0 +1,35 @@ +// run-rustfix +#![warn(clippy::lossy_float_literal)] + +fn main() { + // Lossy whole-number float literals + let _: f32 = 16_777_216.0; + let _: f32 = 16_777_220.0; + let _: f32 = 16_777_220.0; + let _: f32 = 16_777_220.0; + let _ = 16_777_220_f32; + let _: f32 = -16_777_220.0; + let _: f64 = 9_007_199_254_740_992.0; + let _: f64 = 9_007_199_254_740_992.0; + let _: f64 = 9_007_199_254_740_992.0; + let _ = 9_007_199_254_740_992_f64; + let _: f64 = -9_007_199_254_740_992.0; + + // Lossless whole number float literals + let _: f32 = 16_777_216.0; + let _: f32 = 16_777_218.0; + let _: f32 = 16_777_220.0; + let _: f32 = -16_777_216.0; + let _: f32 = -16_777_220.0; + let _: f64 = 16_777_217.0; + let _: f64 = -16_777_217.0; + let _: f64 = 9_007_199_254_740_992.0; + let _: f64 = -9_007_199_254_740_992.0; + + // Ignored whole number float literals + let _: f32 = 1e25; + let _: f32 = 1E25; + let _: f64 = 1e99; + let _: f64 = 1E99; + let _: f32 = 0.1; +} diff --git a/src/tools/clippy/tests/ui/lossy_float_literal.rs b/src/tools/clippy/tests/ui/lossy_float_literal.rs new file mode 100644 index 00000000000..3dcf98fa0bd --- /dev/null +++ b/src/tools/clippy/tests/ui/lossy_float_literal.rs @@ -0,0 +1,35 @@ +// run-rustfix +#![warn(clippy::lossy_float_literal)] + +fn main() { + // Lossy whole-number float literals + let _: f32 = 16_777_217.0; + let _: f32 = 16_777_219.0; + let _: f32 = 16_777_219.; + let _: f32 = 16_777_219.000; + let _ = 16_777_219f32; + let _: f32 = -16_777_219.0; + let _: f64 = 9_007_199_254_740_993.0; + let _: f64 = 9_007_199_254_740_993.; + let _: f64 = 9_007_199_254_740_993.00; + let _ = 9_007_199_254_740_993f64; + let _: f64 = -9_007_199_254_740_993.0; + + // Lossless whole number float literals + let _: f32 = 16_777_216.0; + let _: f32 = 16_777_218.0; + let _: f32 = 16_777_220.0; + let _: f32 = -16_777_216.0; + let _: f32 = -16_777_220.0; + let _: f64 = 16_777_217.0; + let _: f64 = -16_777_217.0; + let _: f64 = 9_007_199_254_740_992.0; + let _: f64 = -9_007_199_254_740_992.0; + + // Ignored whole number float literals + let _: f32 = 1e25; + let _: f32 = 1E25; + let _: f64 = 1e99; + let _: f64 = 1E99; + let _: f32 = 0.1; +} diff --git a/src/tools/clippy/tests/ui/lossy_float_literal.stderr b/src/tools/clippy/tests/ui/lossy_float_literal.stderr new file mode 100644 index 00000000000..d2193c0c819 --- /dev/null +++ b/src/tools/clippy/tests/ui/lossy_float_literal.stderr @@ -0,0 +1,70 @@ +error: literal cannot be represented as the underlying type without loss of precision + --> $DIR/lossy_float_literal.rs:6:18 + | +LL | let _: f32 = 16_777_217.0; + | ^^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_216.0` + | + = note: `-D clippy::lossy-float-literal` implied by `-D warnings` + +error: literal cannot be represented as the underlying type without loss of precision + --> $DIR/lossy_float_literal.rs:7:18 + | +LL | let _: f32 = 16_777_219.0; + | ^^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_220.0` + +error: literal cannot be represented as the underlying type without loss of precision + --> $DIR/lossy_float_literal.rs:8:18 + | +LL | let _: f32 = 16_777_219.; + | ^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_220.0` + +error: literal cannot be represented as the underlying type without loss of precision + --> $DIR/lossy_float_literal.rs:9:18 + | +LL | let _: f32 = 16_777_219.000; + | ^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_220.0` + +error: literal cannot be represented as the underlying type without loss of precision + --> $DIR/lossy_float_literal.rs:10:13 + | +LL | let _ = 16_777_219f32; + | ^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_220_f32` + +error: literal cannot be represented as the underlying type without loss of precision + --> $DIR/lossy_float_literal.rs:11:19 + | +LL | let _: f32 = -16_777_219.0; + | ^^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_220.0` + +error: literal cannot be represented as the underlying type without loss of precision + --> $DIR/lossy_float_literal.rs:12:18 + | +LL | let _: f64 = 9_007_199_254_740_993.0; + | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `9_007_199_254_740_992.0` + +error: literal cannot be represented as the underlying type without loss of precision + --> $DIR/lossy_float_literal.rs:13:18 + | +LL | let _: f64 = 9_007_199_254_740_993.; + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `9_007_199_254_740_992.0` + +error: literal cannot be represented as the underlying type without loss of precision + --> $DIR/lossy_float_literal.rs:14:18 + | +LL | let _: f64 = 9_007_199_254_740_993.00; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `9_007_199_254_740_992.0` + +error: literal cannot be represented as the underlying type without loss of precision + --> $DIR/lossy_float_literal.rs:15:13 + | +LL | let _ = 9_007_199_254_740_993f64; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `9_007_199_254_740_992_f64` + +error: literal cannot be represented as the underlying type without loss of precision + --> $DIR/lossy_float_literal.rs:16:19 + | +LL | let _: f64 = -9_007_199_254_740_993.0; + | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `9_007_199_254_740_992.0` + +error: aborting due to 11 previous errors + diff --git a/src/tools/clippy/tests/ui/macro_use_imports.rs b/src/tools/clippy/tests/ui/macro_use_imports.rs new file mode 100644 index 00000000000..60c64ee8146 --- /dev/null +++ b/src/tools/clippy/tests/ui/macro_use_imports.rs @@ -0,0 +1,11 @@ +// edition:2018 +#![warn(clippy::macro_use_imports)] + +use std::collections::HashMap; +#[macro_use] +use std::prelude; + +fn main() { + let _ = HashMap::<u8, u8>::new(); + println!(); +} diff --git a/src/tools/clippy/tests/ui/macro_use_imports.stderr b/src/tools/clippy/tests/ui/macro_use_imports.stderr new file mode 100644 index 00000000000..b5e3dbec572 --- /dev/null +++ b/src/tools/clippy/tests/ui/macro_use_imports.stderr @@ -0,0 +1,10 @@ +error: `macro_use` attributes are no longer needed in the Rust 2018 edition + --> $DIR/macro_use_imports.rs:5:1 + | +LL | #[macro_use] + | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use std::prelude::<macro name>` + | + = note: `-D clippy::macro-use-imports` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/manual_memcpy.rs b/src/tools/clippy/tests/ui/manual_memcpy.rs new file mode 100644 index 00000000000..aa347288875 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_memcpy.rs @@ -0,0 +1,110 @@ +#![warn(clippy::needless_range_loop, clippy::manual_memcpy)] + +const LOOP_OFFSET: usize = 5000; + +pub fn manual_copy(src: &[i32], dst: &mut [i32], dst2: &mut [i32]) { + // plain manual memcpy + for i in 0..src.len() { + dst[i] = src[i]; + } + + // dst offset memcpy + for i in 0..src.len() { + dst[i + 10] = src[i]; + } + + // src offset memcpy + for i in 0..src.len() { + dst[i] = src[i + 10]; + } + + // src offset memcpy + for i in 11..src.len() { + dst[i] = src[i - 10]; + } + + // overwrite entire dst + for i in 0..dst.len() { + dst[i] = src[i]; + } + + // manual copy with branch - can't easily convert to memcpy! + for i in 0..src.len() { + dst[i] = src[i]; + if dst[i] > 5 { + break; + } + } + + // multiple copies - suggest two memcpy statements + for i in 10..256 { + dst[i] = src[i - 5]; + dst2[i + 500] = src[i] + } + + // this is a reversal - the copy lint shouldn't be triggered + for i in 10..LOOP_OFFSET { + dst[i + LOOP_OFFSET] = src[LOOP_OFFSET - i]; + } + + let some_var = 5; + // Offset in variable + for i in 10..LOOP_OFFSET { + dst[i + LOOP_OFFSET] = src[i - some_var]; + } + + // Non continuous copy - don't trigger lint + for i in 0..10 { + dst[i + i] = src[i]; + } + + let src_vec = vec![1, 2, 3, 4, 5]; + let mut dst_vec = vec![0, 0, 0, 0, 0]; + + // make sure vectors are supported + for i in 0..src_vec.len() { + dst_vec[i] = src_vec[i]; + } + + // lint should not trigger when either + // source or destination type is not + // slice-like, like DummyStruct + struct DummyStruct(i32); + + impl ::std::ops::Index<usize> for DummyStruct { + type Output = i32; + + fn index(&self, _: usize) -> &i32 { + &self.0 + } + } + + let src = DummyStruct(5); + let mut dst_vec = vec![0; 10]; + + for i in 0..10 { + dst_vec[i] = src[i]; + } + + // Simplify suggestion (issue #3004) + let src = [0, 1, 2, 3, 4]; + let mut dst = [0, 0, 0, 0, 0, 0]; + let from = 1; + + for i in from..from + src.len() { + dst[i] = src[i - from]; + } + + for i in from..from + 3 { + dst[i] = src[i - from]; + } +} + +#[warn(clippy::needless_range_loop, clippy::manual_memcpy)] +pub fn manual_clone(src: &[String], dst: &mut [String]) { + for i in 0..src.len() { + dst[i] = src[i].clone(); + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/manual_memcpy.stderr b/src/tools/clippy/tests/ui/manual_memcpy.stderr new file mode 100644 index 00000000000..3dbb2155d4d --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_memcpy.stderr @@ -0,0 +1,76 @@ +error: it looks like you're manually copying between slices + --> $DIR/manual_memcpy.rs:7:14 + | +LL | for i in 0..src.len() { + | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[..])` + | + = note: `-D clippy::manual-memcpy` implied by `-D warnings` + +error: it looks like you're manually copying between slices + --> $DIR/manual_memcpy.rs:12:14 + | +LL | for i in 0..src.len() { + | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[10..(src.len() + 10)].clone_from_slice(&src[..])` + +error: it looks like you're manually copying between slices + --> $DIR/manual_memcpy.rs:17:14 + | +LL | for i in 0..src.len() { + | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[10..])` + +error: it looks like you're manually copying between slices + --> $DIR/manual_memcpy.rs:22:14 + | +LL | for i in 11..src.len() { + | ^^^^^^^^^^^^^ help: try replacing the loop by: `dst[11..src.len()].clone_from_slice(&src[(11 - 10)..(src.len() - 10)])` + +error: it looks like you're manually copying between slices + --> $DIR/manual_memcpy.rs:27:14 + | +LL | for i in 0..dst.len() { + | ^^^^^^^^^^^^ help: try replacing the loop by: `dst.clone_from_slice(&src[..dst.len()])` + +error: it looks like you're manually copying between slices + --> $DIR/manual_memcpy.rs:40:14 + | +LL | for i in 10..256 { + | ^^^^^^^ + | +help: try replacing the loop by + | +LL | for i in dst[10..256].clone_from_slice(&src[(10 - 5)..(256 - 5)]) +LL | dst2[(10 + 500)..(256 + 500)].clone_from_slice(&src[10..256]) { + | + +error: it looks like you're manually copying between slices + --> $DIR/manual_memcpy.rs:52:14 + | +LL | for i in 10..LOOP_OFFSET { + | ^^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[(10 + LOOP_OFFSET)..(LOOP_OFFSET + LOOP_OFFSET)].clone_from_slice(&src[(10 - some_var)..(LOOP_OFFSET - some_var)])` + +error: it looks like you're manually copying between slices + --> $DIR/manual_memcpy.rs:65:14 + | +LL | for i in 0..src_vec.len() { + | ^^^^^^^^^^^^^^^^ help: try replacing the loop by: `dst_vec[..src_vec.len()].clone_from_slice(&src_vec[..])` + +error: it looks like you're manually copying between slices + --> $DIR/manual_memcpy.rs:94:14 + | +LL | for i in from..from + src.len() { + | ^^^^^^^^^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[from..from + src.len()].clone_from_slice(&src[0..(from + src.len() - from)])` + +error: it looks like you're manually copying between slices + --> $DIR/manual_memcpy.rs:98:14 + | +LL | for i in from..from + 3 { + | ^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[from..from + 3].clone_from_slice(&src[0..(from + 3 - from)])` + +error: it looks like you're manually copying between slices + --> $DIR/manual_memcpy.rs:105:14 + | +LL | for i in 0..src.len() { + | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[..])` + +error: aborting due to 11 previous errors + diff --git a/src/tools/clippy/tests/ui/manual_saturating_arithmetic.fixed b/src/tools/clippy/tests/ui/manual_saturating_arithmetic.fixed new file mode 100644 index 00000000000..c4f53c446c9 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_saturating_arithmetic.fixed @@ -0,0 +1,45 @@ +// run-rustfix + +#![allow(unused_imports)] + +use std::{i128, i32, u128, u32}; + +fn main() { + let _ = 1u32.saturating_add(1); + let _ = 1u32.saturating_add(1); + let _ = 1u8.saturating_add(1); + let _ = 1u128.saturating_add(1); + let _ = 1u32.checked_add(1).unwrap_or(1234); // ok + let _ = 1u8.checked_add(1).unwrap_or(0); // ok + let _ = 1u32.saturating_mul(1); + + let _ = 1u32.saturating_sub(1); + let _ = 1u32.saturating_sub(1); + let _ = 1u8.saturating_sub(1); + let _ = 1u32.checked_sub(1).unwrap_or(1234); // ok + let _ = 1u8.checked_sub(1).unwrap_or(255); // ok + + let _ = 1i32.saturating_add(1); + let _ = 1i32.saturating_add(1); + let _ = 1i8.saturating_add(1); + let _ = 1i128.saturating_add(1); + let _ = 1i32.saturating_add(-1); + let _ = 1i32.saturating_add(-1); + let _ = 1i8.saturating_add(-1); + let _ = 1i128.saturating_add(-1); + let _ = 1i32.checked_add(1).unwrap_or(1234); // ok + let _ = 1i8.checked_add(1).unwrap_or(-128); // ok + let _ = 1i8.checked_add(-1).unwrap_or(127); // ok + + let _ = 1i32.saturating_sub(1); + let _ = 1i32.saturating_sub(1); + let _ = 1i8.saturating_sub(1); + let _ = 1i128.saturating_sub(1); + let _ = 1i32.saturating_sub(-1); + let _ = 1i32.saturating_sub(-1); + let _ = 1i8.saturating_sub(-1); + let _ = 1i128.saturating_sub(-1); + let _ = 1i32.checked_sub(1).unwrap_or(1234); // ok + let _ = 1i8.checked_sub(1).unwrap_or(127); // ok + let _ = 1i8.checked_sub(-1).unwrap_or(-128); // ok +} diff --git a/src/tools/clippy/tests/ui/manual_saturating_arithmetic.rs b/src/tools/clippy/tests/ui/manual_saturating_arithmetic.rs new file mode 100644 index 00000000000..cd83cf6e65e --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_saturating_arithmetic.rs @@ -0,0 +1,55 @@ +// run-rustfix + +#![allow(unused_imports)] + +use std::{i128, i32, u128, u32}; + +fn main() { + let _ = 1u32.checked_add(1).unwrap_or(u32::max_value()); + let _ = 1u32.checked_add(1).unwrap_or(u32::MAX); + let _ = 1u8.checked_add(1).unwrap_or(255); + let _ = 1u128 + .checked_add(1) + .unwrap_or(340_282_366_920_938_463_463_374_607_431_768_211_455); + let _ = 1u32.checked_add(1).unwrap_or(1234); // ok + let _ = 1u8.checked_add(1).unwrap_or(0); // ok + let _ = 1u32.checked_mul(1).unwrap_or(u32::MAX); + + let _ = 1u32.checked_sub(1).unwrap_or(u32::min_value()); + let _ = 1u32.checked_sub(1).unwrap_or(u32::MIN); + let _ = 1u8.checked_sub(1).unwrap_or(0); + let _ = 1u32.checked_sub(1).unwrap_or(1234); // ok + let _ = 1u8.checked_sub(1).unwrap_or(255); // ok + + let _ = 1i32.checked_add(1).unwrap_or(i32::max_value()); + let _ = 1i32.checked_add(1).unwrap_or(i32::MAX); + let _ = 1i8.checked_add(1).unwrap_or(127); + let _ = 1i128 + .checked_add(1) + .unwrap_or(170_141_183_460_469_231_731_687_303_715_884_105_727); + let _ = 1i32.checked_add(-1).unwrap_or(i32::min_value()); + let _ = 1i32.checked_add(-1).unwrap_or(i32::MIN); + let _ = 1i8.checked_add(-1).unwrap_or(-128); + let _ = 1i128 + .checked_add(-1) + .unwrap_or(-170_141_183_460_469_231_731_687_303_715_884_105_728); + let _ = 1i32.checked_add(1).unwrap_or(1234); // ok + let _ = 1i8.checked_add(1).unwrap_or(-128); // ok + let _ = 1i8.checked_add(-1).unwrap_or(127); // ok + + let _ = 1i32.checked_sub(1).unwrap_or(i32::min_value()); + let _ = 1i32.checked_sub(1).unwrap_or(i32::MIN); + let _ = 1i8.checked_sub(1).unwrap_or(-128); + let _ = 1i128 + .checked_sub(1) + .unwrap_or(-170_141_183_460_469_231_731_687_303_715_884_105_728); + let _ = 1i32.checked_sub(-1).unwrap_or(i32::max_value()); + let _ = 1i32.checked_sub(-1).unwrap_or(i32::MAX); + let _ = 1i8.checked_sub(-1).unwrap_or(127); + let _ = 1i128 + .checked_sub(-1) + .unwrap_or(170_141_183_460_469_231_731_687_303_715_884_105_727); + let _ = 1i32.checked_sub(1).unwrap_or(1234); // ok + let _ = 1i8.checked_sub(1).unwrap_or(127); // ok + let _ = 1i8.checked_sub(-1).unwrap_or(-128); // ok +} diff --git a/src/tools/clippy/tests/ui/manual_saturating_arithmetic.stderr b/src/tools/clippy/tests/ui/manual_saturating_arithmetic.stderr new file mode 100644 index 00000000000..d985f2e754b --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_saturating_arithmetic.stderr @@ -0,0 +1,163 @@ +error: manual saturating arithmetic + --> $DIR/manual_saturating_arithmetic.rs:8:13 + | +LL | let _ = 1u32.checked_add(1).unwrap_or(u32::max_value()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1u32.saturating_add(1)` + | + = note: `-D clippy::manual-saturating-arithmetic` implied by `-D warnings` + +error: manual saturating arithmetic + --> $DIR/manual_saturating_arithmetic.rs:9:13 + | +LL | let _ = 1u32.checked_add(1).unwrap_or(u32::MAX); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1u32.saturating_add(1)` + +error: manual saturating arithmetic + --> $DIR/manual_saturating_arithmetic.rs:10:13 + | +LL | let _ = 1u8.checked_add(1).unwrap_or(255); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1u8.saturating_add(1)` + +error: manual saturating arithmetic + --> $DIR/manual_saturating_arithmetic.rs:11:13 + | +LL | let _ = 1u128 + | _____________^ +LL | | .checked_add(1) +LL | | .unwrap_or(340_282_366_920_938_463_463_374_607_431_768_211_455); + | |_______________________________________________________________________^ help: try using `saturating_add`: `1u128.saturating_add(1)` + +error: manual saturating arithmetic + --> $DIR/manual_saturating_arithmetic.rs:16:13 + | +LL | let _ = 1u32.checked_mul(1).unwrap_or(u32::MAX); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_mul`: `1u32.saturating_mul(1)` + +error: manual saturating arithmetic + --> $DIR/manual_saturating_arithmetic.rs:18:13 + | +LL | let _ = 1u32.checked_sub(1).unwrap_or(u32::min_value()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1u32.saturating_sub(1)` + +error: manual saturating arithmetic + --> $DIR/manual_saturating_arithmetic.rs:19:13 + | +LL | let _ = 1u32.checked_sub(1).unwrap_or(u32::MIN); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1u32.saturating_sub(1)` + +error: manual saturating arithmetic + --> $DIR/manual_saturating_arithmetic.rs:20:13 + | +LL | let _ = 1u8.checked_sub(1).unwrap_or(0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1u8.saturating_sub(1)` + +error: manual saturating arithmetic + --> $DIR/manual_saturating_arithmetic.rs:24:13 + | +LL | let _ = 1i32.checked_add(1).unwrap_or(i32::max_value()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1i32.saturating_add(1)` + +error: manual saturating arithmetic + --> $DIR/manual_saturating_arithmetic.rs:25:13 + | +LL | let _ = 1i32.checked_add(1).unwrap_or(i32::MAX); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1i32.saturating_add(1)` + +error: manual saturating arithmetic + --> $DIR/manual_saturating_arithmetic.rs:26:13 + | +LL | let _ = 1i8.checked_add(1).unwrap_or(127); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1i8.saturating_add(1)` + +error: manual saturating arithmetic + --> $DIR/manual_saturating_arithmetic.rs:27:13 + | +LL | let _ = 1i128 + | _____________^ +LL | | .checked_add(1) +LL | | .unwrap_or(170_141_183_460_469_231_731_687_303_715_884_105_727); + | |_______________________________________________________________________^ help: try using `saturating_add`: `1i128.saturating_add(1)` + +error: manual saturating arithmetic + --> $DIR/manual_saturating_arithmetic.rs:30:13 + | +LL | let _ = 1i32.checked_add(-1).unwrap_or(i32::min_value()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1i32.saturating_add(-1)` + +error: manual saturating arithmetic + --> $DIR/manual_saturating_arithmetic.rs:31:13 + | +LL | let _ = 1i32.checked_add(-1).unwrap_or(i32::MIN); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1i32.saturating_add(-1)` + +error: manual saturating arithmetic + --> $DIR/manual_saturating_arithmetic.rs:32:13 + | +LL | let _ = 1i8.checked_add(-1).unwrap_or(-128); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_add`: `1i8.saturating_add(-1)` + +error: manual saturating arithmetic + --> $DIR/manual_saturating_arithmetic.rs:33:13 + | +LL | let _ = 1i128 + | _____________^ +LL | | .checked_add(-1) +LL | | .unwrap_or(-170_141_183_460_469_231_731_687_303_715_884_105_728); + | |________________________________________________________________________^ help: try using `saturating_add`: `1i128.saturating_add(-1)` + +error: manual saturating arithmetic + --> $DIR/manual_saturating_arithmetic.rs:40:13 + | +LL | let _ = 1i32.checked_sub(1).unwrap_or(i32::min_value()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1i32.saturating_sub(1)` + +error: manual saturating arithmetic + --> $DIR/manual_saturating_arithmetic.rs:41:13 + | +LL | let _ = 1i32.checked_sub(1).unwrap_or(i32::MIN); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1i32.saturating_sub(1)` + +error: manual saturating arithmetic + --> $DIR/manual_saturating_arithmetic.rs:42:13 + | +LL | let _ = 1i8.checked_sub(1).unwrap_or(-128); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1i8.saturating_sub(1)` + +error: manual saturating arithmetic + --> $DIR/manual_saturating_arithmetic.rs:43:13 + | +LL | let _ = 1i128 + | _____________^ +LL | | .checked_sub(1) +LL | | .unwrap_or(-170_141_183_460_469_231_731_687_303_715_884_105_728); + | |________________________________________________________________________^ help: try using `saturating_sub`: `1i128.saturating_sub(1)` + +error: manual saturating arithmetic + --> $DIR/manual_saturating_arithmetic.rs:46:13 + | +LL | let _ = 1i32.checked_sub(-1).unwrap_or(i32::max_value()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1i32.saturating_sub(-1)` + +error: manual saturating arithmetic + --> $DIR/manual_saturating_arithmetic.rs:47:13 + | +LL | let _ = 1i32.checked_sub(-1).unwrap_or(i32::MAX); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1i32.saturating_sub(-1)` + +error: manual saturating arithmetic + --> $DIR/manual_saturating_arithmetic.rs:48:13 + | +LL | let _ = 1i8.checked_sub(-1).unwrap_or(127); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `saturating_sub`: `1i8.saturating_sub(-1)` + +error: manual saturating arithmetic + --> $DIR/manual_saturating_arithmetic.rs:49:13 + | +LL | let _ = 1i128 + | _____________^ +LL | | .checked_sub(-1) +LL | | .unwrap_or(170_141_183_460_469_231_731_687_303_715_884_105_727); + | |_______________________________________________________________________^ help: try using `saturating_sub`: `1i128.saturating_sub(-1)` + +error: aborting due to 24 previous errors + diff --git a/src/tools/clippy/tests/ui/many_single_char_names.rs b/src/tools/clippy/tests/ui/many_single_char_names.rs new file mode 100644 index 00000000000..80800e48724 --- /dev/null +++ b/src/tools/clippy/tests/ui/many_single_char_names.rs @@ -0,0 +1,73 @@ +#[warn(clippy::many_single_char_names)] + +fn bla() { + let a: i32; + let (b, c, d): (i32, i64, i16); + { + { + let cdefg: i32; + let blar: i32; + } + { + let e: i32; + } + { + let e: i32; + let f: i32; + } + match 5 { + 1 => println!(), + e => panic!(), + } + match 5 { + 1 => println!(), + _ => panic!(), + } + } +} + +fn bindings(a: i32, b: i32, c: i32, d: i32, e: i32, f: i32, g: i32, h: i32) {} + +fn bindings2() { + let (a, b, c, d, e, f, g, h): (bool, bool, bool, bool, bool, bool, bool, bool) = unimplemented!(); +} + +fn shadowing() { + let a = 0i32; + let a = 0i32; + let a = 0i32; + let a = 0i32; + let a = 0i32; + let a = 0i32; + { + let a = 0i32; + } +} + +fn patterns() { + enum Z { + A(i32), + B(i32), + C(i32), + D(i32), + E(i32), + F(i32), + } + + // These should not trigger a warning, since the pattern bindings are a new scope. + match Z::A(0) { + Z::A(a) => {}, + Z::B(b) => {}, + Z::C(c) => {}, + Z::D(d) => {}, + Z::E(e) => {}, + Z::F(f) => {}, + } +} + +#[allow(clippy::many_single_char_names)] +fn issue_3198_allow_works() { + let (a, b, c, d, e) = (0, 0, 0, 0, 0); +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/many_single_char_names.stderr b/src/tools/clippy/tests/ui/many_single_char_names.stderr new file mode 100644 index 00000000000..27e62e641ad --- /dev/null +++ b/src/tools/clippy/tests/ui/many_single_char_names.stderr @@ -0,0 +1,51 @@ +error: 5 bindings with single-character names in scope + --> $DIR/many_single_char_names.rs:4:9 + | +LL | let a: i32; + | ^ +LL | let (b, c, d): (i32, i64, i16); + | ^ ^ ^ +... +LL | let e: i32; + | ^ + | + = note: `-D clippy::many-single-char-names` implied by `-D warnings` + +error: 6 bindings with single-character names in scope + --> $DIR/many_single_char_names.rs:4:9 + | +LL | let a: i32; + | ^ +LL | let (b, c, d): (i32, i64, i16); + | ^ ^ ^ +... +LL | let e: i32; + | ^ +LL | let f: i32; + | ^ + +error: 5 bindings with single-character names in scope + --> $DIR/many_single_char_names.rs:4:9 + | +LL | let a: i32; + | ^ +LL | let (b, c, d): (i32, i64, i16); + | ^ ^ ^ +... +LL | e => panic!(), + | ^ + +error: 8 bindings with single-character names in scope + --> $DIR/many_single_char_names.rs:29:13 + | +LL | fn bindings(a: i32, b: i32, c: i32, d: i32, e: i32, f: i32, g: i32, h: i32) {} + | ^ ^ ^ ^ ^ ^ ^ ^ + +error: 8 bindings with single-character names in scope + --> $DIR/many_single_char_names.rs:32:10 + | +LL | let (a, b, c, d, e, f, g, h): (bool, bool, bool, bool, bool, bool, bool, bool) = unimplemented!(); + | ^ ^ ^ ^ ^ ^ ^ ^ + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/map_clone.fixed b/src/tools/clippy/tests/ui/map_clone.fixed new file mode 100644 index 00000000000..81c7f659efb --- /dev/null +++ b/src/tools/clippy/tests/ui/map_clone.fixed @@ -0,0 +1,47 @@ +// run-rustfix +#![warn(clippy::all, clippy::pedantic)] +#![allow(clippy::iter_cloned_collect)] +#![allow(clippy::clone_on_copy, clippy::redundant_clone)] +#![allow(clippy::missing_docs_in_private_items)] +#![allow(clippy::redundant_closure_for_method_calls)] +#![allow(clippy::many_single_char_names)] + +fn main() { + let _: Vec<i8> = vec![5_i8; 6].iter().copied().collect(); + let _: Vec<String> = vec![String::new()].iter().cloned().collect(); + let _: Vec<u32> = vec![42, 43].iter().copied().collect(); + let _: Option<u64> = Some(Box::new(16)).map(|b| *b); + let _: Option<u64> = Some(&16).copied(); + let _: Option<u8> = Some(&1).copied(); + + // Don't lint these + let v = vec![5_i8; 6]; + let a = 0; + let b = &a; + let _ = v.iter().map(|_x| *b); + let _ = v.iter().map(|_x| a.clone()); + let _ = v.iter().map(|&_x| a); + + // Issue #498 + let _ = std::env::args(); + + // Issue #4824 item types that aren't references + { + use std::rc::Rc; + + let o: Option<Rc<u32>> = Some(Rc::new(0_u32)); + let _: Option<u32> = o.map(|x| *x); + let v: Vec<Rc<u32>> = vec![Rc::new(0_u32)]; + let _: Vec<u32> = v.into_iter().map(|x| *x).collect(); + } + + // Issue #5524 mutable references + { + let mut c = 42; + let v = vec![&mut c]; + let _: Vec<u32> = v.into_iter().map(|x| *x).collect(); + let mut d = 21; + let v = vec![&mut d]; + let _: Vec<u32> = v.into_iter().map(|&mut x| x).collect(); + } +} diff --git a/src/tools/clippy/tests/ui/map_clone.rs b/src/tools/clippy/tests/ui/map_clone.rs new file mode 100644 index 00000000000..8ed164f0ed5 --- /dev/null +++ b/src/tools/clippy/tests/ui/map_clone.rs @@ -0,0 +1,47 @@ +// run-rustfix +#![warn(clippy::all, clippy::pedantic)] +#![allow(clippy::iter_cloned_collect)] +#![allow(clippy::clone_on_copy, clippy::redundant_clone)] +#![allow(clippy::missing_docs_in_private_items)] +#![allow(clippy::redundant_closure_for_method_calls)] +#![allow(clippy::many_single_char_names)] + +fn main() { + let _: Vec<i8> = vec![5_i8; 6].iter().map(|x| *x).collect(); + let _: Vec<String> = vec![String::new()].iter().map(|x| x.clone()).collect(); + let _: Vec<u32> = vec![42, 43].iter().map(|&x| x).collect(); + let _: Option<u64> = Some(Box::new(16)).map(|b| *b); + let _: Option<u64> = Some(&16).map(|b| *b); + let _: Option<u8> = Some(&1).map(|x| x.clone()); + + // Don't lint these + let v = vec![5_i8; 6]; + let a = 0; + let b = &a; + let _ = v.iter().map(|_x| *b); + let _ = v.iter().map(|_x| a.clone()); + let _ = v.iter().map(|&_x| a); + + // Issue #498 + let _ = std::env::args().map(|v| v.clone()); + + // Issue #4824 item types that aren't references + { + use std::rc::Rc; + + let o: Option<Rc<u32>> = Some(Rc::new(0_u32)); + let _: Option<u32> = o.map(|x| *x); + let v: Vec<Rc<u32>> = vec![Rc::new(0_u32)]; + let _: Vec<u32> = v.into_iter().map(|x| *x).collect(); + } + + // Issue #5524 mutable references + { + let mut c = 42; + let v = vec![&mut c]; + let _: Vec<u32> = v.into_iter().map(|x| *x).collect(); + let mut d = 21; + let v = vec![&mut d]; + let _: Vec<u32> = v.into_iter().map(|&mut x| x).collect(); + } +} diff --git a/src/tools/clippy/tests/ui/map_clone.stderr b/src/tools/clippy/tests/ui/map_clone.stderr new file mode 100644 index 00000000000..9eec6928e8c --- /dev/null +++ b/src/tools/clippy/tests/ui/map_clone.stderr @@ -0,0 +1,40 @@ +error: You are using an explicit closure for copying elements + --> $DIR/map_clone.rs:10:22 + | +LL | let _: Vec<i8> = vec![5_i8; 6].iter().map(|x| *x).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider calling the dedicated `copied` method: `vec![5_i8; 6].iter().copied()` + | + = note: `-D clippy::map-clone` implied by `-D warnings` + +error: You are using an explicit closure for cloning elements + --> $DIR/map_clone.rs:11:26 + | +LL | let _: Vec<String> = vec![String::new()].iter().map(|x| x.clone()).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider calling the dedicated `cloned` method: `vec![String::new()].iter().cloned()` + +error: You are using an explicit closure for copying elements + --> $DIR/map_clone.rs:12:23 + | +LL | let _: Vec<u32> = vec![42, 43].iter().map(|&x| x).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider calling the dedicated `copied` method: `vec![42, 43].iter().copied()` + +error: You are using an explicit closure for copying elements + --> $DIR/map_clone.rs:14:26 + | +LL | let _: Option<u64> = Some(&16).map(|b| *b); + | ^^^^^^^^^^^^^^^^^^^^^ help: Consider calling the dedicated `copied` method: `Some(&16).copied()` + +error: You are using an explicit closure for copying elements + --> $DIR/map_clone.rs:15:25 + | +LL | let _: Option<u8> = Some(&1).map(|x| x.clone()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider calling the dedicated `copied` method: `Some(&1).copied()` + +error: You are needlessly cloning iterator elements + --> $DIR/map_clone.rs:26:29 + | +LL | let _ = std::env::args().map(|v| v.clone()); + | ^^^^^^^^^^^^^^^^^^^ help: Remove the `map` call + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/map_flatten.fixed b/src/tools/clippy/tests/ui/map_flatten.fixed new file mode 100644 index 00000000000..7ac368878ab --- /dev/null +++ b/src/tools/clippy/tests/ui/map_flatten.fixed @@ -0,0 +1,9 @@ +// run-rustfix + +#![warn(clippy::all, clippy::pedantic)] +#![allow(clippy::missing_docs_in_private_items)] + +fn main() { + let _: Vec<_> = vec![5_i8; 6].into_iter().flat_map(|x| 0..x).collect(); + let _: Option<_> = (Some(Some(1))).and_then(|x| x); +} diff --git a/src/tools/clippy/tests/ui/map_flatten.rs b/src/tools/clippy/tests/ui/map_flatten.rs new file mode 100644 index 00000000000..a608601039c --- /dev/null +++ b/src/tools/clippy/tests/ui/map_flatten.rs @@ -0,0 +1,9 @@ +// run-rustfix + +#![warn(clippy::all, clippy::pedantic)] +#![allow(clippy::missing_docs_in_private_items)] + +fn main() { + let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect(); + let _: Option<_> = (Some(Some(1))).map(|x| x).flatten(); +} diff --git a/src/tools/clippy/tests/ui/map_flatten.stderr b/src/tools/clippy/tests/ui/map_flatten.stderr new file mode 100644 index 00000000000..3cf2abd5b6d --- /dev/null +++ b/src/tools/clippy/tests/ui/map_flatten.stderr @@ -0,0 +1,16 @@ +error: called `map(..).flatten()` on an `Iterator`. This is more succinctly expressed by calling `.flat_map(..)` + --> $DIR/map_flatten.rs:7:21 + | +LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `flat_map` instead: `vec![5_i8; 6].into_iter().flat_map(|x| 0..x)` + | + = note: `-D clippy::map-flatten` implied by `-D warnings` + +error: called `map(..).flatten()` on an `Option`. This is more succinctly expressed by calling `.and_then(..)` + --> $DIR/map_flatten.rs:8:24 + | +LL | let _: Option<_> = (Some(Some(1))).map(|x| x).flatten(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `and_then` instead: `(Some(Some(1))).and_then(|x| x)` + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/map_unit_fn.rs b/src/tools/clippy/tests/ui/map_unit_fn.rs new file mode 100644 index 00000000000..9a74da4e3b8 --- /dev/null +++ b/src/tools/clippy/tests/ui/map_unit_fn.rs @@ -0,0 +1,11 @@ +#![allow(unused)] +struct Mappable {} + +impl Mappable { + pub fn map(&self) {} +} + +fn main() { + let m = Mappable {}; + m.map(); +} diff --git a/src/tools/clippy/tests/ui/match_as_ref.fixed b/src/tools/clippy/tests/ui/match_as_ref.fixed new file mode 100644 index 00000000000..c61eb921664 --- /dev/null +++ b/src/tools/clippy/tests/ui/match_as_ref.fixed @@ -0,0 +1,35 @@ +// run-rustfix + +#![allow(unused)] +#![warn(clippy::match_as_ref)] + +fn match_as_ref() { + let owned: Option<()> = None; + let borrowed: Option<&()> = owned.as_ref(); + + let mut mut_owned: Option<()> = None; + let borrow_mut: Option<&mut ()> = mut_owned.as_mut(); +} + +mod issue4437 { + use std::{error::Error, fmt, num::ParseIntError}; + + #[derive(Debug)] + struct E { + source: Option<ParseIntError>, + } + + impl Error for E { + fn source(&self) -> Option<&(dyn Error + 'static)> { + self.source.as_ref().map(|x| x as _) + } + } + + impl fmt::Display for E { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + unimplemented!() + } + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/match_as_ref.rs b/src/tools/clippy/tests/ui/match_as_ref.rs new file mode 100644 index 00000000000..2fbd0b255fa --- /dev/null +++ b/src/tools/clippy/tests/ui/match_as_ref.rs @@ -0,0 +1,44 @@ +// run-rustfix + +#![allow(unused)] +#![warn(clippy::match_as_ref)] + +fn match_as_ref() { + let owned: Option<()> = None; + let borrowed: Option<&()> = match owned { + None => None, + Some(ref v) => Some(v), + }; + + let mut mut_owned: Option<()> = None; + let borrow_mut: Option<&mut ()> = match mut_owned { + None => None, + Some(ref mut v) => Some(v), + }; +} + +mod issue4437 { + use std::{error::Error, fmt, num::ParseIntError}; + + #[derive(Debug)] + struct E { + source: Option<ParseIntError>, + } + + impl Error for E { + fn source(&self) -> Option<&(dyn Error + 'static)> { + match self.source { + Some(ref s) => Some(s), + None => None, + } + } + } + + impl fmt::Display for E { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + unimplemented!() + } + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/match_as_ref.stderr b/src/tools/clippy/tests/ui/match_as_ref.stderr new file mode 100644 index 00000000000..c3b62849cb3 --- /dev/null +++ b/src/tools/clippy/tests/ui/match_as_ref.stderr @@ -0,0 +1,33 @@ +error: use `as_ref()` instead + --> $DIR/match_as_ref.rs:8:33 + | +LL | let borrowed: Option<&()> = match owned { + | _________________________________^ +LL | | None => None, +LL | | Some(ref v) => Some(v), +LL | | }; + | |_____^ help: try this: `owned.as_ref()` + | + = note: `-D clippy::match-as-ref` implied by `-D warnings` + +error: use `as_mut()` instead + --> $DIR/match_as_ref.rs:14:39 + | +LL | let borrow_mut: Option<&mut ()> = match mut_owned { + | _______________________________________^ +LL | | None => None, +LL | | Some(ref mut v) => Some(v), +LL | | }; + | |_____^ help: try this: `mut_owned.as_mut()` + +error: use `as_ref()` instead + --> $DIR/match_as_ref.rs:30:13 + | +LL | / match self.source { +LL | | Some(ref s) => Some(s), +LL | | None => None, +LL | | } + | |_____________^ help: try this: `self.source.as_ref().map(|x| x as _)` + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/match_bool.rs b/src/tools/clippy/tests/ui/match_bool.rs new file mode 100644 index 00000000000..9ed55ca7ae7 --- /dev/null +++ b/src/tools/clippy/tests/ui/match_bool.rs @@ -0,0 +1,55 @@ +#![deny(clippy::match_bool)] + +fn match_bool() { + let test: bool = true; + + match test { + true => 0, + false => 42, + }; + + let option = 1; + match option == 1 { + true => 1, + false => 0, + }; + + match test { + true => (), + false => { + println!("Noooo!"); + }, + }; + + match test { + false => { + println!("Noooo!"); + }, + _ => (), + }; + + match test && test { + false => { + println!("Noooo!"); + }, + _ => (), + }; + + match test { + false => { + println!("Noooo!"); + }, + true => { + println!("Yes!"); + }, + }; + + // Not linted + match option { + 1..=10 => 1, + 11..=20 => 2, + _ => 3, + }; +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/match_bool.stderr b/src/tools/clippy/tests/ui/match_bool.stderr new file mode 100644 index 00000000000..1ad78c740c6 --- /dev/null +++ b/src/tools/clippy/tests/ui/match_bool.stderr @@ -0,0 +1,117 @@ +error: this boolean expression can be simplified + --> $DIR/match_bool.rs:31:11 + | +LL | match test && test { + | ^^^^^^^^^^^^ help: try: `test` + | + = note: `-D clippy::nonminimal-bool` implied by `-D warnings` + +error: you seem to be trying to match on a boolean expression + --> $DIR/match_bool.rs:6:5 + | +LL | / match test { +LL | | true => 0, +LL | | false => 42, +LL | | }; + | |_____^ help: consider using an `if`/`else` expression: `if test { 0 } else { 42 }` + | +note: the lint level is defined here + --> $DIR/match_bool.rs:1:9 + | +LL | #![deny(clippy::match_bool)] + | ^^^^^^^^^^^^^^^^^^ + +error: you seem to be trying to match on a boolean expression + --> $DIR/match_bool.rs:12:5 + | +LL | / match option == 1 { +LL | | true => 1, +LL | | false => 0, +LL | | }; + | |_____^ help: consider using an `if`/`else` expression: `if option == 1 { 1 } else { 0 }` + +error: you seem to be trying to match on a boolean expression + --> $DIR/match_bool.rs:17:5 + | +LL | / match test { +LL | | true => (), +LL | | false => { +LL | | println!("Noooo!"); +LL | | }, +LL | | }; + | |_____^ + | +help: consider using an `if`/`else` expression + | +LL | if !test { +LL | println!("Noooo!"); +LL | }; + | + +error: you seem to be trying to match on a boolean expression + --> $DIR/match_bool.rs:24:5 + | +LL | / match test { +LL | | false => { +LL | | println!("Noooo!"); +LL | | }, +LL | | _ => (), +LL | | }; + | |_____^ + | +help: consider using an `if`/`else` expression + | +LL | if !test { +LL | println!("Noooo!"); +LL | }; + | + +error: you seem to be trying to match on a boolean expression + --> $DIR/match_bool.rs:31:5 + | +LL | / match test && test { +LL | | false => { +LL | | println!("Noooo!"); +LL | | }, +LL | | _ => (), +LL | | }; + | |_____^ + | +help: consider using an `if`/`else` expression + | +LL | if !(test && test) { +LL | println!("Noooo!"); +LL | }; + | + +error: equal expressions as operands to `&&` + --> $DIR/match_bool.rs:31:11 + | +LL | match test && test { + | ^^^^^^^^^^^^ + | + = note: `#[deny(clippy::eq_op)]` on by default + +error: you seem to be trying to match on a boolean expression + --> $DIR/match_bool.rs:38:5 + | +LL | / match test { +LL | | false => { +LL | | println!("Noooo!"); +LL | | }, +... | +LL | | }, +LL | | }; + | |_____^ + | +help: consider using an `if`/`else` expression + | +LL | if test { +LL | println!("Yes!"); +LL | } else { +LL | println!("Noooo!"); +LL | }; + | + +error: aborting due to 8 previous errors + diff --git a/src/tools/clippy/tests/ui/match_on_vec_items.rs b/src/tools/clippy/tests/ui/match_on_vec_items.rs new file mode 100644 index 00000000000..0bb39d77e46 --- /dev/null +++ b/src/tools/clippy/tests/ui/match_on_vec_items.rs @@ -0,0 +1,130 @@ +#![warn(clippy::match_on_vec_items)] + +fn match_with_wildcard() { + let arr = vec![0, 1, 2, 3]; + let range = 1..3; + let idx = 1; + + // Lint, may panic + match arr[idx] { + 0 => println!("0"), + 1 => println!("1"), + _ => {}, + } + + // Lint, may panic + match arr[range] { + [0, 1] => println!("0 1"), + [1, 2] => println!("1 2"), + _ => {}, + } +} + +fn match_without_wildcard() { + let arr = vec![0, 1, 2, 3]; + let range = 1..3; + let idx = 2; + + // Lint, may panic + match arr[idx] { + 0 => println!("0"), + 1 => println!("1"), + num => {}, + } + + // Lint, may panic + match arr[range] { + [0, 1] => println!("0 1"), + [1, 2] => println!("1 2"), + [ref sub @ ..] => {}, + } +} + +fn match_wildcard_and_action() { + let arr = vec![0, 1, 2, 3]; + let range = 1..3; + let idx = 3; + + // Lint, may panic + match arr[idx] { + 0 => println!("0"), + 1 => println!("1"), + _ => println!("Hello, World!"), + } + + // Lint, may panic + match arr[range] { + [0, 1] => println!("0 1"), + [1, 2] => println!("1 2"), + _ => println!("Hello, World!"), + } +} + +fn match_vec_ref() { + let arr = &vec![0, 1, 2, 3]; + let range = 1..3; + let idx = 3; + + // Lint, may panic + match arr[idx] { + 0 => println!("0"), + 1 => println!("1"), + _ => {}, + } + + // Lint, may panic + match arr[range] { + [0, 1] => println!("0 1"), + [1, 2] => println!("1 2"), + _ => {}, + } +} + +fn match_with_get() { + let arr = vec![0, 1, 2, 3]; + let range = 1..3; + let idx = 3; + + // Ok + match arr.get(idx) { + Some(0) => println!("0"), + Some(1) => println!("1"), + _ => {}, + } + + // Ok + match arr.get(range) { + Some(&[0, 1]) => println!("0 1"), + Some(&[1, 2]) => println!("1 2"), + _ => {}, + } +} + +fn match_with_array() { + let arr = [0, 1, 2, 3]; + let range = 1..3; + let idx = 3; + + // Ok + match arr[idx] { + 0 => println!("0"), + 1 => println!("1"), + _ => {}, + } + + // Ok + match arr[range] { + [0, 1] => println!("0 1"), + [1, 2] => println!("1 2"), + _ => {}, + } +} + +fn main() { + match_with_wildcard(); + match_without_wildcard(); + match_wildcard_and_action(); + match_vec_ref(); + match_with_get(); + match_with_array(); +} diff --git a/src/tools/clippy/tests/ui/match_on_vec_items.stderr b/src/tools/clippy/tests/ui/match_on_vec_items.stderr new file mode 100644 index 00000000000..49446d715ab --- /dev/null +++ b/src/tools/clippy/tests/ui/match_on_vec_items.stderr @@ -0,0 +1,52 @@ +error: indexing into a vector may panic + --> $DIR/match_on_vec_items.rs:9:11 + | +LL | match arr[idx] { + | ^^^^^^^^ help: try this: `arr.get(idx)` + | + = note: `-D clippy::match-on-vec-items` implied by `-D warnings` + +error: indexing into a vector may panic + --> $DIR/match_on_vec_items.rs:16:11 + | +LL | match arr[range] { + | ^^^^^^^^^^ help: try this: `arr.get(range)` + +error: indexing into a vector may panic + --> $DIR/match_on_vec_items.rs:29:11 + | +LL | match arr[idx] { + | ^^^^^^^^ help: try this: `arr.get(idx)` + +error: indexing into a vector may panic + --> $DIR/match_on_vec_items.rs:36:11 + | +LL | match arr[range] { + | ^^^^^^^^^^ help: try this: `arr.get(range)` + +error: indexing into a vector may panic + --> $DIR/match_on_vec_items.rs:49:11 + | +LL | match arr[idx] { + | ^^^^^^^^ help: try this: `arr.get(idx)` + +error: indexing into a vector may panic + --> $DIR/match_on_vec_items.rs:56:11 + | +LL | match arr[range] { + | ^^^^^^^^^^ help: try this: `arr.get(range)` + +error: indexing into a vector may panic + --> $DIR/match_on_vec_items.rs:69:11 + | +LL | match arr[idx] { + | ^^^^^^^^ help: try this: `arr.get(idx)` + +error: indexing into a vector may panic + --> $DIR/match_on_vec_items.rs:76:11 + | +LL | match arr[range] { + | ^^^^^^^^^^ help: try this: `arr.get(range)` + +error: aborting due to 8 previous errors + diff --git a/src/tools/clippy/tests/ui/match_overlapping_arm.rs b/src/tools/clippy/tests/ui/match_overlapping_arm.rs new file mode 100644 index 00000000000..97789bb766f --- /dev/null +++ b/src/tools/clippy/tests/ui/match_overlapping_arm.rs @@ -0,0 +1,82 @@ +#![feature(exclusive_range_pattern)] +#![feature(half_open_range_patterns)] +#![warn(clippy::match_overlapping_arm)] +#![allow(clippy::redundant_pattern_matching)] + +/// Tests for match_overlapping_arm + +fn overlapping() { + const FOO: u64 = 2; + + match 42 { + 0..=10 => println!("0 ... 10"), + 0..=11 => println!("0 ... 11"), + _ => (), + } + + match 42 { + 0..=5 => println!("0 ... 5"), + 6..=7 => println!("6 ... 7"), + FOO..=11 => println!("0 ... 11"), + _ => (), + } + + match 42 { + 2 => println!("2"), + 0..=5 => println!("0 ... 5"), + _ => (), + } + + match 42 { + 2 => println!("2"), + 0..=2 => println!("0 ... 2"), + _ => (), + } + + match 42 { + 0..=10 => println!("0 ... 10"), + 11..=50 => println!("11 ... 50"), + _ => (), + } + + match 42 { + 2 => println!("2"), + 0..2 => println!("0 .. 2"), + _ => (), + } + + match 42 { + 0..10 => println!("0 .. 10"), + 10..50 => println!("10 .. 50"), + _ => (), + } + + match 42 { + 0..11 => println!("0 .. 11"), + 0..=11 => println!("0 ... 11"), + _ => (), + } + + /* + // FIXME(JohnTitor): uncomment this once rustfmt knows half-open patterns + match 42 { + 0.. => println!("0 .. 42"), + 3.. => println!("3 .. 42"), + _ => (), + } + + match 42 { + ..=23 => println!("0 ... 23"), + ..26 => println!("0 .. 26"), + _ => (), + } + */ + + if let None = Some(42) { + // nothing + } else if let None = Some(42) { + // another nothing :-) + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/match_overlapping_arm.stderr b/src/tools/clippy/tests/ui/match_overlapping_arm.stderr new file mode 100644 index 00000000000..eb20d5405a9 --- /dev/null +++ b/src/tools/clippy/tests/ui/match_overlapping_arm.stderr @@ -0,0 +1,63 @@ +error: some ranges overlap + --> $DIR/match_overlapping_arm.rs:12:9 + | +LL | 0..=10 => println!("0 ... 10"), + | ^^^^^^ + | + = note: `-D clippy::match-overlapping-arm` implied by `-D warnings` +note: overlaps with this + --> $DIR/match_overlapping_arm.rs:13:9 + | +LL | 0..=11 => println!("0 ... 11"), + | ^^^^^^ + +error: some ranges overlap + --> $DIR/match_overlapping_arm.rs:18:9 + | +LL | 0..=5 => println!("0 ... 5"), + | ^^^^^ + | +note: overlaps with this + --> $DIR/match_overlapping_arm.rs:20:9 + | +LL | FOO..=11 => println!("0 ... 11"), + | ^^^^^^^^ + +error: some ranges overlap + --> $DIR/match_overlapping_arm.rs:26:9 + | +LL | 0..=5 => println!("0 ... 5"), + | ^^^^^ + | +note: overlaps with this + --> $DIR/match_overlapping_arm.rs:25:9 + | +LL | 2 => println!("2"), + | ^ + +error: some ranges overlap + --> $DIR/match_overlapping_arm.rs:32:9 + | +LL | 0..=2 => println!("0 ... 2"), + | ^^^^^ + | +note: overlaps with this + --> $DIR/match_overlapping_arm.rs:31:9 + | +LL | 2 => println!("2"), + | ^ + +error: some ranges overlap + --> $DIR/match_overlapping_arm.rs:55:9 + | +LL | 0..11 => println!("0 .. 11"), + | ^^^^^ + | +note: overlaps with this + --> $DIR/match_overlapping_arm.rs:56:9 + | +LL | 0..=11 => println!("0 ... 11"), + | ^^^^^^ + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/match_ref_pats.rs b/src/tools/clippy/tests/ui/match_ref_pats.rs new file mode 100644 index 00000000000..5de43733ad3 --- /dev/null +++ b/src/tools/clippy/tests/ui/match_ref_pats.rs @@ -0,0 +1,74 @@ +#![warn(clippy::match_ref_pats)] + +fn ref_pats() { + { + let v = &Some(0); + match v { + &Some(v) => println!("{:?}", v), + &None => println!("none"), + } + match v { + // This doesn't trigger; we have a different pattern. + &Some(v) => println!("some"), + other => println!("other"), + } + } + let tup = &(1, 2); + match tup { + &(v, 1) => println!("{}", v), + _ => println!("none"), + } + // Special case: using `&` both in expr and pats. + let w = Some(0); + match &w { + &Some(v) => println!("{:?}", v), + &None => println!("none"), + } + // False positive: only wildcard pattern. + let w = Some(0); + #[allow(clippy::match_single_binding)] + match w { + _ => println!("none"), + } + + let a = &Some(0); + if let &None = a { + println!("none"); + } + + let b = Some(0); + if let &None = &b { + println!("none"); + } +} + +mod ice_3719 { + macro_rules! foo_variant( + ($idx:expr) => (Foo::get($idx).unwrap()) + ); + + enum Foo { + A, + B, + } + + impl Foo { + fn get(idx: u8) -> Option<&'static Self> { + match idx { + 0 => Some(&Foo::A), + 1 => Some(&Foo::B), + _ => None, + } + } + } + + fn ice_3719() { + // ICE #3719 + match foo_variant!(0) { + &Foo::A => println!("A"), + _ => println!("Wild"), + } + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/match_ref_pats.stderr b/src/tools/clippy/tests/ui/match_ref_pats.stderr new file mode 100644 index 00000000000..52cb4a14b72 --- /dev/null +++ b/src/tools/clippy/tests/ui/match_ref_pats.stderr @@ -0,0 +1,91 @@ +error: you don't need to add `&` to all patterns + --> $DIR/match_ref_pats.rs:6:9 + | +LL | / match v { +LL | | &Some(v) => println!("{:?}", v), +LL | | &None => println!("none"), +LL | | } + | |_________^ + | + = note: `-D clippy::match-ref-pats` implied by `-D warnings` +help: instead of prefixing all patterns with `&`, you can dereference the expression + | +LL | match *v { +LL | Some(v) => println!("{:?}", v), +LL | None => println!("none"), + | + +error: you don't need to add `&` to all patterns + --> $DIR/match_ref_pats.rs:17:5 + | +LL | / match tup { +LL | | &(v, 1) => println!("{}", v), +LL | | _ => println!("none"), +LL | | } + | |_____^ + | +help: instead of prefixing all patterns with `&`, you can dereference the expression + | +LL | match *tup { +LL | (v, 1) => println!("{}", v), + | + +error: you don't need to add `&` to both the expression and the patterns + --> $DIR/match_ref_pats.rs:23:5 + | +LL | / match &w { +LL | | &Some(v) => println!("{:?}", v), +LL | | &None => println!("none"), +LL | | } + | |_____^ + | +help: try + | +LL | match w { +LL | Some(v) => println!("{:?}", v), +LL | None => println!("none"), + | + +error: you don't need to add `&` to all patterns + --> $DIR/match_ref_pats.rs:35:5 + | +LL | / if let &None = a { +LL | | println!("none"); +LL | | } + | |_____^ + | +help: instead of prefixing all patterns with `&`, you can dereference the expression + | +LL | if let None = *a { + | ^^^^ ^^ + +error: you don't need to add `&` to both the expression and the patterns + --> $DIR/match_ref_pats.rs:40:5 + | +LL | / if let &None = &b { +LL | | println!("none"); +LL | | } + | |_____^ + | +help: try + | +LL | if let None = b { + | ^^^^ ^ + +error: you don't need to add `&` to all patterns + --> $DIR/match_ref_pats.rs:67:9 + | +LL | / match foo_variant!(0) { +LL | | &Foo::A => println!("A"), +LL | | _ => println!("Wild"), +LL | | } + | |_________^ + | +help: instead of prefixing all patterns with `&`, you can dereference the expression + | +LL | match *foo_variant!(0) { +LL | Foo::A => println!("A"), + | + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/match_same_arms.rs b/src/tools/clippy/tests/ui/match_same_arms.rs new file mode 100644 index 00000000000..0b9342c9c42 --- /dev/null +++ b/src/tools/clippy/tests/ui/match_same_arms.rs @@ -0,0 +1,56 @@ +#![warn(clippy::match_same_arms)] + +pub enum Abc { + A, + B, + C, +} + +fn match_same_arms() { + let _ = match Abc::A { + Abc::A => 0, + Abc::B => 1, + _ => 0, //~ ERROR match arms have same body + }; + + match (1, 2, 3) { + (1, .., 3) => 42, + (.., 3) => 42, //~ ERROR match arms have same body + _ => 0, + }; + + let _ = match 42 { + 42 => 1, + 51 => 1, //~ ERROR match arms have same body + 41 => 2, + 52 => 2, //~ ERROR match arms have same body + _ => 0, + }; + + let _ = match 42 { + 1 => 2, + 2 => 2, //~ ERROR 2nd matched arms have same body + 3 => 2, //~ ERROR 3rd matched arms have same body + 4 => 3, + _ => 0, + }; +} + +mod issue4244 { + #[derive(PartialEq, PartialOrd, Eq, Ord)] + pub enum CommandInfo { + BuiltIn { name: String, about: Option<String> }, + External { name: String, path: std::path::PathBuf }, + } + + impl CommandInfo { + pub fn name(&self) -> String { + match self { + CommandInfo::BuiltIn { name, .. } => name.to_string(), + CommandInfo::External { name, .. } => name.to_string(), + } + } + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/match_same_arms.stderr b/src/tools/clippy/tests/ui/match_same_arms.stderr new file mode 100644 index 00000000000..0549886a1e8 --- /dev/null +++ b/src/tools/clippy/tests/ui/match_same_arms.stderr @@ -0,0 +1,139 @@ +error: this `match` has identical arm bodies + --> $DIR/match_same_arms.rs:13:14 + | +LL | _ => 0, //~ ERROR match arms have same body + | ^ + | + = note: `-D clippy::match-same-arms` implied by `-D warnings` +note: same as this + --> $DIR/match_same_arms.rs:11:19 + | +LL | Abc::A => 0, + | ^ +note: `Abc::A` has the same arm body as the `_` wildcard, consider removing it + --> $DIR/match_same_arms.rs:11:19 + | +LL | Abc::A => 0, + | ^ + +error: this `match` has identical arm bodies + --> $DIR/match_same_arms.rs:18:20 + | +LL | (.., 3) => 42, //~ ERROR match arms have same body + | ^^ + | +note: same as this + --> $DIR/match_same_arms.rs:17:23 + | +LL | (1, .., 3) => 42, + | ^^ +help: consider refactoring into `(1, .., 3) | (.., 3)` + --> $DIR/match_same_arms.rs:17:9 + | +LL | (1, .., 3) => 42, + | ^^^^^^^^^^ + +error: this `match` has identical arm bodies + --> $DIR/match_same_arms.rs:24:15 + | +LL | 51 => 1, //~ ERROR match arms have same body + | ^ + | +note: same as this + --> $DIR/match_same_arms.rs:23:15 + | +LL | 42 => 1, + | ^ +help: consider refactoring into `42 | 51` + --> $DIR/match_same_arms.rs:23:9 + | +LL | 42 => 1, + | ^^ + +error: this `match` has identical arm bodies + --> $DIR/match_same_arms.rs:26:15 + | +LL | 52 => 2, //~ ERROR match arms have same body + | ^ + | +note: same as this + --> $DIR/match_same_arms.rs:25:15 + | +LL | 41 => 2, + | ^ +help: consider refactoring into `41 | 52` + --> $DIR/match_same_arms.rs:25:9 + | +LL | 41 => 2, + | ^^ + +error: this `match` has identical arm bodies + --> $DIR/match_same_arms.rs:32:14 + | +LL | 2 => 2, //~ ERROR 2nd matched arms have same body + | ^ + | +note: same as this + --> $DIR/match_same_arms.rs:31:14 + | +LL | 1 => 2, + | ^ +help: consider refactoring into `1 | 2` + --> $DIR/match_same_arms.rs:31:9 + | +LL | 1 => 2, + | ^ + +error: this `match` has identical arm bodies + --> $DIR/match_same_arms.rs:33:14 + | +LL | 3 => 2, //~ ERROR 3rd matched arms have same body + | ^ + | +note: same as this + --> $DIR/match_same_arms.rs:31:14 + | +LL | 1 => 2, + | ^ +help: consider refactoring into `1 | 3` + --> $DIR/match_same_arms.rs:31:9 + | +LL | 1 => 2, + | ^ + +error: this `match` has identical arm bodies + --> $DIR/match_same_arms.rs:33:14 + | +LL | 3 => 2, //~ ERROR 3rd matched arms have same body + | ^ + | +note: same as this + --> $DIR/match_same_arms.rs:32:14 + | +LL | 2 => 2, //~ ERROR 2nd matched arms have same body + | ^ +help: consider refactoring into `2 | 3` + --> $DIR/match_same_arms.rs:32:9 + | +LL | 2 => 2, //~ ERROR 2nd matched arms have same body + | ^ + +error: this `match` has identical arm bodies + --> $DIR/match_same_arms.rs:50:55 + | +LL | CommandInfo::External { name, .. } => name.to_string(), + | ^^^^^^^^^^^^^^^^ + | +note: same as this + --> $DIR/match_same_arms.rs:49:54 + | +LL | CommandInfo::BuiltIn { name, .. } => name.to_string(), + | ^^^^^^^^^^^^^^^^ +help: consider refactoring into `CommandInfo::BuiltIn { name, .. } | CommandInfo::External { name, .. }` + --> $DIR/match_same_arms.rs:49:17 + | +LL | CommandInfo::BuiltIn { name, .. } => name.to_string(), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 8 previous errors + diff --git a/src/tools/clippy/tests/ui/match_same_arms2.rs b/src/tools/clippy/tests/ui/match_same_arms2.rs new file mode 100644 index 00000000000..e1401d2796a --- /dev/null +++ b/src/tools/clippy/tests/ui/match_same_arms2.rs @@ -0,0 +1,124 @@ +#![warn(clippy::match_same_arms)] +#![allow(clippy::blacklisted_name)] + +fn bar<T>(_: T) {} +fn foo() -> bool { + unimplemented!() +} + +fn match_same_arms() { + let _ = match 42 { + 42 => { + foo(); + let mut a = 42 + [23].len() as i32; + if true { + a += 7; + } + a = -31 - a; + a + }, + _ => { + //~ ERROR match arms have same body + foo(); + let mut a = 42 + [23].len() as i32; + if true { + a += 7; + } + a = -31 - a; + a + }, + }; + + let _ = match 42 { + 42 => foo(), + 51 => foo(), //~ ERROR match arms have same body + _ => true, + }; + + let _ = match Some(42) { + Some(_) => 24, + None => 24, //~ ERROR match arms have same body + }; + + let _ = match Some(42) { + Some(foo) => 24, + None => 24, + }; + + let _ = match Some(42) { + Some(42) => 24, + Some(a) => 24, // bindings are different + None => 0, + }; + + let _ = match Some(42) { + Some(a) if a > 0 => 24, + Some(a) => 24, // one arm has a guard + None => 0, + }; + + match (Some(42), Some(42)) { + (Some(a), None) => bar(a), + (None, Some(a)) => bar(a), //~ ERROR match arms have same body + _ => (), + } + + match (Some(42), Some(42)) { + (Some(a), ..) => bar(a), + (.., Some(a)) => bar(a), //~ ERROR match arms have same body + _ => (), + } + + let _ = match Some(()) { + Some(()) => 0.0, + None => -0.0, + }; + + match (Some(42), Some("")) { + (Some(a), None) => bar(a), + (None, Some(a)) => bar(a), // bindings have different types + _ => (), + } + + let x: Result<i32, &str> = Ok(3); + + // No warning because of the guard. + match x { + Ok(x) if x * x == 64 => println!("ok"), + Ok(_) => println!("ok"), + Err(_) => println!("err"), + } + + // This used to be a false positive; see issue #1996. + match x { + Ok(3) => println!("ok"), + Ok(x) if x * x == 64 => println!("ok 64"), + Ok(_) => println!("ok"), + Err(_) => println!("err"), + } + + match (x, Some(1i32)) { + (Ok(x), Some(_)) => println!("ok {}", x), + (Ok(_), Some(x)) => println!("ok {}", x), + _ => println!("err"), + } + + // No warning; different types for `x`. + match (x, Some(1.0f64)) { + (Ok(x), Some(_)) => println!("ok {}", x), + (Ok(_), Some(x)) => println!("ok {}", x), + _ => println!("err"), + } + + // False negative #2251. + match x { + Ok(_tmp) => println!("ok"), + Ok(3) => println!("ok"), + Ok(_) => println!("ok"), + Err(_) => { + unreachable!(); + }, + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/match_same_arms2.stderr b/src/tools/clippy/tests/ui/match_same_arms2.stderr new file mode 100644 index 00000000000..26c65f32ad7 --- /dev/null +++ b/src/tools/clippy/tests/ui/match_same_arms2.stderr @@ -0,0 +1,145 @@ +error: this `match` has identical arm bodies + --> $DIR/match_same_arms2.rs:20:14 + | +LL | _ => { + | ______________^ +LL | | //~ ERROR match arms have same body +LL | | foo(); +LL | | let mut a = 42 + [23].len() as i32; +... | +LL | | a +LL | | }, + | |_________^ + | + = note: `-D clippy::match-same-arms` implied by `-D warnings` +note: same as this + --> $DIR/match_same_arms2.rs:11:15 + | +LL | 42 => { + | _______________^ +LL | | foo(); +LL | | let mut a = 42 + [23].len() as i32; +LL | | if true { +... | +LL | | a +LL | | }, + | |_________^ +note: `42` has the same arm body as the `_` wildcard, consider removing it + --> $DIR/match_same_arms2.rs:11:15 + | +LL | 42 => { + | _______________^ +LL | | foo(); +LL | | let mut a = 42 + [23].len() as i32; +LL | | if true { +... | +LL | | a +LL | | }, + | |_________^ + +error: this `match` has identical arm bodies + --> $DIR/match_same_arms2.rs:34:15 + | +LL | 51 => foo(), //~ ERROR match arms have same body + | ^^^^^ + | +note: same as this + --> $DIR/match_same_arms2.rs:33:15 + | +LL | 42 => foo(), + | ^^^^^ +help: consider refactoring into `42 | 51` + --> $DIR/match_same_arms2.rs:33:9 + | +LL | 42 => foo(), + | ^^ + +error: this `match` has identical arm bodies + --> $DIR/match_same_arms2.rs:40:17 + | +LL | None => 24, //~ ERROR match arms have same body + | ^^ + | +note: same as this + --> $DIR/match_same_arms2.rs:39:20 + | +LL | Some(_) => 24, + | ^^ +help: consider refactoring into `Some(_) | None` + --> $DIR/match_same_arms2.rs:39:9 + | +LL | Some(_) => 24, + | ^^^^^^^ + +error: this `match` has identical arm bodies + --> $DIR/match_same_arms2.rs:62:28 + | +LL | (None, Some(a)) => bar(a), //~ ERROR match arms have same body + | ^^^^^^ + | +note: same as this + --> $DIR/match_same_arms2.rs:61:28 + | +LL | (Some(a), None) => bar(a), + | ^^^^^^ +help: consider refactoring into `(Some(a), None) | (None, Some(a))` + --> $DIR/match_same_arms2.rs:61:9 + | +LL | (Some(a), None) => bar(a), + | ^^^^^^^^^^^^^^^ + +error: this `match` has identical arm bodies + --> $DIR/match_same_arms2.rs:68:26 + | +LL | (.., Some(a)) => bar(a), //~ ERROR match arms have same body + | ^^^^^^ + | +note: same as this + --> $DIR/match_same_arms2.rs:67:26 + | +LL | (Some(a), ..) => bar(a), + | ^^^^^^ +help: consider refactoring into `(Some(a), ..) | (.., Some(a))` + --> $DIR/match_same_arms2.rs:67:9 + | +LL | (Some(a), ..) => bar(a), + | ^^^^^^^^^^^^^ + +error: this `match` has identical arm bodies + --> $DIR/match_same_arms2.rs:102:29 + | +LL | (Ok(_), Some(x)) => println!("ok {}", x), + | ^^^^^^^^^^^^^^^^^^^^ + | +note: same as this + --> $DIR/match_same_arms2.rs:101:29 + | +LL | (Ok(x), Some(_)) => println!("ok {}", x), + | ^^^^^^^^^^^^^^^^^^^^ +help: consider refactoring into `(Ok(x), Some(_)) | (Ok(_), Some(x))` + --> $DIR/match_same_arms2.rs:101:9 + | +LL | (Ok(x), Some(_)) => println!("ok {}", x), + | ^^^^^^^^^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: this `match` has identical arm bodies + --> $DIR/match_same_arms2.rs:117:18 + | +LL | Ok(_) => println!("ok"), + | ^^^^^^^^^^^^^^ + | +note: same as this + --> $DIR/match_same_arms2.rs:116:18 + | +LL | Ok(3) => println!("ok"), + | ^^^^^^^^^^^^^^ +help: consider refactoring into `Ok(3) | Ok(_)` + --> $DIR/match_same_arms2.rs:116:9 + | +LL | Ok(3) => println!("ok"), + | ^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/tests/ui/match_single_binding.fixed b/src/tools/clippy/tests/ui/match_single_binding.fixed new file mode 100644 index 00000000000..f3627902eec --- /dev/null +++ b/src/tools/clippy/tests/ui/match_single_binding.fixed @@ -0,0 +1,90 @@ +// run-rustfix + +#![warn(clippy::match_single_binding)] +#![allow(unused_variables, clippy::many_single_char_names, clippy::toplevel_ref_arg)] + +struct Point { + x: i32, + y: i32, +} + +fn coords() -> Point { + Point { x: 1, y: 2 } +} + +macro_rules! foo { + ($param:expr) => { + match $param { + _ => println!("whatever"), + } + }; +} + +fn main() { + let a = 1; + let b = 2; + let c = 3; + // Lint + let (x, y, z) = (a, b, c); + { + println!("{} {} {}", x, y, z); + } + // Lint + let (x, y, z) = (a, b, c); + println!("{} {} {}", x, y, z); + // Ok + foo!(a); + // Ok + match a { + 2 => println!("2"), + _ => println!("Not 2"), + } + // Ok + let d = Some(5); + match d { + Some(d) => println!("{}", d), + _ => println!("None"), + } + // Lint + println!("whatever"); + // Lint + { + let x = 29; + println!("x has a value of {}", x); + } + // Lint + { + let e = 5 * a; + if e >= 5 { + println!("e is superior to 5"); + } + } + // Lint + let p = Point { x: 0, y: 7 }; + let Point { x, y } = p; + println!("Coords: ({}, {})", x, y); + // Lint + let Point { x: x1, y: y1 } = p; + println!("Coords: ({}, {})", x1, y1); + // Lint + let x = 5; + let ref r = x; + println!("Got a reference to {}", r); + // Lint + let mut x = 5; + let ref mut mr = x; + println!("Got a mutable reference to {}", mr); + // Lint + let Point { x, y } = coords(); + let product = x * y; + // Lint + let v = vec![Some(1), Some(2), Some(3), Some(4)]; + #[allow(clippy::let_and_return)] + let _ = v + .iter() + .map(|i| { + let unwrapped = i.unwrap(); + unwrapped + }) + .collect::<Vec<u8>>(); +} diff --git a/src/tools/clippy/tests/ui/match_single_binding.rs b/src/tools/clippy/tests/ui/match_single_binding.rs new file mode 100644 index 00000000000..8c182148ae1 --- /dev/null +++ b/src/tools/clippy/tests/ui/match_single_binding.rs @@ -0,0 +1,102 @@ +// run-rustfix + +#![warn(clippy::match_single_binding)] +#![allow(unused_variables, clippy::many_single_char_names, clippy::toplevel_ref_arg)] + +struct Point { + x: i32, + y: i32, +} + +fn coords() -> Point { + Point { x: 1, y: 2 } +} + +macro_rules! foo { + ($param:expr) => { + match $param { + _ => println!("whatever"), + } + }; +} + +fn main() { + let a = 1; + let b = 2; + let c = 3; + // Lint + match (a, b, c) { + (x, y, z) => { + println!("{} {} {}", x, y, z); + }, + } + // Lint + match (a, b, c) { + (x, y, z) => println!("{} {} {}", x, y, z), + } + // Ok + foo!(a); + // Ok + match a { + 2 => println!("2"), + _ => println!("Not 2"), + } + // Ok + let d = Some(5); + match d { + Some(d) => println!("{}", d), + _ => println!("None"), + } + // Lint + match a { + _ => println!("whatever"), + } + // Lint + match a { + _ => { + let x = 29; + println!("x has a value of {}", x); + }, + } + // Lint + match a { + _ => { + let e = 5 * a; + if e >= 5 { + println!("e is superior to 5"); + } + }, + } + // Lint + let p = Point { x: 0, y: 7 }; + match p { + Point { x, y } => println!("Coords: ({}, {})", x, y), + } + // Lint + match p { + Point { x: x1, y: y1 } => println!("Coords: ({}, {})", x1, y1), + } + // Lint + let x = 5; + match x { + ref r => println!("Got a reference to {}", r), + } + // Lint + let mut x = 5; + match x { + ref mut mr => println!("Got a mutable reference to {}", mr), + } + // Lint + let product = match coords() { + Point { x, y } => x * y, + }; + // Lint + let v = vec![Some(1), Some(2), Some(3), Some(4)]; + #[allow(clippy::let_and_return)] + let _ = v + .iter() + .map(|i| match i.unwrap() { + unwrapped => unwrapped, + }) + .collect::<Vec<u8>>(); +} diff --git a/src/tools/clippy/tests/ui/match_single_binding.stderr b/src/tools/clippy/tests/ui/match_single_binding.stderr new file mode 100644 index 00000000000..795c8c3e24d --- /dev/null +++ b/src/tools/clippy/tests/ui/match_single_binding.stderr @@ -0,0 +1,171 @@ +error: this match could be written as a `let` statement + --> $DIR/match_single_binding.rs:28:5 + | +LL | / match (a, b, c) { +LL | | (x, y, z) => { +LL | | println!("{} {} {}", x, y, z); +LL | | }, +LL | | } + | |_____^ + | + = note: `-D clippy::match-single-binding` implied by `-D warnings` +help: consider using `let` statement + | +LL | let (x, y, z) = (a, b, c); +LL | { +LL | println!("{} {} {}", x, y, z); +LL | } + | + +error: this match could be written as a `let` statement + --> $DIR/match_single_binding.rs:34:5 + | +LL | / match (a, b, c) { +LL | | (x, y, z) => println!("{} {} {}", x, y, z), +LL | | } + | |_____^ + | +help: consider using `let` statement + | +LL | let (x, y, z) = (a, b, c); +LL | println!("{} {} {}", x, y, z); + | + +error: this match could be replaced by its body itself + --> $DIR/match_single_binding.rs:51:5 + | +LL | / match a { +LL | | _ => println!("whatever"), +LL | | } + | |_____^ help: consider using the match body instead: `println!("whatever");` + +error: this match could be replaced by its body itself + --> $DIR/match_single_binding.rs:55:5 + | +LL | / match a { +LL | | _ => { +LL | | let x = 29; +LL | | println!("x has a value of {}", x); +LL | | }, +LL | | } + | |_____^ + | +help: consider using the match body instead + | +LL | { +LL | let x = 29; +LL | println!("x has a value of {}", x); +LL | } + | + +error: this match could be replaced by its body itself + --> $DIR/match_single_binding.rs:62:5 + | +LL | / match a { +LL | | _ => { +LL | | let e = 5 * a; +LL | | if e >= 5 { +... | +LL | | }, +LL | | } + | |_____^ + | +help: consider using the match body instead + | +LL | { +LL | let e = 5 * a; +LL | if e >= 5 { +LL | println!("e is superior to 5"); +LL | } +LL | } + | + +error: this match could be written as a `let` statement + --> $DIR/match_single_binding.rs:72:5 + | +LL | / match p { +LL | | Point { x, y } => println!("Coords: ({}, {})", x, y), +LL | | } + | |_____^ + | +help: consider using `let` statement + | +LL | let Point { x, y } = p; +LL | println!("Coords: ({}, {})", x, y); + | + +error: this match could be written as a `let` statement + --> $DIR/match_single_binding.rs:76:5 + | +LL | / match p { +LL | | Point { x: x1, y: y1 } => println!("Coords: ({}, {})", x1, y1), +LL | | } + | |_____^ + | +help: consider using `let` statement + | +LL | let Point { x: x1, y: y1 } = p; +LL | println!("Coords: ({}, {})", x1, y1); + | + +error: this match could be written as a `let` statement + --> $DIR/match_single_binding.rs:81:5 + | +LL | / match x { +LL | | ref r => println!("Got a reference to {}", r), +LL | | } + | |_____^ + | +help: consider using `let` statement + | +LL | let ref r = x; +LL | println!("Got a reference to {}", r); + | + +error: this match could be written as a `let` statement + --> $DIR/match_single_binding.rs:86:5 + | +LL | / match x { +LL | | ref mut mr => println!("Got a mutable reference to {}", mr), +LL | | } + | |_____^ + | +help: consider using `let` statement + | +LL | let ref mut mr = x; +LL | println!("Got a mutable reference to {}", mr); + | + +error: this match could be written as a `let` statement + --> $DIR/match_single_binding.rs:90:5 + | +LL | / let product = match coords() { +LL | | Point { x, y } => x * y, +LL | | }; + | |______^ + | +help: consider using `let` statement + | +LL | let Point { x, y } = coords(); +LL | let product = x * y; + | + +error: this match could be written as a `let` statement + --> $DIR/match_single_binding.rs:98:18 + | +LL | .map(|i| match i.unwrap() { + | __________________^ +LL | | unwrapped => unwrapped, +LL | | }) + | |_________^ + | +help: consider using `let` statement + | +LL | .map(|i| { +LL | let unwrapped = i.unwrap(); +LL | unwrapped +LL | }) + | + +error: aborting due to 11 previous errors + diff --git a/src/tools/clippy/tests/ui/match_wild_err_arm.rs b/src/tools/clippy/tests/ui/match_wild_err_arm.rs new file mode 100644 index 00000000000..823be65efe0 --- /dev/null +++ b/src/tools/clippy/tests/ui/match_wild_err_arm.rs @@ -0,0 +1,65 @@ +#![feature(exclusive_range_pattern)] +#![allow(clippy::match_same_arms)] +#![warn(clippy::match_wild_err_arm)] + +fn match_wild_err_arm() { + let x: Result<i32, &str> = Ok(3); + + match x { + Ok(3) => println!("ok"), + Ok(_) => println!("ok"), + Err(_) => panic!("err"), + } + + match x { + Ok(3) => println!("ok"), + Ok(_) => println!("ok"), + Err(_) => panic!(), + } + + match x { + Ok(3) => println!("ok"), + Ok(_) => println!("ok"), + Err(_) => { + panic!(); + }, + } + + match x { + Ok(3) => println!("ok"), + Ok(_) => println!("ok"), + Err(_e) => panic!(), + } + + // Allowed when used in `panic!`. + match x { + Ok(3) => println!("ok"), + Ok(_) => println!("ok"), + Err(_e) => panic!("{}", _e), + } + + // Allowed when not with `panic!` block. + match x { + Ok(3) => println!("ok"), + Ok(_) => println!("ok"), + Err(_) => println!("err"), + } + + // Allowed when used with `unreachable!`. + match x { + Ok(3) => println!("ok"), + Ok(_) => println!("ok"), + Err(_) => unreachable!(), + } + + // Allowed when used with `unreachable!`. + match x { + Ok(3) => println!("ok"), + Ok(_) => println!("ok"), + Err(_) => { + unreachable!(); + }, + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/match_wild_err_arm.stderr b/src/tools/clippy/tests/ui/match_wild_err_arm.stderr new file mode 100644 index 00000000000..20d4c933418 --- /dev/null +++ b/src/tools/clippy/tests/ui/match_wild_err_arm.stderr @@ -0,0 +1,35 @@ +error: `Err(_)` matches all errors + --> $DIR/match_wild_err_arm.rs:11:9 + | +LL | Err(_) => panic!("err"), + | ^^^^^^ + | + = note: `-D clippy::match-wild-err-arm` implied by `-D warnings` + = note: match each error separately or use the error output + +error: `Err(_)` matches all errors + --> $DIR/match_wild_err_arm.rs:17:9 + | +LL | Err(_) => panic!(), + | ^^^^^^ + | + = note: match each error separately or use the error output + +error: `Err(_)` matches all errors + --> $DIR/match_wild_err_arm.rs:23:9 + | +LL | Err(_) => { + | ^^^^^^ + | + = note: match each error separately or use the error output + +error: `Err(_e)` matches all errors + --> $DIR/match_wild_err_arm.rs:31:9 + | +LL | Err(_e) => panic!(), + | ^^^^^^^ + | + = note: match each error separately or use the error output + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/mem_discriminant.fixed b/src/tools/clippy/tests/ui/mem_discriminant.fixed new file mode 100644 index 00000000000..69a8f286d05 --- /dev/null +++ b/src/tools/clippy/tests/ui/mem_discriminant.fixed @@ -0,0 +1,45 @@ +// run-rustfix + +#![deny(clippy::mem_discriminant_non_enum)] + +use std::mem; + +enum Foo { + One(usize), + Two(u8), +} + +fn main() { + // bad + mem::discriminant(&Some(2)); + mem::discriminant(&None::<u8>); + mem::discriminant(&Foo::One(5)); + mem::discriminant(&Foo::Two(5)); + + let ro = &Some(3); + let rro = &ro; + mem::discriminant(ro); + mem::discriminant(*rro); + mem::discriminant(*rro); + + macro_rules! mem_discriminant_but_in_a_macro { + ($param:expr) => { + mem::discriminant($param) + }; + } + + mem_discriminant_but_in_a_macro!(*rro); + + let rrrrro = &&&rro; + mem::discriminant(****rrrrro); + mem::discriminant(****rrrrro); + + // ok + mem::discriminant(&Some(2)); + mem::discriminant(&None::<u8>); + mem::discriminant(&Foo::One(5)); + mem::discriminant(&Foo::Two(5)); + mem::discriminant(ro); + mem::discriminant(*rro); + mem::discriminant(****rrrrro); +} diff --git a/src/tools/clippy/tests/ui/mem_discriminant.rs b/src/tools/clippy/tests/ui/mem_discriminant.rs new file mode 100644 index 00000000000..55db50fcdc7 --- /dev/null +++ b/src/tools/clippy/tests/ui/mem_discriminant.rs @@ -0,0 +1,45 @@ +// run-rustfix + +#![deny(clippy::mem_discriminant_non_enum)] + +use std::mem; + +enum Foo { + One(usize), + Two(u8), +} + +fn main() { + // bad + mem::discriminant(&&Some(2)); + mem::discriminant(&&None::<u8>); + mem::discriminant(&&Foo::One(5)); + mem::discriminant(&&Foo::Two(5)); + + let ro = &Some(3); + let rro = &ro; + mem::discriminant(&ro); + mem::discriminant(rro); + mem::discriminant(&rro); + + macro_rules! mem_discriminant_but_in_a_macro { + ($param:expr) => { + mem::discriminant($param) + }; + } + + mem_discriminant_but_in_a_macro!(&rro); + + let rrrrro = &&&rro; + mem::discriminant(&rrrrro); + mem::discriminant(*rrrrro); + + // ok + mem::discriminant(&Some(2)); + mem::discriminant(&None::<u8>); + mem::discriminant(&Foo::One(5)); + mem::discriminant(&Foo::Two(5)); + mem::discriminant(ro); + mem::discriminant(*rro); + mem::discriminant(****rrrrro); +} diff --git a/src/tools/clippy/tests/ui/mem_discriminant.stderr b/src/tools/clippy/tests/ui/mem_discriminant.stderr new file mode 100644 index 00000000000..8d9810970ad --- /dev/null +++ b/src/tools/clippy/tests/ui/mem_discriminant.stderr @@ -0,0 +1,94 @@ +error: calling `mem::discriminant` on non-enum type `&std::option::Option<i32>` + --> $DIR/mem_discriminant.rs:14:5 + | +LL | mem::discriminant(&&Some(2)); + | ^^^^^^^^^^^^^^^^^^---------^ + | | + | help: try dereferencing: `&Some(2)` + | +note: the lint level is defined here + --> $DIR/mem_discriminant.rs:3:9 + | +LL | #![deny(clippy::mem_discriminant_non_enum)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: calling `mem::discriminant` on non-enum type `&std::option::Option<u8>` + --> $DIR/mem_discriminant.rs:15:5 + | +LL | mem::discriminant(&&None::<u8>); + | ^^^^^^^^^^^^^^^^^^------------^ + | | + | help: try dereferencing: `&None::<u8>` + +error: calling `mem::discriminant` on non-enum type `&Foo` + --> $DIR/mem_discriminant.rs:16:5 + | +LL | mem::discriminant(&&Foo::One(5)); + | ^^^^^^^^^^^^^^^^^^-------------^ + | | + | help: try dereferencing: `&Foo::One(5)` + +error: calling `mem::discriminant` on non-enum type `&Foo` + --> $DIR/mem_discriminant.rs:17:5 + | +LL | mem::discriminant(&&Foo::Two(5)); + | ^^^^^^^^^^^^^^^^^^-------------^ + | | + | help: try dereferencing: `&Foo::Two(5)` + +error: calling `mem::discriminant` on non-enum type `&std::option::Option<i32>` + --> $DIR/mem_discriminant.rs:21:5 + | +LL | mem::discriminant(&ro); + | ^^^^^^^^^^^^^^^^^^---^ + | | + | help: try dereferencing: `ro` + +error: calling `mem::discriminant` on non-enum type `&std::option::Option<i32>` + --> $DIR/mem_discriminant.rs:22:5 + | +LL | mem::discriminant(rro); + | ^^^^^^^^^^^^^^^^^^---^ + | | + | help: try dereferencing: `*rro` + +error: calling `mem::discriminant` on non-enum type `&&std::option::Option<i32>` + --> $DIR/mem_discriminant.rs:23:5 + | +LL | mem::discriminant(&rro); + | ^^^^^^^^^^^^^^^^^^----^ + | | + | help: try dereferencing: `*rro` + +error: calling `mem::discriminant` on non-enum type `&&std::option::Option<i32>` + --> $DIR/mem_discriminant.rs:27:13 + | +LL | mem::discriminant($param) + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | mem_discriminant_but_in_a_macro!(&rro); + | --------------------------------------- + | | | + | | help: try dereferencing: `*rro` + | in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: calling `mem::discriminant` on non-enum type `&&&&&std::option::Option<i32>` + --> $DIR/mem_discriminant.rs:34:5 + | +LL | mem::discriminant(&rrrrro); + | ^^^^^^^^^^^^^^^^^^-------^ + | | + | help: try dereferencing: `****rrrrro` + +error: calling `mem::discriminant` on non-enum type `&&&std::option::Option<i32>` + --> $DIR/mem_discriminant.rs:35:5 + | +LL | mem::discriminant(*rrrrro); + | ^^^^^^^^^^^^^^^^^^-------^ + | | + | help: try dereferencing: `****rrrrro` + +error: aborting due to 10 previous errors + diff --git a/src/tools/clippy/tests/ui/mem_discriminant_unfixable.rs b/src/tools/clippy/tests/ui/mem_discriminant_unfixable.rs new file mode 100644 index 00000000000..e245d3257d5 --- /dev/null +++ b/src/tools/clippy/tests/ui/mem_discriminant_unfixable.rs @@ -0,0 +1,16 @@ +#![deny(clippy::mem_discriminant_non_enum)] + +use std::mem; + +enum Foo { + One(usize), + Two(u8), +} + +struct A(Foo); + +fn main() { + // bad + mem::discriminant(&"hello"); + mem::discriminant(&A(Foo::One(0))); +} diff --git a/src/tools/clippy/tests/ui/mem_discriminant_unfixable.stderr b/src/tools/clippy/tests/ui/mem_discriminant_unfixable.stderr new file mode 100644 index 00000000000..e2de3776f2c --- /dev/null +++ b/src/tools/clippy/tests/ui/mem_discriminant_unfixable.stderr @@ -0,0 +1,20 @@ +error: calling `mem::discriminant` on non-enum type `&str` + --> $DIR/mem_discriminant_unfixable.rs:14:5 + | +LL | mem::discriminant(&"hello"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/mem_discriminant_unfixable.rs:1:9 + | +LL | #![deny(clippy::mem_discriminant_non_enum)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: calling `mem::discriminant` on non-enum type `A` + --> $DIR/mem_discriminant_unfixable.rs:15:5 + | +LL | mem::discriminant(&A(Foo::One(0))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/mem_forget.rs b/src/tools/clippy/tests/ui/mem_forget.rs new file mode 100644 index 00000000000..e5b35c098a2 --- /dev/null +++ b/src/tools/clippy/tests/ui/mem_forget.rs @@ -0,0 +1,23 @@ +use std::rc::Rc; +use std::sync::Arc; + +use std::mem as memstuff; +use std::mem::forget as forgetSomething; + +#[warn(clippy::mem_forget)] +#[allow(clippy::forget_copy)] +fn main() { + let five: i32 = 5; + forgetSomething(five); + + let six: Arc<i32> = Arc::new(6); + memstuff::forget(six); + + let seven: Rc<i32> = Rc::new(7); + std::mem::forget(seven); + + let eight: Vec<i32> = vec![8]; + forgetSomething(eight); + + std::mem::forget(7); +} diff --git a/src/tools/clippy/tests/ui/mem_forget.stderr b/src/tools/clippy/tests/ui/mem_forget.stderr new file mode 100644 index 00000000000..a90d8b1655d --- /dev/null +++ b/src/tools/clippy/tests/ui/mem_forget.stderr @@ -0,0 +1,22 @@ +error: usage of `mem::forget` on `Drop` type + --> $DIR/mem_forget.rs:14:5 + | +LL | memstuff::forget(six); + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::mem-forget` implied by `-D warnings` + +error: usage of `mem::forget` on `Drop` type + --> $DIR/mem_forget.rs:17:5 + | +LL | std::mem::forget(seven); + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: usage of `mem::forget` on `Drop` type + --> $DIR/mem_forget.rs:20:5 + | +LL | forgetSomething(eight); + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/mem_replace.fixed b/src/tools/clippy/tests/ui/mem_replace.fixed new file mode 100644 index 00000000000..54e962e7116 --- /dev/null +++ b/src/tools/clippy/tests/ui/mem_replace.fixed @@ -0,0 +1,30 @@ +// run-rustfix +#![allow(unused_imports)] +#![warn( + clippy::all, + clippy::style, + clippy::mem_replace_option_with_none, + clippy::mem_replace_with_default +)] + +use std::mem; + +fn replace_option_with_none() { + let mut an_option = Some(1); + let _ = an_option.take(); + let an_option = &mut Some(1); + let _ = an_option.take(); +} + +fn replace_with_default() { + let mut s = String::from("foo"); + let _ = std::mem::take(&mut s); + let s = &mut String::from("foo"); + let _ = std::mem::take(s); + let _ = std::mem::take(s); +} + +fn main() { + replace_option_with_none(); + replace_with_default(); +} diff --git a/src/tools/clippy/tests/ui/mem_replace.rs b/src/tools/clippy/tests/ui/mem_replace.rs new file mode 100644 index 00000000000..60f52781071 --- /dev/null +++ b/src/tools/clippy/tests/ui/mem_replace.rs @@ -0,0 +1,30 @@ +// run-rustfix +#![allow(unused_imports)] +#![warn( + clippy::all, + clippy::style, + clippy::mem_replace_option_with_none, + clippy::mem_replace_with_default +)] + +use std::mem; + +fn replace_option_with_none() { + let mut an_option = Some(1); + let _ = mem::replace(&mut an_option, None); + let an_option = &mut Some(1); + let _ = mem::replace(an_option, None); +} + +fn replace_with_default() { + let mut s = String::from("foo"); + let _ = std::mem::replace(&mut s, String::default()); + let s = &mut String::from("foo"); + let _ = std::mem::replace(s, String::default()); + let _ = std::mem::replace(s, Default::default()); +} + +fn main() { + replace_option_with_none(); + replace_with_default(); +} diff --git a/src/tools/clippy/tests/ui/mem_replace.stderr b/src/tools/clippy/tests/ui/mem_replace.stderr new file mode 100644 index 00000000000..245d33aa4f2 --- /dev/null +++ b/src/tools/clippy/tests/ui/mem_replace.stderr @@ -0,0 +1,36 @@ +error: replacing an `Option` with `None` + --> $DIR/mem_replace.rs:14:13 + | +LL | let _ = mem::replace(&mut an_option, None); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `an_option.take()` + | + = note: `-D clippy::mem-replace-option-with-none` implied by `-D warnings` + +error: replacing an `Option` with `None` + --> $DIR/mem_replace.rs:16:13 + | +LL | let _ = mem::replace(an_option, None); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `an_option.take()` + +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` + --> $DIR/mem_replace.rs:21:13 + | +LL | let _ = std::mem::replace(&mut s, String::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut s)` + | + = note: `-D clippy::mem-replace-with-default` implied by `-D warnings` + +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` + --> $DIR/mem_replace.rs:23:13 + | +LL | let _ = std::mem::replace(s, String::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(s)` + +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` + --> $DIR/mem_replace.rs:24:13 + | +LL | let _ = std::mem::replace(s, Default::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(s)` + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/mem_replace_macro.rs b/src/tools/clippy/tests/ui/mem_replace_macro.rs new file mode 100644 index 00000000000..0c09344b80d --- /dev/null +++ b/src/tools/clippy/tests/ui/mem_replace_macro.rs @@ -0,0 +1,21 @@ +// aux-build:macro_rules.rs +#![warn(clippy::mem_replace_with_default)] + +#[macro_use] +extern crate macro_rules; + +macro_rules! take { + ($s:expr) => { + std::mem::replace($s, Default::default()) + }; +} + +fn replace_with_default() { + let s = &mut String::from("foo"); + take!(s); + take_external!(s); +} + +fn main() { + replace_with_default(); +} diff --git a/src/tools/clippy/tests/ui/mem_replace_macro.stderr b/src/tools/clippy/tests/ui/mem_replace_macro.stderr new file mode 100644 index 00000000000..4971a91050b --- /dev/null +++ b/src/tools/clippy/tests/ui/mem_replace_macro.stderr @@ -0,0 +1,14 @@ +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` + --> $DIR/mem_replace_macro.rs:9:9 + | +LL | std::mem::replace($s, Default::default()) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | take!(s); + | --------- in this macro invocation + | + = note: `-D clippy::mem-replace-with-default` implied by `-D warnings` + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/methods.rs b/src/tools/clippy/tests/ui/methods.rs new file mode 100644 index 00000000000..7880cf36415 --- /dev/null +++ b/src/tools/clippy/tests/ui/methods.rs @@ -0,0 +1,247 @@ +// aux-build:option_helpers.rs +// edition:2018 + +#![warn(clippy::all, clippy::pedantic)] +#![allow( + clippy::blacklisted_name, + clippy::default_trait_access, + clippy::missing_docs_in_private_items, + clippy::missing_safety_doc, + clippy::non_ascii_literal, + clippy::new_without_default, + clippy::needless_pass_by_value, + clippy::print_stdout, + clippy::must_use_candidate, + clippy::use_self, + clippy::useless_format, + clippy::wrong_self_convention, + clippy::unused_self, + unused +)] + +#[macro_use] +extern crate option_helpers; + +use std::collections::BTreeMap; +use std::collections::HashMap; +use std::collections::HashSet; +use std::collections::VecDeque; +use std::iter::FromIterator; +use std::ops::Mul; +use std::rc::{self, Rc}; +use std::sync::{self, Arc}; + +use option_helpers::IteratorFalsePositives; + +pub struct T; + +impl T { + pub fn add(self, other: T) -> T { + self + } + + // no error, not public interface + pub(crate) fn drop(&mut self) {} + + // no error, private function + fn neg(self) -> Self { + self + } + + // no error, private function + fn eq(&self, other: T) -> bool { + true + } + + // No error; self is a ref. + fn sub(&self, other: T) -> &T { + self + } + + // No error; different number of arguments. + fn div(self) -> T { + self + } + + // No error; wrong return type. + fn rem(self, other: T) {} + + // Fine + fn into_u32(self) -> u32 { + 0 + } + + fn into_u16(&self) -> u16 { + 0 + } + + fn to_something(self) -> u32 { + 0 + } + + fn new(self) -> Self { + unimplemented!(); + } +} + +pub struct T1; + +impl T1 { + // Shouldn't trigger lint as it is unsafe. + pub unsafe fn add(self, rhs: T1) -> T1 { + self + } + + // Should not trigger lint since this is an async function. + pub async fn next(&mut self) -> Option<T1> { + None + } +} + +struct Lt<'a> { + foo: &'a u32, +} + +impl<'a> Lt<'a> { + // The lifetime is different, but that’s irrelevant; see issue #734. + #[allow(clippy::needless_lifetimes)] + pub fn new<'b>(s: &'b str) -> Lt<'b> { + unimplemented!() + } +} + +struct Lt2<'a> { + foo: &'a u32, +} + +impl<'a> Lt2<'a> { + // The lifetime is different, but that’s irrelevant; see issue #734. + pub fn new(s: &str) -> Lt2 { + unimplemented!() + } +} + +struct Lt3<'a> { + foo: &'a u32, +} + +impl<'a> Lt3<'a> { + // The lifetime is different, but that’s irrelevant; see issue #734. + pub fn new() -> Lt3<'static> { + unimplemented!() + } +} + +#[derive(Clone, Copy)] +struct U; + +impl U { + fn new() -> Self { + U + } + // Ok because `U` is `Copy`. + fn to_something(self) -> u32 { + 0 + } +} + +struct V<T> { + _dummy: T, +} + +impl<T> V<T> { + fn new() -> Option<V<T>> { + None + } +} + +struct AsyncNew; + +impl AsyncNew { + async fn new() -> Option<Self> { + None + } +} + +struct BadNew; + +impl BadNew { + fn new() -> i32 { + 0 + } +} + +impl Mul<T> for T { + type Output = T; + // No error, obviously. + fn mul(self, other: T) -> T { + self + } +} + +/// Checks implementation of `FILTER_NEXT` lint. +#[rustfmt::skip] +fn filter_next() { + let v = vec![3, 2, 1, 0, -1, -2, -3]; + + // Single-line case. + let _ = v.iter().filter(|&x| *x < 0).next(); + + // Multi-line case. + let _ = v.iter().filter(|&x| { + *x < 0 + } + ).next(); + + // Check that hat we don't lint if the caller is not an `Iterator`. + let foo = IteratorFalsePositives { foo: 0 }; + let _ = foo.filter().next(); +} + +/// Checks implementation of `SEARCH_IS_SOME` lint. +#[rustfmt::skip] +fn search_is_some() { + let v = vec![3, 2, 1, 0, -1, -2, -3]; + let y = &&42; + + // Check `find().is_some()`, single-line case. + let _ = v.iter().find(|&x| *x < 0).is_some(); + let _ = (0..1).find(|x| **y == *x).is_some(); // one dereference less + let _ = (0..1).find(|x| *x == 0).is_some(); + let _ = v.iter().find(|x| **x == 0).is_some(); + + // Check `find().is_some()`, multi-line case. + let _ = v.iter().find(|&x| { + *x < 0 + } + ).is_some(); + + // Check `position().is_some()`, single-line case. + let _ = v.iter().position(|&x| x < 0).is_some(); + + // Check `position().is_some()`, multi-line case. + let _ = v.iter().position(|&x| { + x < 0 + } + ).is_some(); + + // Check `rposition().is_some()`, single-line case. + let _ = v.iter().rposition(|&x| x < 0).is_some(); + + // Check `rposition().is_some()`, multi-line case. + let _ = v.iter().rposition(|&x| { + x < 0 + } + ).is_some(); + + // Check that we don't lint if the caller is not an `Iterator`. + let foo = IteratorFalsePositives { foo: 0 }; + let _ = foo.find().is_some(); + let _ = foo.position().is_some(); + let _ = foo.rposition().is_some(); +} + +fn main() { + filter_next(); + search_is_some(); +} diff --git a/src/tools/clippy/tests/ui/methods.stderr b/src/tools/clippy/tests/ui/methods.stderr new file mode 100644 index 00000000000..01cf487ac14 --- /dev/null +++ b/src/tools/clippy/tests/ui/methods.stderr @@ -0,0 +1,109 @@ +error: defining a method called `add` on this type; consider implementing the `std::ops::Add` trait or choosing a less ambiguous name + --> $DIR/methods.rs:39:5 + | +LL | / pub fn add(self, other: T) -> T { +LL | | self +LL | | } + | |_____^ + | + = note: `-D clippy::should-implement-trait` implied by `-D warnings` + +error: methods called `new` usually return `Self` + --> $DIR/methods.rs:169:5 + | +LL | / fn new() -> i32 { +LL | | 0 +LL | | } + | |_____^ + | + = note: `-D clippy::new-ret-no-self` implied by `-D warnings` + +error: called `filter(p).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(p)` instead. + --> $DIR/methods.rs:188:13 + | +LL | let _ = v.iter().filter(|&x| *x < 0).next(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::filter-next` implied by `-D warnings` + = note: replace `filter(|&x| *x < 0).next()` with `find(|&x| *x < 0)` + +error: called `filter(p).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(p)` instead. + --> $DIR/methods.rs:191:13 + | +LL | let _ = v.iter().filter(|&x| { + | _____________^ +LL | | *x < 0 +LL | | } +LL | | ).next(); + | |___________________________^ + +error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. + --> $DIR/methods.rs:208:22 + | +LL | let _ = v.iter().find(|&x| *x < 0).is_some(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| *x < 0)` + | + = note: `-D clippy::search-is-some` implied by `-D warnings` + +error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. + --> $DIR/methods.rs:209:20 + | +LL | let _ = (0..1).find(|x| **y == *x).is_some(); // one dereference less + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| **y == x)` + +error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. + --> $DIR/methods.rs:210:20 + | +LL | let _ = (0..1).find(|x| *x == 0).is_some(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| x == 0)` + +error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. + --> $DIR/methods.rs:211:22 + | +LL | let _ = v.iter().find(|x| **x == 0).is_some(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| *x == 0)` + +error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. + --> $DIR/methods.rs:214:13 + | +LL | let _ = v.iter().find(|&x| { + | _____________^ +LL | | *x < 0 +LL | | } +LL | | ).is_some(); + | |______________________________^ + +error: called `is_some()` after searching an `Iterator` with position. This is more succinctly expressed by calling `any()`. + --> $DIR/methods.rs:220:22 + | +LL | let _ = v.iter().position(|&x| x < 0).is_some(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|&x| x < 0)` + +error: called `is_some()` after searching an `Iterator` with position. This is more succinctly expressed by calling `any()`. + --> $DIR/methods.rs:223:13 + | +LL | let _ = v.iter().position(|&x| { + | _____________^ +LL | | x < 0 +LL | | } +LL | | ).is_some(); + | |______________________________^ + +error: called `is_some()` after searching an `Iterator` with rposition. This is more succinctly expressed by calling `any()`. + --> $DIR/methods.rs:229:22 + | +LL | let _ = v.iter().rposition(|&x| x < 0).is_some(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|&x| x < 0)` + +error: called `is_some()` after searching an `Iterator` with rposition. This is more succinctly expressed by calling `any()`. + --> $DIR/methods.rs:232:13 + | +LL | let _ = v.iter().rposition(|&x| { + | _____________^ +LL | | x < 0 +LL | | } +LL | | ).is_some(); + | |______________________________^ + +error: aborting due to 13 previous errors + diff --git a/src/tools/clippy/tests/ui/min_max.rs b/src/tools/clippy/tests/ui/min_max.rs new file mode 100644 index 00000000000..8307d4b3019 --- /dev/null +++ b/src/tools/clippy/tests/ui/min_max.rs @@ -0,0 +1,33 @@ +#![warn(clippy::all)] + +use std::cmp::max as my_max; +use std::cmp::min as my_min; +use std::cmp::{max, min}; + +const LARGE: usize = 3; + +fn main() { + let x; + x = 2usize; + min(1, max(3, x)); + min(max(3, x), 1); + max(min(x, 1), 3); + max(3, min(x, 1)); + + my_max(3, my_min(x, 1)); + + min(3, max(1, x)); // ok, could be 1, 2 or 3 depending on x + + min(1, max(LARGE, x)); // no error, we don't lookup consts here + + let y = 2isize; + min(max(y, -1), 3); + + let s; + s = "Hello"; + + min("Apple", max("Zoo", s)); + max(min(s, "Apple"), "Zoo"); + + max("Apple", min(s, "Zoo")); // ok +} diff --git a/src/tools/clippy/tests/ui/min_max.stderr b/src/tools/clippy/tests/ui/min_max.stderr new file mode 100644 index 00000000000..b552c137f7c --- /dev/null +++ b/src/tools/clippy/tests/ui/min_max.stderr @@ -0,0 +1,46 @@ +error: this `min`/`max` combination leads to constant result + --> $DIR/min_max.rs:12:5 + | +LL | min(1, max(3, x)); + | ^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::min-max` implied by `-D warnings` + +error: this `min`/`max` combination leads to constant result + --> $DIR/min_max.rs:13:5 + | +LL | min(max(3, x), 1); + | ^^^^^^^^^^^^^^^^^ + +error: this `min`/`max` combination leads to constant result + --> $DIR/min_max.rs:14:5 + | +LL | max(min(x, 1), 3); + | ^^^^^^^^^^^^^^^^^ + +error: this `min`/`max` combination leads to constant result + --> $DIR/min_max.rs:15:5 + | +LL | max(3, min(x, 1)); + | ^^^^^^^^^^^^^^^^^ + +error: this `min`/`max` combination leads to constant result + --> $DIR/min_max.rs:17:5 + | +LL | my_max(3, my_min(x, 1)); + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: this `min`/`max` combination leads to constant result + --> $DIR/min_max.rs:29:5 + | +LL | min("Apple", max("Zoo", s)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this `min`/`max` combination leads to constant result + --> $DIR/min_max.rs:30:5 + | +LL | max(min(s, "Apple"), "Zoo"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/tests/ui/mismatched_target_os_non_unix.fixed b/src/tools/clippy/tests/ui/mismatched_target_os_non_unix.fixed new file mode 100644 index 00000000000..3ee77dcac31 --- /dev/null +++ b/src/tools/clippy/tests/ui/mismatched_target_os_non_unix.fixed @@ -0,0 +1,30 @@ +// run-rustfix + +#![warn(clippy::mismatched_target_os)] +#![allow(unused)] + +#[cfg(target_os = "cloudabi")] +fn cloudabi() {} + +#[cfg(target_os = "hermit")] +fn hermit() {} + +#[cfg(target_os = "wasi")] +fn wasi() {} + +#[cfg(target_os = "none")] +fn none() {} + +// list with conditions +#[cfg(all(not(any(windows, target_os = "cloudabi")), target_os = "wasi"))] +fn list() {} + +// windows is a valid target family, should be ignored +#[cfg(windows)] +fn windows() {} + +// correct use, should be ignored +#[cfg(target_os = "hermit")] +fn correct() {} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/mismatched_target_os_non_unix.rs b/src/tools/clippy/tests/ui/mismatched_target_os_non_unix.rs new file mode 100644 index 00000000000..9cc411418e4 --- /dev/null +++ b/src/tools/clippy/tests/ui/mismatched_target_os_non_unix.rs @@ -0,0 +1,30 @@ +// run-rustfix + +#![warn(clippy::mismatched_target_os)] +#![allow(unused)] + +#[cfg(cloudabi)] +fn cloudabi() {} + +#[cfg(hermit)] +fn hermit() {} + +#[cfg(wasi)] +fn wasi() {} + +#[cfg(none)] +fn none() {} + +// list with conditions +#[cfg(all(not(any(windows, cloudabi)), wasi))] +fn list() {} + +// windows is a valid target family, should be ignored +#[cfg(windows)] +fn windows() {} + +// correct use, should be ignored +#[cfg(target_os = "hermit")] +fn correct() {} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/mismatched_target_os_non_unix.stderr b/src/tools/clippy/tests/ui/mismatched_target_os_non_unix.stderr new file mode 100644 index 00000000000..78fc27752d2 --- /dev/null +++ b/src/tools/clippy/tests/ui/mismatched_target_os_non_unix.stderr @@ -0,0 +1,51 @@ +error: operating system used in target family position + --> $DIR/mismatched_target_os_non_unix.rs:6:1 + | +LL | #[cfg(cloudabi)] + | ^^^^^^--------^^ + | | + | help: try: `target_os = "cloudabi"` + | + = note: `-D clippy::mismatched-target-os` implied by `-D warnings` + +error: operating system used in target family position + --> $DIR/mismatched_target_os_non_unix.rs:9:1 + | +LL | #[cfg(hermit)] + | ^^^^^^------^^ + | | + | help: try: `target_os = "hermit"` + +error: operating system used in target family position + --> $DIR/mismatched_target_os_non_unix.rs:12:1 + | +LL | #[cfg(wasi)] + | ^^^^^^----^^ + | | + | help: try: `target_os = "wasi"` + +error: operating system used in target family position + --> $DIR/mismatched_target_os_non_unix.rs:15:1 + | +LL | #[cfg(none)] + | ^^^^^^----^^ + | | + | help: try: `target_os = "none"` + +error: operating system used in target family position + --> $DIR/mismatched_target_os_non_unix.rs:19:1 + | +LL | #[cfg(all(not(any(windows, cloudabi)), wasi))] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL | #[cfg(all(not(any(windows, target_os = "cloudabi")), wasi))] + | ^^^^^^^^^^^^^^^^^^^^^^ +help: try + | +LL | #[cfg(all(not(any(windows, cloudabi)), target_os = "wasi"))] + | ^^^^^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/mismatched_target_os_unix.fixed b/src/tools/clippy/tests/ui/mismatched_target_os_unix.fixed new file mode 100644 index 00000000000..7d9d406d99d --- /dev/null +++ b/src/tools/clippy/tests/ui/mismatched_target_os_unix.fixed @@ -0,0 +1,62 @@ +// run-rustfix + +#![warn(clippy::mismatched_target_os)] +#![allow(unused)] + +#[cfg(target_os = "linux")] +fn linux() {} + +#[cfg(target_os = "freebsd")] +fn freebsd() {} + +#[cfg(target_os = "dragonfly")] +fn dragonfly() {} + +#[cfg(target_os = "openbsd")] +fn openbsd() {} + +#[cfg(target_os = "netbsd")] +fn netbsd() {} + +#[cfg(target_os = "macos")] +fn macos() {} + +#[cfg(target_os = "ios")] +fn ios() {} + +#[cfg(target_os = "android")] +fn android() {} + +#[cfg(target_os = "emscripten")] +fn emscripten() {} + +#[cfg(target_os = "fuchsia")] +fn fuchsia() {} + +#[cfg(target_os = "haiku")] +fn haiku() {} + +#[cfg(target_os = "illumos")] +fn illumos() {} + +#[cfg(target_os = "l4re")] +fn l4re() {} + +#[cfg(target_os = "redox")] +fn redox() {} + +#[cfg(target_os = "solaris")] +fn solaris() {} + +#[cfg(target_os = "vxworks")] +fn vxworks() {} + +// list with conditions +#[cfg(all(not(any(target_os = "solaris", target_os = "linux")), target_os = "freebsd"))] +fn list() {} + +// correct use, should be ignored +#[cfg(target_os = "freebsd")] +fn correct() {} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/mismatched_target_os_unix.rs b/src/tools/clippy/tests/ui/mismatched_target_os_unix.rs new file mode 100644 index 00000000000..c1177f1eedc --- /dev/null +++ b/src/tools/clippy/tests/ui/mismatched_target_os_unix.rs @@ -0,0 +1,62 @@ +// run-rustfix + +#![warn(clippy::mismatched_target_os)] +#![allow(unused)] + +#[cfg(linux)] +fn linux() {} + +#[cfg(freebsd)] +fn freebsd() {} + +#[cfg(dragonfly)] +fn dragonfly() {} + +#[cfg(openbsd)] +fn openbsd() {} + +#[cfg(netbsd)] +fn netbsd() {} + +#[cfg(macos)] +fn macos() {} + +#[cfg(ios)] +fn ios() {} + +#[cfg(android)] +fn android() {} + +#[cfg(emscripten)] +fn emscripten() {} + +#[cfg(fuchsia)] +fn fuchsia() {} + +#[cfg(haiku)] +fn haiku() {} + +#[cfg(illumos)] +fn illumos() {} + +#[cfg(l4re)] +fn l4re() {} + +#[cfg(redox)] +fn redox() {} + +#[cfg(solaris)] +fn solaris() {} + +#[cfg(vxworks)] +fn vxworks() {} + +// list with conditions +#[cfg(all(not(any(solaris, linux)), freebsd))] +fn list() {} + +// correct use, should be ignored +#[cfg(target_os = "freebsd")] +fn correct() {} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/mismatched_target_os_unix.stderr b/src/tools/clippy/tests/ui/mismatched_target_os_unix.stderr new file mode 100644 index 00000000000..fe9aeedb59c --- /dev/null +++ b/src/tools/clippy/tests/ui/mismatched_target_os_unix.stderr @@ -0,0 +1,183 @@ +error: operating system used in target family position + --> $DIR/mismatched_target_os_unix.rs:6:1 + | +LL | #[cfg(linux)] + | ^^^^^^-----^^ + | | + | help: try: `target_os = "linux"` + | + = note: `-D clippy::mismatched-target-os` implied by `-D warnings` + = help: Did you mean `unix`? + +error: operating system used in target family position + --> $DIR/mismatched_target_os_unix.rs:9:1 + | +LL | #[cfg(freebsd)] + | ^^^^^^-------^^ + | | + | help: try: `target_os = "freebsd"` + | + = help: Did you mean `unix`? + +error: operating system used in target family position + --> $DIR/mismatched_target_os_unix.rs:12:1 + | +LL | #[cfg(dragonfly)] + | ^^^^^^---------^^ + | | + | help: try: `target_os = "dragonfly"` + | + = help: Did you mean `unix`? + +error: operating system used in target family position + --> $DIR/mismatched_target_os_unix.rs:15:1 + | +LL | #[cfg(openbsd)] + | ^^^^^^-------^^ + | | + | help: try: `target_os = "openbsd"` + | + = help: Did you mean `unix`? + +error: operating system used in target family position + --> $DIR/mismatched_target_os_unix.rs:18:1 + | +LL | #[cfg(netbsd)] + | ^^^^^^------^^ + | | + | help: try: `target_os = "netbsd"` + | + = help: Did you mean `unix`? + +error: operating system used in target family position + --> $DIR/mismatched_target_os_unix.rs:21:1 + | +LL | #[cfg(macos)] + | ^^^^^^-----^^ + | | + | help: try: `target_os = "macos"` + | + = help: Did you mean `unix`? + +error: operating system used in target family position + --> $DIR/mismatched_target_os_unix.rs:24:1 + | +LL | #[cfg(ios)] + | ^^^^^^---^^ + | | + | help: try: `target_os = "ios"` + | + = help: Did you mean `unix`? + +error: operating system used in target family position + --> $DIR/mismatched_target_os_unix.rs:27:1 + | +LL | #[cfg(android)] + | ^^^^^^-------^^ + | | + | help: try: `target_os = "android"` + | + = help: Did you mean `unix`? + +error: operating system used in target family position + --> $DIR/mismatched_target_os_unix.rs:30:1 + | +LL | #[cfg(emscripten)] + | ^^^^^^----------^^ + | | + | help: try: `target_os = "emscripten"` + | + = help: Did you mean `unix`? + +error: operating system used in target family position + --> $DIR/mismatched_target_os_unix.rs:33:1 + | +LL | #[cfg(fuchsia)] + | ^^^^^^-------^^ + | | + | help: try: `target_os = "fuchsia"` + | + = help: Did you mean `unix`? + +error: operating system used in target family position + --> $DIR/mismatched_target_os_unix.rs:36:1 + | +LL | #[cfg(haiku)] + | ^^^^^^-----^^ + | | + | help: try: `target_os = "haiku"` + | + = help: Did you mean `unix`? + +error: operating system used in target family position + --> $DIR/mismatched_target_os_unix.rs:39:1 + | +LL | #[cfg(illumos)] + | ^^^^^^-------^^ + | | + | help: try: `target_os = "illumos"` + | + = help: Did you mean `unix`? + +error: operating system used in target family position + --> $DIR/mismatched_target_os_unix.rs:42:1 + | +LL | #[cfg(l4re)] + | ^^^^^^----^^ + | | + | help: try: `target_os = "l4re"` + | + = help: Did you mean `unix`? + +error: operating system used in target family position + --> $DIR/mismatched_target_os_unix.rs:45:1 + | +LL | #[cfg(redox)] + | ^^^^^^-----^^ + | | + | help: try: `target_os = "redox"` + | + = help: Did you mean `unix`? + +error: operating system used in target family position + --> $DIR/mismatched_target_os_unix.rs:48:1 + | +LL | #[cfg(solaris)] + | ^^^^^^-------^^ + | | + | help: try: `target_os = "solaris"` + | + = help: Did you mean `unix`? + +error: operating system used in target family position + --> $DIR/mismatched_target_os_unix.rs:51:1 + | +LL | #[cfg(vxworks)] + | ^^^^^^-------^^ + | | + | help: try: `target_os = "vxworks"` + | + = help: Did you mean `unix`? + +error: operating system used in target family position + --> $DIR/mismatched_target_os_unix.rs:55:1 + | +LL | #[cfg(all(not(any(solaris, linux)), freebsd))] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: Did you mean `unix`? +help: try + | +LL | #[cfg(all(not(any(target_os = "solaris", linux)), freebsd))] + | ^^^^^^^^^^^^^^^^^^^^^ +help: try + | +LL | #[cfg(all(not(any(solaris, target_os = "linux")), freebsd))] + | ^^^^^^^^^^^^^^^^^^^ +help: try + | +LL | #[cfg(all(not(any(solaris, linux)), target_os = "freebsd"))] + | ^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 17 previous errors + diff --git a/src/tools/clippy/tests/ui/missing-doc-crate-missing.rs b/src/tools/clippy/tests/ui/missing-doc-crate-missing.rs new file mode 100644 index 00000000000..51fd57df8df --- /dev/null +++ b/src/tools/clippy/tests/ui/missing-doc-crate-missing.rs @@ -0,0 +1,3 @@ +#![warn(clippy::missing_docs_in_private_items)] + +fn main() {} diff --git a/src/tools/clippy/tests/ui/missing-doc-crate-missing.stderr b/src/tools/clippy/tests/ui/missing-doc-crate-missing.stderr new file mode 100644 index 00000000000..da46f988636 --- /dev/null +++ b/src/tools/clippy/tests/ui/missing-doc-crate-missing.stderr @@ -0,0 +1,12 @@ +error: missing documentation for crate + --> $DIR/missing-doc-crate-missing.rs:1:1 + | +LL | / #![warn(clippy::missing_docs_in_private_items)] +LL | | +LL | | fn main() {} + | |____________^ + | + = note: `-D clippy::missing-docs-in-private-items` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/missing-doc-crate.rs b/src/tools/clippy/tests/ui/missing-doc-crate.rs new file mode 100644 index 00000000000..04711f86488 --- /dev/null +++ b/src/tools/clippy/tests/ui/missing-doc-crate.rs @@ -0,0 +1,5 @@ +#![warn(clippy::missing_docs_in_private_items)] +#![feature(external_doc)] +#![doc(include = "../../README.md")] + +fn main() {} diff --git a/src/tools/clippy/tests/ui/missing-doc-crate.stderr b/src/tools/clippy/tests/ui/missing-doc-crate.stderr new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/src/tools/clippy/tests/ui/missing-doc-crate.stderr diff --git a/src/tools/clippy/tests/ui/missing-doc-impl.rs b/src/tools/clippy/tests/ui/missing-doc-impl.rs new file mode 100644 index 00000000000..57af84dcdf4 --- /dev/null +++ b/src/tools/clippy/tests/ui/missing-doc-impl.rs @@ -0,0 +1,87 @@ +#![warn(clippy::missing_docs_in_private_items)] +#![allow(dead_code)] +#![feature(associated_type_defaults)] + +//! Some garbage docs for the crate here +#![doc = "More garbage"] + +struct Foo { + a: isize, + b: isize, +} + +pub struct PubFoo { + pub a: isize, + b: isize, +} + +#[allow(clippy::missing_docs_in_private_items)] +pub struct PubFoo2 { + pub a: isize, + pub c: isize, +} + +/// dox +pub trait A { + /// dox + fn foo(&self); + /// dox + fn foo_with_impl(&self) {} +} + +#[allow(clippy::missing_docs_in_private_items)] +trait B { + fn foo(&self); + fn foo_with_impl(&self) {} +} + +pub trait C { + fn foo(&self); + fn foo_with_impl(&self) {} +} + +#[allow(clippy::missing_docs_in_private_items)] +pub trait D { + fn dummy(&self) {} +} + +/// dox +pub trait E: Sized { + type AssociatedType; + type AssociatedTypeDef = Self; + + /// dox + type DocumentedType; + /// dox + type DocumentedTypeDef = Self; + /// dox + fn dummy(&self) {} +} + +impl Foo { + pub fn foo() {} + fn bar() {} +} + +impl PubFoo { + pub fn foo() {} + /// dox + pub fn foo1() {} + fn foo2() {} + #[allow(clippy::missing_docs_in_private_items)] + pub fn foo3() {} +} + +#[allow(clippy::missing_docs_in_private_items)] +trait F { + fn a(); + fn b(&self); +} + +// should need to redefine documentation for implementations of traits +impl F for Foo { + fn a() {} + fn b(&self) {} +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/missing-doc-impl.stderr b/src/tools/clippy/tests/ui/missing-doc-impl.stderr new file mode 100644 index 00000000000..9656a39abce --- /dev/null +++ b/src/tools/clippy/tests/ui/missing-doc-impl.stderr @@ -0,0 +1,103 @@ +error: missing documentation for a struct + --> $DIR/missing-doc-impl.rs:8:1 + | +LL | / struct Foo { +LL | | a: isize, +LL | | b: isize, +LL | | } + | |_^ + | + = note: `-D clippy::missing-docs-in-private-items` implied by `-D warnings` + +error: missing documentation for a struct field + --> $DIR/missing-doc-impl.rs:9:5 + | +LL | a: isize, + | ^^^^^^^^ + +error: missing documentation for a struct field + --> $DIR/missing-doc-impl.rs:10:5 + | +LL | b: isize, + | ^^^^^^^^ + +error: missing documentation for a struct + --> $DIR/missing-doc-impl.rs:13:1 + | +LL | / pub struct PubFoo { +LL | | pub a: isize, +LL | | b: isize, +LL | | } + | |_^ + +error: missing documentation for a struct field + --> $DIR/missing-doc-impl.rs:14:5 + | +LL | pub a: isize, + | ^^^^^^^^^^^^ + +error: missing documentation for a struct field + --> $DIR/missing-doc-impl.rs:15:5 + | +LL | b: isize, + | ^^^^^^^^ + +error: missing documentation for a trait + --> $DIR/missing-doc-impl.rs:38:1 + | +LL | / pub trait C { +LL | | fn foo(&self); +LL | | fn foo_with_impl(&self) {} +LL | | } + | |_^ + +error: missing documentation for a trait method + --> $DIR/missing-doc-impl.rs:39:5 + | +LL | fn foo(&self); + | ^^^^^^^^^^^^^^ + +error: missing documentation for a trait method + --> $DIR/missing-doc-impl.rs:40:5 + | +LL | fn foo_with_impl(&self) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: missing documentation for an associated type + --> $DIR/missing-doc-impl.rs:50:5 + | +LL | type AssociatedType; + | ^^^^^^^^^^^^^^^^^^^^ + +error: missing documentation for an associated type + --> $DIR/missing-doc-impl.rs:51:5 + | +LL | type AssociatedTypeDef = Self; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: missing documentation for a method + --> $DIR/missing-doc-impl.rs:62:5 + | +LL | pub fn foo() {} + | ^^^^^^^^^^^^^^^ + +error: missing documentation for a method + --> $DIR/missing-doc-impl.rs:63:5 + | +LL | fn bar() {} + | ^^^^^^^^^^^ + +error: missing documentation for a method + --> $DIR/missing-doc-impl.rs:67:5 + | +LL | pub fn foo() {} + | ^^^^^^^^^^^^^^^ + +error: missing documentation for a method + --> $DIR/missing-doc-impl.rs:70:5 + | +LL | fn foo2() {} + | ^^^^^^^^^^^^ + +error: aborting due to 15 previous errors + diff --git a/src/tools/clippy/tests/ui/missing-doc.rs b/src/tools/clippy/tests/ui/missing-doc.rs new file mode 100644 index 00000000000..a9bf7140a1e --- /dev/null +++ b/src/tools/clippy/tests/ui/missing-doc.rs @@ -0,0 +1,102 @@ +#![warn(clippy::missing_docs_in_private_items)] +// When denying at the crate level, be sure to not get random warnings from the +// injected intrinsics by the compiler. +#![allow(dead_code)] +#![feature(global_asm)] + +//! Some garbage docs for the crate here +#![doc = "More garbage"] + +type Typedef = String; +pub type PubTypedef = String; + +mod module_no_dox {} +pub mod pub_module_no_dox {} + +/// dox +pub fn foo() {} +pub fn foo2() {} +fn foo3() {} +#[allow(clippy::missing_docs_in_private_items)] +pub fn foo4() {} + +// It sure is nice if doc(hidden) implies allow(missing_docs), and that it +// applies recursively +#[doc(hidden)] +mod a { + pub fn baz() {} + pub mod b { + pub fn baz() {} + } +} + +enum Baz { + BazA { a: isize, b: isize }, + BarB, +} + +pub enum PubBaz { + PubBazA { a: isize }, +} + +/// dox +pub enum PubBaz2 { + /// dox + PubBaz2A { + /// dox + a: isize, + }, +} + +#[allow(clippy::missing_docs_in_private_items)] +pub enum PubBaz3 { + PubBaz3A { b: isize }, +} + +#[doc(hidden)] +pub fn baz() {} + +const FOO: u32 = 0; +/// dox +pub const FOO1: u32 = 0; +#[allow(clippy::missing_docs_in_private_items)] +pub const FOO2: u32 = 0; +#[doc(hidden)] +pub const FOO3: u32 = 0; +pub const FOO4: u32 = 0; + +static BAR: u32 = 0; +/// dox +pub static BAR1: u32 = 0; +#[allow(clippy::missing_docs_in_private_items)] +pub static BAR2: u32 = 0; +#[doc(hidden)] +pub static BAR3: u32 = 0; +pub static BAR4: u32 = 0; + +mod internal_impl { + /// dox + pub fn documented() {} + pub fn undocumented1() {} + pub fn undocumented2() {} + fn undocumented3() {} + /// dox + pub mod globbed { + /// dox + pub fn also_documented() {} + pub fn also_undocumented1() {} + fn also_undocumented2() {} + } +} +/// dox +pub mod public_interface { + pub use internal_impl::documented as foo; + pub use internal_impl::globbed::*; + pub use internal_impl::undocumented1 as bar; + pub use internal_impl::{documented, undocumented2}; +} + +fn main() {} + +// Ensure global asm doesn't require documentation. +global_asm! { "" } diff --git a/src/tools/clippy/tests/ui/missing-doc.stderr b/src/tools/clippy/tests/ui/missing-doc.stderr new file mode 100644 index 00000000000..a876dc078eb --- /dev/null +++ b/src/tools/clippy/tests/ui/missing-doc.stderr @@ -0,0 +1,159 @@ +error: missing documentation for a type alias + --> $DIR/missing-doc.rs:10:1 + | +LL | type Typedef = String; + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::missing-docs-in-private-items` implied by `-D warnings` + +error: missing documentation for a type alias + --> $DIR/missing-doc.rs:11:1 + | +LL | pub type PubTypedef = String; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: missing documentation for a module + --> $DIR/missing-doc.rs:13:1 + | +LL | mod module_no_dox {} + | ^^^^^^^^^^^^^^^^^^^^ + +error: missing documentation for a module + --> $DIR/missing-doc.rs:14:1 + | +LL | pub mod pub_module_no_dox {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: missing documentation for a function + --> $DIR/missing-doc.rs:18:1 + | +LL | pub fn foo2() {} + | ^^^^^^^^^^^^^^^^ + +error: missing documentation for a function + --> $DIR/missing-doc.rs:19:1 + | +LL | fn foo3() {} + | ^^^^^^^^^^^^ + +error: missing documentation for an enum + --> $DIR/missing-doc.rs:33:1 + | +LL | / enum Baz { +LL | | BazA { a: isize, b: isize }, +LL | | BarB, +LL | | } + | |_^ + +error: missing documentation for a variant + --> $DIR/missing-doc.rs:34:5 + | +LL | BazA { a: isize, b: isize }, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: missing documentation for a struct field + --> $DIR/missing-doc.rs:34:12 + | +LL | BazA { a: isize, b: isize }, + | ^^^^^^^^ + +error: missing documentation for a struct field + --> $DIR/missing-doc.rs:34:22 + | +LL | BazA { a: isize, b: isize }, + | ^^^^^^^^ + +error: missing documentation for a variant + --> $DIR/missing-doc.rs:35:5 + | +LL | BarB, + | ^^^^ + +error: missing documentation for an enum + --> $DIR/missing-doc.rs:38:1 + | +LL | / pub enum PubBaz { +LL | | PubBazA { a: isize }, +LL | | } + | |_^ + +error: missing documentation for a variant + --> $DIR/missing-doc.rs:39:5 + | +LL | PubBazA { a: isize }, + | ^^^^^^^^^^^^^^^^^^^^ + +error: missing documentation for a struct field + --> $DIR/missing-doc.rs:39:15 + | +LL | PubBazA { a: isize }, + | ^^^^^^^^ + +error: missing documentation for a constant + --> $DIR/missing-doc.rs:59:1 + | +LL | const FOO: u32 = 0; + | ^^^^^^^^^^^^^^^^^^^ + +error: missing documentation for a constant + --> $DIR/missing-doc.rs:66:1 + | +LL | pub const FOO4: u32 = 0; + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: missing documentation for a static + --> $DIR/missing-doc.rs:68:1 + | +LL | static BAR: u32 = 0; + | ^^^^^^^^^^^^^^^^^^^^ + +error: missing documentation for a static + --> $DIR/missing-doc.rs:75:1 + | +LL | pub static BAR4: u32 = 0; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: missing documentation for a module + --> $DIR/missing-doc.rs:77:1 + | +LL | / mod internal_impl { +LL | | /// dox +LL | | pub fn documented() {} +LL | | pub fn undocumented1() {} +... | +LL | | } +LL | | } + | |_^ + +error: missing documentation for a function + --> $DIR/missing-doc.rs:80:5 + | +LL | pub fn undocumented1() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: missing documentation for a function + --> $DIR/missing-doc.rs:81:5 + | +LL | pub fn undocumented2() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: missing documentation for a function + --> $DIR/missing-doc.rs:82:5 + | +LL | fn undocumented3() {} + | ^^^^^^^^^^^^^^^^^^^^^ + +error: missing documentation for a function + --> $DIR/missing-doc.rs:87:9 + | +LL | pub fn also_undocumented1() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: missing documentation for a function + --> $DIR/missing-doc.rs:88:9 + | +LL | fn also_undocumented2() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 24 previous errors + diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs b/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs new file mode 100644 index 00000000000..ba352ef9ee9 --- /dev/null +++ b/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs @@ -0,0 +1,103 @@ +//! False-positive tests to ensure we don't suggest `const` for things where it would cause a +//! compilation error. +//! The .stderr output of this test should be empty. Otherwise it's a bug somewhere. + +#![warn(clippy::missing_const_for_fn)] +#![allow(incomplete_features)] +#![feature(start, const_generics)] + +struct Game; + +// This should not be linted because it's already const +const fn already_const() -> i32 { + 32 +} + +impl Game { + // This should not be linted because it's already const + pub const fn already_const() -> i32 { + 32 + } +} + +// Allowing on this function, because it would lint, which we don't want in this case. +#[allow(clippy::missing_const_for_fn)] +fn random() -> u32 { + 42 +} + +// We should not suggest to make this function `const` because `random()` is non-const +fn random_caller() -> u32 { + random() +} + +static Y: u32 = 0; + +// We should not suggest to make this function `const` because const functions are not allowed to +// refer to a static variable +fn get_y() -> u32 { + Y + //~^ ERROR E0013 +} + +// Don't lint entrypoint functions +#[start] +fn init(num: isize, something: *const *const u8) -> isize { + 1 +} + +trait Foo { + // This should not be suggested to be made const + // (rustc doesn't allow const trait methods) + fn f() -> u32; + + // This should not be suggested to be made const either + fn g() -> u32 { + 33 + } +} + +// Don't lint in external macros (derive) +#[derive(PartialEq, Eq)] +struct Point(isize, isize); + +impl std::ops::Add for Point { + type Output = Self; + + // Don't lint in trait impls of derived methods + fn add(self, other: Self) -> Self { + Point(self.0 + other.0, self.1 + other.1) + } +} + +mod with_drop { + pub struct A; + pub struct B; + impl Drop for A { + fn drop(&mut self) {} + } + + impl A { + // This can not be const because the type implements `Drop`. + pub fn a(self) -> B { + B + } + } + + impl B { + // This can not be const because `a` implements `Drop`. + pub fn a(self, a: A) -> B { + B + } + } +} + +fn const_generic_params<T, const N: usize>(t: &[T; N]) -> &[T; N] { + t +} + +fn const_generic_return<T, const N: usize>(t: &[T]) -> &[T; N] { + let p = t.as_ptr() as *const [T; N]; + + unsafe { &*p } +} diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.stderr b/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.stderr new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.stderr diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs new file mode 100644 index 00000000000..c6f44b7daa3 --- /dev/null +++ b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs @@ -0,0 +1,74 @@ +#![warn(clippy::missing_const_for_fn)] +#![allow(incomplete_features, clippy::let_and_return)] +#![feature(const_generics)] + +use std::mem::transmute; + +struct Game { + guess: i32, +} + +impl Game { + // Could be const + pub fn new() -> Self { + Self { guess: 42 } + } + + fn const_generic_params<'a, T, const N: usize>(&self, b: &'a [T; N]) -> &'a [T; N] { + b + } +} + +// Could be const +fn one() -> i32 { + 1 +} + +// Could also be const +fn two() -> i32 { + let abc = 2; + abc +} + +// Could be const (since Rust 1.39) +fn string() -> String { + String::new() +} + +// Could be const +unsafe fn four() -> i32 { + 4 +} + +// Could also be const +fn generic<T>(t: T) -> T { + t +} + +fn sub(x: u32) -> usize { + unsafe { transmute(&x) } +} + +// NOTE: This is currently not yet allowed to be const +// Once implemented, Clippy should be able to suggest this as const, too. +fn generic_arr<T: Copy>(t: [T; 1]) -> T { + t[0] +} + +mod with_drop { + pub struct A; + pub struct B; + impl Drop for A { + fn drop(&mut self) {} + } + + impl B { + // This can be const, because `a` is passed by reference + pub fn b(self, a: &A) -> B { + B + } + } +} + +// Should not be const +fn main() {} diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr new file mode 100644 index 00000000000..8dde56cd79f --- /dev/null +++ b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr @@ -0,0 +1,77 @@ +error: this could be a `const fn` + --> $DIR/could_be_const.rs:13:5 + | +LL | / pub fn new() -> Self { +LL | | Self { guess: 42 } +LL | | } + | |_____^ + | + = note: `-D clippy::missing-const-for-fn` implied by `-D warnings` + +error: this could be a `const fn` + --> $DIR/could_be_const.rs:17:5 + | +LL | / fn const_generic_params<'a, T, const N: usize>(&self, b: &'a [T; N]) -> &'a [T; N] { +LL | | b +LL | | } + | |_____^ + +error: this could be a `const fn` + --> $DIR/could_be_const.rs:23:1 + | +LL | / fn one() -> i32 { +LL | | 1 +LL | | } + | |_^ + +error: this could be a `const fn` + --> $DIR/could_be_const.rs:28:1 + | +LL | / fn two() -> i32 { +LL | | let abc = 2; +LL | | abc +LL | | } + | |_^ + +error: this could be a `const fn` + --> $DIR/could_be_const.rs:34:1 + | +LL | / fn string() -> String { +LL | | String::new() +LL | | } + | |_^ + +error: this could be a `const fn` + --> $DIR/could_be_const.rs:39:1 + | +LL | / unsafe fn four() -> i32 { +LL | | 4 +LL | | } + | |_^ + +error: this could be a `const fn` + --> $DIR/could_be_const.rs:44:1 + | +LL | / fn generic<T>(t: T) -> T { +LL | | t +LL | | } + | |_^ + +error: this could be a `const fn` + --> $DIR/could_be_const.rs:48:1 + | +LL | / fn sub(x: u32) -> usize { +LL | | unsafe { transmute(&x) } +LL | | } + | |_^ + +error: this could be a `const fn` + --> $DIR/could_be_const.rs:67:9 + | +LL | / pub fn b(self, a: &A) -> B { +LL | | B +LL | | } + | |_________^ + +error: aborting due to 9 previous errors + diff --git a/src/tools/clippy/tests/ui/missing_inline.rs b/src/tools/clippy/tests/ui/missing_inline.rs new file mode 100644 index 00000000000..b73b24b8e0a --- /dev/null +++ b/src/tools/clippy/tests/ui/missing_inline.rs @@ -0,0 +1,66 @@ +#![warn(clippy::missing_inline_in_public_items)] +#![crate_type = "dylib"] +// When denying at the crate level, be sure to not get random warnings from the +// injected intrinsics by the compiler. +#![allow(dead_code, non_snake_case)] + +type Typedef = String; +pub type PubTypedef = String; + +struct Foo {} // ok +pub struct PubFoo {} // ok +enum FooE {} // ok +pub enum PubFooE {} // ok + +mod module {} // ok +pub mod pub_module {} // ok + +fn foo() {} +pub fn pub_foo() {} // missing #[inline] +#[inline] +pub fn pub_foo_inline() {} // ok +#[inline(always)] +pub fn pub_foo_inline_always() {} // ok + +#[allow(clippy::missing_inline_in_public_items)] +pub fn pub_foo_no_inline() {} + +trait Bar { + fn Bar_a(); // ok + fn Bar_b() {} // ok +} + +pub trait PubBar { + fn PubBar_a(); // ok + fn PubBar_b() {} // missing #[inline] + #[inline] + fn PubBar_c() {} // ok +} + +// none of these need inline because Foo is not exported +impl PubBar for Foo { + fn PubBar_a() {} // ok + fn PubBar_b() {} // ok + fn PubBar_c() {} // ok +} + +// all of these need inline because PubFoo is exported +impl PubBar for PubFoo { + fn PubBar_a() {} // missing #[inline] + fn PubBar_b() {} // missing #[inline] + fn PubBar_c() {} // missing #[inline] +} + +// do not need inline because Foo is not exported +impl Foo { + fn FooImpl() {} // ok +} + +// need inline because PubFoo is exported +impl PubFoo { + pub fn PubFooImpl() {} // missing #[inline] +} + +// do not lint this since users cannot control the external code +#[derive(Debug)] +pub struct S {} diff --git a/src/tools/clippy/tests/ui/missing_inline.stderr b/src/tools/clippy/tests/ui/missing_inline.stderr new file mode 100644 index 00000000000..40b92b7647b --- /dev/null +++ b/src/tools/clippy/tests/ui/missing_inline.stderr @@ -0,0 +1,40 @@ +error: missing `#[inline]` for a function + --> $DIR/missing_inline.rs:19:1 + | +LL | pub fn pub_foo() {} // missing #[inline] + | ^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::missing-inline-in-public-items` implied by `-D warnings` + +error: missing `#[inline]` for a default trait method + --> $DIR/missing_inline.rs:35:5 + | +LL | fn PubBar_b() {} // missing #[inline] + | ^^^^^^^^^^^^^^^^ + +error: missing `#[inline]` for a method + --> $DIR/missing_inline.rs:49:5 + | +LL | fn PubBar_a() {} // missing #[inline] + | ^^^^^^^^^^^^^^^^ + +error: missing `#[inline]` for a method + --> $DIR/missing_inline.rs:50:5 + | +LL | fn PubBar_b() {} // missing #[inline] + | ^^^^^^^^^^^^^^^^ + +error: missing `#[inline]` for a method + --> $DIR/missing_inline.rs:51:5 + | +LL | fn PubBar_c() {} // missing #[inline] + | ^^^^^^^^^^^^^^^^ + +error: missing `#[inline]` for a method + --> $DIR/missing_inline.rs:61:5 + | +LL | pub fn PubFooImpl() {} // missing #[inline] + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/mistyped_literal_suffix.fixed b/src/tools/clippy/tests/ui/mistyped_literal_suffix.fixed new file mode 100644 index 00000000000..baee7735730 --- /dev/null +++ b/src/tools/clippy/tests/ui/mistyped_literal_suffix.fixed @@ -0,0 +1,24 @@ +// run-rustfix + +#![allow(dead_code, unused_variables, clippy::excessive_precision)] + +fn main() { + let fail14 = 2_i32; + let fail15 = 4_i64; + let fail16 = 7_i8; // + let fail17 = 23_i16; // + let ok18 = 23_128; + + let fail20 = 2_i8; // + let fail21 = 4_i16; // + + let fail24 = 12.34_f64; + let fail25 = 1E2_f32; + let fail26 = 43E7_f64; + let fail27 = 243E17_f32; + #[allow(overflowing_literals)] + let fail28 = 241_251_235E723_f64; + let fail29 = 42_279.911_f32; + + let _ = 1.123_45E1_f32; +} diff --git a/src/tools/clippy/tests/ui/mistyped_literal_suffix.rs b/src/tools/clippy/tests/ui/mistyped_literal_suffix.rs new file mode 100644 index 00000000000..6de447f4021 --- /dev/null +++ b/src/tools/clippy/tests/ui/mistyped_literal_suffix.rs @@ -0,0 +1,24 @@ +// run-rustfix + +#![allow(dead_code, unused_variables, clippy::excessive_precision)] + +fn main() { + let fail14 = 2_32; + let fail15 = 4_64; + let fail16 = 7_8; // + let fail17 = 23_16; // + let ok18 = 23_128; + + let fail20 = 2__8; // + let fail21 = 4___16; // + + let fail24 = 12.34_64; + let fail25 = 1E2_32; + let fail26 = 43E7_64; + let fail27 = 243E17_32; + #[allow(overflowing_literals)] + let fail28 = 241251235E723_64; + let fail29 = 42279.911_32; + + let _ = 1.12345E1_32; +} diff --git a/src/tools/clippy/tests/ui/mistyped_literal_suffix.stderr b/src/tools/clippy/tests/ui/mistyped_literal_suffix.stderr new file mode 100644 index 00000000000..48a7ae90494 --- /dev/null +++ b/src/tools/clippy/tests/ui/mistyped_literal_suffix.stderr @@ -0,0 +1,82 @@ +error: mistyped literal suffix + --> $DIR/mistyped_literal_suffix.rs:6:18 + | +LL | let fail14 = 2_32; + | ^^^^ help: did you mean to write: `2_i32` + | + = note: `#[deny(clippy::mistyped_literal_suffixes)]` on by default + +error: mistyped literal suffix + --> $DIR/mistyped_literal_suffix.rs:7:18 + | +LL | let fail15 = 4_64; + | ^^^^ help: did you mean to write: `4_i64` + +error: mistyped literal suffix + --> $DIR/mistyped_literal_suffix.rs:8:18 + | +LL | let fail16 = 7_8; // + | ^^^ help: did you mean to write: `7_i8` + +error: mistyped literal suffix + --> $DIR/mistyped_literal_suffix.rs:9:18 + | +LL | let fail17 = 23_16; // + | ^^^^^ help: did you mean to write: `23_i16` + +error: mistyped literal suffix + --> $DIR/mistyped_literal_suffix.rs:12:18 + | +LL | let fail20 = 2__8; // + | ^^^^ help: did you mean to write: `2_i8` + +error: mistyped literal suffix + --> $DIR/mistyped_literal_suffix.rs:13:18 + | +LL | let fail21 = 4___16; // + | ^^^^^^ help: did you mean to write: `4_i16` + +error: mistyped literal suffix + --> $DIR/mistyped_literal_suffix.rs:15:18 + | +LL | let fail24 = 12.34_64; + | ^^^^^^^^ help: did you mean to write: `12.34_f64` + +error: mistyped literal suffix + --> $DIR/mistyped_literal_suffix.rs:16:18 + | +LL | let fail25 = 1E2_32; + | ^^^^^^ help: did you mean to write: `1E2_f32` + +error: mistyped literal suffix + --> $DIR/mistyped_literal_suffix.rs:17:18 + | +LL | let fail26 = 43E7_64; + | ^^^^^^^ help: did you mean to write: `43E7_f64` + +error: mistyped literal suffix + --> $DIR/mistyped_literal_suffix.rs:18:18 + | +LL | let fail27 = 243E17_32; + | ^^^^^^^^^ help: did you mean to write: `243E17_f32` + +error: mistyped literal suffix + --> $DIR/mistyped_literal_suffix.rs:20:18 + | +LL | let fail28 = 241251235E723_64; + | ^^^^^^^^^^^^^^^^ help: did you mean to write: `241_251_235E723_f64` + +error: mistyped literal suffix + --> $DIR/mistyped_literal_suffix.rs:21:18 + | +LL | let fail29 = 42279.911_32; + | ^^^^^^^^^^^^ help: did you mean to write: `42_279.911_f32` + +error: mistyped literal suffix + --> $DIR/mistyped_literal_suffix.rs:23:13 + | +LL | let _ = 1.12345E1_32; + | ^^^^^^^^^^^^ help: did you mean to write: `1.123_45E1_f32` + +error: aborting due to 13 previous errors + diff --git a/src/tools/clippy/tests/ui/module_inception.rs b/src/tools/clippy/tests/ui/module_inception.rs new file mode 100644 index 00000000000..a23aba9164a --- /dev/null +++ b/src/tools/clippy/tests/ui/module_inception.rs @@ -0,0 +1,21 @@ +#![warn(clippy::module_inception)] + +mod foo { + mod bar { + mod bar { + mod foo {} + } + mod foo {} + } + mod foo { + mod bar {} + } +} + +// No warning. See <https://github.com/rust-lang/rust-clippy/issues/1220>. +mod bar { + #[allow(clippy::module_inception)] + mod bar {} +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/module_inception.stderr b/src/tools/clippy/tests/ui/module_inception.stderr new file mode 100644 index 00000000000..77564dce9eb --- /dev/null +++ b/src/tools/clippy/tests/ui/module_inception.stderr @@ -0,0 +1,20 @@ +error: module has the same name as its containing module + --> $DIR/module_inception.rs:5:9 + | +LL | / mod bar { +LL | | mod foo {} +LL | | } + | |_________^ + | + = note: `-D clippy::module-inception` implied by `-D warnings` + +error: module has the same name as its containing module + --> $DIR/module_inception.rs:10:5 + | +LL | / mod foo { +LL | | mod bar {} +LL | | } + | |_____^ + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/module_name_repetitions.rs b/src/tools/clippy/tests/ui/module_name_repetitions.rs new file mode 100644 index 00000000000..669bf01a84c --- /dev/null +++ b/src/tools/clippy/tests/ui/module_name_repetitions.rs @@ -0,0 +1,26 @@ +// compile-flags: --test + +#![warn(clippy::module_name_repetitions)] +#![allow(dead_code)] + +mod foo { + pub fn foo() {} + pub fn foo_bar() {} + pub fn bar_foo() {} + pub struct FooCake {} + pub enum CakeFoo {} + pub struct Foo7Bar; + + // Should not warn + pub struct Foobar; +} + +#[cfg(test)] +mod test { + #[test] + fn it_works() { + assert_eq!(2 + 2, 4); + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/module_name_repetitions.stderr b/src/tools/clippy/tests/ui/module_name_repetitions.stderr new file mode 100644 index 00000000000..bdd217a969c --- /dev/null +++ b/src/tools/clippy/tests/ui/module_name_repetitions.stderr @@ -0,0 +1,34 @@ +error: item name starts with its containing module's name + --> $DIR/module_name_repetitions.rs:8:5 + | +LL | pub fn foo_bar() {} + | ^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::module-name-repetitions` implied by `-D warnings` + +error: item name ends with its containing module's name + --> $DIR/module_name_repetitions.rs:9:5 + | +LL | pub fn bar_foo() {} + | ^^^^^^^^^^^^^^^^^^^ + +error: item name starts with its containing module's name + --> $DIR/module_name_repetitions.rs:10:5 + | +LL | pub struct FooCake {} + | ^^^^^^^^^^^^^^^^^^^^^ + +error: item name ends with its containing module's name + --> $DIR/module_name_repetitions.rs:11:5 + | +LL | pub enum CakeFoo {} + | ^^^^^^^^^^^^^^^^^^^ + +error: item name starts with its containing module's name + --> $DIR/module_name_repetitions.rs:12:5 + | +LL | pub struct Foo7Bar; + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/modulo_arithmetic_float.rs b/src/tools/clippy/tests/ui/modulo_arithmetic_float.rs new file mode 100644 index 00000000000..b010b0dbdfa --- /dev/null +++ b/src/tools/clippy/tests/ui/modulo_arithmetic_float.rs @@ -0,0 +1,36 @@ +#![warn(clippy::modulo_arithmetic)] +#![allow( + unused, + clippy::shadow_reuse, + clippy::shadow_unrelated, + clippy::no_effect, + clippy::unnecessary_operation, + clippy::modulo_one +)] + +fn main() { + // Lint when both sides are const and of the opposite sign + -1.6 % 2.1; + 1.6 % -2.1; + (1.1 - 2.3) % (1.1 + 2.3); + (1.1 + 2.3) % (1.1 - 2.3); + + // Lint on floating point numbers + let a_f32: f32 = -1.6; + let mut b_f32: f32 = 2.1; + a_f32 % b_f32; + b_f32 % a_f32; + b_f32 %= a_f32; + + let a_f64: f64 = -1.6; + let mut b_f64: f64 = 2.1; + a_f64 % b_f64; + b_f64 % a_f64; + b_f64 %= a_f64; + + // No lint when both sides are const and of the same sign + 1.6 % 2.1; + -1.6 % -2.1; + (1.1 + 2.3) % (-1.1 + 2.3); + (-1.1 - 2.3) % (1.1 - 2.3); +} diff --git a/src/tools/clippy/tests/ui/modulo_arithmetic_float.stderr b/src/tools/clippy/tests/ui/modulo_arithmetic_float.stderr new file mode 100644 index 00000000000..7bfdb0bde60 --- /dev/null +++ b/src/tools/clippy/tests/ui/modulo_arithmetic_float.stderr @@ -0,0 +1,83 @@ +error: you are using modulo operator on constants with different signs: `-1.600 % 2.100` + --> $DIR/modulo_arithmetic_float.rs:13:5 + | +LL | -1.6 % 2.1; + | ^^^^^^^^^^ + | + = note: `-D clippy::modulo-arithmetic` implied by `-D warnings` + = note: double check for expected result especially when interoperating with different languages + +error: you are using modulo operator on constants with different signs: `1.600 % -2.100` + --> $DIR/modulo_arithmetic_float.rs:14:5 + | +LL | 1.6 % -2.1; + | ^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + +error: you are using modulo operator on constants with different signs: `-1.200 % 3.400` + --> $DIR/modulo_arithmetic_float.rs:15:5 + | +LL | (1.1 - 2.3) % (1.1 + 2.3); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + +error: you are using modulo operator on constants with different signs: `3.400 % -1.200` + --> $DIR/modulo_arithmetic_float.rs:16:5 + | +LL | (1.1 + 2.3) % (1.1 - 2.3); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + +error: you are using modulo operator on types that might have different signs + --> $DIR/modulo_arithmetic_float.rs:21:5 + | +LL | a_f32 % b_f32; + | ^^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + +error: you are using modulo operator on types that might have different signs + --> $DIR/modulo_arithmetic_float.rs:22:5 + | +LL | b_f32 % a_f32; + | ^^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + +error: you are using modulo operator on types that might have different signs + --> $DIR/modulo_arithmetic_float.rs:23:5 + | +LL | b_f32 %= a_f32; + | ^^^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + +error: you are using modulo operator on types that might have different signs + --> $DIR/modulo_arithmetic_float.rs:27:5 + | +LL | a_f64 % b_f64; + | ^^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + +error: you are using modulo operator on types that might have different signs + --> $DIR/modulo_arithmetic_float.rs:28:5 + | +LL | b_f64 % a_f64; + | ^^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + +error: you are using modulo operator on types that might have different signs + --> $DIR/modulo_arithmetic_float.rs:29:5 + | +LL | b_f64 %= a_f64; + | ^^^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + +error: aborting due to 10 previous errors + diff --git a/src/tools/clippy/tests/ui/modulo_arithmetic_integral.rs b/src/tools/clippy/tests/ui/modulo_arithmetic_integral.rs new file mode 100644 index 00000000000..779d035c5f8 --- /dev/null +++ b/src/tools/clippy/tests/ui/modulo_arithmetic_integral.rs @@ -0,0 +1,90 @@ +#![warn(clippy::modulo_arithmetic)] +#![allow( + unused, + clippy::shadow_reuse, + clippy::shadow_unrelated, + clippy::no_effect, + clippy::unnecessary_operation, + clippy::modulo_one +)] + +fn main() { + // Lint on signed integral numbers + let a = -1; + let mut b = 2; + a % b; + b % a; + b %= a; + + let a_i8: i8 = 1; + let mut b_i8: i8 = 2; + a_i8 % b_i8; + b_i8 %= a_i8; + + let a_i16: i16 = 1; + let mut b_i16: i16 = 2; + a_i16 % b_i16; + b_i16 %= a_i16; + + let a_i32: i32 = 1; + let mut b_i32: i32 = 2; + a_i32 % b_i32; + b_i32 %= a_i32; + + let a_i64: i64 = 1; + let mut b_i64: i64 = 2; + a_i64 % b_i64; + b_i64 %= a_i64; + + let a_i128: i128 = 1; + let mut b_i128: i128 = 2; + a_i128 % b_i128; + b_i128 %= a_i128; + + let a_isize: isize = 1; + let mut b_isize: isize = 2; + a_isize % b_isize; + b_isize %= a_isize; + + let a = 1; + let mut b = 2; + a % b; + b %= a; + + // No lint on unsigned integral value + let a_u8: u8 = 17; + let b_u8: u8 = 3; + a_u8 % b_u8; + let mut a_u8: u8 = 1; + a_u8 %= 2; + + let a_u16: u16 = 17; + let b_u16: u16 = 3; + a_u16 % b_u16; + let mut a_u16: u16 = 1; + a_u16 %= 2; + + let a_u32: u32 = 17; + let b_u32: u32 = 3; + a_u32 % b_u32; + let mut a_u32: u32 = 1; + a_u32 %= 2; + + let a_u64: u64 = 17; + let b_u64: u64 = 3; + a_u64 % b_u64; + let mut a_u64: u64 = 1; + a_u64 %= 2; + + let a_u128: u128 = 17; + let b_u128: u128 = 3; + a_u128 % b_u128; + let mut a_u128: u128 = 1; + a_u128 %= 2; + + let a_usize: usize = 17; + let b_usize: usize = 3; + a_usize % b_usize; + let mut a_usize: usize = 1; + a_usize %= 2; +} diff --git a/src/tools/clippy/tests/ui/modulo_arithmetic_integral.stderr b/src/tools/clippy/tests/ui/modulo_arithmetic_integral.stderr new file mode 100644 index 00000000000..e863b838699 --- /dev/null +++ b/src/tools/clippy/tests/ui/modulo_arithmetic_integral.stderr @@ -0,0 +1,156 @@ +error: you are using modulo operator on types that might have different signs + --> $DIR/modulo_arithmetic_integral.rs:15:5 + | +LL | a % b; + | ^^^^^ + | + = note: `-D clippy::modulo-arithmetic` implied by `-D warnings` + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: you are using modulo operator on types that might have different signs + --> $DIR/modulo_arithmetic_integral.rs:16:5 + | +LL | b % a; + | ^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: you are using modulo operator on types that might have different signs + --> $DIR/modulo_arithmetic_integral.rs:17:5 + | +LL | b %= a; + | ^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: you are using modulo operator on types that might have different signs + --> $DIR/modulo_arithmetic_integral.rs:21:5 + | +LL | a_i8 % b_i8; + | ^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: you are using modulo operator on types that might have different signs + --> $DIR/modulo_arithmetic_integral.rs:22:5 + | +LL | b_i8 %= a_i8; + | ^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: you are using modulo operator on types that might have different signs + --> $DIR/modulo_arithmetic_integral.rs:26:5 + | +LL | a_i16 % b_i16; + | ^^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: you are using modulo operator on types that might have different signs + --> $DIR/modulo_arithmetic_integral.rs:27:5 + | +LL | b_i16 %= a_i16; + | ^^^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: you are using modulo operator on types that might have different signs + --> $DIR/modulo_arithmetic_integral.rs:31:5 + | +LL | a_i32 % b_i32; + | ^^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: you are using modulo operator on types that might have different signs + --> $DIR/modulo_arithmetic_integral.rs:32:5 + | +LL | b_i32 %= a_i32; + | ^^^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: you are using modulo operator on types that might have different signs + --> $DIR/modulo_arithmetic_integral.rs:36:5 + | +LL | a_i64 % b_i64; + | ^^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: you are using modulo operator on types that might have different signs + --> $DIR/modulo_arithmetic_integral.rs:37:5 + | +LL | b_i64 %= a_i64; + | ^^^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: you are using modulo operator on types that might have different signs + --> $DIR/modulo_arithmetic_integral.rs:41:5 + | +LL | a_i128 % b_i128; + | ^^^^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: you are using modulo operator on types that might have different signs + --> $DIR/modulo_arithmetic_integral.rs:42:5 + | +LL | b_i128 %= a_i128; + | ^^^^^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: you are using modulo operator on types that might have different signs + --> $DIR/modulo_arithmetic_integral.rs:46:5 + | +LL | a_isize % b_isize; + | ^^^^^^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: you are using modulo operator on types that might have different signs + --> $DIR/modulo_arithmetic_integral.rs:47:5 + | +LL | b_isize %= a_isize; + | ^^^^^^^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: you are using modulo operator on types that might have different signs + --> $DIR/modulo_arithmetic_integral.rs:51:5 + | +LL | a % b; + | ^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: you are using modulo operator on types that might have different signs + --> $DIR/modulo_arithmetic_integral.rs:52:5 + | +LL | b %= a; + | ^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: aborting due to 17 previous errors + diff --git a/src/tools/clippy/tests/ui/modulo_arithmetic_integral_const.rs b/src/tools/clippy/tests/ui/modulo_arithmetic_integral_const.rs new file mode 100644 index 00000000000..57a96692c00 --- /dev/null +++ b/src/tools/clippy/tests/ui/modulo_arithmetic_integral_const.rs @@ -0,0 +1,44 @@ +#![warn(clippy::modulo_arithmetic)] +#![allow( + unused, + clippy::shadow_reuse, + clippy::shadow_unrelated, + clippy::no_effect, + clippy::unnecessary_operation, + clippy::modulo_one +)] + +fn main() { + // Lint when both sides are const and of the opposite sign + -1 % 2; + 1 % -2; + (1 - 2) % (1 + 2); + (1 + 2) % (1 - 2); + 35 * (7 - 4 * 2) % (-500 * -600); + + -1i8 % 2i8; + 1i8 % -2i8; + -1i16 % 2i16; + 1i16 % -2i16; + -1i32 % 2i32; + 1i32 % -2i32; + -1i64 % 2i64; + 1i64 % -2i64; + -1i128 % 2i128; + 1i128 % -2i128; + -1isize % 2isize; + 1isize % -2isize; + + // No lint when both sides are const and of the same sign + 1 % 2; + -1 % -2; + (1 + 2) % (-1 + 2); + (-1 - 2) % (1 - 2); + + 1u8 % 2u8; + 1u16 % 2u16; + 1u32 % 2u32; + 1u64 % 2u64; + 1u128 % 2u128; + 1usize % 2usize; +} diff --git a/src/tools/clippy/tests/ui/modulo_arithmetic_integral_const.stderr b/src/tools/clippy/tests/ui/modulo_arithmetic_integral_const.stderr new file mode 100644 index 00000000000..de328bb75fe --- /dev/null +++ b/src/tools/clippy/tests/ui/modulo_arithmetic_integral_const.stderr @@ -0,0 +1,156 @@ +error: you are using modulo operator on constants with different signs: `-1 % 2` + --> $DIR/modulo_arithmetic_integral_const.rs:13:5 + | +LL | -1 % 2; + | ^^^^^^ + | + = note: `-D clippy::modulo-arithmetic` implied by `-D warnings` + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: you are using modulo operator on constants with different signs: `1 % -2` + --> $DIR/modulo_arithmetic_integral_const.rs:14:5 + | +LL | 1 % -2; + | ^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: you are using modulo operator on constants with different signs: `-1 % 3` + --> $DIR/modulo_arithmetic_integral_const.rs:15:5 + | +LL | (1 - 2) % (1 + 2); + | ^^^^^^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: you are using modulo operator on constants with different signs: `3 % -1` + --> $DIR/modulo_arithmetic_integral_const.rs:16:5 + | +LL | (1 + 2) % (1 - 2); + | ^^^^^^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: you are using modulo operator on constants with different signs: `-35 % 300000` + --> $DIR/modulo_arithmetic_integral_const.rs:17:5 + | +LL | 35 * (7 - 4 * 2) % (-500 * -600); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: you are using modulo operator on constants with different signs: `-1 % 2` + --> $DIR/modulo_arithmetic_integral_const.rs:19:5 + | +LL | -1i8 % 2i8; + | ^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: you are using modulo operator on constants with different signs: `1 % -2` + --> $DIR/modulo_arithmetic_integral_const.rs:20:5 + | +LL | 1i8 % -2i8; + | ^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: you are using modulo operator on constants with different signs: `-1 % 2` + --> $DIR/modulo_arithmetic_integral_const.rs:21:5 + | +LL | -1i16 % 2i16; + | ^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: you are using modulo operator on constants with different signs: `1 % -2` + --> $DIR/modulo_arithmetic_integral_const.rs:22:5 + | +LL | 1i16 % -2i16; + | ^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: you are using modulo operator on constants with different signs: `-1 % 2` + --> $DIR/modulo_arithmetic_integral_const.rs:23:5 + | +LL | -1i32 % 2i32; + | ^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: you are using modulo operator on constants with different signs: `1 % -2` + --> $DIR/modulo_arithmetic_integral_const.rs:24:5 + | +LL | 1i32 % -2i32; + | ^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: you are using modulo operator on constants with different signs: `-1 % 2` + --> $DIR/modulo_arithmetic_integral_const.rs:25:5 + | +LL | -1i64 % 2i64; + | ^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: you are using modulo operator on constants with different signs: `1 % -2` + --> $DIR/modulo_arithmetic_integral_const.rs:26:5 + | +LL | 1i64 % -2i64; + | ^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: you are using modulo operator on constants with different signs: `-1 % 2` + --> $DIR/modulo_arithmetic_integral_const.rs:27:5 + | +LL | -1i128 % 2i128; + | ^^^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: you are using modulo operator on constants with different signs: `1 % -2` + --> $DIR/modulo_arithmetic_integral_const.rs:28:5 + | +LL | 1i128 % -2i128; + | ^^^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: you are using modulo operator on constants with different signs: `-1 % 2` + --> $DIR/modulo_arithmetic_integral_const.rs:29:5 + | +LL | -1isize % 2isize; + | ^^^^^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: you are using modulo operator on constants with different signs: `1 % -2` + --> $DIR/modulo_arithmetic_integral_const.rs:30:5 + | +LL | 1isize % -2isize; + | ^^^^^^^^^^^^^^^^ + | + = note: double check for expected result especially when interoperating with different languages + = note: or consider using `rem_euclid` or similar function + +error: aborting due to 17 previous errors + diff --git a/src/tools/clippy/tests/ui/modulo_one.rs b/src/tools/clippy/tests/ui/modulo_one.rs new file mode 100644 index 00000000000..cc8c8e7cdae --- /dev/null +++ b/src/tools/clippy/tests/ui/modulo_one.rs @@ -0,0 +1,14 @@ +#![warn(clippy::modulo_one)] +#![allow(clippy::no_effect, clippy::unnecessary_operation)] + +static STATIC_ONE: usize = 2 - 1; + +fn main() { + 10 % 1; + 10 % 2; + + const ONE: u32 = 1 * 1; + + 2 % ONE; + 5 % STATIC_ONE; +} diff --git a/src/tools/clippy/tests/ui/modulo_one.stderr b/src/tools/clippy/tests/ui/modulo_one.stderr new file mode 100644 index 00000000000..6bee68360b6 --- /dev/null +++ b/src/tools/clippy/tests/ui/modulo_one.stderr @@ -0,0 +1,30 @@ +error: any number modulo 1 will be 0 + --> $DIR/modulo_one.rs:7:5 + | +LL | 10 % 1; + | ^^^^^^ + | + = note: `-D clippy::modulo-one` implied by `-D warnings` + +error: the operation is ineffective. Consider reducing it to `1` + --> $DIR/modulo_one.rs:10:22 + | +LL | const ONE: u32 = 1 * 1; + | ^^^^^ + | + = note: `-D clippy::identity-op` implied by `-D warnings` + +error: the operation is ineffective. Consider reducing it to `1` + --> $DIR/modulo_one.rs:10:22 + | +LL | const ONE: u32 = 1 * 1; + | ^^^^^ + +error: any number modulo 1 will be 0 + --> $DIR/modulo_one.rs:12:5 + | +LL | 2 % ONE; + | ^^^^^^^ + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/must_use_candidates.fixed b/src/tools/clippy/tests/ui/must_use_candidates.fixed new file mode 100644 index 00000000000..9556f6f82cc --- /dev/null +++ b/src/tools/clippy/tests/ui/must_use_candidates.fixed @@ -0,0 +1,93 @@ +// run-rustfix +#![feature(never_type)] +#![allow(unused_mut, clippy::redundant_allocation)] +#![warn(clippy::must_use_candidate)] +use std::rc::Rc; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Arc; + +pub struct MyAtomic(AtomicBool); +pub struct MyPure; + +#[must_use] pub fn pure(i: u8) -> u8 { + i +} + +impl MyPure { + #[must_use] pub fn inherent_pure(&self) -> u8 { + 0 + } +} + +pub trait MyPureTrait { + fn trait_pure(&self, i: u32) -> u32 { + self.trait_impl_pure(i) + 1 + } + + fn trait_impl_pure(&self, i: u32) -> u32; +} + +impl MyPureTrait for MyPure { + fn trait_impl_pure(&self, i: u32) -> u32 { + i + } +} + +pub fn without_result() { + // OK +} + +pub fn impure_primitive(i: &mut u8) -> u8 { + *i +} + +pub fn with_callback<F: Fn(u32) -> bool>(f: &F) -> bool { + f(0) +} + +#[must_use] pub fn with_marker(_d: std::marker::PhantomData<&mut u32>) -> bool { + true +} + +pub fn quoth_the_raven(_more: !) -> u32 { + unimplemented!(); +} + +pub fn atomics(b: &AtomicBool) -> bool { + b.load(Ordering::SeqCst) +} + +#[must_use] pub fn rcd(_x: Rc<u32>) -> bool { + true +} + +pub fn rcmut(_x: Rc<&mut u32>) -> bool { + true +} + +#[must_use] pub fn arcd(_x: Arc<u32>) -> bool { + false +} + +pub fn inner_types(_m: &MyAtomic) -> bool { + true +} + +static mut COUNTER: usize = 0; + +/// # Safety +/// +/// Don't ever call this from multiple threads +pub unsafe fn mutates_static() -> usize { + COUNTER += 1; + COUNTER +} + +#[no_mangle] +pub fn unmangled(i: bool) -> bool { + !i +} + +fn main() { + assert_eq!(1, pure(1)); +} diff --git a/src/tools/clippy/tests/ui/must_use_candidates.rs b/src/tools/clippy/tests/ui/must_use_candidates.rs new file mode 100644 index 00000000000..37324220171 --- /dev/null +++ b/src/tools/clippy/tests/ui/must_use_candidates.rs @@ -0,0 +1,93 @@ +// run-rustfix +#![feature(never_type)] +#![allow(unused_mut, clippy::redundant_allocation)] +#![warn(clippy::must_use_candidate)] +use std::rc::Rc; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Arc; + +pub struct MyAtomic(AtomicBool); +pub struct MyPure; + +pub fn pure(i: u8) -> u8 { + i +} + +impl MyPure { + pub fn inherent_pure(&self) -> u8 { + 0 + } +} + +pub trait MyPureTrait { + fn trait_pure(&self, i: u32) -> u32 { + self.trait_impl_pure(i) + 1 + } + + fn trait_impl_pure(&self, i: u32) -> u32; +} + +impl MyPureTrait for MyPure { + fn trait_impl_pure(&self, i: u32) -> u32 { + i + } +} + +pub fn without_result() { + // OK +} + +pub fn impure_primitive(i: &mut u8) -> u8 { + *i +} + +pub fn with_callback<F: Fn(u32) -> bool>(f: &F) -> bool { + f(0) +} + +pub fn with_marker(_d: std::marker::PhantomData<&mut u32>) -> bool { + true +} + +pub fn quoth_the_raven(_more: !) -> u32 { + unimplemented!(); +} + +pub fn atomics(b: &AtomicBool) -> bool { + b.load(Ordering::SeqCst) +} + +pub fn rcd(_x: Rc<u32>) -> bool { + true +} + +pub fn rcmut(_x: Rc<&mut u32>) -> bool { + true +} + +pub fn arcd(_x: Arc<u32>) -> bool { + false +} + +pub fn inner_types(_m: &MyAtomic) -> bool { + true +} + +static mut COUNTER: usize = 0; + +/// # Safety +/// +/// Don't ever call this from multiple threads +pub unsafe fn mutates_static() -> usize { + COUNTER += 1; + COUNTER +} + +#[no_mangle] +pub fn unmangled(i: bool) -> bool { + !i +} + +fn main() { + assert_eq!(1, pure(1)); +} diff --git a/src/tools/clippy/tests/ui/must_use_candidates.stderr b/src/tools/clippy/tests/ui/must_use_candidates.stderr new file mode 100644 index 00000000000..0fa3849d03b --- /dev/null +++ b/src/tools/clippy/tests/ui/must_use_candidates.stderr @@ -0,0 +1,34 @@ +error: this function could have a `#[must_use]` attribute + --> $DIR/must_use_candidates.rs:12:1 + | +LL | pub fn pure(i: u8) -> u8 { + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: add the attribute: `#[must_use] pub fn pure(i: u8) -> u8` + | + = note: `-D clippy::must-use-candidate` implied by `-D warnings` + +error: this method could have a `#[must_use]` attribute + --> $DIR/must_use_candidates.rs:17:5 + | +LL | pub fn inherent_pure(&self) -> u8 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add the attribute: `#[must_use] pub fn inherent_pure(&self) -> u8` + +error: this function could have a `#[must_use]` attribute + --> $DIR/must_use_candidates.rs:48:1 + | +LL | pub fn with_marker(_d: std::marker::PhantomData<&mut u32>) -> bool { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add the attribute: `#[must_use] pub fn with_marker(_d: std::marker::PhantomData<&mut u32>) -> bool` + +error: this function could have a `#[must_use]` attribute + --> $DIR/must_use_candidates.rs:60:1 + | +LL | pub fn rcd(_x: Rc<u32>) -> bool { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add the attribute: `#[must_use] pub fn rcd(_x: Rc<u32>) -> bool` + +error: this function could have a `#[must_use]` attribute + --> $DIR/must_use_candidates.rs:68:1 + | +LL | pub fn arcd(_x: Arc<u32>) -> bool { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add the attribute: `#[must_use] pub fn arcd(_x: Arc<u32>) -> bool` + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/must_use_unit.fixed b/src/tools/clippy/tests/ui/must_use_unit.fixed new file mode 100644 index 00000000000..6c9aa434ac0 --- /dev/null +++ b/src/tools/clippy/tests/ui/must_use_unit.fixed @@ -0,0 +1,26 @@ +//run-rustfix +// aux-build:macro_rules.rs + +#![warn(clippy::must_use_unit)] +#![allow(clippy::unused_unit)] + +#[macro_use] +extern crate macro_rules; + + +pub fn must_use_default() {} + + +pub fn must_use_unit() -> () {} + + +pub fn must_use_with_note() {} + +fn main() { + must_use_default(); + must_use_unit(); + must_use_with_note(); + + // We should not lint in external macros + must_use_unit!(); +} diff --git a/src/tools/clippy/tests/ui/must_use_unit.rs b/src/tools/clippy/tests/ui/must_use_unit.rs new file mode 100644 index 00000000000..8a395dc284d --- /dev/null +++ b/src/tools/clippy/tests/ui/must_use_unit.rs @@ -0,0 +1,26 @@ +//run-rustfix +// aux-build:macro_rules.rs + +#![warn(clippy::must_use_unit)] +#![allow(clippy::unused_unit)] + +#[macro_use] +extern crate macro_rules; + +#[must_use] +pub fn must_use_default() {} + +#[must_use] +pub fn must_use_unit() -> () {} + +#[must_use = "With note"] +pub fn must_use_with_note() {} + +fn main() { + must_use_default(); + must_use_unit(); + must_use_with_note(); + + // We should not lint in external macros + must_use_unit!(); +} diff --git a/src/tools/clippy/tests/ui/must_use_unit.stderr b/src/tools/clippy/tests/ui/must_use_unit.stderr new file mode 100644 index 00000000000..15e0906b66b --- /dev/null +++ b/src/tools/clippy/tests/ui/must_use_unit.stderr @@ -0,0 +1,28 @@ +error: this unit-returning function has a `#[must_use]` attribute + --> $DIR/must_use_unit.rs:11:1 + | +LL | #[must_use] + | ----------- help: remove the attribute +LL | pub fn must_use_default() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::must-use-unit` implied by `-D warnings` + +error: this unit-returning function has a `#[must_use]` attribute + --> $DIR/must_use_unit.rs:14:1 + | +LL | #[must_use] + | ----------- help: remove the attribute +LL | pub fn must_use_unit() -> () {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this unit-returning function has a `#[must_use]` attribute + --> $DIR/must_use_unit.rs:17:1 + | +LL | #[must_use = "With note"] + | ------------------------- help: remove the attribute +LL | pub fn must_use_with_note() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/mut_from_ref.rs b/src/tools/clippy/tests/ui/mut_from_ref.rs new file mode 100644 index 00000000000..a9a04c8f56b --- /dev/null +++ b/src/tools/clippy/tests/ui/mut_from_ref.rs @@ -0,0 +1,46 @@ +#![allow(unused)] +#![warn(clippy::mut_from_ref)] + +struct Foo; + +impl Foo { + fn this_wont_hurt_a_bit(&self) -> &mut Foo { + unimplemented!() + } +} + +trait Ouch { + fn ouch(x: &Foo) -> &mut Foo; +} + +impl Ouch for Foo { + fn ouch(x: &Foo) -> &mut Foo { + unimplemented!() + } +} + +fn fail(x: &u32) -> &mut u16 { + unimplemented!() +} + +fn fail_lifetime<'a>(x: &'a u32, y: &mut u32) -> &'a mut u32 { + unimplemented!() +} + +fn fail_double<'a, 'b>(x: &'a u32, y: &'a u32, z: &'b mut u32) -> &'a mut u32 { + unimplemented!() +} + +// this is OK, because the result borrows y +fn works<'a>(x: &u32, y: &'a mut u32) -> &'a mut u32 { + unimplemented!() +} + +// this is also OK, because the result could borrow y +fn also_works<'a>(x: &'a u32, y: &'a mut u32) -> &'a mut u32 { + unimplemented!() +} + +fn main() { + //TODO +} diff --git a/src/tools/clippy/tests/ui/mut_from_ref.stderr b/src/tools/clippy/tests/ui/mut_from_ref.stderr new file mode 100644 index 00000000000..4787999920b --- /dev/null +++ b/src/tools/clippy/tests/ui/mut_from_ref.stderr @@ -0,0 +1,63 @@ +error: mutable borrow from immutable input(s) + --> $DIR/mut_from_ref.rs:7:39 + | +LL | fn this_wont_hurt_a_bit(&self) -> &mut Foo { + | ^^^^^^^^ + | + = note: `-D clippy::mut-from-ref` implied by `-D warnings` +note: immutable borrow here + --> $DIR/mut_from_ref.rs:7:29 + | +LL | fn this_wont_hurt_a_bit(&self) -> &mut Foo { + | ^^^^^ + +error: mutable borrow from immutable input(s) + --> $DIR/mut_from_ref.rs:13:25 + | +LL | fn ouch(x: &Foo) -> &mut Foo; + | ^^^^^^^^ + | +note: immutable borrow here + --> $DIR/mut_from_ref.rs:13:16 + | +LL | fn ouch(x: &Foo) -> &mut Foo; + | ^^^^ + +error: mutable borrow from immutable input(s) + --> $DIR/mut_from_ref.rs:22:21 + | +LL | fn fail(x: &u32) -> &mut u16 { + | ^^^^^^^^ + | +note: immutable borrow here + --> $DIR/mut_from_ref.rs:22:12 + | +LL | fn fail(x: &u32) -> &mut u16 { + | ^^^^ + +error: mutable borrow from immutable input(s) + --> $DIR/mut_from_ref.rs:26:50 + | +LL | fn fail_lifetime<'a>(x: &'a u32, y: &mut u32) -> &'a mut u32 { + | ^^^^^^^^^^^ + | +note: immutable borrow here + --> $DIR/mut_from_ref.rs:26:25 + | +LL | fn fail_lifetime<'a>(x: &'a u32, y: &mut u32) -> &'a mut u32 { + | ^^^^^^^ + +error: mutable borrow from immutable input(s) + --> $DIR/mut_from_ref.rs:30:67 + | +LL | fn fail_double<'a, 'b>(x: &'a u32, y: &'a u32, z: &'b mut u32) -> &'a mut u32 { + | ^^^^^^^^^^^ + | +note: immutable borrow here + --> $DIR/mut_from_ref.rs:30:27 + | +LL | fn fail_double<'a, 'b>(x: &'a u32, y: &'a u32, z: &'b mut u32) -> &'a mut u32 { + | ^^^^^^^ ^^^^^^^ + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/mut_key.rs b/src/tools/clippy/tests/ui/mut_key.rs new file mode 100644 index 00000000000..2d227e6654c --- /dev/null +++ b/src/tools/clippy/tests/ui/mut_key.rs @@ -0,0 +1,55 @@ +use std::collections::{HashMap, HashSet}; +use std::hash::{Hash, Hasher}; +use std::sync::atomic::{AtomicUsize, Ordering::Relaxed}; + +struct Key(AtomicUsize); + +impl Clone for Key { + fn clone(&self) -> Self { + Key(AtomicUsize::new(self.0.load(Relaxed))) + } +} + +impl PartialEq for Key { + fn eq(&self, other: &Self) -> bool { + self.0.load(Relaxed) == other.0.load(Relaxed) + } +} + +impl Eq for Key {} + +impl Hash for Key { + fn hash<H: Hasher>(&self, h: &mut H) { + self.0.load(Relaxed).hash(h); + } +} + +fn should_not_take_this_arg(m: &mut HashMap<Key, usize>, _n: usize) -> HashSet<Key> { + let _other: HashMap<Key, bool> = HashMap::new(); + m.keys().cloned().collect() +} + +fn this_is_ok(_m: &mut HashMap<usize, Key>) {} + +#[allow(unused)] +trait Trait { + type AssociatedType; + + fn trait_fn(&self, set: std::collections::HashSet<Self::AssociatedType>); +} + +fn generics_are_ok_too<K>(_m: &mut HashSet<K>) { + // nothing to see here, move along +} + +fn tuples<U>(_m: &mut HashMap<((), U), ()>) {} + +fn tuples_bad<U>(_m: &mut HashMap<(Key, U), bool>) {} + +fn main() { + let _ = should_not_take_this_arg(&mut HashMap::new(), 1); + this_is_ok(&mut HashMap::new()); + tuples::<Key>(&mut HashMap::new()); + tuples::<()>(&mut HashMap::new()); + tuples_bad::<()>(&mut HashMap::new()); +} diff --git a/src/tools/clippy/tests/ui/mut_key.stderr b/src/tools/clippy/tests/ui/mut_key.stderr new file mode 100644 index 00000000000..8d6a259c7e3 --- /dev/null +++ b/src/tools/clippy/tests/ui/mut_key.stderr @@ -0,0 +1,28 @@ +error: mutable key type + --> $DIR/mut_key.rs:27:32 + | +LL | fn should_not_take_this_arg(m: &mut HashMap<Key, usize>, _n: usize) -> HashSet<Key> { + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[deny(clippy::mutable_key_type)]` on by default + +error: mutable key type + --> $DIR/mut_key.rs:27:72 + | +LL | fn should_not_take_this_arg(m: &mut HashMap<Key, usize>, _n: usize) -> HashSet<Key> { + | ^^^^^^^^^^^^ + +error: mutable key type + --> $DIR/mut_key.rs:28:5 + | +LL | let _other: HashMap<Key, bool> = HashMap::new(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: mutable key type + --> $DIR/mut_key.rs:47:22 + | +LL | fn tuples_bad<U>(_m: &mut HashMap<(Key, U), bool>) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/mut_mut.rs b/src/tools/clippy/tests/ui/mut_mut.rs new file mode 100644 index 00000000000..8965cef66de --- /dev/null +++ b/src/tools/clippy/tests/ui/mut_mut.rs @@ -0,0 +1,49 @@ +#![allow(unused, clippy::no_effect, clippy::unnecessary_operation)] +#![warn(clippy::mut_mut)] + +fn fun(x: &mut &mut u32) -> bool { + **x > 0 +} + +fn less_fun(x: *mut *mut u32) { + let y = x; +} + +macro_rules! mut_ptr { + ($p:expr) => { + &mut $p + }; +} + +#[allow(unused_mut, unused_variables)] +fn main() { + let mut x = &mut &mut 1u32; + { + let mut y = &mut x; + } + + if fun(x) { + let y: &mut &mut u32 = &mut &mut 2; + **y + **x; + } + + if fun(x) { + let y: &mut &mut &mut u32 = &mut &mut &mut 2; + ***y + **x; + } + + let mut z = mut_ptr!(&mut 3u32); +} + +fn issue939() { + let array = [5, 6, 7, 8, 9]; + let mut args = array.iter().skip(2); + for &arg in &mut args { + println!("{}", arg); + } + + let args = &mut args; + for arg in args { + println!(":{}", arg); + } +} diff --git a/src/tools/clippy/tests/ui/mut_mut.stderr b/src/tools/clippy/tests/ui/mut_mut.stderr new file mode 100644 index 00000000000..44e81422714 --- /dev/null +++ b/src/tools/clippy/tests/ui/mut_mut.stderr @@ -0,0 +1,63 @@ +error: generally you want to avoid `&mut &mut _` if possible + --> $DIR/mut_mut.rs:4:11 + | +LL | fn fun(x: &mut &mut u32) -> bool { + | ^^^^^^^^^^^^^ + | + = note: `-D clippy::mut-mut` implied by `-D warnings` + +error: generally you want to avoid `&mut &mut _` if possible + --> $DIR/mut_mut.rs:20:17 + | +LL | let mut x = &mut &mut 1u32; + | ^^^^^^^^^^^^^^ + +error: generally you want to avoid `&mut &mut _` if possible + --> $DIR/mut_mut.rs:14:9 + | +LL | &mut $p + | ^^^^^^^ +... +LL | let mut z = mut_ptr!(&mut 3u32); + | ------------------- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: this expression mutably borrows a mutable reference. Consider reborrowing + --> $DIR/mut_mut.rs:22:21 + | +LL | let mut y = &mut x; + | ^^^^^^ + +error: generally you want to avoid `&mut &mut _` if possible + --> $DIR/mut_mut.rs:26:32 + | +LL | let y: &mut &mut u32 = &mut &mut 2; + | ^^^^^^^^^^^ + +error: generally you want to avoid `&mut &mut _` if possible + --> $DIR/mut_mut.rs:26:16 + | +LL | let y: &mut &mut u32 = &mut &mut 2; + | ^^^^^^^^^^^^^ + +error: generally you want to avoid `&mut &mut _` if possible + --> $DIR/mut_mut.rs:31:37 + | +LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2; + | ^^^^^^^^^^^^^^^^ + +error: generally you want to avoid `&mut &mut _` if possible + --> $DIR/mut_mut.rs:31:16 + | +LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2; + | ^^^^^^^^^^^^^^^^^^ + +error: generally you want to avoid `&mut &mut _` if possible + --> $DIR/mut_mut.rs:31:21 + | +LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2; + | ^^^^^^^^^^^^^ + +error: aborting due to 9 previous errors + diff --git a/src/tools/clippy/tests/ui/mut_range_bound.rs b/src/tools/clippy/tests/ui/mut_range_bound.rs new file mode 100644 index 00000000000..1348dd2a3d8 --- /dev/null +++ b/src/tools/clippy/tests/ui/mut_range_bound.rs @@ -0,0 +1,63 @@ +#![allow(unused)] + +fn main() { + mut_range_bound_upper(); + mut_range_bound_lower(); + mut_range_bound_both(); + mut_range_bound_no_mutation(); + immut_range_bound(); + mut_borrow_range_bound(); + immut_borrow_range_bound(); +} + +fn mut_range_bound_upper() { + let mut m = 4; + for i in 0..m { + m = 5; + } // warning +} + +fn mut_range_bound_lower() { + let mut m = 4; + for i in m..10 { + m *= 2; + } // warning +} + +fn mut_range_bound_both() { + let mut m = 4; + let mut n = 6; + for i in m..n { + m = 5; + n = 7; + } // warning (1 for each mutated bound) +} + +fn mut_range_bound_no_mutation() { + let mut m = 4; + for i in 0..m { + continue; + } // no warning +} + +fn mut_borrow_range_bound() { + let mut m = 4; + for i in 0..m { + let n = &mut m; // warning + *n += 1; + } +} + +fn immut_borrow_range_bound() { + let mut m = 4; + for i in 0..m { + let n = &m; // should be no warning? + } +} + +fn immut_range_bound() { + let m = 4; + for i in 0..m { + continue; + } // no warning +} diff --git a/src/tools/clippy/tests/ui/mut_range_bound.stderr b/src/tools/clippy/tests/ui/mut_range_bound.stderr new file mode 100644 index 00000000000..0eeb76e0ec5 --- /dev/null +++ b/src/tools/clippy/tests/ui/mut_range_bound.stderr @@ -0,0 +1,34 @@ +error: attempt to mutate range bound within loop; note that the range of the loop is unchanged + --> $DIR/mut_range_bound.rs:16:9 + | +LL | m = 5; + | ^ + | + = note: `-D clippy::mut-range-bound` implied by `-D warnings` + +error: attempt to mutate range bound within loop; note that the range of the loop is unchanged + --> $DIR/mut_range_bound.rs:23:9 + | +LL | m *= 2; + | ^ + +error: attempt to mutate range bound within loop; note that the range of the loop is unchanged + --> $DIR/mut_range_bound.rs:31:9 + | +LL | m = 5; + | ^ + +error: attempt to mutate range bound within loop; note that the range of the loop is unchanged + --> $DIR/mut_range_bound.rs:32:9 + | +LL | n = 7; + | ^ + +error: attempt to mutate range bound within loop; note that the range of the loop is unchanged + --> $DIR/mut_range_bound.rs:46:22 + | +LL | let n = &mut m; // warning + | ^ + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/mut_reference.rs b/src/tools/clippy/tests/ui/mut_reference.rs new file mode 100644 index 00000000000..73906121c40 --- /dev/null +++ b/src/tools/clippy/tests/ui/mut_reference.rs @@ -0,0 +1,43 @@ +#![allow(unused_variables)] + +fn takes_an_immutable_reference(a: &i32) {} +fn takes_a_mutable_reference(a: &mut i32) {} + +struct MyStruct; + +impl MyStruct { + fn takes_an_immutable_reference(&self, a: &i32) {} + + fn takes_a_mutable_reference(&self, a: &mut i32) {} +} + +#[warn(clippy::unnecessary_mut_passed)] +fn main() { + // Functions + takes_an_immutable_reference(&mut 42); + let as_ptr: fn(&i32) = takes_an_immutable_reference; + as_ptr(&mut 42); + + // Methods + let my_struct = MyStruct; + my_struct.takes_an_immutable_reference(&mut 42); + + // No error + + // Functions + takes_an_immutable_reference(&42); + let as_ptr: fn(&i32) = takes_an_immutable_reference; + as_ptr(&42); + + takes_a_mutable_reference(&mut 42); + let as_ptr: fn(&mut i32) = takes_a_mutable_reference; + as_ptr(&mut 42); + + let a = &mut 42; + takes_an_immutable_reference(a); + + // Methods + my_struct.takes_an_immutable_reference(&42); + my_struct.takes_a_mutable_reference(&mut 42); + my_struct.takes_an_immutable_reference(a); +} diff --git a/src/tools/clippy/tests/ui/mut_reference.stderr b/src/tools/clippy/tests/ui/mut_reference.stderr new file mode 100644 index 00000000000..fa8c82ae0f3 --- /dev/null +++ b/src/tools/clippy/tests/ui/mut_reference.stderr @@ -0,0 +1,22 @@ +error: The function/method `takes_an_immutable_reference` doesn't need a mutable reference + --> $DIR/mut_reference.rs:17:34 + | +LL | takes_an_immutable_reference(&mut 42); + | ^^^^^^^ + | + = note: `-D clippy::unnecessary-mut-passed` implied by `-D warnings` + +error: The function/method `as_ptr` doesn't need a mutable reference + --> $DIR/mut_reference.rs:19:12 + | +LL | as_ptr(&mut 42); + | ^^^^^^^ + +error: The function/method `takes_an_immutable_reference` doesn't need a mutable reference + --> $DIR/mut_reference.rs:23:44 + | +LL | my_struct.takes_an_immutable_reference(&mut 42); + | ^^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/mutex_atomic.rs b/src/tools/clippy/tests/ui/mutex_atomic.rs new file mode 100644 index 00000000000..b9d78b7f479 --- /dev/null +++ b/src/tools/clippy/tests/ui/mutex_atomic.rs @@ -0,0 +1,15 @@ +#![warn(clippy::all)] +#![warn(clippy::mutex_integer)] + +fn main() { + use std::sync::Mutex; + Mutex::new(true); + Mutex::new(5usize); + Mutex::new(9isize); + let mut x = 4u32; + Mutex::new(&x as *const u32); + Mutex::new(&mut x as *mut u32); + Mutex::new(0u32); + Mutex::new(0i32); + Mutex::new(0f32); // there are no float atomics, so this should not lint +} diff --git a/src/tools/clippy/tests/ui/mutex_atomic.stderr b/src/tools/clippy/tests/ui/mutex_atomic.stderr new file mode 100644 index 00000000000..7dac0865855 --- /dev/null +++ b/src/tools/clippy/tests/ui/mutex_atomic.stderr @@ -0,0 +1,48 @@ +error: Consider using an `AtomicBool` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`. + --> $DIR/mutex_atomic.rs:6:5 + | +LL | Mutex::new(true); + | ^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::mutex-atomic` implied by `-D warnings` + +error: Consider using an `AtomicUsize` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`. + --> $DIR/mutex_atomic.rs:7:5 + | +LL | Mutex::new(5usize); + | ^^^^^^^^^^^^^^^^^^ + +error: Consider using an `AtomicIsize` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`. + --> $DIR/mutex_atomic.rs:8:5 + | +LL | Mutex::new(9isize); + | ^^^^^^^^^^^^^^^^^^ + +error: Consider using an `AtomicPtr` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`. + --> $DIR/mutex_atomic.rs:10:5 + | +LL | Mutex::new(&x as *const u32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: Consider using an `AtomicPtr` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`. + --> $DIR/mutex_atomic.rs:11:5 + | +LL | Mutex::new(&mut x as *mut u32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: Consider using an `AtomicUsize` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`. + --> $DIR/mutex_atomic.rs:12:5 + | +LL | Mutex::new(0u32); + | ^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::mutex-integer` implied by `-D warnings` + +error: Consider using an `AtomicIsize` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`. + --> $DIR/mutex_atomic.rs:13:5 + | +LL | Mutex::new(0i32); + | ^^^^^^^^^^^^^^^^ + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/tests/ui/needless_bool/fixable.fixed b/src/tools/clippy/tests/ui/needless_bool/fixable.fixed new file mode 100644 index 00000000000..567dbc54100 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_bool/fixable.fixed @@ -0,0 +1,98 @@ +// run-rustfix + +#![warn(clippy::needless_bool)] +#![allow( + unused, + dead_code, + clippy::no_effect, + clippy::if_same_then_else, + clippy::needless_return +)] + +use std::cell::Cell; + +macro_rules! bool_comparison_trigger { + ($($i:ident: $def:expr, $stb:expr );+ $(;)*) => ( + + #[derive(Clone)] + pub struct Trigger { + $($i: (Cell<bool>, bool, bool)),+ + } + + #[allow(dead_code)] + impl Trigger { + pub fn trigger(&self, key: &str) -> bool { + $( + if let stringify!($i) = key { + return self.$i.1 && self.$i.2 == $def; + } + )+ + false + } + } + ) +} + +fn main() { + let x = true; + let y = false; + x; + !x; + !(x && y); + if x { + x + } else { + false + }; // would also be questionable, but we don't catch this yet + bool_ret3(x); + bool_ret4(x); + bool_ret5(x, x); + bool_ret6(x, x); + needless_bool(x); + needless_bool2(x); + needless_bool3(x); +} + +fn bool_ret3(x: bool) -> bool { + return x; +} + +fn bool_ret4(x: bool) -> bool { + return !x; +} + +fn bool_ret5(x: bool, y: bool) -> bool { + return x && y; +} + +fn bool_ret6(x: bool, y: bool) -> bool { + return !(x && y); +} + +fn needless_bool(x: bool) { + if x {}; +} + +fn needless_bool2(x: bool) { + if !x {}; +} + +fn needless_bool3(x: bool) { + bool_comparison_trigger! { + test_one: false, false; + test_three: false, false; + test_two: true, true; + } + + if x {}; + if !x {}; +} + +fn needless_bool_in_the_suggestion_wraps_the_predicate_of_if_else_statement_in_brackets() { + let b = false; + let returns_bool = || false; + + let x = if b { + true + } else { !returns_bool() }; +} diff --git a/src/tools/clippy/tests/ui/needless_bool/fixable.rs b/src/tools/clippy/tests/ui/needless_bool/fixable.rs new file mode 100644 index 00000000000..10126ad4dbb --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_bool/fixable.rs @@ -0,0 +1,130 @@ +// run-rustfix + +#![warn(clippy::needless_bool)] +#![allow( + unused, + dead_code, + clippy::no_effect, + clippy::if_same_then_else, + clippy::needless_return +)] + +use std::cell::Cell; + +macro_rules! bool_comparison_trigger { + ($($i:ident: $def:expr, $stb:expr );+ $(;)*) => ( + + #[derive(Clone)] + pub struct Trigger { + $($i: (Cell<bool>, bool, bool)),+ + } + + #[allow(dead_code)] + impl Trigger { + pub fn trigger(&self, key: &str) -> bool { + $( + if let stringify!($i) = key { + return self.$i.1 && self.$i.2 == $def; + } + )+ + false + } + } + ) +} + +fn main() { + let x = true; + let y = false; + if x { + true + } else { + false + }; + if x { + false + } else { + true + }; + if x && y { + false + } else { + true + }; + if x { + x + } else { + false + }; // would also be questionable, but we don't catch this yet + bool_ret3(x); + bool_ret4(x); + bool_ret5(x, x); + bool_ret6(x, x); + needless_bool(x); + needless_bool2(x); + needless_bool3(x); +} + +fn bool_ret3(x: bool) -> bool { + if x { + return true; + } else { + return false; + }; +} + +fn bool_ret4(x: bool) -> bool { + if x { + return false; + } else { + return true; + }; +} + +fn bool_ret5(x: bool, y: bool) -> bool { + if x && y { + return true; + } else { + return false; + }; +} + +fn bool_ret6(x: bool, y: bool) -> bool { + if x && y { + return false; + } else { + return true; + }; +} + +fn needless_bool(x: bool) { + if x == true {}; +} + +fn needless_bool2(x: bool) { + if x == false {}; +} + +fn needless_bool3(x: bool) { + bool_comparison_trigger! { + test_one: false, false; + test_three: false, false; + test_two: true, true; + } + + if x == true {}; + if x == false {}; +} + +fn needless_bool_in_the_suggestion_wraps_the_predicate_of_if_else_statement_in_brackets() { + let b = false; + let returns_bool = || false; + + let x = if b { + true + } else if returns_bool() { + false + } else { + true + }; +} diff --git a/src/tools/clippy/tests/ui/needless_bool/fixable.stderr b/src/tools/clippy/tests/ui/needless_bool/fixable.stderr new file mode 100644 index 00000000000..25abfb2a472 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_bool/fixable.stderr @@ -0,0 +1,111 @@ +error: this if-then-else expression returns a bool literal + --> $DIR/fixable.rs:39:5 + | +LL | / if x { +LL | | true +LL | | } else { +LL | | false +LL | | }; + | |_____^ help: you can reduce it to: `x` + | + = note: `-D clippy::needless-bool` implied by `-D warnings` + +error: this if-then-else expression returns a bool literal + --> $DIR/fixable.rs:44:5 + | +LL | / if x { +LL | | false +LL | | } else { +LL | | true +LL | | }; + | |_____^ help: you can reduce it to: `!x` + +error: this if-then-else expression returns a bool literal + --> $DIR/fixable.rs:49:5 + | +LL | / if x && y { +LL | | false +LL | | } else { +LL | | true +LL | | }; + | |_____^ help: you can reduce it to: `!(x && y)` + +error: this if-then-else expression returns a bool literal + --> $DIR/fixable.rs:69:5 + | +LL | / if x { +LL | | return true; +LL | | } else { +LL | | return false; +LL | | }; + | |_____^ help: you can reduce it to: `return x` + +error: this if-then-else expression returns a bool literal + --> $DIR/fixable.rs:77:5 + | +LL | / if x { +LL | | return false; +LL | | } else { +LL | | return true; +LL | | }; + | |_____^ help: you can reduce it to: `return !x` + +error: this if-then-else expression returns a bool literal + --> $DIR/fixable.rs:85:5 + | +LL | / if x && y { +LL | | return true; +LL | | } else { +LL | | return false; +LL | | }; + | |_____^ help: you can reduce it to: `return x && y` + +error: this if-then-else expression returns a bool literal + --> $DIR/fixable.rs:93:5 + | +LL | / if x && y { +LL | | return false; +LL | | } else { +LL | | return true; +LL | | }; + | |_____^ help: you can reduce it to: `return !(x && y)` + +error: equality checks against true are unnecessary + --> $DIR/fixable.rs:101:8 + | +LL | if x == true {}; + | ^^^^^^^^^ help: try simplifying it as shown: `x` + | + = note: `-D clippy::bool-comparison` implied by `-D warnings` + +error: equality checks against false can be replaced by a negation + --> $DIR/fixable.rs:105:8 + | +LL | if x == false {}; + | ^^^^^^^^^^ help: try simplifying it as shown: `!x` + +error: equality checks against true are unnecessary + --> $DIR/fixable.rs:115:8 + | +LL | if x == true {}; + | ^^^^^^^^^ help: try simplifying it as shown: `x` + +error: equality checks against false can be replaced by a negation + --> $DIR/fixable.rs:116:8 + | +LL | if x == false {}; + | ^^^^^^^^^^ help: try simplifying it as shown: `!x` + +error: this if-then-else expression returns a bool literal + --> $DIR/fixable.rs:125:12 + | +LL | } else if returns_bool() { + | ____________^ +LL | | false +LL | | } else { +LL | | true +LL | | }; + | |_____^ help: you can reduce it to: `{ !returns_bool() }` + +error: aborting due to 12 previous errors + diff --git a/src/tools/clippy/tests/ui/needless_bool/simple.rs b/src/tools/clippy/tests/ui/needless_bool/simple.rs new file mode 100644 index 00000000000..e9f1428fc3a --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_bool/simple.rs @@ -0,0 +1,46 @@ +#![warn(clippy::needless_bool)] +#![allow( + unused, + dead_code, + clippy::no_effect, + clippy::if_same_then_else, + clippy::needless_return +)] + +fn main() { + let x = true; + let y = false; + if x { + true + } else { + true + }; + if x { + false + } else { + false + }; + if x { + x + } else { + false + }; // would also be questionable, but we don't catch this yet + bool_ret(x); + bool_ret2(x); +} + +fn bool_ret(x: bool) -> bool { + if x { + return true; + } else { + return true; + }; +} + +fn bool_ret2(x: bool) -> bool { + if x { + return false; + } else { + return false; + }; +} diff --git a/src/tools/clippy/tests/ui/needless_bool/simple.stderr b/src/tools/clippy/tests/ui/needless_bool/simple.stderr new file mode 100644 index 00000000000..c57a8a042fb --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_bool/simple.stderr @@ -0,0 +1,44 @@ +error: this if-then-else expression will always return true + --> $DIR/simple.rs:13:5 + | +LL | / if x { +LL | | true +LL | | } else { +LL | | true +LL | | }; + | |_____^ + | + = note: `-D clippy::needless-bool` implied by `-D warnings` + +error: this if-then-else expression will always return false + --> $DIR/simple.rs:18:5 + | +LL | / if x { +LL | | false +LL | | } else { +LL | | false +LL | | }; + | |_____^ + +error: this if-then-else expression will always return true + --> $DIR/simple.rs:33:5 + | +LL | / if x { +LL | | return true; +LL | | } else { +LL | | return true; +LL | | }; + | |_____^ + +error: this if-then-else expression will always return false + --> $DIR/simple.rs:41:5 + | +LL | / if x { +LL | | return false; +LL | | } else { +LL | | return false; +LL | | }; + | |_____^ + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/needless_borrow.fixed b/src/tools/clippy/tests/ui/needless_borrow.fixed new file mode 100644 index 00000000000..5ae4a0e79b9 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_borrow.fixed @@ -0,0 +1,61 @@ +// run-rustfix + +#![allow(clippy::needless_borrowed_reference)] + +fn x(y: &i32) -> i32 { + *y +} + +#[warn(clippy::all, clippy::needless_borrow)] +#[allow(unused_variables)] +fn main() { + let a = 5; + let b = x(&a); + let c = x(&a); + let s = &String::from("hi"); + let s_ident = f(&s); // should not error, because `&String` implements Copy, but `String` does not + let g_val = g(&Vec::new()); // should not error, because `&Vec<T>` derefs to `&[T]` + let vec = Vec::new(); + let vec_val = g(&vec); // should not error, because `&Vec<T>` derefs to `&[T]` + h(&"foo"); // should not error, because the `&&str` is required, due to `&Trait` + if let Some(cake) = Some(&5) {} + let garbl = match 42 { + 44 => &a, + 45 => { + println!("foo"); + &&a // FIXME: this should lint, too + }, + 46 => &a, + _ => panic!(), + }; +} + +fn f<T: Copy>(y: &T) -> T { + *y +} + +fn g(y: &[u8]) -> u8 { + y[0] +} + +trait Trait {} + +impl<'a> Trait for &'a str {} + +fn h(_: &dyn Trait) {} +#[warn(clippy::needless_borrow)] +#[allow(dead_code)] +fn issue_1432() { + let mut v = Vec::<String>::new(); + let _ = v.iter_mut().filter(|&ref a| a.is_empty()); + let _ = v.iter().filter(|&a| a.is_empty()); + + let _ = v.iter().filter(|&a| a.is_empty()); +} + +#[allow(dead_code)] +#[warn(clippy::needless_borrow)] +#[derive(Debug)] +enum Foo<'a> { + Str(&'a str), +} diff --git a/src/tools/clippy/tests/ui/needless_borrow.rs b/src/tools/clippy/tests/ui/needless_borrow.rs new file mode 100644 index 00000000000..1e281316c8a --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_borrow.rs @@ -0,0 +1,61 @@ +// run-rustfix + +#![allow(clippy::needless_borrowed_reference)] + +fn x(y: &i32) -> i32 { + *y +} + +#[warn(clippy::all, clippy::needless_borrow)] +#[allow(unused_variables)] +fn main() { + let a = 5; + let b = x(&a); + let c = x(&&a); + let s = &String::from("hi"); + let s_ident = f(&s); // should not error, because `&String` implements Copy, but `String` does not + let g_val = g(&Vec::new()); // should not error, because `&Vec<T>` derefs to `&[T]` + let vec = Vec::new(); + let vec_val = g(&vec); // should not error, because `&Vec<T>` derefs to `&[T]` + h(&"foo"); // should not error, because the `&&str` is required, due to `&Trait` + if let Some(ref cake) = Some(&5) {} + let garbl = match 42 { + 44 => &a, + 45 => { + println!("foo"); + &&a // FIXME: this should lint, too + }, + 46 => &&a, + _ => panic!(), + }; +} + +fn f<T: Copy>(y: &T) -> T { + *y +} + +fn g(y: &[u8]) -> u8 { + y[0] +} + +trait Trait {} + +impl<'a> Trait for &'a str {} + +fn h(_: &dyn Trait) {} +#[warn(clippy::needless_borrow)] +#[allow(dead_code)] +fn issue_1432() { + let mut v = Vec::<String>::new(); + let _ = v.iter_mut().filter(|&ref a| a.is_empty()); + let _ = v.iter().filter(|&ref a| a.is_empty()); + + let _ = v.iter().filter(|&a| a.is_empty()); +} + +#[allow(dead_code)] +#[warn(clippy::needless_borrow)] +#[derive(Debug)] +enum Foo<'a> { + Str(&'a str), +} diff --git a/src/tools/clippy/tests/ui/needless_borrow.stderr b/src/tools/clippy/tests/ui/needless_borrow.stderr new file mode 100644 index 00000000000..0bfeda7914d --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_borrow.stderr @@ -0,0 +1,28 @@ +error: this expression borrows a reference that is immediately dereferenced by the compiler + --> $DIR/needless_borrow.rs:14:15 + | +LL | let c = x(&&a); + | ^^^ help: change this to: `&a` + | + = note: `-D clippy::needless-borrow` implied by `-D warnings` + +error: this pattern creates a reference to a reference + --> $DIR/needless_borrow.rs:21:17 + | +LL | if let Some(ref cake) = Some(&5) {} + | ^^^^^^^^ help: change this to: `cake` + +error: this expression borrows a reference that is immediately dereferenced by the compiler + --> $DIR/needless_borrow.rs:28:15 + | +LL | 46 => &&a, + | ^^^ help: change this to: `&a` + +error: this pattern creates a reference to a reference + --> $DIR/needless_borrow.rs:51:31 + | +LL | let _ = v.iter().filter(|&ref a| a.is_empty()); + | ^^^^^ help: change this to: `a` + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/needless_borrowed_ref.fixed b/src/tools/clippy/tests/ui/needless_borrowed_ref.fixed new file mode 100644 index 00000000000..a0937a2c5f6 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_borrowed_ref.fixed @@ -0,0 +1,45 @@ +// run-rustfix + +#[warn(clippy::needless_borrowed_reference)] +#[allow(unused_variables)] +fn main() { + let mut v = Vec::<String>::new(); + let _ = v.iter_mut().filter(|a| a.is_empty()); + // ^ should be linted + + let var = 3; + let thingy = Some(&var); + if let Some(&ref v) = thingy { + // ^ should be linted + } + + let mut var2 = 5; + let thingy2 = Some(&mut var2); + if let Some(&mut ref mut v) = thingy2 { + // ^ should **not** be linted + // v is borrowed as mutable. + *v = 10; + } + if let Some(&mut ref v) = thingy2 { + // ^ should **not** be linted + // here, v is borrowed as immutable. + // can't do that: + //*v = 15; + } +} + +#[allow(dead_code)] +enum Animal { + Cat(u64), + Dog(u64), +} + +#[allow(unused_variables)] +#[allow(dead_code)] +fn foo(a: &Animal, b: &Animal) { + match (a, b) { + (&Animal::Cat(v), &ref k) | (&ref k, &Animal::Cat(v)) => (), // lifetime mismatch error if there is no '&ref' + // ^ and ^ should **not** be linted + (&Animal::Dog(ref a), &Animal::Dog(_)) => (), // ^ should **not** be linted + } +} diff --git a/src/tools/clippy/tests/ui/needless_borrowed_ref.rs b/src/tools/clippy/tests/ui/needless_borrowed_ref.rs new file mode 100644 index 00000000000..500ac448f0d --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_borrowed_ref.rs @@ -0,0 +1,45 @@ +// run-rustfix + +#[warn(clippy::needless_borrowed_reference)] +#[allow(unused_variables)] +fn main() { + let mut v = Vec::<String>::new(); + let _ = v.iter_mut().filter(|&ref a| a.is_empty()); + // ^ should be linted + + let var = 3; + let thingy = Some(&var); + if let Some(&ref v) = thingy { + // ^ should be linted + } + + let mut var2 = 5; + let thingy2 = Some(&mut var2); + if let Some(&mut ref mut v) = thingy2 { + // ^ should **not** be linted + // v is borrowed as mutable. + *v = 10; + } + if let Some(&mut ref v) = thingy2 { + // ^ should **not** be linted + // here, v is borrowed as immutable. + // can't do that: + //*v = 15; + } +} + +#[allow(dead_code)] +enum Animal { + Cat(u64), + Dog(u64), +} + +#[allow(unused_variables)] +#[allow(dead_code)] +fn foo(a: &Animal, b: &Animal) { + match (a, b) { + (&Animal::Cat(v), &ref k) | (&ref k, &Animal::Cat(v)) => (), // lifetime mismatch error if there is no '&ref' + // ^ and ^ should **not** be linted + (&Animal::Dog(ref a), &Animal::Dog(_)) => (), // ^ should **not** be linted + } +} diff --git a/src/tools/clippy/tests/ui/needless_borrowed_ref.stderr b/src/tools/clippy/tests/ui/needless_borrowed_ref.stderr new file mode 100644 index 00000000000..0a5cfb3db0b --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_borrowed_ref.stderr @@ -0,0 +1,10 @@ +error: this pattern takes a reference on something that is being de-referenced + --> $DIR/needless_borrowed_ref.rs:7:34 + | +LL | let _ = v.iter_mut().filter(|&ref a| a.is_empty()); + | ^^^^^^ help: try removing the `&ref` part and just keep: `a` + | + = note: `-D clippy::needless-borrowed-reference` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/needless_collect.fixed b/src/tools/clippy/tests/ui/needless_collect.fixed new file mode 100644 index 00000000000..b4227eaf2f8 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_collect.fixed @@ -0,0 +1,21 @@ +// run-rustfix + +#![allow(unused, clippy::suspicious_map)] + +use std::collections::{BTreeSet, HashMap, HashSet}; + +#[warn(clippy::needless_collect)] +#[allow(unused_variables, clippy::iter_cloned_collect)] +fn main() { + let sample = [1; 5]; + let len = sample.iter().count(); + if sample.iter().next().is_none() { + // Empty + } + sample.iter().cloned().any(|x| x == 1); + sample.iter().map(|x| (x, x)).count(); + // Notice the `HashSet`--this should not be linted + sample.iter().collect::<HashSet<_>>().len(); + // Neither should this + sample.iter().collect::<BTreeSet<_>>().len(); +} diff --git a/src/tools/clippy/tests/ui/needless_collect.rs b/src/tools/clippy/tests/ui/needless_collect.rs new file mode 100644 index 00000000000..7ee603afeb0 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_collect.rs @@ -0,0 +1,21 @@ +// run-rustfix + +#![allow(unused, clippy::suspicious_map)] + +use std::collections::{BTreeSet, HashMap, HashSet}; + +#[warn(clippy::needless_collect)] +#[allow(unused_variables, clippy::iter_cloned_collect)] +fn main() { + let sample = [1; 5]; + let len = sample.iter().collect::<Vec<_>>().len(); + if sample.iter().collect::<Vec<_>>().is_empty() { + // Empty + } + sample.iter().cloned().collect::<Vec<_>>().contains(&1); + sample.iter().map(|x| (x, x)).collect::<HashMap<_, _>>().len(); + // Notice the `HashSet`--this should not be linted + sample.iter().collect::<HashSet<_>>().len(); + // Neither should this + sample.iter().collect::<BTreeSet<_>>().len(); +} diff --git a/src/tools/clippy/tests/ui/needless_collect.stderr b/src/tools/clippy/tests/ui/needless_collect.stderr new file mode 100644 index 00000000000..8884c8e1612 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_collect.stderr @@ -0,0 +1,28 @@ +error: avoid using `collect()` when not needed + --> $DIR/needless_collect.rs:11:28 + | +LL | let len = sample.iter().collect::<Vec<_>>().len(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `.count()` + | + = note: `-D clippy::needless-collect` implied by `-D warnings` + +error: avoid using `collect()` when not needed + --> $DIR/needless_collect.rs:12:21 + | +LL | if sample.iter().collect::<Vec<_>>().is_empty() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `.next().is_none()` + +error: avoid using `collect()` when not needed + --> $DIR/needless_collect.rs:15:27 + | +LL | sample.iter().cloned().collect::<Vec<_>>().contains(&1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `.any(|x| x == 1)` + +error: avoid using `collect()` when not needed + --> $DIR/needless_collect.rs:16:34 + | +LL | sample.iter().map(|x| (x, x)).collect::<HashMap<_, _>>().len(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `.count()` + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/needless_continue.rs b/src/tools/clippy/tests/ui/needless_continue.rs new file mode 100644 index 00000000000..5da95647f2c --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_continue.rs @@ -0,0 +1,115 @@ +#![warn(clippy::needless_continue)] + +macro_rules! zero { + ($x:expr) => { + $x == 0 + }; +} + +macro_rules! nonzero { + ($x:expr) => { + !zero!($x) + }; +} + +fn main() { + let mut i = 1; + while i < 10 { + i += 1; + + if i % 2 == 0 && i % 3 == 0 { + println!("{}", i); + println!("{}", i + 1); + if i % 5 == 0 { + println!("{}", i + 2); + } + let i = 0; + println!("bar {} ", i); + } else { + continue; + } + + println!("bleh"); + { + println!("blah"); + } + + // some comments that also should ideally be included in the + // output of the lint suggestion if possible. + if !(!(i == 2) || !(i == 5)) { + println!("lama"); + } + + if (zero!(i % 2) || nonzero!(i % 5)) && i % 3 != 0 { + continue; + } else { + println!("Blabber"); + println!("Jabber"); + } + + println!("bleh"); + } +} + +mod issue_2329 { + fn condition() -> bool { + unimplemented!() + } + fn update_condition() {} + + // only the outer loop has a label + fn foo() { + 'outer: loop { + println!("Entry"); + while condition() { + update_condition(); + if condition() { + println!("foo-1"); + } else { + continue 'outer; // should not lint here + } + println!("foo-2"); + + update_condition(); + if condition() { + continue 'outer; // should not lint here + } else { + println!("foo-3"); + } + println!("foo-4"); + } + } + } + + // both loops have labels + fn bar() { + 'outer: loop { + println!("Entry"); + 'inner: while condition() { + update_condition(); + if condition() { + println!("bar-1"); + } else { + continue 'outer; // should not lint here + } + println!("bar-2"); + + update_condition(); + if condition() { + println!("bar-3"); + } else { + continue 'inner; // should lint here + } + println!("bar-4"); + + update_condition(); + if condition() { + continue; // should lint here + } else { + println!("bar-5"); + } + println!("bar-6"); + } + } + } +} diff --git a/src/tools/clippy/tests/ui/needless_continue.stderr b/src/tools/clippy/tests/ui/needless_continue.stderr new file mode 100644 index 00000000000..8d6a37df960 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_continue.stderr @@ -0,0 +1,99 @@ +error: this `else` block is redundant + --> $DIR/needless_continue.rs:28:16 + | +LL | } else { + | ________________^ +LL | | continue; +LL | | } + | |_________^ + | + = note: `-D clippy::needless-continue` implied by `-D warnings` + = help: consider dropping the `else` clause and merging the code that follows (in the loop) with the `if` block + if i % 2 == 0 && i % 3 == 0 { + println!("{}", i); + println!("{}", i + 1); + if i % 5 == 0 { + println!("{}", i + 2); + } + let i = 0; + println!("bar {} ", i); + // merged code follows: + println!("bleh"); + { + println!("blah"); + } + if !(!(i == 2) || !(i == 5)) { + println!("lama"); + } + if (zero!(i % 2) || nonzero!(i % 5)) && i % 3 != 0 { + continue; + } else { + println!("Blabber"); + println!("Jabber"); + } + println!("bleh"); + } + +error: there is no need for an explicit `else` block for this `if` expression + --> $DIR/needless_continue.rs:43:9 + | +LL | / if (zero!(i % 2) || nonzero!(i % 5)) && i % 3 != 0 { +LL | | continue; +LL | | } else { +LL | | println!("Blabber"); +LL | | println!("Jabber"); +LL | | } + | |_________^ + | + = help: consider dropping the `else` clause + if (zero!(i % 2) || nonzero!(i % 5)) && i % 3 != 0 { + continue; + } + { + println!("Blabber"); + println!("Jabber"); + } + +error: this `else` block is redundant + --> $DIR/needless_continue.rs:100:24 + | +LL | } else { + | ________________________^ +LL | | continue 'inner; // should lint here +LL | | } + | |_________________^ + | + = help: consider dropping the `else` clause and merging the code that follows (in the loop) with the `if` block + if condition() { + println!("bar-3"); + // merged code follows: + println!("bar-4"); + update_condition(); + if condition() { + continue; // should lint here + } else { + println!("bar-5"); + } + println!("bar-6"); + } + +error: there is no need for an explicit `else` block for this `if` expression + --> $DIR/needless_continue.rs:106:17 + | +LL | / if condition() { +LL | | continue; // should lint here +LL | | } else { +LL | | println!("bar-5"); +LL | | } + | |_________________^ + | + = help: consider dropping the `else` clause + if condition() { + continue; // should lint here + } + { + println!("bar-5"); + } + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/needless_doc_main.rs b/src/tools/clippy/tests/ui/needless_doc_main.rs new file mode 100644 index 00000000000..682d7b3c4ce --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_doc_main.rs @@ -0,0 +1,74 @@ +/// This is a test for needless `fn main()` in doctests. +/// +/// # Examples +/// +/// This should lint +/// ``` +/// fn main() { +/// unimplemented!(); +/// } +/// ``` +/// +/// This should, too. +/// +/// ```rust +/// fn main() { +/// unimplemented!(); +/// } +/// ``` +/// +/// This one too. +/// +/// ```no_run +/// fn main() { +/// unimplemented!(); +/// } +/// ``` +fn bad_doctests() {} + +/// # Examples +/// +/// This shouldn't lint, because the `main` is empty: +/// ``` +/// fn main(){} +/// ``` +/// +/// This shouldn't lint either, because there's a `static`: +/// ``` +/// static ANSWER: i32 = 42; +/// +/// fn main() { +/// assert_eq!(42, ANSWER); +/// } +/// ``` +/// +/// Neither should this lint because of `extern crate`: +/// ``` +/// #![feature(test)] +/// extern crate test; +/// fn main() { +/// assert_eq(1u8, test::black_box(1)); +/// } +/// ``` +/// +/// We should not lint ignored examples: +/// +/// ```rust,ignore +/// fn main() { +/// unimplemented!(); +/// } +/// ``` +/// +/// Or even non-rust examples: +/// +/// ```text +/// fn main() { +/// is what starts the program +/// } +/// ``` +fn no_false_positives() {} + +fn main() { + bad_doctests(); + no_false_positives(); +} diff --git a/src/tools/clippy/tests/ui/needless_doc_main.stderr b/src/tools/clippy/tests/ui/needless_doc_main.stderr new file mode 100644 index 00000000000..65d40ee6832 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_doc_main.stderr @@ -0,0 +1,22 @@ +error: needless `fn main` in doctest + --> $DIR/needless_doc_main.rs:7:4 + | +LL | /// fn main() { + | ^^^^^^^^^^^^ + | + = note: `-D clippy::needless-doctest-main` implied by `-D warnings` + +error: needless `fn main` in doctest + --> $DIR/needless_doc_main.rs:15:4 + | +LL | /// fn main() { + | ^^^^^^^^^^^^ + +error: needless `fn main` in doctest + --> $DIR/needless_doc_main.rs:23:4 + | +LL | /// fn main() { + | ^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/needless_lifetimes.rs b/src/tools/clippy/tests/ui/needless_lifetimes.rs new file mode 100644 index 00000000000..913cd004f19 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_lifetimes.rs @@ -0,0 +1,262 @@ +#![warn(clippy::needless_lifetimes)] +#![allow(dead_code, clippy::needless_pass_by_value)] + +fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {} + +fn distinct_and_static<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: &'static u8) {} + +// No error; same lifetime on two params. +fn same_lifetime_on_input<'a>(_x: &'a u8, _y: &'a u8) {} + +// No error; static involved. +fn only_static_on_input(_x: &u8, _y: &u8, _z: &'static u8) {} + +fn mut_and_static_input(_x: &mut u8, _y: &'static str) {} + +fn in_and_out<'a>(x: &'a u8, _y: u8) -> &'a u8 { + x +} + +// No error; multiple input refs. +fn multiple_in_and_out_1<'a>(x: &'a u8, _y: &'a u8) -> &'a u8 { + x +} + +// No error; multiple input refs. +fn multiple_in_and_out_2<'a, 'b>(x: &'a u8, _y: &'b u8) -> &'a u8 { + x +} + +// No error; static involved. +fn in_static_and_out<'a>(x: &'a u8, _y: &'static u8) -> &'a u8 { + x +} + +// No error. +fn deep_reference_1<'a, 'b>(x: &'a u8, _y: &'b u8) -> Result<&'a u8, ()> { + Ok(x) +} + +// No error; two input refs. +fn deep_reference_2<'a>(x: Result<&'a u8, &'a u8>) -> &'a u8 { + x.unwrap() +} + +fn deep_reference_3<'a>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> { + Ok(x) +} + +// Where-clause, but without lifetimes. +fn where_clause_without_lt<'a, T>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> +where + T: Copy, +{ + Ok(x) +} + +type Ref<'r> = &'r u8; + +// No error; same lifetime on two params. +fn lifetime_param_1<'a>(_x: Ref<'a>, _y: &'a u8) {} + +fn lifetime_param_2<'a, 'b>(_x: Ref<'a>, _y: &'b u8) {} + +// No error; bounded lifetime. +fn lifetime_param_3<'a, 'b: 'a>(_x: Ref<'a>, _y: &'b u8) {} + +// No error; bounded lifetime. +fn lifetime_param_4<'a, 'b>(_x: Ref<'a>, _y: &'b u8) +where + 'b: 'a, +{ +} + +struct Lt<'a, I: 'static> { + x: &'a I, +} + +// No error; fn bound references `'a`. +fn fn_bound<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I> +where + F: Fn(Lt<'a, I>) -> Lt<'a, I>, +{ + unreachable!() +} + +fn fn_bound_2<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I> +where + for<'x> F: Fn(Lt<'x, I>) -> Lt<'x, I>, +{ + unreachable!() +} + +// No error; see below. +fn fn_bound_3<'a, F: FnOnce(&'a i32)>(x: &'a i32, f: F) { + f(x); +} + +fn fn_bound_3_cannot_elide() { + let x = 42; + let p = &x; + let mut q = &x; + // This will fail if we elide lifetimes of `fn_bound_3`. + fn_bound_3(p, |y| q = y); +} + +// No error; multiple input refs. +fn fn_bound_4<'a, F: FnOnce() -> &'a ()>(cond: bool, x: &'a (), f: F) -> &'a () { + if cond { + x + } else { + f() + } +} + +struct X { + x: u8, +} + +impl X { + fn self_and_out<'s>(&'s self) -> &'s u8 { + &self.x + } + + // No error; multiple input refs. + fn self_and_in_out<'s, 't>(&'s self, _x: &'t u8) -> &'s u8 { + &self.x + } + + fn distinct_self_and_in<'s, 't>(&'s self, _x: &'t u8) {} + + // No error; same lifetimes on two params. + fn self_and_same_in<'s>(&'s self, _x: &'s u8) {} +} + +struct Foo<'a>(&'a u8); + +impl<'a> Foo<'a> { + // No error; lifetime `'a` not defined in method. + fn self_shared_lifetime(&self, _: &'a u8) {} + // No error; bounds exist. + fn self_bound_lifetime<'b: 'a>(&self, _: &'b u8) {} +} + +fn already_elided<'a>(_: &u8, _: &'a u8) -> &'a u8 { + unimplemented!() +} + +fn struct_with_lt<'a>(_foo: Foo<'a>) -> &'a str { + unimplemented!() +} + +// No warning; two input lifetimes (named on the reference, anonymous on `Foo`). +fn struct_with_lt2<'a>(_foo: &'a Foo) -> &'a str { + unimplemented!() +} + +// No warning; two input lifetimes (anonymous on the reference, named on `Foo`). +fn struct_with_lt3<'a>(_foo: &Foo<'a>) -> &'a str { + unimplemented!() +} + +// No warning; two input lifetimes. +fn struct_with_lt4<'a, 'b>(_foo: &'a Foo<'b>) -> &'a str { + unimplemented!() +} + +trait WithLifetime<'a> {} + +type WithLifetimeAlias<'a> = dyn WithLifetime<'a>; + +// Should not warn because it won't build without the lifetime. +fn trait_obj_elided<'a>(_arg: &'a dyn WithLifetime) -> &'a str { + unimplemented!() +} + +// Should warn because there is no lifetime on `Drop`, so this would be +// unambiguous if we elided the lifetime. +fn trait_obj_elided2<'a>(_arg: &'a dyn Drop) -> &'a str { + unimplemented!() +} + +type FooAlias<'a> = Foo<'a>; + +fn alias_with_lt<'a>(_foo: FooAlias<'a>) -> &'a str { + unimplemented!() +} + +// No warning; two input lifetimes (named on the reference, anonymous on `FooAlias`). +fn alias_with_lt2<'a>(_foo: &'a FooAlias) -> &'a str { + unimplemented!() +} + +// No warning; two input lifetimes (anonymous on the reference, named on `FooAlias`). +fn alias_with_lt3<'a>(_foo: &FooAlias<'a>) -> &'a str { + unimplemented!() +} + +// No warning; two input lifetimes. +fn alias_with_lt4<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'a str { + unimplemented!() +} + +fn named_input_elided_output<'a>(_arg: &'a str) -> &str { + unimplemented!() +} + +fn elided_input_named_output<'a>(_arg: &str) -> &'a str { + unimplemented!() +} + +fn trait_bound_ok<'a, T: WithLifetime<'static>>(_: &'a u8, _: T) { + unimplemented!() +} +fn trait_bound<'a, T: WithLifetime<'a>>(_: &'a u8, _: T) { + unimplemented!() +} + +// Don't warn on these; see issue #292. +fn trait_bound_bug<'a, T: WithLifetime<'a>>() { + unimplemented!() +} + +// See issue #740. +struct Test { + vec: Vec<usize>, +} + +impl Test { + fn iter<'a>(&'a self) -> Box<dyn Iterator<Item = usize> + 'a> { + unimplemented!() + } +} + +trait LintContext<'a> {} + +fn f<'a, T: LintContext<'a>>(_: &T) {} + +fn test<'a>(x: &'a [u8]) -> u8 { + let y: &'a u8 = &x[5]; + *y +} + +// Issue #3284: give hint regarding lifetime in return type. +struct Cow<'a> { + x: &'a str, +} +fn out_return_type_lts<'a>(e: &'a str) -> Cow<'a> { + unimplemented!() +} + +// Make sure we still warn on implementations +mod issue4291 { + trait BadTrait { + fn needless_lt<'a>(x: &'a u8) {} + } + + impl BadTrait for () { + fn needless_lt<'a>(_x: &'a u8) {} + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/needless_lifetimes.stderr b/src/tools/clippy/tests/ui/needless_lifetimes.stderr new file mode 100644 index 00000000000..d3a360ed8b5 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_lifetimes.stderr @@ -0,0 +1,106 @@ +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:4:1 + | +LL | fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::needless-lifetimes` implied by `-D warnings` + +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:6:1 + | +LL | fn distinct_and_static<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: &'static u8) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:16:1 + | +LL | fn in_and_out<'a>(x: &'a u8, _y: u8) -> &'a u8 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:45:1 + | +LL | fn deep_reference_3<'a>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:50:1 + | +LL | fn where_clause_without_lt<'a, T>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:62:1 + | +LL | fn lifetime_param_2<'a, 'b>(_x: Ref<'a>, _y: &'b u8) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:86:1 + | +LL | fn fn_bound_2<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I> + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:120:5 + | +LL | fn self_and_out<'s>(&'s self) -> &'s u8 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:129:5 + | +LL | fn distinct_self_and_in<'s, 't>(&'s self, _x: &'t u8) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:148:1 + | +LL | fn struct_with_lt<'a>(_foo: Foo<'a>) -> &'a str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:178:1 + | +LL | fn trait_obj_elided2<'a>(_arg: &'a dyn Drop) -> &'a str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:184:1 + | +LL | fn alias_with_lt<'a>(_foo: FooAlias<'a>) -> &'a str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:203:1 + | +LL | fn named_input_elided_output<'a>(_arg: &'a str) -> &str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:211:1 + | +LL | fn trait_bound_ok<'a, T: WithLifetime<'static>>(_: &'a u8, _: T) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:247:1 + | +LL | fn out_return_type_lts<'a>(e: &'a str) -> Cow<'a> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:254:9 + | +LL | fn needless_lt<'a>(x: &'a u8) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:258:9 + | +LL | fn needless_lt<'a>(_x: &'a u8) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 17 previous errors + diff --git a/src/tools/clippy/tests/ui/needless_pass_by_value.rs b/src/tools/clippy/tests/ui/needless_pass_by_value.rs new file mode 100644 index 00000000000..e93a7fe2985 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_pass_by_value.rs @@ -0,0 +1,161 @@ +#![warn(clippy::needless_pass_by_value)] +#![allow( + dead_code, + clippy::single_match, + clippy::redundant_pattern_matching, + clippy::many_single_char_names, + clippy::option_option, + clippy::redundant_clone +)] + +use std::borrow::Borrow; +use std::collections::HashSet; +use std::convert::AsRef; +use std::mem::MaybeUninit; + +// `v` should be warned +// `w`, `x` and `y` are allowed (moved or mutated) +fn foo<T: Default>(v: Vec<T>, w: Vec<T>, mut x: Vec<T>, y: Vec<T>) -> Vec<T> { + assert_eq!(v.len(), 42); + + consume(w); + + x.push(T::default()); + + y +} + +fn consume<T>(_: T) {} + +struct Wrapper(String); + +fn bar(x: String, y: Wrapper) { + assert_eq!(x.len(), 42); + assert_eq!(y.0.len(), 42); +} + +// V implements `Borrow<V>`, but should be warned correctly +fn test_borrow_trait<T: Borrow<str>, U: AsRef<str>, V>(t: T, u: U, v: V) { + println!("{}", t.borrow()); + println!("{}", u.as_ref()); + consume(&v); +} + +// ok +fn test_fn<F: Fn(i32) -> i32>(f: F) { + f(1); +} + +// x should be warned, but y is ok +fn test_match(x: Option<Option<String>>, y: Option<Option<String>>) { + match x { + Some(Some(_)) => 1, // not moved + _ => 0, + }; + + match y { + Some(Some(s)) => consume(s), // moved + _ => (), + }; +} + +// x and y should be warned, but z is ok +fn test_destructure(x: Wrapper, y: Wrapper, z: Wrapper) { + let Wrapper(s) = z; // moved + let Wrapper(ref t) = y; // not moved + let Wrapper(_) = y; // still not moved + + assert_eq!(x.0.len(), s.len()); + println!("{}", t); +} + +trait Foo {} + +// `S: Serialize` is allowed to be passed by value, since a caller can pass `&S` instead +trait Serialize {} +impl<'a, T> Serialize for &'a T where T: Serialize {} +impl Serialize for i32 {} + +fn test_blanket_ref<T: Foo, S: Serialize>(_foo: T, _serializable: S) {} + +fn issue_2114(s: String, t: String, u: Vec<i32>, v: Vec<i32>) { + s.capacity(); + let _ = t.clone(); + u.capacity(); + let _ = v.clone(); +} + +struct S<T, U>(T, U); + +impl<T: Serialize, U> S<T, U> { + fn foo( + self, + // taking `self` by value is always allowed + s: String, + t: String, + ) -> usize { + s.len() + t.capacity() + } + + fn bar(_t: T, // Ok, since `&T: Serialize` too + ) { + } + + fn baz(&self, _u: U, _s: Self) {} +} + +trait FalsePositive { + fn visit_str(s: &str); + fn visit_string(s: String) { + Self::visit_str(&s); + } +} + +// shouldn't warn on extern funcs +extern "C" fn ext(x: MaybeUninit<usize>) -> usize { + unsafe { x.assume_init() } +} + +// whitelist RangeArgument +fn range<T: ::std::ops::RangeBounds<usize>>(range: T) { + let _ = range.start_bound(); +} + +struct CopyWrapper(u32); + +fn bar_copy(x: u32, y: CopyWrapper) { + assert_eq!(x, 42); + assert_eq!(y.0, 42); +} + +// x and y should be warned, but z is ok +fn test_destructure_copy(x: CopyWrapper, y: CopyWrapper, z: CopyWrapper) { + let CopyWrapper(s) = z; // moved + let CopyWrapper(ref t) = y; // not moved + let CopyWrapper(_) = y; // still not moved + + assert_eq!(x.0, s); + println!("{}", t); +} + +// The following 3 lines should not cause an ICE. See #2831 +trait Bar<'a, A> {} +impl<'b, T> Bar<'b, T> for T {} +fn some_fun<'b, S: Bar<'b, ()>>(_item: S) {} + +// Also this should not cause an ICE. See #2831 +trait Club<'a, A> {} +impl<T> Club<'static, T> for T {} +fn more_fun(_item: impl Club<'static, i32>) {} + +fn is_sync<T>(_: T) +where + T: Sync, +{ +} + +fn main() { + // This should not cause an ICE either + // https://github.com/rust-lang/rust-clippy/issues/3144 + is_sync(HashSet::<usize>::new()); +} diff --git a/src/tools/clippy/tests/ui/needless_pass_by_value.stderr b/src/tools/clippy/tests/ui/needless_pass_by_value.stderr new file mode 100644 index 00000000000..9aa783bf904 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_pass_by_value.stderr @@ -0,0 +1,178 @@ +error: this argument is passed by value, but not consumed in the function body + --> $DIR/needless_pass_by_value.rs:18:23 + | +LL | fn foo<T: Default>(v: Vec<T>, w: Vec<T>, mut x: Vec<T>, y: Vec<T>) -> Vec<T> { + | ^^^^^^ help: consider changing the type to: `&[T]` + | + = note: `-D clippy::needless-pass-by-value` implied by `-D warnings` + +error: this argument is passed by value, but not consumed in the function body + --> $DIR/needless_pass_by_value.rs:32:11 + | +LL | fn bar(x: String, y: Wrapper) { + | ^^^^^^ help: consider changing the type to: `&str` + +error: this argument is passed by value, but not consumed in the function body + --> $DIR/needless_pass_by_value.rs:32:22 + | +LL | fn bar(x: String, y: Wrapper) { + | ^^^^^^^ help: consider taking a reference instead: `&Wrapper` + +error: this argument is passed by value, but not consumed in the function body + --> $DIR/needless_pass_by_value.rs:38:71 + | +LL | fn test_borrow_trait<T: Borrow<str>, U: AsRef<str>, V>(t: T, u: U, v: V) { + | ^ help: consider taking a reference instead: `&V` + +error: this argument is passed by value, but not consumed in the function body + --> $DIR/needless_pass_by_value.rs:50:18 + | +LL | fn test_match(x: Option<Option<String>>, y: Option<Option<String>>) { + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider taking a reference instead: `&Option<Option<String>>` + +error: this argument is passed by value, but not consumed in the function body + --> $DIR/needless_pass_by_value.rs:63:24 + | +LL | fn test_destructure(x: Wrapper, y: Wrapper, z: Wrapper) { + | ^^^^^^^ help: consider taking a reference instead: `&Wrapper` + +error: this argument is passed by value, but not consumed in the function body + --> $DIR/needless_pass_by_value.rs:63:36 + | +LL | fn test_destructure(x: Wrapper, y: Wrapper, z: Wrapper) { + | ^^^^^^^ help: consider taking a reference instead: `&Wrapper` + +error: this argument is passed by value, but not consumed in the function body + --> $DIR/needless_pass_by_value.rs:79:49 + | +LL | fn test_blanket_ref<T: Foo, S: Serialize>(_foo: T, _serializable: S) {} + | ^ help: consider taking a reference instead: `&T` + +error: this argument is passed by value, but not consumed in the function body + --> $DIR/needless_pass_by_value.rs:81:18 + | +LL | fn issue_2114(s: String, t: String, u: Vec<i32>, v: Vec<i32>) { + | ^^^^^^ help: consider taking a reference instead: `&String` + +error: this argument is passed by value, but not consumed in the function body + --> $DIR/needless_pass_by_value.rs:81:29 + | +LL | fn issue_2114(s: String, t: String, u: Vec<i32>, v: Vec<i32>) { + | ^^^^^^ + | +help: consider changing the type to + | +LL | fn issue_2114(s: String, t: &str, u: Vec<i32>, v: Vec<i32>) { + | ^^^^ +help: change `t.clone()` to + | +LL | let _ = t.to_string(); + | ^^^^^^^^^^^^^ + +error: this argument is passed by value, but not consumed in the function body + --> $DIR/needless_pass_by_value.rs:81:40 + | +LL | fn issue_2114(s: String, t: String, u: Vec<i32>, v: Vec<i32>) { + | ^^^^^^^^ help: consider taking a reference instead: `&Vec<i32>` + +error: this argument is passed by value, but not consumed in the function body + --> $DIR/needless_pass_by_value.rs:81:53 + | +LL | fn issue_2114(s: String, t: String, u: Vec<i32>, v: Vec<i32>) { + | ^^^^^^^^ + | +help: consider changing the type to + | +LL | fn issue_2114(s: String, t: String, u: Vec<i32>, v: &[i32]) { + | ^^^^^^ +help: change `v.clone()` to + | +LL | let _ = v.to_owned(); + | ^^^^^^^^^^^^ + +error: this argument is passed by value, but not consumed in the function body + --> $DIR/needless_pass_by_value.rs:94:12 + | +LL | s: String, + | ^^^^^^ help: consider changing the type to: `&str` + +error: this argument is passed by value, but not consumed in the function body + --> $DIR/needless_pass_by_value.rs:95:12 + | +LL | t: String, + | ^^^^^^ help: consider taking a reference instead: `&String` + +error: this argument is passed by value, but not consumed in the function body + --> $DIR/needless_pass_by_value.rs:104:23 + | +LL | fn baz(&self, _u: U, _s: Self) {} + | ^ help: consider taking a reference instead: `&U` + +error: this argument is passed by value, but not consumed in the function body + --> $DIR/needless_pass_by_value.rs:104:30 + | +LL | fn baz(&self, _u: U, _s: Self) {} + | ^^^^ help: consider taking a reference instead: `&Self` + +error: this argument is passed by value, but not consumed in the function body + --> $DIR/needless_pass_by_value.rs:126:24 + | +LL | fn bar_copy(x: u32, y: CopyWrapper) { + | ^^^^^^^^^^^ help: consider taking a reference instead: `&CopyWrapper` + | +help: consider marking this type as `Copy` + --> $DIR/needless_pass_by_value.rs:124:1 + | +LL | struct CopyWrapper(u32); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this argument is passed by value, but not consumed in the function body + --> $DIR/needless_pass_by_value.rs:132:29 + | +LL | fn test_destructure_copy(x: CopyWrapper, y: CopyWrapper, z: CopyWrapper) { + | ^^^^^^^^^^^ help: consider taking a reference instead: `&CopyWrapper` + | +help: consider marking this type as `Copy` + --> $DIR/needless_pass_by_value.rs:124:1 + | +LL | struct CopyWrapper(u32); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this argument is passed by value, but not consumed in the function body + --> $DIR/needless_pass_by_value.rs:132:45 + | +LL | fn test_destructure_copy(x: CopyWrapper, y: CopyWrapper, z: CopyWrapper) { + | ^^^^^^^^^^^ help: consider taking a reference instead: `&CopyWrapper` + | +help: consider marking this type as `Copy` + --> $DIR/needless_pass_by_value.rs:124:1 + | +LL | struct CopyWrapper(u32); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this argument is passed by value, but not consumed in the function body + --> $DIR/needless_pass_by_value.rs:132:61 + | +LL | fn test_destructure_copy(x: CopyWrapper, y: CopyWrapper, z: CopyWrapper) { + | ^^^^^^^^^^^ help: consider taking a reference instead: `&CopyWrapper` + | +help: consider marking this type as `Copy` + --> $DIR/needless_pass_by_value.rs:124:1 + | +LL | struct CopyWrapper(u32); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this argument is passed by value, but not consumed in the function body + --> $DIR/needless_pass_by_value.rs:144:40 + | +LL | fn some_fun<'b, S: Bar<'b, ()>>(_item: S) {} + | ^ help: consider taking a reference instead: `&S` + +error: this argument is passed by value, but not consumed in the function body + --> $DIR/needless_pass_by_value.rs:149:20 + | +LL | fn more_fun(_item: impl Club<'static, i32>) {} + | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider taking a reference instead: `&impl Club<'static, i32>` + +error: aborting due to 22 previous errors + diff --git a/src/tools/clippy/tests/ui/needless_pass_by_value_proc_macro.rs b/src/tools/clippy/tests/ui/needless_pass_by_value_proc_macro.rs new file mode 100644 index 00000000000..78a0e92d179 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_pass_by_value_proc_macro.rs @@ -0,0 +1,21 @@ +#![crate_type = "proc-macro"] +#![warn(clippy::needless_pass_by_value)] + +extern crate proc_macro; + +use proc_macro::TokenStream; + +#[proc_macro_derive(Foo)] +pub fn foo(_input: TokenStream) -> TokenStream { + unimplemented!() +} + +#[proc_macro] +pub fn bar(_input: TokenStream) -> TokenStream { + unimplemented!() +} + +#[proc_macro_attribute] +pub fn baz(_args: TokenStream, _input: TokenStream) -> TokenStream { + unimplemented!() +} diff --git a/src/tools/clippy/tests/ui/needless_range_loop.rs b/src/tools/clippy/tests/ui/needless_range_loop.rs new file mode 100644 index 00000000000..3fce34367ae --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_range_loop.rs @@ -0,0 +1,95 @@ +#![warn(clippy::needless_range_loop)] + +static STATIC: [usize; 4] = [0, 1, 8, 16]; +const CONST: [usize; 4] = [0, 1, 8, 16]; +const MAX_LEN: usize = 42; + +fn main() { + let mut vec = vec![1, 2, 3, 4]; + let vec2 = vec![1, 2, 3, 4]; + for i in 0..vec.len() { + println!("{}", vec[i]); + } + + for i in 0..vec.len() { + let i = 42; // make a different `i` + println!("{}", vec[i]); // ok, not the `i` of the for-loop + } + + for i in 0..vec.len() { + let _ = vec[i]; + } + + // ICE #746 + for j in 0..4 { + println!("{:?}", STATIC[j]); + } + + for j in 0..4 { + println!("{:?}", CONST[j]); + } + + for i in 0..vec.len() { + println!("{} {}", vec[i], i); + } + for i in 0..vec.len() { + // not an error, indexing more than one variable + println!("{} {}", vec[i], vec2[i]); + } + + for i in 0..vec.len() { + println!("{}", vec2[i]); + } + + for i in 5..vec.len() { + println!("{}", vec[i]); + } + + for i in 0..MAX_LEN { + println!("{}", vec[i]); + } + + for i in 0..=MAX_LEN { + println!("{}", vec[i]); + } + + for i in 5..10 { + println!("{}", vec[i]); + } + + for i in 5..=10 { + println!("{}", vec[i]); + } + + for i in 5..vec.len() { + println!("{} {}", vec[i], i); + } + + for i in 5..10 { + println!("{} {}", vec[i], i); + } + + // #2542 + for i in 0..vec.len() { + vec[i] = Some(1).unwrap_or_else(|| panic!("error on {}", i)); + } + + // #3788 + let test = Test { + inner: vec![1, 2, 3, 4], + }; + for i in 0..2 { + println!("{}", test[i]); + } +} + +struct Test { + inner: Vec<usize>, +} + +impl std::ops::Index<usize> for Test { + type Output = usize; + fn index(&self, index: usize) -> &Self::Output { + &self.inner[index] + } +} diff --git a/src/tools/clippy/tests/ui/needless_range_loop.stderr b/src/tools/clippy/tests/ui/needless_range_loop.stderr new file mode 100644 index 00000000000..c50c4931fb4 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_range_loop.stderr @@ -0,0 +1,157 @@ +error: the loop variable `i` is only used to index `vec`. + --> $DIR/needless_range_loop.rs:10:14 + | +LL | for i in 0..vec.len() { + | ^^^^^^^^^^^^ + | + = note: `-D clippy::needless-range-loop` implied by `-D warnings` +help: consider using an iterator + | +LL | for <item> in &vec { + | ^^^^^^ ^^^^ + +error: the loop variable `i` is only used to index `vec`. + --> $DIR/needless_range_loop.rs:19:14 + | +LL | for i in 0..vec.len() { + | ^^^^^^^^^^^^ + | +help: consider using an iterator + | +LL | for <item> in &vec { + | ^^^^^^ ^^^^ + +error: the loop variable `j` is only used to index `STATIC`. + --> $DIR/needless_range_loop.rs:24:14 + | +LL | for j in 0..4 { + | ^^^^ + | +help: consider using an iterator + | +LL | for <item> in &STATIC { + | ^^^^^^ ^^^^^^^ + +error: the loop variable `j` is only used to index `CONST`. + --> $DIR/needless_range_loop.rs:28:14 + | +LL | for j in 0..4 { + | ^^^^ + | +help: consider using an iterator + | +LL | for <item> in &CONST { + | ^^^^^^ ^^^^^^ + +error: the loop variable `i` is used to index `vec` + --> $DIR/needless_range_loop.rs:32:14 + | +LL | for i in 0..vec.len() { + | ^^^^^^^^^^^^ + | +help: consider using an iterator + | +LL | for (i, <item>) in vec.iter().enumerate() { + | ^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^ + +error: the loop variable `i` is only used to index `vec2`. + --> $DIR/needless_range_loop.rs:40:14 + | +LL | for i in 0..vec.len() { + | ^^^^^^^^^^^^ + | +help: consider using an iterator + | +LL | for <item> in vec2.iter().take(vec.len()) { + | ^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the loop variable `i` is only used to index `vec`. + --> $DIR/needless_range_loop.rs:44:14 + | +LL | for i in 5..vec.len() { + | ^^^^^^^^^^^^ + | +help: consider using an iterator + | +LL | for <item> in vec.iter().skip(5) { + | ^^^^^^ ^^^^^^^^^^^^^^^^^^ + +error: the loop variable `i` is only used to index `vec`. + --> $DIR/needless_range_loop.rs:48:14 + | +LL | for i in 0..MAX_LEN { + | ^^^^^^^^^^ + | +help: consider using an iterator + | +LL | for <item> in vec.iter().take(MAX_LEN) { + | ^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the loop variable `i` is only used to index `vec`. + --> $DIR/needless_range_loop.rs:52:14 + | +LL | for i in 0..=MAX_LEN { + | ^^^^^^^^^^^ + | +help: consider using an iterator + | +LL | for <item> in vec.iter().take(MAX_LEN + 1) { + | ^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the loop variable `i` is only used to index `vec`. + --> $DIR/needless_range_loop.rs:56:14 + | +LL | for i in 5..10 { + | ^^^^^ + | +help: consider using an iterator + | +LL | for <item> in vec.iter().take(10).skip(5) { + | ^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the loop variable `i` is only used to index `vec`. + --> $DIR/needless_range_loop.rs:60:14 + | +LL | for i in 5..=10 { + | ^^^^^^ + | +help: consider using an iterator + | +LL | for <item> in vec.iter().take(10 + 1).skip(5) { + | ^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the loop variable `i` is used to index `vec` + --> $DIR/needless_range_loop.rs:64:14 + | +LL | for i in 5..vec.len() { + | ^^^^^^^^^^^^ + | +help: consider using an iterator + | +LL | for (i, <item>) in vec.iter().enumerate().skip(5) { + | ^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the loop variable `i` is used to index `vec` + --> $DIR/needless_range_loop.rs:68:14 + | +LL | for i in 5..10 { + | ^^^^^ + | +help: consider using an iterator + | +LL | for (i, <item>) in vec.iter().enumerate().take(10).skip(5) { + | ^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the loop variable `i` is used to index `vec` + --> $DIR/needless_range_loop.rs:73:14 + | +LL | for i in 0..vec.len() { + | ^^^^^^^^^^^^ + | +help: consider using an iterator + | +LL | for (i, <item>) in vec.iter_mut().enumerate() { + | ^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 14 previous errors + diff --git a/src/tools/clippy/tests/ui/needless_range_loop2.rs b/src/tools/clippy/tests/ui/needless_range_loop2.rs new file mode 100644 index 00000000000..2ed1b09bece --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_range_loop2.rs @@ -0,0 +1,85 @@ +#![warn(clippy::needless_range_loop)] + +fn calc_idx(i: usize) -> usize { + (i + i + 20) % 4 +} + +fn main() { + let ns = vec![2, 3, 5, 7]; + + for i in 3..10 { + println!("{}", ns[i]); + } + + for i in 3..10 { + println!("{}", ns[i % 4]); + } + + for i in 3..10 { + println!("{}", ns[i % ns.len()]); + } + + for i in 3..10 { + println!("{}", ns[calc_idx(i)]); + } + + for i in 3..10 { + println!("{}", ns[calc_idx(i) % 4]); + } + + let mut ms = vec![1, 2, 3, 4, 5, 6]; + for i in 0..ms.len() { + ms[i] *= 2; + } + assert_eq!(ms, vec![2, 4, 6, 8, 10, 12]); + + let mut ms = vec![1, 2, 3, 4, 5, 6]; + for i in 0..ms.len() { + let x = &mut ms[i]; + *x *= 2; + } + assert_eq!(ms, vec![2, 4, 6, 8, 10, 12]); + + let g = vec![1, 2, 3, 4, 5, 6]; + let glen = g.len(); + for i in 0..glen { + let x: u32 = g[i + 1..].iter().sum(); + println!("{}", g[i] + x); + } + assert_eq!(g, vec![20, 18, 15, 11, 6, 0]); + + let mut g = vec![1, 2, 3, 4, 5, 6]; + let glen = g.len(); + for i in 0..glen { + g[i] = g[i + 1..].iter().sum(); + } + assert_eq!(g, vec![20, 18, 15, 11, 6, 0]); + + let x = 5; + let mut vec = vec![0; 9]; + + for i in x..x + 4 { + vec[i] += 1; + } + + let x = 5; + let mut vec = vec![0; 10]; + + for i in x..=x + 4 { + vec[i] += 1; + } + + let arr = [1, 2, 3]; + + for i in 0..3 { + println!("{}", arr[i]); + } + + for i in 0..2 { + println!("{}", arr[i]); + } + + for i in 1..3 { + println!("{}", arr[i]); + } +} diff --git a/src/tools/clippy/tests/ui/needless_range_loop2.stderr b/src/tools/clippy/tests/ui/needless_range_loop2.stderr new file mode 100644 index 00000000000..c54ab5ec980 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_range_loop2.stderr @@ -0,0 +1,91 @@ +error: the loop variable `i` is only used to index `ns`. + --> $DIR/needless_range_loop2.rs:10:14 + | +LL | for i in 3..10 { + | ^^^^^ + | + = note: `-D clippy::needless-range-loop` implied by `-D warnings` +help: consider using an iterator + | +LL | for <item> in ns.iter().take(10).skip(3) { + | ^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the loop variable `i` is only used to index `ms`. + --> $DIR/needless_range_loop2.rs:31:14 + | +LL | for i in 0..ms.len() { + | ^^^^^^^^^^^ + | +help: consider using an iterator + | +LL | for <item> in &mut ms { + | ^^^^^^ ^^^^^^^ + +error: the loop variable `i` is only used to index `ms`. + --> $DIR/needless_range_loop2.rs:37:14 + | +LL | for i in 0..ms.len() { + | ^^^^^^^^^^^ + | +help: consider using an iterator + | +LL | for <item> in &mut ms { + | ^^^^^^ ^^^^^^^ + +error: the loop variable `i` is only used to index `vec`. + --> $DIR/needless_range_loop2.rs:61:14 + | +LL | for i in x..x + 4 { + | ^^^^^^^^ + | +help: consider using an iterator + | +LL | for <item> in vec.iter_mut().skip(x).take(4) { + | ^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the loop variable `i` is only used to index `vec`. + --> $DIR/needless_range_loop2.rs:68:14 + | +LL | for i in x..=x + 4 { + | ^^^^^^^^^ + | +help: consider using an iterator + | +LL | for <item> in vec.iter_mut().skip(x).take(4 + 1) { + | ^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the loop variable `i` is only used to index `arr`. + --> $DIR/needless_range_loop2.rs:74:14 + | +LL | for i in 0..3 { + | ^^^^ + | +help: consider using an iterator + | +LL | for <item> in &arr { + | ^^^^^^ ^^^^ + +error: the loop variable `i` is only used to index `arr`. + --> $DIR/needless_range_loop2.rs:78:14 + | +LL | for i in 0..2 { + | ^^^^ + | +help: consider using an iterator + | +LL | for <item> in arr.iter().take(2) { + | ^^^^^^ ^^^^^^^^^^^^^^^^^^ + +error: the loop variable `i` is only used to index `arr`. + --> $DIR/needless_range_loop2.rs:82:14 + | +LL | for i in 1..3 { + | ^^^^ + | +help: consider using an iterator + | +LL | for <item> in arr.iter().skip(1) { + | ^^^^^^ ^^^^^^^^^^^^^^^^^^ + +error: aborting due to 8 previous errors + diff --git a/src/tools/clippy/tests/ui/needless_return.fixed b/src/tools/clippy/tests/ui/needless_return.fixed new file mode 100644 index 00000000000..ad20e238107 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_return.fixed @@ -0,0 +1,78 @@ +// run-rustfix + +#![allow(unused, clippy::needless_bool)] +#![allow(clippy::if_same_then_else, clippy::single_match)] +#![warn(clippy::needless_return)] + +macro_rules! the_answer { + () => { + 42 + }; +} + +fn test_end_of_fn() -> bool { + if true { + // no error! + return true; + } + true +} + +fn test_no_semicolon() -> bool { + true +} + +fn test_if_block() -> bool { + if true { + true + } else { + false + } +} + +fn test_match(x: bool) -> bool { + match x { + true => false, + false => { + true + }, + } +} + +fn test_closure() { + let _ = || { + true + }; + let _ = || true; +} + +fn test_macro_call() -> i32 { + return the_answer!(); +} + +fn test_void_fun() { + +} + +fn test_void_if_fun(b: bool) { + if b { + + } else { + + } +} + +fn test_void_match(x: u32) { + match x { + 0 => (), + _ => {}, + } +} + +fn main() { + let _ = test_end_of_fn(); + let _ = test_no_semicolon(); + let _ = test_if_block(); + let _ = test_match(true); + test_closure(); +} diff --git a/src/tools/clippy/tests/ui/needless_return.rs b/src/tools/clippy/tests/ui/needless_return.rs new file mode 100644 index 00000000000..af0cdfb207f --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_return.rs @@ -0,0 +1,78 @@ +// run-rustfix + +#![allow(unused, clippy::needless_bool)] +#![allow(clippy::if_same_then_else, clippy::single_match)] +#![warn(clippy::needless_return)] + +macro_rules! the_answer { + () => { + 42 + }; +} + +fn test_end_of_fn() -> bool { + if true { + // no error! + return true; + } + return true; +} + +fn test_no_semicolon() -> bool { + return true; +} + +fn test_if_block() -> bool { + if true { + return true; + } else { + return false; + } +} + +fn test_match(x: bool) -> bool { + match x { + true => return false, + false => { + return true; + }, + } +} + +fn test_closure() { + let _ = || { + return true; + }; + let _ = || return true; +} + +fn test_macro_call() -> i32 { + return the_answer!(); +} + +fn test_void_fun() { + return; +} + +fn test_void_if_fun(b: bool) { + if b { + return; + } else { + return; + } +} + +fn test_void_match(x: u32) { + match x { + 0 => (), + _ => return, + } +} + +fn main() { + let _ = test_end_of_fn(); + let _ = test_no_semicolon(); + let _ = test_if_block(); + let _ = test_match(true); + test_closure(); +} diff --git a/src/tools/clippy/tests/ui/needless_return.stderr b/src/tools/clippy/tests/ui/needless_return.stderr new file mode 100644 index 00000000000..c34eecbcbb6 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_return.stderr @@ -0,0 +1,76 @@ +error: unneeded `return` statement + --> $DIR/needless_return.rs:18:5 + | +LL | return true; + | ^^^^^^^^^^^^ help: remove `return`: `true` + | + = note: `-D clippy::needless-return` implied by `-D warnings` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:22:5 + | +LL | return true; + | ^^^^^^^^^^^^ help: remove `return`: `true` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:27:9 + | +LL | return true; + | ^^^^^^^^^^^^ help: remove `return`: `true` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:29:9 + | +LL | return false; + | ^^^^^^^^^^^^^ help: remove `return`: `false` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:35:17 + | +LL | true => return false, + | ^^^^^^^^^^^^ help: remove `return`: `false` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:37:13 + | +LL | return true; + | ^^^^^^^^^^^^ help: remove `return`: `true` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:44:9 + | +LL | return true; + | ^^^^^^^^^^^^ help: remove `return`: `true` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:46:16 + | +LL | let _ = || return true; + | ^^^^^^^^^^^ help: remove `return`: `true` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:54:5 + | +LL | return; + | ^^^^^^^ help: remove `return` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:59:9 + | +LL | return; + | ^^^^^^^ help: remove `return` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:61:9 + | +LL | return; + | ^^^^^^^ help: remove `return` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:68:14 + | +LL | _ => return, + | ^^^^^^ help: replace `return` with an empty block: `{}` + +error: aborting due to 12 previous errors + diff --git a/src/tools/clippy/tests/ui/needless_update.rs b/src/tools/clippy/tests/ui/needless_update.rs new file mode 100644 index 00000000000..bfa005a19f9 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_update.rs @@ -0,0 +1,14 @@ +#![warn(clippy::needless_update)] +#![allow(clippy::no_effect)] + +struct S { + pub a: i32, + pub b: i32, +} + +fn main() { + let base = S { a: 0, b: 0 }; + S { ..base }; // no error + S { a: 1, ..base }; // no error + S { a: 1, b: 1, ..base }; +} diff --git a/src/tools/clippy/tests/ui/needless_update.stderr b/src/tools/clippy/tests/ui/needless_update.stderr new file mode 100644 index 00000000000..133c834880d --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_update.stderr @@ -0,0 +1,10 @@ +error: struct update has no effect, all the fields in the struct have already been specified + --> $DIR/needless_update.rs:13:23 + | +LL | S { a: 1, b: 1, ..base }; + | ^^^^ + | + = note: `-D clippy::needless-update` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/neg_cmp_op_on_partial_ord.rs b/src/tools/clippy/tests/ui/neg_cmp_op_on_partial_ord.rs new file mode 100644 index 00000000000..856a430ba2b --- /dev/null +++ b/src/tools/clippy/tests/ui/neg_cmp_op_on_partial_ord.rs @@ -0,0 +1,61 @@ +//! This test case utilizes `f64` an easy example for `PartialOrd` only types +//! but the lint itself actually validates any expression where the left +//! operand implements `PartialOrd` but not `Ord`. + +use std::cmp::Ordering; + +#[warn(clippy::neg_cmp_op_on_partial_ord)] +fn main() { + let a_value = 1.0; + let another_value = 7.0; + + // --- Bad --- + + // Not Less but potentially Greater, Equal or Uncomparable. + let _not_less = !(a_value < another_value); + + // Not Less or Equal but potentially Greater or Uncomparable. + let _not_less_or_equal = !(a_value <= another_value); + + // Not Greater but potentially Less, Equal or Uncomparable. + let _not_greater = !(a_value > another_value); + + // Not Greater or Equal but potentially Less or Uncomparable. + let _not_greater_or_equal = !(a_value >= another_value); + + // --- Good --- + + let _not_less = match a_value.partial_cmp(&another_value) { + None | Some(Ordering::Greater) | Some(Ordering::Equal) => true, + _ => false, + }; + let _not_less_or_equal = match a_value.partial_cmp(&another_value) { + None | Some(Ordering::Greater) => true, + _ => false, + }; + let _not_greater = match a_value.partial_cmp(&another_value) { + None | Some(Ordering::Less) | Some(Ordering::Equal) => true, + _ => false, + }; + let _not_greater_or_equal = match a_value.partial_cmp(&another_value) { + None | Some(Ordering::Less) => true, + _ => false, + }; + + // --- Should not trigger --- + + let _ = a_value < another_value; + let _ = a_value <= another_value; + let _ = a_value > another_value; + let _ = a_value >= another_value; + + // --- regression tests --- + + // Issue 2856: False positive on assert!() + // + // The macro always negates the result of the given comparison in its + // internal check which automatically triggered the lint. As it's an + // external macro there was no chance to do anything about it which led + // to a whitelisting of all external macros. + assert!(a_value < another_value); +} diff --git a/src/tools/clippy/tests/ui/neg_cmp_op_on_partial_ord.stderr b/src/tools/clippy/tests/ui/neg_cmp_op_on_partial_ord.stderr new file mode 100644 index 00000000000..d05fd34ce33 --- /dev/null +++ b/src/tools/clippy/tests/ui/neg_cmp_op_on_partial_ord.stderr @@ -0,0 +1,28 @@ +error: The use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable. + --> $DIR/neg_cmp_op_on_partial_ord.rs:15:21 + | +LL | let _not_less = !(a_value < another_value); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::neg-cmp-op-on-partial-ord` implied by `-D warnings` + +error: The use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable. + --> $DIR/neg_cmp_op_on_partial_ord.rs:18:30 + | +LL | let _not_less_or_equal = !(a_value <= another_value); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: The use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable. + --> $DIR/neg_cmp_op_on_partial_ord.rs:21:24 + | +LL | let _not_greater = !(a_value > another_value); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: The use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable. + --> $DIR/neg_cmp_op_on_partial_ord.rs:24:33 + | +LL | let _not_greater_or_equal = !(a_value >= another_value); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/neg_multiply.rs b/src/tools/clippy/tests/ui/neg_multiply.rs new file mode 100644 index 00000000000..d4a20ce9db1 --- /dev/null +++ b/src/tools/clippy/tests/ui/neg_multiply.rs @@ -0,0 +1,35 @@ +#![warn(clippy::neg_multiply)] +#![allow(clippy::no_effect, clippy::unnecessary_operation)] + +use std::ops::Mul; + +struct X; + +impl Mul<isize> for X { + type Output = X; + + fn mul(self, _r: isize) -> Self { + self + } +} + +impl Mul<X> for isize { + type Output = X; + + fn mul(self, _r: X) -> X { + X + } +} + +fn main() { + let x = 0; + + x * -1; + + -1 * x; + + -1 * -1; // should be ok + + X * -1; // should be ok + -1 * X; // should also be ok +} diff --git a/src/tools/clippy/tests/ui/neg_multiply.stderr b/src/tools/clippy/tests/ui/neg_multiply.stderr new file mode 100644 index 00000000000..f08bbd6a12c --- /dev/null +++ b/src/tools/clippy/tests/ui/neg_multiply.stderr @@ -0,0 +1,16 @@ +error: Negation by multiplying with `-1` + --> $DIR/neg_multiply.rs:27:5 + | +LL | x * -1; + | ^^^^^^ + | + = note: `-D clippy::neg-multiply` implied by `-D warnings` + +error: Negation by multiplying with `-1` + --> $DIR/neg_multiply.rs:29:5 + | +LL | -1 * x; + | ^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/never_loop.rs b/src/tools/clippy/tests/ui/never_loop.rs new file mode 100644 index 00000000000..cbc4ca39161 --- /dev/null +++ b/src/tools/clippy/tests/ui/never_loop.rs @@ -0,0 +1,204 @@ +#![allow( + clippy::single_match, + unused_assignments, + unused_variables, + clippy::while_immutable_condition +)] + +fn test1() { + let mut x = 0; + loop { + // clippy::never_loop + x += 1; + if x == 1 { + return; + } + break; + } +} + +fn test2() { + let mut x = 0; + loop { + x += 1; + if x == 1 { + break; + } + } +} + +fn test3() { + let mut x = 0; + loop { + // never loops + x += 1; + break; + } +} + +fn test4() { + let mut x = 1; + loop { + x += 1; + match x { + 5 => return, + _ => (), + } + } +} + +fn test5() { + let i = 0; + loop { + // never loops + while i == 0 { + // never loops + break; + } + return; + } +} + +fn test6() { + let mut x = 0; + 'outer: loop { + x += 1; + loop { + // never loops + if x == 5 { + break; + } + continue 'outer; + } + return; + } +} + +fn test7() { + let mut x = 0; + loop { + x += 1; + match x { + 1 => continue, + _ => (), + } + return; + } +} + +fn test8() { + let mut x = 0; + loop { + x += 1; + match x { + 5 => return, + _ => continue, + } + } +} + +fn test9() { + let x = Some(1); + while let Some(y) = x { + // never loops + return; + } +} + +fn test10() { + for x in 0..10 { + // never loops + match x { + 1 => break, + _ => return, + } + } +} + +fn test11<F: FnMut() -> i32>(mut f: F) { + loop { + return match f() { + 1 => continue, + _ => (), + }; + } +} + +pub fn test12(a: bool, b: bool) { + 'label: loop { + loop { + if a { + continue 'label; + } + if b { + break; + } + } + break; + } +} + +pub fn test13() { + let mut a = true; + loop { + // infinite loop + while a { + if true { + a = false; + continue; + } + return; + } + } +} + +pub fn test14() { + let mut a = true; + 'outer: while a { + // never loops + while a { + if a { + a = false; + continue; + } + } + break 'outer; + } +} + +// Issue #1991: the outter loop should not warn. +pub fn test15() { + 'label: loop { + while false { + break 'label; + } + } +} + +// Issue #4058: `continue` in `break` expression +pub fn test16() { + let mut n = 1; + loop { + break if n != 5 { + n += 1; + continue; + }; + } +} + +fn main() { + test1(); + test2(); + test3(); + test4(); + test5(); + test6(); + test7(); + test8(); + test9(); + test10(); + test11(|| 0); + test12(true, false); + test13(); + test14(); +} diff --git a/src/tools/clippy/tests/ui/never_loop.stderr b/src/tools/clippy/tests/ui/never_loop.stderr new file mode 100644 index 00000000000..c00b4c78cf2 --- /dev/null +++ b/src/tools/clippy/tests/ui/never_loop.stderr @@ -0,0 +1,100 @@ +error: this loop never actually loops + --> $DIR/never_loop.rs:10:5 + | +LL | / loop { +LL | | // clippy::never_loop +LL | | x += 1; +LL | | if x == 1 { +... | +LL | | break; +LL | | } + | |_____^ + | + = note: `#[deny(clippy::never_loop)]` on by default + +error: this loop never actually loops + --> $DIR/never_loop.rs:32:5 + | +LL | / loop { +LL | | // never loops +LL | | x += 1; +LL | | break; +LL | | } + | |_____^ + +error: this loop never actually loops + --> $DIR/never_loop.rs:52:5 + | +LL | / loop { +LL | | // never loops +LL | | while i == 0 { +LL | | // never loops +... | +LL | | return; +LL | | } + | |_____^ + +error: this loop never actually loops + --> $DIR/never_loop.rs:54:9 + | +LL | / while i == 0 { +LL | | // never loops +LL | | break; +LL | | } + | |_________^ + +error: this loop never actually loops + --> $DIR/never_loop.rs:66:9 + | +LL | / loop { +LL | | // never loops +LL | | if x == 5 { +LL | | break; +LL | | } +LL | | continue 'outer; +LL | | } + | |_________^ + +error: this loop never actually loops + --> $DIR/never_loop.rs:102:5 + | +LL | / while let Some(y) = x { +LL | | // never loops +LL | | return; +LL | | } + | |_____^ + +error: this loop never actually loops + --> $DIR/never_loop.rs:109:5 + | +LL | / for x in 0..10 { +LL | | // never loops +LL | | match x { +LL | | 1 => break, +LL | | _ => return, +LL | | } +LL | | } + | |_____^ + +error: this loop never actually loops + --> $DIR/never_loop.rs:157:5 + | +LL | / 'outer: while a { +LL | | // never loops +LL | | while a { +LL | | if a { +... | +LL | | break 'outer; +LL | | } + | |_____^ + +error: this loop never actually loops + --> $DIR/never_loop.rs:172:9 + | +LL | / while false { +LL | | break 'label; +LL | | } + | |_________^ + +error: aborting due to 9 previous errors + diff --git a/src/tools/clippy/tests/ui/new_ret_no_self.rs b/src/tools/clippy/tests/ui/new_ret_no_self.rs new file mode 100644 index 00000000000..2c2d1e27589 --- /dev/null +++ b/src/tools/clippy/tests/ui/new_ret_no_self.rs @@ -0,0 +1,212 @@ +#![warn(clippy::new_ret_no_self)] +#![allow(dead_code)] + +fn main() {} + +trait R { + type Item; +} + +trait Q { + type Item; + type Item2; +} + +struct S; + +impl R for S { + type Item = Self; +} + +impl S { + // should not trigger the lint + pub fn new() -> impl R<Item = Self> { + S + } +} + +struct S2; + +impl R for S2 { + type Item = Self; +} + +impl S2 { + // should not trigger the lint + pub fn new(_: String) -> impl R<Item = Self> { + S2 + } +} + +struct S3; + +impl R for S3 { + type Item = u32; +} + +impl S3 { + // should trigger the lint + pub fn new(_: String) -> impl R<Item = u32> { + S3 + } +} + +struct S4; + +impl Q for S4 { + type Item = u32; + type Item2 = Self; +} + +impl S4 { + // should not trigger the lint + pub fn new(_: String) -> impl Q<Item = u32, Item2 = Self> { + S4 + } +} + +struct T; + +impl T { + // should not trigger lint + pub fn new() -> Self { + unimplemented!(); + } +} + +struct U; + +impl U { + // should trigger lint + pub fn new() -> u32 { + unimplemented!(); + } +} + +struct V; + +impl V { + // should trigger lint + pub fn new(_: String) -> u32 { + unimplemented!(); + } +} + +struct TupleReturnerOk; + +impl TupleReturnerOk { + // should not trigger lint + pub fn new() -> (Self, u32) { + unimplemented!(); + } +} + +struct TupleReturnerOk2; + +impl TupleReturnerOk2 { + // should not trigger lint (it doesn't matter which element in the tuple is Self) + pub fn new() -> (u32, Self) { + unimplemented!(); + } +} + +struct TupleReturnerOk3; + +impl TupleReturnerOk3 { + // should not trigger lint (tuple can contain multiple Self) + pub fn new() -> (Self, Self) { + unimplemented!(); + } +} + +struct TupleReturnerBad; + +impl TupleReturnerBad { + // should trigger lint + pub fn new() -> (u32, u32) { + unimplemented!(); + } +} + +struct MutPointerReturnerOk; + +impl MutPointerReturnerOk { + // should not trigger lint + pub fn new() -> *mut Self { + unimplemented!(); + } +} + +struct MutPointerReturnerOk2; + +impl MutPointerReturnerOk2 { + // should not trigger lint + pub fn new() -> *const Self { + unimplemented!(); + } +} + +struct MutPointerReturnerBad; + +impl MutPointerReturnerBad { + // should trigger lint + pub fn new() -> *mut V { + unimplemented!(); + } +} + +struct GenericReturnerOk; + +impl GenericReturnerOk { + // should not trigger lint + pub fn new() -> Option<Self> { + unimplemented!(); + } +} + +struct GenericReturnerBad; + +impl GenericReturnerBad { + // should trigger lint + pub fn new() -> Option<u32> { + unimplemented!(); + } +} + +struct NestedReturnerOk; + +impl NestedReturnerOk { + // should not trigger lint + pub fn new() -> (Option<Self>, u32) { + unimplemented!(); + } +} + +struct NestedReturnerOk2; + +impl NestedReturnerOk2 { + // should not trigger lint + pub fn new() -> ((Self, u32), u32) { + unimplemented!(); + } +} + +struct NestedReturnerOk3; + +impl NestedReturnerOk3 { + // should not trigger lint + pub fn new() -> Option<(Self, u32)> { + unimplemented!(); + } +} + +struct WithLifetime<'a> { + cat: &'a str, +} + +impl<'a> WithLifetime<'a> { + // should not trigger the lint, because the lifetimes are different + pub fn new<'b: 'a>(s: &'b str) -> WithLifetime<'b> { + unimplemented!(); + } +} diff --git a/src/tools/clippy/tests/ui/new_ret_no_self.stderr b/src/tools/clippy/tests/ui/new_ret_no_self.stderr new file mode 100644 index 00000000000..dd5a24bcbe7 --- /dev/null +++ b/src/tools/clippy/tests/ui/new_ret_no_self.stderr @@ -0,0 +1,52 @@ +error: methods called `new` usually return `Self` + --> $DIR/new_ret_no_self.rs:49:5 + | +LL | / pub fn new(_: String) -> impl R<Item = u32> { +LL | | S3 +LL | | } + | |_____^ + | + = note: `-D clippy::new-ret-no-self` implied by `-D warnings` + +error: methods called `new` usually return `Self` + --> $DIR/new_ret_no_self.rs:81:5 + | +LL | / pub fn new() -> u32 { +LL | | unimplemented!(); +LL | | } + | |_____^ + +error: methods called `new` usually return `Self` + --> $DIR/new_ret_no_self.rs:90:5 + | +LL | / pub fn new(_: String) -> u32 { +LL | | unimplemented!(); +LL | | } + | |_____^ + +error: methods called `new` usually return `Self` + --> $DIR/new_ret_no_self.rs:126:5 + | +LL | / pub fn new() -> (u32, u32) { +LL | | unimplemented!(); +LL | | } + | |_____^ + +error: methods called `new` usually return `Self` + --> $DIR/new_ret_no_self.rs:153:5 + | +LL | / pub fn new() -> *mut V { +LL | | unimplemented!(); +LL | | } + | |_____^ + +error: methods called `new` usually return `Self` + --> $DIR/new_ret_no_self.rs:171:5 + | +LL | / pub fn new() -> Option<u32> { +LL | | unimplemented!(); +LL | | } + | |_____^ + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/new_without_default.rs b/src/tools/clippy/tests/ui/new_without_default.rs new file mode 100644 index 00000000000..781ea7bb152 --- /dev/null +++ b/src/tools/clippy/tests/ui/new_without_default.rs @@ -0,0 +1,151 @@ +#![feature(const_fn)] +#![allow(dead_code, clippy::missing_safety_doc)] +#![warn(clippy::new_without_default)] + +pub struct Foo; + +impl Foo { + pub fn new() -> Foo { + Foo + } +} + +pub struct Bar; + +impl Bar { + pub fn new() -> Self { + Bar + } +} + +pub struct Ok; + +impl Ok { + pub fn new() -> Self { + Ok + } +} + +impl Default for Ok { + fn default() -> Self { + Ok + } +} + +pub struct Params; + +impl Params { + pub fn new(_: u32) -> Self { + Params + } +} + +pub struct GenericsOk<T> { + bar: T, +} + +impl<U> Default for GenericsOk<U> { + fn default() -> Self { + unimplemented!(); + } +} + +impl<'c, V> GenericsOk<V> { + pub fn new() -> GenericsOk<V> { + unimplemented!() + } +} + +pub struct LtOk<'a> { + foo: &'a bool, +} + +impl<'b> Default for LtOk<'b> { + fn default() -> Self { + unimplemented!(); + } +} + +impl<'c> LtOk<'c> { + pub fn new() -> LtOk<'c> { + unimplemented!() + } +} + +pub struct LtKo<'a> { + foo: &'a bool, +} + +impl<'c> LtKo<'c> { + pub fn new() -> LtKo<'c> { + unimplemented!() + } + // FIXME: that suggestion is missing lifetimes +} + +struct Private; + +impl Private { + fn new() -> Private { + unimplemented!() + } // We don't lint private items +} + +struct Const; + +impl Const { + pub const fn new() -> Const { + Const + } // const fns can't be implemented via Default +} + +pub struct IgnoreGenericNew; + +impl IgnoreGenericNew { + pub fn new<T>() -> Self { + IgnoreGenericNew + } // the derived Default does not make sense here as the result depends on T +} + +pub trait TraitWithNew: Sized { + fn new() -> Self { + panic!() + } +} + +pub struct IgnoreUnsafeNew; + +impl IgnoreUnsafeNew { + pub unsafe fn new() -> Self { + IgnoreUnsafeNew + } +} + +#[derive(Default)] +pub struct OptionRefWrapper<'a, T>(Option<&'a T>); + +impl<'a, T> OptionRefWrapper<'a, T> { + pub fn new() -> Self { + OptionRefWrapper(None) + } +} + +pub struct Allow(Foo); + +impl Allow { + #[allow(clippy::new_without_default)] + pub fn new() -> Self { + unimplemented!() + } +} + +pub struct AllowDerive; + +impl AllowDerive { + #[allow(clippy::new_without_default)] + pub fn new() -> Self { + unimplemented!() + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/new_without_default.stderr b/src/tools/clippy/tests/ui/new_without_default.stderr new file mode 100644 index 00000000000..5e485d40663 --- /dev/null +++ b/src/tools/clippy/tests/ui/new_without_default.stderr @@ -0,0 +1,46 @@ +error: you should consider deriving a `Default` implementation for `Foo` + --> $DIR/new_without_default.rs:8:5 + | +LL | / pub fn new() -> Foo { +LL | | Foo +LL | | } + | |_____^ + | + = note: `-D clippy::new-without-default` implied by `-D warnings` +help: try this + | +LL | #[derive(Default)] + | + +error: you should consider deriving a `Default` implementation for `Bar` + --> $DIR/new_without_default.rs:16:5 + | +LL | / pub fn new() -> Self { +LL | | Bar +LL | | } + | |_____^ + | +help: try this + | +LL | #[derive(Default)] + | + +error: you should consider adding a `Default` implementation for `LtKo<'c>` + --> $DIR/new_without_default.rs:80:5 + | +LL | / pub fn new() -> LtKo<'c> { +LL | | unimplemented!() +LL | | } + | |_____^ + | +help: try this + | +LL | impl Default for LtKo<'c> { +LL | fn default() -> Self { +LL | Self::new() +LL | } +LL | } + | + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/no_effect.rs b/src/tools/clippy/tests/ui/no_effect.rs new file mode 100644 index 00000000000..8fbfcb79860 --- /dev/null +++ b/src/tools/clippy/tests/ui/no_effect.rs @@ -0,0 +1,102 @@ +#![feature(box_syntax)] +#![warn(clippy::no_effect)] +#![allow(dead_code)] +#![allow(path_statements)] +#![allow(clippy::deref_addrof)] +#![allow(clippy::redundant_field_names)] +#![feature(untagged_unions)] + +struct Unit; +struct Tuple(i32); +struct Struct { + field: i32, +} +enum Enum { + Tuple(i32), + Struct { field: i32 }, +} +struct DropUnit; +impl Drop for DropUnit { + fn drop(&mut self) {} +} +struct DropStruct { + field: i32, +} +impl Drop for DropStruct { + fn drop(&mut self) {} +} +struct DropTuple(i32); +impl Drop for DropTuple { + fn drop(&mut self) {} +} +enum DropEnum { + Tuple(i32), + Struct { field: i32 }, +} +impl Drop for DropEnum { + fn drop(&mut self) {} +} +struct FooString { + s: String, +} +union Union { + a: u8, + b: f64, +} + +fn get_number() -> i32 { + 0 +} +fn get_struct() -> Struct { + Struct { field: 0 } +} +fn get_drop_struct() -> DropStruct { + DropStruct { field: 0 } +} + +unsafe fn unsafe_fn() -> i32 { + 0 +} + +fn main() { + let s = get_struct(); + let s2 = get_struct(); + + 0; + s2; + Unit; + Tuple(0); + Struct { field: 0 }; + Struct { ..s }; + Union { a: 0 }; + Enum::Tuple(0); + Enum::Struct { field: 0 }; + 5 + 6; + *&42; + &6; + (5, 6, 7); + box 42; + ..; + 5..; + ..5; + 5..6; + 5..=6; + [42, 55]; + [42, 55][1]; + (42, 55).1; + [42; 55]; + [42; 55][13]; + let mut x = 0; + || x += 5; + let s: String = "foo".into(); + FooString { s: s }; + + // Do not warn + get_number(); + unsafe { unsafe_fn() }; + DropUnit; + DropStruct { field: 0 }; + DropTuple(0); + DropEnum::Tuple(0); + DropEnum::Struct { field: 0 }; +} diff --git a/src/tools/clippy/tests/ui/no_effect.stderr b/src/tools/clippy/tests/ui/no_effect.stderr new file mode 100644 index 00000000000..834b9056e31 --- /dev/null +++ b/src/tools/clippy/tests/ui/no_effect.stderr @@ -0,0 +1,154 @@ +error: statement with no effect + --> $DIR/no_effect.rs:65:5 + | +LL | 0; + | ^^ + | + = note: `-D clippy::no-effect` implied by `-D warnings` + +error: statement with no effect + --> $DIR/no_effect.rs:66:5 + | +LL | s2; + | ^^^ + +error: statement with no effect + --> $DIR/no_effect.rs:67:5 + | +LL | Unit; + | ^^^^^ + +error: statement with no effect + --> $DIR/no_effect.rs:68:5 + | +LL | Tuple(0); + | ^^^^^^^^^ + +error: statement with no effect + --> $DIR/no_effect.rs:69:5 + | +LL | Struct { field: 0 }; + | ^^^^^^^^^^^^^^^^^^^^ + +error: statement with no effect + --> $DIR/no_effect.rs:70:5 + | +LL | Struct { ..s }; + | ^^^^^^^^^^^^^^^ + +error: statement with no effect + --> $DIR/no_effect.rs:71:5 + | +LL | Union { a: 0 }; + | ^^^^^^^^^^^^^^^ + +error: statement with no effect + --> $DIR/no_effect.rs:72:5 + | +LL | Enum::Tuple(0); + | ^^^^^^^^^^^^^^^ + +error: statement with no effect + --> $DIR/no_effect.rs:73:5 + | +LL | Enum::Struct { field: 0 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: statement with no effect + --> $DIR/no_effect.rs:74:5 + | +LL | 5 + 6; + | ^^^^^^ + +error: statement with no effect + --> $DIR/no_effect.rs:75:5 + | +LL | *&42; + | ^^^^^ + +error: statement with no effect + --> $DIR/no_effect.rs:76:5 + | +LL | &6; + | ^^^ + +error: statement with no effect + --> $DIR/no_effect.rs:77:5 + | +LL | (5, 6, 7); + | ^^^^^^^^^^ + +error: statement with no effect + --> $DIR/no_effect.rs:78:5 + | +LL | box 42; + | ^^^^^^^ + +error: statement with no effect + --> $DIR/no_effect.rs:79:5 + | +LL | ..; + | ^^^ + +error: statement with no effect + --> $DIR/no_effect.rs:80:5 + | +LL | 5..; + | ^^^^ + +error: statement with no effect + --> $DIR/no_effect.rs:81:5 + | +LL | ..5; + | ^^^^ + +error: statement with no effect + --> $DIR/no_effect.rs:82:5 + | +LL | 5..6; + | ^^^^^ + +error: statement with no effect + --> $DIR/no_effect.rs:84:5 + | +LL | [42, 55]; + | ^^^^^^^^^ + +error: statement with no effect + --> $DIR/no_effect.rs:85:5 + | +LL | [42, 55][1]; + | ^^^^^^^^^^^^ + +error: statement with no effect + --> $DIR/no_effect.rs:86:5 + | +LL | (42, 55).1; + | ^^^^^^^^^^^ + +error: statement with no effect + --> $DIR/no_effect.rs:87:5 + | +LL | [42; 55]; + | ^^^^^^^^^ + +error: statement with no effect + --> $DIR/no_effect.rs:88:5 + | +LL | [42; 55][13]; + | ^^^^^^^^^^^^^ + +error: statement with no effect + --> $DIR/no_effect.rs:90:5 + | +LL | || x += 5; + | ^^^^^^^^^^ + +error: statement with no effect + --> $DIR/no_effect.rs:92:5 + | +LL | FooString { s: s }; + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 25 previous errors + diff --git a/src/tools/clippy/tests/ui/non_expressive_names.rs b/src/tools/clippy/tests/ui/non_expressive_names.rs new file mode 100644 index 00000000000..58415b4aede --- /dev/null +++ b/src/tools/clippy/tests/ui/non_expressive_names.rs @@ -0,0 +1,56 @@ +#![warn(clippy::all)] +#![allow(unused, clippy::println_empty_string)] + +#[derive(Clone, Debug)] +enum MaybeInst { + Split, + Split1(usize), + Split2(usize), +} + +struct InstSplit { + uiae: usize, +} + +impl MaybeInst { + fn fill(&mut self) { + let filled = match *self { + MaybeInst::Split1(goto1) => panic!(1), + MaybeInst::Split2(goto2) => panic!(2), + _ => unimplemented!(), + }; + unimplemented!() + } +} + +fn underscores_and_numbers() { + let _1 = 1; //~ERROR Consider a more descriptive name + let ____1 = 1; //~ERROR Consider a more descriptive name + let __1___2 = 12; //~ERROR Consider a more descriptive name + let _1_ok = 1; +} + +fn issue2927() { + let args = 1; + format!("{:?}", 2); +} + +fn issue3078() { + match "a" { + stringify!(a) => {}, + _ => {}, + } +} + +struct Bar; + +impl Bar { + fn bar() { + let _1 = 1; + let ____1 = 1; + let __1___2 = 12; + let _1_ok = 1; + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/non_expressive_names.stderr b/src/tools/clippy/tests/ui/non_expressive_names.stderr new file mode 100644 index 00000000000..a0ca46f0efc --- /dev/null +++ b/src/tools/clippy/tests/ui/non_expressive_names.stderr @@ -0,0 +1,40 @@ +error: consider choosing a more descriptive name + --> $DIR/non_expressive_names.rs:27:9 + | +LL | let _1 = 1; //~ERROR Consider a more descriptive name + | ^^ + | + = note: `-D clippy::just-underscores-and-digits` implied by `-D warnings` + +error: consider choosing a more descriptive name + --> $DIR/non_expressive_names.rs:28:9 + | +LL | let ____1 = 1; //~ERROR Consider a more descriptive name + | ^^^^^ + +error: consider choosing a more descriptive name + --> $DIR/non_expressive_names.rs:29:9 + | +LL | let __1___2 = 12; //~ERROR Consider a more descriptive name + | ^^^^^^^ + +error: consider choosing a more descriptive name + --> $DIR/non_expressive_names.rs:49:13 + | +LL | let _1 = 1; + | ^^ + +error: consider choosing a more descriptive name + --> $DIR/non_expressive_names.rs:50:13 + | +LL | let ____1 = 1; + | ^^^^^ + +error: consider choosing a more descriptive name + --> $DIR/non_expressive_names.rs:51:13 + | +LL | let __1___2 = 12; + | ^^^^^^^ + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/non_expressive_names.stdout b/src/tools/clippy/tests/ui/non_expressive_names.stdout new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/src/tools/clippy/tests/ui/non_expressive_names.stdout diff --git a/src/tools/clippy/tests/ui/nonminimal_bool.rs b/src/tools/clippy/tests/ui/nonminimal_bool.rs new file mode 100644 index 00000000000..7ea154cb9b0 --- /dev/null +++ b/src/tools/clippy/tests/ui/nonminimal_bool.rs @@ -0,0 +1,52 @@ +#![allow(unused, clippy::many_single_char_names)] +#![warn(clippy::nonminimal_bool)] + +fn main() { + let a: bool = unimplemented!(); + let b: bool = unimplemented!(); + let c: bool = unimplemented!(); + let d: bool = unimplemented!(); + let e: bool = unimplemented!(); + let _ = !true; + let _ = !false; + let _ = !!a; + let _ = false || a; + // don't lint on cfgs + let _ = cfg!(you_shall_not_not_pass) && a; + let _ = a || !b || !c || !d || !e; + let _ = !(!a && b); + let _ = !(!a || b); + let _ = !a && !(b && c); +} + +fn equality_stuff() { + let a: i32 = unimplemented!(); + let b: i32 = unimplemented!(); + let c: i32 = unimplemented!(); + let d: i32 = unimplemented!(); + let _ = a == b && c == 5 && a == b; + let _ = a == b || c == 5 || a == b; + let _ = a == b && c == 5 && b == a; + let _ = a != b || !(a != b || c == d); + let _ = a != b && !(a != b && c == d); +} + +fn issue3847(a: u32, b: u32) -> bool { + const THRESHOLD: u32 = 1_000; + + if a < THRESHOLD && b >= THRESHOLD || a >= THRESHOLD && b < THRESHOLD { + return false; + } + true +} + +fn issue4548() { + fn f(_i: u32, _j: u32) -> u32 { + unimplemented!(); + } + + let i = 0; + let j = 0; + + if i != j && f(i, j) != 0 || i == j && f(i, j) != 1 {} +} diff --git a/src/tools/clippy/tests/ui/nonminimal_bool.stderr b/src/tools/clippy/tests/ui/nonminimal_bool.stderr new file mode 100644 index 00000000000..d34d106cb2f --- /dev/null +++ b/src/tools/clippy/tests/ui/nonminimal_bool.stderr @@ -0,0 +1,111 @@ +error: this boolean expression can be simplified + --> $DIR/nonminimal_bool.rs:10:13 + | +LL | let _ = !true; + | ^^^^^ help: try: `false` + | + = note: `-D clippy::nonminimal-bool` implied by `-D warnings` + +error: this boolean expression can be simplified + --> $DIR/nonminimal_bool.rs:11:13 + | +LL | let _ = !false; + | ^^^^^^ help: try: `true` + +error: this boolean expression can be simplified + --> $DIR/nonminimal_bool.rs:12:13 + | +LL | let _ = !!a; + | ^^^ help: try: `a` + +error: this boolean expression can be simplified + --> $DIR/nonminimal_bool.rs:13:13 + | +LL | let _ = false || a; + | ^^^^^^^^^^ help: try: `a` + +error: this boolean expression can be simplified + --> $DIR/nonminimal_bool.rs:17:13 + | +LL | let _ = !(!a && b); + | ^^^^^^^^^^ help: try: `a || !b` + +error: this boolean expression can be simplified + --> $DIR/nonminimal_bool.rs:18:13 + | +LL | let _ = !(!a || b); + | ^^^^^^^^^^ help: try: `a && !b` + +error: this boolean expression can be simplified + --> $DIR/nonminimal_bool.rs:19:13 + | +LL | let _ = !a && !(b && c); + | ^^^^^^^^^^^^^^^ help: try: `!(a || b && c)` + +error: this boolean expression can be simplified + --> $DIR/nonminimal_bool.rs:27:13 + | +LL | let _ = a == b && c == 5 && a == b; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL | let _ = a == b && c == 5; + | ^^^^^^^^^^^^^^^^ +LL | let _ = !(a != b || c != 5); + | ^^^^^^^^^^^^^^^^^^^ + +error: this boolean expression can be simplified + --> $DIR/nonminimal_bool.rs:28:13 + | +LL | let _ = a == b || c == 5 || a == b; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL | let _ = a == b || c == 5; + | ^^^^^^^^^^^^^^^^ +LL | let _ = !(a != b && c != 5); + | ^^^^^^^^^^^^^^^^^^^ + +error: this boolean expression can be simplified + --> $DIR/nonminimal_bool.rs:29:13 + | +LL | let _ = a == b && c == 5 && b == a; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL | let _ = a == b && c == 5; + | ^^^^^^^^^^^^^^^^ +LL | let _ = !(a != b || c != 5); + | ^^^^^^^^^^^^^^^^^^^ + +error: this boolean expression can be simplified + --> $DIR/nonminimal_bool.rs:30:13 + | +LL | let _ = a != b || !(a != b || c == d); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL | let _ = a != b || c != d; + | ^^^^^^^^^^^^^^^^ +LL | let _ = !(a == b && c == d); + | ^^^^^^^^^^^^^^^^^^^ + +error: this boolean expression can be simplified + --> $DIR/nonminimal_bool.rs:31:13 + | +LL | let _ = a != b && !(a != b && c == d); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL | let _ = a != b && c != d; + | ^^^^^^^^^^^^^^^^ +LL | let _ = !(a == b || c == d); + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 12 previous errors + diff --git a/src/tools/clippy/tests/ui/nonminimal_bool_methods.rs b/src/tools/clippy/tests/ui/nonminimal_bool_methods.rs new file mode 100644 index 00000000000..4de48cd0879 --- /dev/null +++ b/src/tools/clippy/tests/ui/nonminimal_bool_methods.rs @@ -0,0 +1,110 @@ +#![allow(unused, clippy::many_single_char_names)] +#![warn(clippy::nonminimal_bool)] + +fn methods_with_negation() { + let a: Option<i32> = unimplemented!(); + let b: Result<i32, i32> = unimplemented!(); + let _ = a.is_some(); + let _ = !a.is_some(); + let _ = a.is_none(); + let _ = !a.is_none(); + let _ = b.is_err(); + let _ = !b.is_err(); + let _ = b.is_ok(); + let _ = !b.is_ok(); + let c = false; + let _ = !(a.is_some() && !c); + let _ = !(a.is_some() || !c); + let _ = !(!c ^ c) || !a.is_some(); + let _ = (!c ^ c) || !a.is_some(); + let _ = !c ^ c || !a.is_some(); +} + +// Simplified versions of https://github.com/rust-lang/rust-clippy/issues/2638 +// clippy::nonminimal_bool should only check the built-in Result and Some type, not +// any other types like the following. +enum CustomResultOk<E> { + Ok, + Err(E), +} +enum CustomResultErr<E> { + Ok, + Err(E), +} +enum CustomSomeSome<T> { + Some(T), + None, +} +enum CustomSomeNone<T> { + Some(T), + None, +} + +impl<E> CustomResultOk<E> { + pub fn is_ok(&self) -> bool { + true + } +} + +impl<E> CustomResultErr<E> { + pub fn is_err(&self) -> bool { + true + } +} + +impl<T> CustomSomeSome<T> { + pub fn is_some(&self) -> bool { + true + } +} + +impl<T> CustomSomeNone<T> { + pub fn is_none(&self) -> bool { + true + } +} + +fn dont_warn_for_custom_methods_with_negation() { + let res = CustomResultOk::Err("Error"); + // Should not warn and suggest 'is_err()' because the type does not + // implement is_err(). + if !res.is_ok() {} + + let res = CustomResultErr::Err("Error"); + // Should not warn and suggest 'is_ok()' because the type does not + // implement is_ok(). + if !res.is_err() {} + + let res = CustomSomeSome::Some("thing"); + // Should not warn and suggest 'is_none()' because the type does not + // implement is_none(). + if !res.is_some() {} + + let res = CustomSomeNone::Some("thing"); + // Should not warn and suggest 'is_some()' because the type does not + // implement is_some(). + if !res.is_none() {} +} + +// Only Built-in Result and Some types should suggest the negated alternative +fn warn_for_built_in_methods_with_negation() { + let res: Result<usize, usize> = Ok(1); + if !res.is_ok() {} + if !res.is_err() {} + + let res = Some(1); + if !res.is_some() {} + if !res.is_none() {} +} + +#[allow(clippy::neg_cmp_op_on_partial_ord)] +fn dont_warn_for_negated_partial_ord_comparison() { + let a: f64 = unimplemented!(); + let b: f64 = unimplemented!(); + let _ = !(a < b); + let _ = !(a <= b); + let _ = !(a > b); + let _ = !(a >= b); +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/nonminimal_bool_methods.stderr b/src/tools/clippy/tests/ui/nonminimal_bool_methods.stderr new file mode 100644 index 00000000000..a2df889d623 --- /dev/null +++ b/src/tools/clippy/tests/ui/nonminimal_bool_methods.stderr @@ -0,0 +1,82 @@ +error: this boolean expression can be simplified + --> $DIR/nonminimal_bool_methods.rs:8:13 + | +LL | let _ = !a.is_some(); + | ^^^^^^^^^^^^ help: try: `a.is_none()` + | + = note: `-D clippy::nonminimal-bool` implied by `-D warnings` + +error: this boolean expression can be simplified + --> $DIR/nonminimal_bool_methods.rs:10:13 + | +LL | let _ = !a.is_none(); + | ^^^^^^^^^^^^ help: try: `a.is_some()` + +error: this boolean expression can be simplified + --> $DIR/nonminimal_bool_methods.rs:12:13 + | +LL | let _ = !b.is_err(); + | ^^^^^^^^^^^ help: try: `b.is_ok()` + +error: this boolean expression can be simplified + --> $DIR/nonminimal_bool_methods.rs:14:13 + | +LL | let _ = !b.is_ok(); + | ^^^^^^^^^^ help: try: `b.is_err()` + +error: this boolean expression can be simplified + --> $DIR/nonminimal_bool_methods.rs:16:13 + | +LL | let _ = !(a.is_some() && !c); + | ^^^^^^^^^^^^^^^^^^^^ help: try: `a.is_none() || c` + +error: this boolean expression can be simplified + --> $DIR/nonminimal_bool_methods.rs:17:13 + | +LL | let _ = !(a.is_some() || !c); + | ^^^^^^^^^^^^^^^^^^^^ help: try: `a.is_none() && c` + +error: this boolean expression can be simplified + --> $DIR/nonminimal_bool_methods.rs:18:26 + | +LL | let _ = !(!c ^ c) || !a.is_some(); + | ^^^^^^^^^^^^ help: try: `a.is_none()` + +error: this boolean expression can be simplified + --> $DIR/nonminimal_bool_methods.rs:19:25 + | +LL | let _ = (!c ^ c) || !a.is_some(); + | ^^^^^^^^^^^^ help: try: `a.is_none()` + +error: this boolean expression can be simplified + --> $DIR/nonminimal_bool_methods.rs:20:23 + | +LL | let _ = !c ^ c || !a.is_some(); + | ^^^^^^^^^^^^ help: try: `a.is_none()` + +error: this boolean expression can be simplified + --> $DIR/nonminimal_bool_methods.rs:92:8 + | +LL | if !res.is_ok() {} + | ^^^^^^^^^^^^ help: try: `res.is_err()` + +error: this boolean expression can be simplified + --> $DIR/nonminimal_bool_methods.rs:93:8 + | +LL | if !res.is_err() {} + | ^^^^^^^^^^^^^ help: try: `res.is_ok()` + +error: this boolean expression can be simplified + --> $DIR/nonminimal_bool_methods.rs:96:8 + | +LL | if !res.is_some() {} + | ^^^^^^^^^^^^^^ help: try: `res.is_none()` + +error: this boolean expression can be simplified + --> $DIR/nonminimal_bool_methods.rs:97:8 + | +LL | if !res.is_none() {} + | ^^^^^^^^^^^^^^ help: try: `res.is_some()` + +error: aborting due to 13 previous errors + diff --git a/src/tools/clippy/tests/ui/ok_expect.rs b/src/tools/clippy/tests/ui/ok_expect.rs new file mode 100644 index 00000000000..ff68d38c73b --- /dev/null +++ b/src/tools/clippy/tests/ui/ok_expect.rs @@ -0,0 +1,27 @@ +use std::io; + +struct MyError(()); // doesn't implement Debug + +#[derive(Debug)] +struct MyErrorWithParam<T> { + x: T, +} + +fn main() { + let res: Result<i32, ()> = Ok(0); + let _ = res.unwrap(); + + res.ok().expect("disaster!"); + // the following should not warn, since `expect` isn't implemented unless + // the error type implements `Debug` + let res2: Result<i32, MyError> = Ok(0); + res2.ok().expect("oh noes!"); + let res3: Result<u32, MyErrorWithParam<u8>> = Ok(0); + res3.ok().expect("whoof"); + let res4: Result<u32, io::Error> = Ok(0); + res4.ok().expect("argh"); + let res5: io::Result<u32> = Ok(0); + res5.ok().expect("oops"); + let res6: Result<u32, &str> = Ok(0); + res6.ok().expect("meh"); +} diff --git a/src/tools/clippy/tests/ui/ok_expect.stderr b/src/tools/clippy/tests/ui/ok_expect.stderr new file mode 100644 index 00000000000..b02b28e7f68 --- /dev/null +++ b/src/tools/clippy/tests/ui/ok_expect.stderr @@ -0,0 +1,43 @@ +error: called `ok().expect()` on a `Result` value + --> $DIR/ok_expect.rs:14:5 + | +LL | res.ok().expect("disaster!"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::ok-expect` implied by `-D warnings` + = help: you can call `expect()` directly on the `Result` + +error: called `ok().expect()` on a `Result` value + --> $DIR/ok_expect.rs:20:5 + | +LL | res3.ok().expect("whoof"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: you can call `expect()` directly on the `Result` + +error: called `ok().expect()` on a `Result` value + --> $DIR/ok_expect.rs:22:5 + | +LL | res4.ok().expect("argh"); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: you can call `expect()` directly on the `Result` + +error: called `ok().expect()` on a `Result` value + --> $DIR/ok_expect.rs:24:5 + | +LL | res5.ok().expect("oops"); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: you can call `expect()` directly on the `Result` + +error: called `ok().expect()` on a `Result` value + --> $DIR/ok_expect.rs:26:5 + | +LL | res6.ok().expect("meh"); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: you can call `expect()` directly on the `Result` + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/op_ref.rs b/src/tools/clippy/tests/ui/op_ref.rs new file mode 100644 index 00000000000..6605c967c8e --- /dev/null +++ b/src/tools/clippy/tests/ui/op_ref.rs @@ -0,0 +1,58 @@ +#![allow(unused_variables, clippy::blacklisted_name)] +#![warn(clippy::op_ref)] +#![allow(clippy::many_single_char_names)] +use std::collections::HashSet; +use std::ops::BitAnd; + +fn main() { + let tracked_fds: HashSet<i32> = HashSet::new(); + let new_fds = HashSet::new(); + let unwanted = &tracked_fds - &new_fds; + + let foo = &5 - &6; + + let bar = String::new(); + let bar = "foo" == &bar; + + let a = "a".to_string(); + let b = "a"; + + if b < &a { + println!("OK"); + } + + struct X(i32); + impl BitAnd for X { + type Output = X; + fn bitand(self, rhs: X) -> X { + X(self.0 & rhs.0) + } + } + impl<'a> BitAnd<&'a X> for X { + type Output = X; + fn bitand(self, rhs: &'a X) -> X { + X(self.0 & rhs.0) + } + } + let x = X(1); + let y = X(2); + let z = x & &y; + + #[derive(Copy, Clone)] + struct Y(i32); + impl BitAnd for Y { + type Output = Y; + fn bitand(self, rhs: Y) -> Y { + Y(self.0 & rhs.0) + } + } + impl<'a> BitAnd<&'a Y> for Y { + type Output = Y; + fn bitand(self, rhs: &'a Y) -> Y { + Y(self.0 & rhs.0) + } + } + let x = Y(1); + let y = Y(2); + let z = x & &y; +} diff --git a/src/tools/clippy/tests/ui/op_ref.stderr b/src/tools/clippy/tests/ui/op_ref.stderr new file mode 100644 index 00000000000..a3a9adcc483 --- /dev/null +++ b/src/tools/clippy/tests/ui/op_ref.stderr @@ -0,0 +1,22 @@ +error: needlessly taken reference of both operands + --> $DIR/op_ref.rs:12:15 + | +LL | let foo = &5 - &6; + | ^^^^^^^ + | + = note: `-D clippy::op-ref` implied by `-D warnings` +help: use the values directly + | +LL | let foo = 5 - 6; + | ^ ^ + +error: taken reference of right operand + --> $DIR/op_ref.rs:57:13 + | +LL | let z = x & &y; + | ^^^^-- + | | + | help: use the right value directly: `y` + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/open_options.rs b/src/tools/clippy/tests/ui/open_options.rs new file mode 100644 index 00000000000..9063fafbcd0 --- /dev/null +++ b/src/tools/clippy/tests/ui/open_options.rs @@ -0,0 +1,14 @@ +use std::fs::OpenOptions; + +#[allow(unused_must_use)] +#[warn(clippy::nonsensical_open_options)] +fn main() { + OpenOptions::new().read(true).truncate(true).open("foo.txt"); + OpenOptions::new().append(true).truncate(true).open("foo.txt"); + + OpenOptions::new().read(true).read(false).open("foo.txt"); + OpenOptions::new().create(true).create(false).open("foo.txt"); + OpenOptions::new().write(true).write(false).open("foo.txt"); + OpenOptions::new().append(true).append(false).open("foo.txt"); + OpenOptions::new().truncate(true).truncate(false).open("foo.txt"); +} diff --git a/src/tools/clippy/tests/ui/open_options.stderr b/src/tools/clippy/tests/ui/open_options.stderr new file mode 100644 index 00000000000..26fe9f6fb20 --- /dev/null +++ b/src/tools/clippy/tests/ui/open_options.stderr @@ -0,0 +1,46 @@ +error: file opened with `truncate` and `read` + --> $DIR/open_options.rs:6:5 + | +LL | OpenOptions::new().read(true).truncate(true).open("foo.txt"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::nonsensical-open-options` implied by `-D warnings` + +error: file opened with `append` and `truncate` + --> $DIR/open_options.rs:7:5 + | +LL | OpenOptions::new().append(true).truncate(true).open("foo.txt"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the method `read` is called more than once + --> $DIR/open_options.rs:9:5 + | +LL | OpenOptions::new().read(true).read(false).open("foo.txt"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the method `create` is called more than once + --> $DIR/open_options.rs:10:5 + | +LL | OpenOptions::new().create(true).create(false).open("foo.txt"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the method `write` is called more than once + --> $DIR/open_options.rs:11:5 + | +LL | OpenOptions::new().write(true).write(false).open("foo.txt"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the method `append` is called more than once + --> $DIR/open_options.rs:12:5 + | +LL | OpenOptions::new().append(true).append(false).open("foo.txt"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the method `truncate` is called more than once + --> $DIR/open_options.rs:13:5 + | +LL | OpenOptions::new().truncate(true).truncate(false).open("foo.txt"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/tests/ui/option_and_then_some.fixed b/src/tools/clippy/tests/ui/option_and_then_some.fixed new file mode 100644 index 00000000000..035bc1e5465 --- /dev/null +++ b/src/tools/clippy/tests/ui/option_and_then_some.fixed @@ -0,0 +1,25 @@ +// run-rustfix +#![deny(clippy::option_and_then_some)] + +// need a main anyway, use it get rid of unused warnings too +pub fn main() { + let x = Some(5); + // the easiest cases + let _ = x; + let _ = x.map(|o| o + 1); + // and an easy counter-example + let _ = x.and_then(|o| if o < 32 { Some(o) } else { None }); + + // Different type + let x: Result<u32, &str> = Ok(1); + let _ = x.and_then(Ok); +} + +pub fn foo() -> Option<String> { + let x = Some(String::from("hello")); + Some("hello".to_owned()).and_then(|s| Some(format!("{}{}", s, x?))) +} + +pub fn example2(x: bool) -> Option<&'static str> { + Some("a").and_then(|s| Some(if x { s } else { return None })) +} diff --git a/src/tools/clippy/tests/ui/option_and_then_some.rs b/src/tools/clippy/tests/ui/option_and_then_some.rs new file mode 100644 index 00000000000..d49da7813c6 --- /dev/null +++ b/src/tools/clippy/tests/ui/option_and_then_some.rs @@ -0,0 +1,25 @@ +// run-rustfix +#![deny(clippy::option_and_then_some)] + +// need a main anyway, use it get rid of unused warnings too +pub fn main() { + let x = Some(5); + // the easiest cases + let _ = x.and_then(Some); + let _ = x.and_then(|o| Some(o + 1)); + // and an easy counter-example + let _ = x.and_then(|o| if o < 32 { Some(o) } else { None }); + + // Different type + let x: Result<u32, &str> = Ok(1); + let _ = x.and_then(Ok); +} + +pub fn foo() -> Option<String> { + let x = Some(String::from("hello")); + Some("hello".to_owned()).and_then(|s| Some(format!("{}{}", s, x?))) +} + +pub fn example2(x: bool) -> Option<&'static str> { + Some("a").and_then(|s| Some(if x { s } else { return None })) +} diff --git a/src/tools/clippy/tests/ui/option_and_then_some.stderr b/src/tools/clippy/tests/ui/option_and_then_some.stderr new file mode 100644 index 00000000000..47825491765 --- /dev/null +++ b/src/tools/clippy/tests/ui/option_and_then_some.stderr @@ -0,0 +1,20 @@ +error: using `Option.and_then(Some)`, which is a no-op + --> $DIR/option_and_then_some.rs:8:13 + | +LL | let _ = x.and_then(Some); + | ^^^^^^^^^^^^^^^^ help: use the expression directly: `x` + | +note: the lint level is defined here + --> $DIR/option_and_then_some.rs:2:9 + | +LL | #![deny(clippy::option_and_then_some)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)` + --> $DIR/option_and_then_some.rs:9:13 + | +LL | let _ = x.and_then(|o| Some(o + 1)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `x.map(|o| o + 1)` + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/option_as_ref_deref.fixed b/src/tools/clippy/tests/ui/option_as_ref_deref.fixed new file mode 100644 index 00000000000..076692e6445 --- /dev/null +++ b/src/tools/clippy/tests/ui/option_as_ref_deref.fixed @@ -0,0 +1,41 @@ +// run-rustfix + +#![allow(unused_imports, clippy::redundant_clone)] +#![warn(clippy::option_as_ref_deref)] + +use std::ffi::{CString, OsString}; +use std::ops::{Deref, DerefMut}; +use std::path::PathBuf; + +fn main() { + let mut opt = Some(String::from("123")); + + let _ = opt.clone().as_deref().map(str::len); + + #[rustfmt::skip] + let _ = opt.clone().as_deref() + .map(str::len); + + let _ = opt.as_deref_mut(); + + let _ = opt.as_deref(); + let _ = opt.as_deref(); + let _ = opt.as_deref_mut(); + let _ = opt.as_deref_mut(); + let _ = Some(CString::new(vec![]).unwrap()).as_deref(); + let _ = Some(OsString::new()).as_deref(); + let _ = Some(PathBuf::new()).as_deref(); + let _ = Some(Vec::<()>::new()).as_deref(); + let _ = Some(Vec::<()>::new()).as_deref_mut(); + + let _ = opt.as_deref(); + let _ = opt.clone().as_deref_mut().map(|x| x.len()); + + let vc = vec![String::new()]; + let _ = Some(1_usize).as_ref().map(|x| vc[*x].as_str()); // should not be linted + + let _: Option<&str> = Some(&String::new()).as_ref().map(|x| x.as_str()); // should not be linted + + let _ = opt.as_deref(); + let _ = opt.as_deref_mut(); +} diff --git a/src/tools/clippy/tests/ui/option_as_ref_deref.rs b/src/tools/clippy/tests/ui/option_as_ref_deref.rs new file mode 100644 index 00000000000..3bf5f715f83 --- /dev/null +++ b/src/tools/clippy/tests/ui/option_as_ref_deref.rs @@ -0,0 +1,44 @@ +// run-rustfix + +#![allow(unused_imports, clippy::redundant_clone)] +#![warn(clippy::option_as_ref_deref)] + +use std::ffi::{CString, OsString}; +use std::ops::{Deref, DerefMut}; +use std::path::PathBuf; + +fn main() { + let mut opt = Some(String::from("123")); + + let _ = opt.clone().as_ref().map(Deref::deref).map(str::len); + + #[rustfmt::skip] + let _ = opt.clone() + .as_ref().map( + Deref::deref + ) + .map(str::len); + + let _ = opt.as_mut().map(DerefMut::deref_mut); + + let _ = opt.as_ref().map(String::as_str); + let _ = opt.as_ref().map(|x| x.as_str()); + let _ = opt.as_mut().map(String::as_mut_str); + let _ = opt.as_mut().map(|x| x.as_mut_str()); + let _ = Some(CString::new(vec![]).unwrap()).as_ref().map(CString::as_c_str); + let _ = Some(OsString::new()).as_ref().map(OsString::as_os_str); + let _ = Some(PathBuf::new()).as_ref().map(PathBuf::as_path); + let _ = Some(Vec::<()>::new()).as_ref().map(Vec::as_slice); + let _ = Some(Vec::<()>::new()).as_mut().map(Vec::as_mut_slice); + + let _ = opt.as_ref().map(|x| x.deref()); + let _ = opt.clone().as_mut().map(|x| x.deref_mut()).map(|x| x.len()); + + let vc = vec![String::new()]; + let _ = Some(1_usize).as_ref().map(|x| vc[*x].as_str()); // should not be linted + + let _: Option<&str> = Some(&String::new()).as_ref().map(|x| x.as_str()); // should not be linted + + let _ = opt.as_ref().map(|x| &**x); + let _ = opt.as_mut().map(|x| &mut **x); +} diff --git a/src/tools/clippy/tests/ui/option_as_ref_deref.stderr b/src/tools/clippy/tests/ui/option_as_ref_deref.stderr new file mode 100644 index 00000000000..a106582a633 --- /dev/null +++ b/src/tools/clippy/tests/ui/option_as_ref_deref.stderr @@ -0,0 +1,104 @@ +error: called `.as_ref().map(Deref::deref)` on an Option value. This can be done more directly by calling `opt.clone().as_deref()` instead + --> $DIR/option_as_ref_deref.rs:13:13 + | +LL | let _ = opt.clone().as_ref().map(Deref::deref).map(str::len); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.clone().as_deref()` + | + = note: `-D clippy::option-as-ref-deref` implied by `-D warnings` + +error: called `.as_ref().map(Deref::deref)` on an Option value. This can be done more directly by calling `opt.clone().as_deref()` instead + --> $DIR/option_as_ref_deref.rs:16:13 + | +LL | let _ = opt.clone() + | _____________^ +LL | | .as_ref().map( +LL | | Deref::deref +LL | | ) + | |_________^ help: try using as_deref instead: `opt.clone().as_deref()` + +error: called `.as_mut().map(DerefMut::deref_mut)` on an Option value. This can be done more directly by calling `opt.as_deref_mut()` instead + --> $DIR/option_as_ref_deref.rs:22:13 + | +LL | let _ = opt.as_mut().map(DerefMut::deref_mut); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()` + +error: called `.as_ref().map(String::as_str)` on an Option value. This can be done more directly by calling `opt.as_deref()` instead + --> $DIR/option_as_ref_deref.rs:24:13 + | +LL | let _ = opt.as_ref().map(String::as_str); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()` + +error: called `.as_ref().map(|x| x.as_str())` on an Option value. This can be done more directly by calling `opt.as_deref()` instead + --> $DIR/option_as_ref_deref.rs:25:13 + | +LL | let _ = opt.as_ref().map(|x| x.as_str()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()` + +error: called `.as_mut().map(String::as_mut_str)` on an Option value. This can be done more directly by calling `opt.as_deref_mut()` instead + --> $DIR/option_as_ref_deref.rs:26:13 + | +LL | let _ = opt.as_mut().map(String::as_mut_str); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()` + +error: called `.as_mut().map(|x| x.as_mut_str())` on an Option value. This can be done more directly by calling `opt.as_deref_mut()` instead + --> $DIR/option_as_ref_deref.rs:27:13 + | +LL | let _ = opt.as_mut().map(|x| x.as_mut_str()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()` + +error: called `.as_ref().map(CString::as_c_str)` on an Option value. This can be done more directly by calling `Some(CString::new(vec![]).unwrap()).as_deref()` instead + --> $DIR/option_as_ref_deref.rs:28:13 + | +LL | let _ = Some(CString::new(vec![]).unwrap()).as_ref().map(CString::as_c_str); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(CString::new(vec![]).unwrap()).as_deref()` + +error: called `.as_ref().map(OsString::as_os_str)` on an Option value. This can be done more directly by calling `Some(OsString::new()).as_deref()` instead + --> $DIR/option_as_ref_deref.rs:29:13 + | +LL | let _ = Some(OsString::new()).as_ref().map(OsString::as_os_str); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(OsString::new()).as_deref()` + +error: called `.as_ref().map(PathBuf::as_path)` on an Option value. This can be done more directly by calling `Some(PathBuf::new()).as_deref()` instead + --> $DIR/option_as_ref_deref.rs:30:13 + | +LL | let _ = Some(PathBuf::new()).as_ref().map(PathBuf::as_path); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(PathBuf::new()).as_deref()` + +error: called `.as_ref().map(Vec::as_slice)` on an Option value. This can be done more directly by calling `Some(Vec::<()>::new()).as_deref()` instead + --> $DIR/option_as_ref_deref.rs:31:13 + | +LL | let _ = Some(Vec::<()>::new()).as_ref().map(Vec::as_slice); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `Some(Vec::<()>::new()).as_deref()` + +error: called `.as_mut().map(Vec::as_mut_slice)` on an Option value. This can be done more directly by calling `Some(Vec::<()>::new()).as_deref_mut()` instead + --> $DIR/option_as_ref_deref.rs:32:13 + | +LL | let _ = Some(Vec::<()>::new()).as_mut().map(Vec::as_mut_slice); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `Some(Vec::<()>::new()).as_deref_mut()` + +error: called `.as_ref().map(|x| x.deref())` on an Option value. This can be done more directly by calling `opt.as_deref()` instead + --> $DIR/option_as_ref_deref.rs:34:13 + | +LL | let _ = opt.as_ref().map(|x| x.deref()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()` + +error: called `.as_mut().map(|x| x.deref_mut())` on an Option value. This can be done more directly by calling `opt.clone().as_deref_mut()` instead + --> $DIR/option_as_ref_deref.rs:35:13 + | +LL | let _ = opt.clone().as_mut().map(|x| x.deref_mut()).map(|x| x.len()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.clone().as_deref_mut()` + +error: called `.as_ref().map(|x| &**x)` on an Option value. This can be done more directly by calling `opt.as_deref()` instead + --> $DIR/option_as_ref_deref.rs:42:13 + | +LL | let _ = opt.as_ref().map(|x| &**x); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()` + +error: called `.as_mut().map(|x| &mut **x)` on an Option value. This can be done more directly by calling `opt.as_deref_mut()` instead + --> $DIR/option_as_ref_deref.rs:43:13 + | +LL | let _ = opt.as_mut().map(|x| &mut **x); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()` + +error: aborting due to 16 previous errors + diff --git a/src/tools/clippy/tests/ui/option_env_unwrap.rs b/src/tools/clippy/tests/ui/option_env_unwrap.rs new file mode 100644 index 00000000000..642c77460a3 --- /dev/null +++ b/src/tools/clippy/tests/ui/option_env_unwrap.rs @@ -0,0 +1,23 @@ +// aux-build:macro_rules.rs +#![warn(clippy::option_env_unwrap)] + +#[macro_use] +extern crate macro_rules; + +macro_rules! option_env_unwrap { + ($env: expr) => { + option_env!($env).unwrap() + }; + ($env: expr, $message: expr) => { + option_env!($env).expect($message) + }; +} + +fn main() { + let _ = option_env!("PATH").unwrap(); + let _ = option_env!("PATH").expect("environment variable PATH isn't set"); + let _ = option_env_unwrap!("PATH"); + let _ = option_env_unwrap!("PATH", "environment variable PATH isn't set"); + let _ = option_env_unwrap_external!("PATH"); + let _ = option_env_unwrap_external!("PATH", "environment variable PATH isn't set"); +} diff --git a/src/tools/clippy/tests/ui/option_env_unwrap.stderr b/src/tools/clippy/tests/ui/option_env_unwrap.stderr new file mode 100644 index 00000000000..8de9c8a9d29 --- /dev/null +++ b/src/tools/clippy/tests/ui/option_env_unwrap.stderr @@ -0,0 +1,61 @@ +error: this will panic at run-time if the environment variable doesn't exist at compile-time + --> $DIR/option_env_unwrap.rs:17:13 + | +LL | let _ = option_env!("PATH").unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::option-env-unwrap` implied by `-D warnings` + = help: consider using the `env!` macro instead + +error: this will panic at run-time if the environment variable doesn't exist at compile-time + --> $DIR/option_env_unwrap.rs:18:13 + | +LL | let _ = option_env!("PATH").expect("environment variable PATH isn't set"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using the `env!` macro instead + +error: this will panic at run-time if the environment variable doesn't exist at compile-time + --> $DIR/option_env_unwrap.rs:9:9 + | +LL | option_env!($env).unwrap() + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | let _ = option_env_unwrap!("PATH"); + | -------------------------- in this macro invocation + | + = help: consider using the `env!` macro instead + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: this will panic at run-time if the environment variable doesn't exist at compile-time + --> $DIR/option_env_unwrap.rs:12:9 + | +LL | option_env!($env).expect($message) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | let _ = option_env_unwrap!("PATH", "environment variable PATH isn't set"); + | ----------------------------------------------------------------- in this macro invocation + | + = help: consider using the `env!` macro instead + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: this will panic at run-time if the environment variable doesn't exist at compile-time + --> $DIR/option_env_unwrap.rs:21:13 + | +LL | let _ = option_env_unwrap_external!("PATH"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using the `env!` macro instead + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: this will panic at run-time if the environment variable doesn't exist at compile-time + --> $DIR/option_env_unwrap.rs:22:13 + | +LL | let _ = option_env_unwrap_external!("PATH", "environment variable PATH isn't set"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using the `env!` macro instead + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/option_map_or_none.fixed b/src/tools/clippy/tests/ui/option_map_or_none.fixed new file mode 100644 index 00000000000..decbae4e5af --- /dev/null +++ b/src/tools/clippy/tests/ui/option_map_or_none.fixed @@ -0,0 +1,16 @@ +// run-rustfix + +#![allow(clippy::option_and_then_some)] + +fn main() { + let opt = Some(1); + + // Check `OPTION_MAP_OR_NONE`. + // Single line case. + let _ = opt.and_then(|x| Some(x + 1)); + // Multi-line case. + #[rustfmt::skip] + let _ = opt.and_then(|x| { + Some(x + 1) + }); +} diff --git a/src/tools/clippy/tests/ui/option_map_or_none.rs b/src/tools/clippy/tests/ui/option_map_or_none.rs new file mode 100644 index 00000000000..0f1d2218d5d --- /dev/null +++ b/src/tools/clippy/tests/ui/option_map_or_none.rs @@ -0,0 +1,16 @@ +// run-rustfix + +#![allow(clippy::option_and_then_some)] + +fn main() { + let opt = Some(1); + + // Check `OPTION_MAP_OR_NONE`. + // Single line case. + let _ = opt.map_or(None, |x| Some(x + 1)); + // Multi-line case. + #[rustfmt::skip] + let _ = opt.map_or(None, |x| { + Some(x + 1) + }); +} diff --git a/src/tools/clippy/tests/ui/option_map_or_none.stderr b/src/tools/clippy/tests/ui/option_map_or_none.stderr new file mode 100644 index 00000000000..6f707987dbc --- /dev/null +++ b/src/tools/clippy/tests/ui/option_map_or_none.stderr @@ -0,0 +1,26 @@ +error: called `map_or(None, f)` on an `Option` value. This can be done more directly by calling `and_then(f)` instead + --> $DIR/option_map_or_none.rs:10:13 + | +LL | let _ = opt.map_or(None, |x| Some(x + 1)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `and_then` instead: `opt.and_then(|x| Some(x + 1))` + | + = note: `-D clippy::option-map-or-none` implied by `-D warnings` + +error: called `map_or(None, f)` on an `Option` value. This can be done more directly by calling `and_then(f)` instead + --> $DIR/option_map_or_none.rs:13:13 + | +LL | let _ = opt.map_or(None, |x| { + | _____________^ +LL | | Some(x + 1) +LL | | }); + | |_________________________^ + | +help: try using `and_then` instead + | +LL | let _ = opt.and_then(|x| { +LL | Some(x + 1) +LL | }); + | + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.fixed b/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.fixed new file mode 100644 index 00000000000..9a0da404cb6 --- /dev/null +++ b/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.fixed @@ -0,0 +1,84 @@ +// run-rustfix + +#![warn(clippy::option_map_unit_fn)] +#![allow(unused)] + +fn do_nothing<T>(_: T) {} + +fn diverge<T>(_: T) -> ! { + panic!() +} + +fn plus_one(value: usize) -> usize { + value + 1 +} + +fn option() -> Option<usize> { + Some(10) +} + +struct HasOption { + field: Option<usize>, +} + +impl HasOption { + fn do_option_nothing(self: &Self, value: usize) {} + + fn do_option_plus_one(self: &Self, value: usize) -> usize { + value + 1 + } +} +#[rustfmt::skip] +fn option_map_unit_fn() { + let x = HasOption { field: Some(10) }; + + x.field.map(plus_one); + let _ : Option<()> = x.field.map(do_nothing); + + if let Some(x_field) = x.field { do_nothing(x_field) } + + if let Some(x_field) = x.field { do_nothing(x_field) } + + if let Some(x_field) = x.field { diverge(x_field) } + + let captured = 10; + if let Some(value) = x.field { do_nothing(value + captured) }; + let _ : Option<()> = x.field.map(|value| do_nothing(value + captured)); + + if let Some(value) = x.field { x.do_option_nothing(value + captured) } + + if let Some(value) = x.field { x.do_option_plus_one(value + captured); } + + + if let Some(value) = x.field { do_nothing(value + captured) } + + if let Some(value) = x.field { do_nothing(value + captured) } + + if let Some(value) = x.field { do_nothing(value + captured); } + + if let Some(value) = x.field { do_nothing(value + captured); } + + + if let Some(value) = x.field { diverge(value + captured) } + + if let Some(value) = x.field { diverge(value + captured) } + + if let Some(value) = x.field { diverge(value + captured); } + + if let Some(value) = x.field { diverge(value + captured); } + + + x.field.map(|value| plus_one(value + captured)); + x.field.map(|value| { plus_one(value + captured) }); + if let Some(value) = x.field { let y = plus_one(value + captured); } + + if let Some(value) = x.field { plus_one(value + captured); } + + if let Some(value) = x.field { plus_one(value + captured); } + + + if let Some(ref value) = x.field { do_nothing(value + captured) } + + if let Some(a) = option() { do_nothing(a) }} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.rs b/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.rs new file mode 100644 index 00000000000..58041b62df3 --- /dev/null +++ b/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.rs @@ -0,0 +1,84 @@ +// run-rustfix + +#![warn(clippy::option_map_unit_fn)] +#![allow(unused)] + +fn do_nothing<T>(_: T) {} + +fn diverge<T>(_: T) -> ! { + panic!() +} + +fn plus_one(value: usize) -> usize { + value + 1 +} + +fn option() -> Option<usize> { + Some(10) +} + +struct HasOption { + field: Option<usize>, +} + +impl HasOption { + fn do_option_nothing(self: &Self, value: usize) {} + + fn do_option_plus_one(self: &Self, value: usize) -> usize { + value + 1 + } +} +#[rustfmt::skip] +fn option_map_unit_fn() { + let x = HasOption { field: Some(10) }; + + x.field.map(plus_one); + let _ : Option<()> = x.field.map(do_nothing); + + x.field.map(do_nothing); + + x.field.map(do_nothing); + + x.field.map(diverge); + + let captured = 10; + if let Some(value) = x.field { do_nothing(value + captured) }; + let _ : Option<()> = x.field.map(|value| do_nothing(value + captured)); + + x.field.map(|value| x.do_option_nothing(value + captured)); + + x.field.map(|value| { x.do_option_plus_one(value + captured); }); + + + x.field.map(|value| do_nothing(value + captured)); + + x.field.map(|value| { do_nothing(value + captured) }); + + x.field.map(|value| { do_nothing(value + captured); }); + + x.field.map(|value| { { do_nothing(value + captured); } }); + + + x.field.map(|value| diverge(value + captured)); + + x.field.map(|value| { diverge(value + captured) }); + + x.field.map(|value| { diverge(value + captured); }); + + x.field.map(|value| { { diverge(value + captured); } }); + + + x.field.map(|value| plus_one(value + captured)); + x.field.map(|value| { plus_one(value + captured) }); + x.field.map(|value| { let y = plus_one(value + captured); }); + + x.field.map(|value| { plus_one(value + captured); }); + + x.field.map(|value| { { plus_one(value + captured); } }); + + + x.field.map(|ref value| { do_nothing(value + captured) }); + + option().map(do_nothing);} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.stderr b/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.stderr new file mode 100644 index 00000000000..1312c70b6d5 --- /dev/null +++ b/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.stderr @@ -0,0 +1,148 @@ +error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type + --> $DIR/option_map_unit_fn_fixable.rs:38:5 + | +LL | x.field.map(do_nothing); + | ^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Some(x_field) = x.field { do_nothing(x_field) }` + | + = note: `-D clippy::option-map-unit-fn` implied by `-D warnings` + +error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type + --> $DIR/option_map_unit_fn_fixable.rs:40:5 + | +LL | x.field.map(do_nothing); + | ^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Some(x_field) = x.field { do_nothing(x_field) }` + +error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type + --> $DIR/option_map_unit_fn_fixable.rs:42:5 + | +LL | x.field.map(diverge); + | ^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Some(x_field) = x.field { diverge(x_field) }` + +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type + --> $DIR/option_map_unit_fn_fixable.rs:48:5 + | +LL | x.field.map(|value| x.do_option_nothing(value + captured)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Some(value) = x.field { x.do_option_nothing(value + captured) }` + +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type + --> $DIR/option_map_unit_fn_fixable.rs:50:5 + | +LL | x.field.map(|value| { x.do_option_plus_one(value + captured); }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Some(value) = x.field { x.do_option_plus_one(value + captured); }` + +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type + --> $DIR/option_map_unit_fn_fixable.rs:53:5 + | +LL | x.field.map(|value| do_nothing(value + captured)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Some(value) = x.field { do_nothing(value + captured) }` + +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type + --> $DIR/option_map_unit_fn_fixable.rs:55:5 + | +LL | x.field.map(|value| { do_nothing(value + captured) }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Some(value) = x.field { do_nothing(value + captured) }` + +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type + --> $DIR/option_map_unit_fn_fixable.rs:57:5 + | +LL | x.field.map(|value| { do_nothing(value + captured); }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Some(value) = x.field { do_nothing(value + captured); }` + +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type + --> $DIR/option_map_unit_fn_fixable.rs:59:5 + | +LL | x.field.map(|value| { { do_nothing(value + captured); } }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Some(value) = x.field { do_nothing(value + captured); }` + +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type + --> $DIR/option_map_unit_fn_fixable.rs:62:5 + | +LL | x.field.map(|value| diverge(value + captured)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Some(value) = x.field { diverge(value + captured) }` + +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type + --> $DIR/option_map_unit_fn_fixable.rs:64:5 + | +LL | x.field.map(|value| { diverge(value + captured) }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Some(value) = x.field { diverge(value + captured) }` + +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type + --> $DIR/option_map_unit_fn_fixable.rs:66:5 + | +LL | x.field.map(|value| { diverge(value + captured); }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Some(value) = x.field { diverge(value + captured); }` + +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type + --> $DIR/option_map_unit_fn_fixable.rs:68:5 + | +LL | x.field.map(|value| { { diverge(value + captured); } }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Some(value) = x.field { diverge(value + captured); }` + +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type + --> $DIR/option_map_unit_fn_fixable.rs:73:5 + | +LL | x.field.map(|value| { let y = plus_one(value + captured); }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Some(value) = x.field { let y = plus_one(value + captured); }` + +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type + --> $DIR/option_map_unit_fn_fixable.rs:75:5 + | +LL | x.field.map(|value| { plus_one(value + captured); }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Some(value) = x.field { plus_one(value + captured); }` + +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type + --> $DIR/option_map_unit_fn_fixable.rs:77:5 + | +LL | x.field.map(|value| { { plus_one(value + captured); } }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Some(value) = x.field { plus_one(value + captured); }` + +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type + --> $DIR/option_map_unit_fn_fixable.rs:80:5 + | +LL | x.field.map(|ref value| { do_nothing(value + captured) }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Some(ref value) = x.field { do_nothing(value + captured) }` + +error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type + --> $DIR/option_map_unit_fn_fixable.rs:82:5 + | +LL | option().map(do_nothing);} + | ^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Some(a) = option() { do_nothing(a) }` + +error: aborting due to 18 previous errors + diff --git a/src/tools/clippy/tests/ui/option_map_unit_fn_unfixable.rs b/src/tools/clippy/tests/ui/option_map_unit_fn_unfixable.rs new file mode 100644 index 00000000000..20e6c15b18d --- /dev/null +++ b/src/tools/clippy/tests/ui/option_map_unit_fn_unfixable.rs @@ -0,0 +1,39 @@ +#![warn(clippy::option_map_unit_fn)] +#![allow(unused)] + +fn do_nothing<T>(_: T) {} + +fn diverge<T>(_: T) -> ! { + panic!() +} + +fn plus_one(value: usize) -> usize { + value + 1 +} + +#[rustfmt::skip] +fn option_map_unit_fn() { + + x.field.map(|value| { do_nothing(value); do_nothing(value) }); + + x.field.map(|value| if value > 0 { do_nothing(value); do_nothing(value) }); + + // Suggestion for the let block should be `{ ... }` as it's too difficult to build a + // proper suggestion for these cases + x.field.map(|value| { + do_nothing(value); + do_nothing(value) + }); + x.field.map(|value| { do_nothing(value); do_nothing(value); }); + + // The following should suggest `if let Some(_X) ...` as it's difficult to generate a proper let variable name for them + Some(42).map(diverge); + "12".parse::<i32>().ok().map(diverge); + Some(plus_one(1)).map(do_nothing); + + // Should suggest `if let Some(_y) ...` to not override the existing foo variable + let y = Some(42); + y.map(do_nothing); +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/option_map_unit_fn_unfixable.stderr b/src/tools/clippy/tests/ui/option_map_unit_fn_unfixable.stderr new file mode 100644 index 00000000000..a53f5889c58 --- /dev/null +++ b/src/tools/clippy/tests/ui/option_map_unit_fn_unfixable.stderr @@ -0,0 +1,27 @@ +error[E0425]: cannot find value `x` in this scope + --> $DIR/option_map_unit_fn_unfixable.rs:17:5 + | +LL | x.field.map(|value| { do_nothing(value); do_nothing(value) }); + | ^ not found in this scope + +error[E0425]: cannot find value `x` in this scope + --> $DIR/option_map_unit_fn_unfixable.rs:19:5 + | +LL | x.field.map(|value| if value > 0 { do_nothing(value); do_nothing(value) }); + | ^ not found in this scope + +error[E0425]: cannot find value `x` in this scope + --> $DIR/option_map_unit_fn_unfixable.rs:23:5 + | +LL | x.field.map(|value| { + | ^ not found in this scope + +error[E0425]: cannot find value `x` in this scope + --> $DIR/option_map_unit_fn_unfixable.rs:27:5 + | +LL | x.field.map(|value| { do_nothing(value); do_nothing(value); }); + | ^ not found in this scope + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0425`. diff --git a/src/tools/clippy/tests/ui/option_map_unwrap_or.rs b/src/tools/clippy/tests/ui/option_map_unwrap_or.rs new file mode 100644 index 00000000000..0364d83663a --- /dev/null +++ b/src/tools/clippy/tests/ui/option_map_unwrap_or.rs @@ -0,0 +1,88 @@ +// FIXME: Add "run-rustfix" once it's supported for multipart suggestions +// aux-build:option_helpers.rs + +#![warn(clippy::option_map_unwrap_or, clippy::option_map_unwrap_or_else)] + +#[macro_use] +extern crate option_helpers; + +use std::collections::HashMap; + +/// Checks implementation of the following lints: +/// * `OPTION_MAP_UNWRAP_OR` +/// * `OPTION_MAP_UNWRAP_OR_ELSE` +#[rustfmt::skip] +fn option_methods() { + let opt = Some(1); + + // Check `OPTION_MAP_UNWRAP_OR`. + // Single line case. + let _ = opt.map(|x| x + 1) + // Should lint even though this call is on a separate line. + .unwrap_or(0); + // Multi-line cases. + let _ = opt.map(|x| { + x + 1 + } + ).unwrap_or(0); + let _ = opt.map(|x| x + 1) + .unwrap_or({ + 0 + }); + // Single line `map(f).unwrap_or(None)` case. + let _ = opt.map(|x| Some(x + 1)).unwrap_or(None); + // Multi-line `map(f).unwrap_or(None)` cases. + let _ = opt.map(|x| { + Some(x + 1) + } + ).unwrap_or(None); + let _ = opt + .map(|x| Some(x + 1)) + .unwrap_or(None); + // macro case + let _ = opt_map!(opt, |x| x + 1).unwrap_or(0); // should not lint + + // Should not lint if not copyable + let id: String = "identifier".to_string(); + let _ = Some("prefix").map(|p| format!("{}.{}", p, id)).unwrap_or(id); + // ...but DO lint if the `unwrap_or` argument is not used in the `map` + let id: String = "identifier".to_string(); + let _ = Some("prefix").map(|p| format!("{}.", p)).unwrap_or(id); + + // Check OPTION_MAP_UNWRAP_OR_ELSE + // single line case + let _ = opt.map(|x| x + 1) + // Should lint even though this call is on a separate line. + .unwrap_or_else(|| 0); + // Multi-line cases. + let _ = opt.map(|x| { + x + 1 + } + ).unwrap_or_else(|| 0); + let _ = opt.map(|x| x + 1) + .unwrap_or_else(|| + 0 + ); + // Macro case. + // Should not lint. + let _ = opt_map!(opt, |x| x + 1).unwrap_or_else(|| 0); + + // Issue #4144 + { + let mut frequencies = HashMap::new(); + let word = "foo"; + + frequencies + .get_mut(word) + .map(|count| { + *count += 1; + }) + .unwrap_or_else(|| { + frequencies.insert(word.to_owned(), 1); + }); + } +} + +fn main() { + option_methods(); +} diff --git a/src/tools/clippy/tests/ui/option_map_unwrap_or.stderr b/src/tools/clippy/tests/ui/option_map_unwrap_or.stderr new file mode 100644 index 00000000000..f05f2893de2 --- /dev/null +++ b/src/tools/clippy/tests/ui/option_map_unwrap_or.stderr @@ -0,0 +1,138 @@ +error: called `map(f).unwrap_or(a)` on an `Option` value. This can be done more directly by calling `map_or(a, f)` instead + --> $DIR/option_map_unwrap_or.rs:20:13 + | +LL | let _ = opt.map(|x| x + 1) + | _____________^ +LL | | // Should lint even though this call is on a separate line. +LL | | .unwrap_or(0); + | |_____________________^ + | + = note: `-D clippy::option-map-unwrap-or` implied by `-D warnings` +help: use `map_or(a, f)` instead + | +LL | let _ = opt.map_or(0, |x| x + 1); + | ^^^^^^ ^^ -- + +error: called `map(f).unwrap_or(a)` on an `Option` value. This can be done more directly by calling `map_or(a, f)` instead + --> $DIR/option_map_unwrap_or.rs:24:13 + | +LL | let _ = opt.map(|x| { + | _____________^ +LL | | x + 1 +LL | | } +LL | | ).unwrap_or(0); + | |__________________^ + | +help: use `map_or(a, f)` instead + | +LL | let _ = opt.map_or(0, |x| { +LL | x + 1 +LL | } +LL | ); + | + +error: called `map(f).unwrap_or(a)` on an `Option` value. This can be done more directly by calling `map_or(a, f)` instead + --> $DIR/option_map_unwrap_or.rs:28:13 + | +LL | let _ = opt.map(|x| x + 1) + | _____________^ +LL | | .unwrap_or({ +LL | | 0 +LL | | }); + | |__________^ + | +help: use `map_or(a, f)` instead + | +LL | let _ = opt.map_or({ +LL | 0 +LL | }, |x| x + 1); + | + +error: called `map(f).unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then(f)` instead + --> $DIR/option_map_unwrap_or.rs:33:13 + | +LL | let _ = opt.map(|x| Some(x + 1)).unwrap_or(None); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `and_then(f)` instead + | +LL | let _ = opt.and_then(|x| Some(x + 1)); + | ^^^^^^^^ -- + +error: called `map(f).unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then(f)` instead + --> $DIR/option_map_unwrap_or.rs:35:13 + | +LL | let _ = opt.map(|x| { + | _____________^ +LL | | Some(x + 1) +LL | | } +LL | | ).unwrap_or(None); + | |_____________________^ + | +help: use `and_then(f)` instead + | +LL | let _ = opt.and_then(|x| { +LL | Some(x + 1) +LL | } +LL | ); + | + +error: called `map(f).unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then(f)` instead + --> $DIR/option_map_unwrap_or.rs:39:13 + | +LL | let _ = opt + | _____________^ +LL | | .map(|x| Some(x + 1)) +LL | | .unwrap_or(None); + | |________________________^ + | +help: use `and_then(f)` instead + | +LL | .and_then(|x| Some(x + 1)); + | ^^^^^^^^ -- + +error: called `map(f).unwrap_or(a)` on an `Option` value. This can be done more directly by calling `map_or(a, f)` instead + --> $DIR/option_map_unwrap_or.rs:50:13 + | +LL | let _ = Some("prefix").map(|p| format!("{}.", p)).unwrap_or(id); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `map_or(a, f)` instead + | +LL | let _ = Some("prefix").map_or(id, |p| format!("{}.", p)); + | ^^^^^^ ^^^ -- + +error: called `map(f).unwrap_or_else(g)` on an `Option` value. This can be done more directly by calling `map_or_else(g, f)` instead + --> $DIR/option_map_unwrap_or.rs:54:13 + | +LL | let _ = opt.map(|x| x + 1) + | _____________^ +LL | | // Should lint even though this call is on a separate line. +LL | | .unwrap_or_else(|| 0); + | |_____________________________^ + | + = note: `-D clippy::option-map-unwrap-or-else` implied by `-D warnings` + = note: replace `map(|x| x + 1).unwrap_or_else(|| 0)` with `map_or_else(|| 0, |x| x + 1)` + +error: called `map(f).unwrap_or_else(g)` on an `Option` value. This can be done more directly by calling `map_or_else(g, f)` instead + --> $DIR/option_map_unwrap_or.rs:58:13 + | +LL | let _ = opt.map(|x| { + | _____________^ +LL | | x + 1 +LL | | } +LL | | ).unwrap_or_else(|| 0); + | |__________________________^ + +error: called `map(f).unwrap_or_else(g)` on an `Option` value. This can be done more directly by calling `map_or_else(g, f)` instead + --> $DIR/option_map_unwrap_or.rs:62:13 + | +LL | let _ = opt.map(|x| x + 1) + | _____________^ +LL | | .unwrap_or_else(|| +LL | | 0 +LL | | ); + | |_________^ + +error: aborting due to 10 previous errors + diff --git a/src/tools/clippy/tests/ui/option_option.rs b/src/tools/clippy/tests/ui/option_option.rs new file mode 100644 index 00000000000..904c50e1403 --- /dev/null +++ b/src/tools/clippy/tests/ui/option_option.rs @@ -0,0 +1,62 @@ +#![deny(clippy::option_option)] + +fn input(_: Option<Option<u8>>) {} + +fn output() -> Option<Option<u8>> { + None +} + +fn output_nested() -> Vec<Option<Option<u8>>> { + vec![None] +} + +// The lint only generates one warning for this +fn output_nested_nested() -> Option<Option<Option<u8>>> { + None +} + +struct Struct { + x: Option<Option<u8>>, +} + +impl Struct { + fn struct_fn() -> Option<Option<u8>> { + None + } +} + +trait Trait { + fn trait_fn() -> Option<Option<u8>>; +} + +enum Enum { + Tuple(Option<Option<u8>>), + Struct { x: Option<Option<u8>> }, +} + +// The lint allows this +type OptionOption = Option<Option<u32>>; + +// The lint allows this +fn output_type_alias() -> OptionOption { + None +} + +// The line allows this +impl Trait for Struct { + fn trait_fn() -> Option<Option<u8>> { + None + } +} + +fn main() { + input(None); + output(); + output_nested(); + + // The lint allows this + let local: Option<Option<u8>> = None; + + // The lint allows this + let expr = Some(Some(true)); +} diff --git a/src/tools/clippy/tests/ui/option_option.stderr b/src/tools/clippy/tests/ui/option_option.stderr new file mode 100644 index 00000000000..79db186d7ea --- /dev/null +++ b/src/tools/clippy/tests/ui/option_option.stderr @@ -0,0 +1,62 @@ +error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases + --> $DIR/option_option.rs:3:13 + | +LL | fn input(_: Option<Option<u8>>) {} + | ^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/option_option.rs:1:9 + | +LL | #![deny(clippy::option_option)] + | ^^^^^^^^^^^^^^^^^^^^^ + +error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases + --> $DIR/option_option.rs:5:16 + | +LL | fn output() -> Option<Option<u8>> { + | ^^^^^^^^^^^^^^^^^^ + +error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases + --> $DIR/option_option.rs:9:27 + | +LL | fn output_nested() -> Vec<Option<Option<u8>>> { + | ^^^^^^^^^^^^^^^^^^ + +error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases + --> $DIR/option_option.rs:14:30 + | +LL | fn output_nested_nested() -> Option<Option<Option<u8>>> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases + --> $DIR/option_option.rs:19:8 + | +LL | x: Option<Option<u8>>, + | ^^^^^^^^^^^^^^^^^^ + +error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases + --> $DIR/option_option.rs:23:23 + | +LL | fn struct_fn() -> Option<Option<u8>> { + | ^^^^^^^^^^^^^^^^^^ + +error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases + --> $DIR/option_option.rs:29:22 + | +LL | fn trait_fn() -> Option<Option<u8>>; + | ^^^^^^^^^^^^^^^^^^ + +error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases + --> $DIR/option_option.rs:33:11 + | +LL | Tuple(Option<Option<u8>>), + | ^^^^^^^^^^^^^^^^^^ + +error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases + --> $DIR/option_option.rs:34:17 + | +LL | Struct { x: Option<Option<u8>> }, + | ^^^^^^^^^^^^^^^^^^ + +error: aborting due to 9 previous errors + diff --git a/src/tools/clippy/tests/ui/or_fun_call.fixed b/src/tools/clippy/tests/ui/or_fun_call.fixed new file mode 100644 index 00000000000..8ea03fe4261 --- /dev/null +++ b/src/tools/clippy/tests/ui/or_fun_call.fixed @@ -0,0 +1,110 @@ +// run-rustfix + +#![warn(clippy::or_fun_call)] +#![allow(dead_code)] + +use std::collections::BTreeMap; +use std::collections::HashMap; +use std::time::Duration; + +/// Checks implementation of the `OR_FUN_CALL` lint. +fn or_fun_call() { + struct Foo; + + impl Foo { + fn new() -> Foo { + Foo + } + } + + enum Enum { + A(i32), + } + + fn make<T>() -> T { + unimplemented!(); + } + + let with_enum = Some(Enum::A(1)); + with_enum.unwrap_or(Enum::A(5)); + + let with_const_fn = Some(Duration::from_secs(1)); + with_const_fn.unwrap_or(Duration::from_secs(5)); + + let with_constructor = Some(vec![1]); + with_constructor.unwrap_or_else(make); + + let with_new = Some(vec![1]); + with_new.unwrap_or_default(); + + let with_const_args = Some(vec![1]); + with_const_args.unwrap_or_else(|| Vec::with_capacity(12)); + + let with_err: Result<_, ()> = Ok(vec![1]); + with_err.unwrap_or_else(|_| make()); + + let with_err_args: Result<_, ()> = Ok(vec![1]); + with_err_args.unwrap_or_else(|_| Vec::with_capacity(12)); + + let with_default_trait = Some(1); + with_default_trait.unwrap_or_default(); + + let with_default_type = Some(1); + with_default_type.unwrap_or_default(); + + let with_vec = Some(vec![1]); + with_vec.unwrap_or_default(); + + let without_default = Some(Foo); + without_default.unwrap_or_else(Foo::new); + + let mut map = HashMap::<u64, String>::new(); + map.entry(42).or_insert_with(String::new); + + let mut btree = BTreeMap::<u64, String>::new(); + btree.entry(42).or_insert_with(String::new); + + let stringy = Some(String::from("")); + let _ = stringy.unwrap_or_else(|| "".to_owned()); + + let opt = Some(1); + let hello = "Hello"; + let _ = opt.ok_or(format!("{} world.", hello)); +} + +struct Foo(u8); +struct Bar(String, Duration); +#[rustfmt::skip] +fn test_or_with_ctors() { + let opt = Some(1); + let opt_opt = Some(Some(1)); + // we also test for const promotion, this makes sure we don't hit that + let two = 2; + + let _ = opt_opt.unwrap_or(Some(2)); + let _ = opt_opt.unwrap_or(Some(two)); + let _ = opt.ok_or(Some(2)); + let _ = opt.ok_or(Some(two)); + let _ = opt.ok_or(Foo(2)); + let _ = opt.ok_or(Foo(two)); + let _ = opt.or(Some(2)); + let _ = opt.or(Some(two)); + + let _ = Some("a".to_string()).or_else(|| Some("b".to_string())); + + let b = "b".to_string(); + let _ = Some(Bar("a".to_string(), Duration::from_secs(1))) + .or(Some(Bar(b, Duration::from_secs(2)))); +} + +// Issue 4514 - early return +fn f() -> Option<()> { + let a = Some(1); + let b = 1i32; + + let _ = a.unwrap_or(b.checked_mul(3)?.min(240)); + + Some(()) +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/or_fun_call.rs b/src/tools/clippy/tests/ui/or_fun_call.rs new file mode 100644 index 00000000000..7599b945a91 --- /dev/null +++ b/src/tools/clippy/tests/ui/or_fun_call.rs @@ -0,0 +1,110 @@ +// run-rustfix + +#![warn(clippy::or_fun_call)] +#![allow(dead_code)] + +use std::collections::BTreeMap; +use std::collections::HashMap; +use std::time::Duration; + +/// Checks implementation of the `OR_FUN_CALL` lint. +fn or_fun_call() { + struct Foo; + + impl Foo { + fn new() -> Foo { + Foo + } + } + + enum Enum { + A(i32), + } + + fn make<T>() -> T { + unimplemented!(); + } + + let with_enum = Some(Enum::A(1)); + with_enum.unwrap_or(Enum::A(5)); + + let with_const_fn = Some(Duration::from_secs(1)); + with_const_fn.unwrap_or(Duration::from_secs(5)); + + let with_constructor = Some(vec![1]); + with_constructor.unwrap_or(make()); + + let with_new = Some(vec![1]); + with_new.unwrap_or(Vec::new()); + + let with_const_args = Some(vec![1]); + with_const_args.unwrap_or(Vec::with_capacity(12)); + + let with_err: Result<_, ()> = Ok(vec![1]); + with_err.unwrap_or(make()); + + let with_err_args: Result<_, ()> = Ok(vec![1]); + with_err_args.unwrap_or(Vec::with_capacity(12)); + + let with_default_trait = Some(1); + with_default_trait.unwrap_or(Default::default()); + + let with_default_type = Some(1); + with_default_type.unwrap_or(u64::default()); + + let with_vec = Some(vec![1]); + with_vec.unwrap_or(vec![]); + + let without_default = Some(Foo); + without_default.unwrap_or(Foo::new()); + + let mut map = HashMap::<u64, String>::new(); + map.entry(42).or_insert(String::new()); + + let mut btree = BTreeMap::<u64, String>::new(); + btree.entry(42).or_insert(String::new()); + + let stringy = Some(String::from("")); + let _ = stringy.unwrap_or("".to_owned()); + + let opt = Some(1); + let hello = "Hello"; + let _ = opt.ok_or(format!("{} world.", hello)); +} + +struct Foo(u8); +struct Bar(String, Duration); +#[rustfmt::skip] +fn test_or_with_ctors() { + let opt = Some(1); + let opt_opt = Some(Some(1)); + // we also test for const promotion, this makes sure we don't hit that + let two = 2; + + let _ = opt_opt.unwrap_or(Some(2)); + let _ = opt_opt.unwrap_or(Some(two)); + let _ = opt.ok_or(Some(2)); + let _ = opt.ok_or(Some(two)); + let _ = opt.ok_or(Foo(2)); + let _ = opt.ok_or(Foo(two)); + let _ = opt.or(Some(2)); + let _ = opt.or(Some(two)); + + let _ = Some("a".to_string()).or(Some("b".to_string())); + + let b = "b".to_string(); + let _ = Some(Bar("a".to_string(), Duration::from_secs(1))) + .or(Some(Bar(b, Duration::from_secs(2)))); +} + +// Issue 4514 - early return +fn f() -> Option<()> { + let a = Some(1); + let b = 1i32; + + let _ = a.unwrap_or(b.checked_mul(3)?.min(240)); + + Some(()) +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/or_fun_call.stderr b/src/tools/clippy/tests/ui/or_fun_call.stderr new file mode 100644 index 00000000000..96d55771e6c --- /dev/null +++ b/src/tools/clippy/tests/ui/or_fun_call.stderr @@ -0,0 +1,82 @@ +error: use of `unwrap_or` followed by a function call + --> $DIR/or_fun_call.rs:35:22 + | +LL | with_constructor.unwrap_or(make()); + | ^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(make)` + | + = note: `-D clippy::or-fun-call` implied by `-D warnings` + +error: use of `unwrap_or` followed by a call to `new` + --> $DIR/or_fun_call.rs:38:5 + | +LL | with_new.unwrap_or(Vec::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `with_new.unwrap_or_default()` + +error: use of `unwrap_or` followed by a function call + --> $DIR/or_fun_call.rs:41:21 + | +LL | with_const_args.unwrap_or(Vec::with_capacity(12)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| Vec::with_capacity(12))` + +error: use of `unwrap_or` followed by a function call + --> $DIR/or_fun_call.rs:44:14 + | +LL | with_err.unwrap_or(make()); + | ^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|_| make())` + +error: use of `unwrap_or` followed by a function call + --> $DIR/or_fun_call.rs:47:19 + | +LL | with_err_args.unwrap_or(Vec::with_capacity(12)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|_| Vec::with_capacity(12))` + +error: use of `unwrap_or` followed by a call to `default` + --> $DIR/or_fun_call.rs:50:5 + | +LL | with_default_trait.unwrap_or(Default::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `with_default_trait.unwrap_or_default()` + +error: use of `unwrap_or` followed by a call to `default` + --> $DIR/or_fun_call.rs:53:5 + | +LL | with_default_type.unwrap_or(u64::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `with_default_type.unwrap_or_default()` + +error: use of `unwrap_or` followed by a call to `new` + --> $DIR/or_fun_call.rs:56:5 + | +LL | with_vec.unwrap_or(vec![]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `with_vec.unwrap_or_default()` + +error: use of `unwrap_or` followed by a function call + --> $DIR/or_fun_call.rs:59:21 + | +LL | without_default.unwrap_or(Foo::new()); + | ^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(Foo::new)` + +error: use of `or_insert` followed by a function call + --> $DIR/or_fun_call.rs:62:19 + | +LL | map.entry(42).or_insert(String::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_insert_with(String::new)` + +error: use of `or_insert` followed by a function call + --> $DIR/or_fun_call.rs:65:21 + | +LL | btree.entry(42).or_insert(String::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_insert_with(String::new)` + +error: use of `unwrap_or` followed by a function call + --> $DIR/or_fun_call.rs:68:21 + | +LL | let _ = stringy.unwrap_or("".to_owned()); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| "".to_owned())` + +error: use of `or` followed by a function call + --> $DIR/or_fun_call.rs:93:35 + | +LL | let _ = Some("a".to_string()).or(Some("b".to_string())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some("b".to_string()))` + +error: aborting due to 13 previous errors + diff --git a/src/tools/clippy/tests/ui/out_of_bounds_indexing/issue-3102.rs b/src/tools/clippy/tests/ui/out_of_bounds_indexing/issue-3102.rs new file mode 100644 index 00000000000..f20a0ede113 --- /dev/null +++ b/src/tools/clippy/tests/ui/out_of_bounds_indexing/issue-3102.rs @@ -0,0 +1,11 @@ +#![warn(clippy::out_of_bounds_indexing)] +#![allow(clippy::no_effect, const_err)] + +fn main() { + let x = [1, 2, 3, 4]; + + // issue 3102 + let num = 1; + &x[num..10]; // should trigger out of bounds error + &x[10..num]; // should trigger out of bounds error +} diff --git a/src/tools/clippy/tests/ui/out_of_bounds_indexing/issue-3102.stderr b/src/tools/clippy/tests/ui/out_of_bounds_indexing/issue-3102.stderr new file mode 100644 index 00000000000..516c1df40be --- /dev/null +++ b/src/tools/clippy/tests/ui/out_of_bounds_indexing/issue-3102.stderr @@ -0,0 +1,16 @@ +error: range is out of bounds + --> $DIR/issue-3102.rs:9:13 + | +LL | &x[num..10]; // should trigger out of bounds error + | ^^ + | + = note: `-D clippy::out-of-bounds-indexing` implied by `-D warnings` + +error: range is out of bounds + --> $DIR/issue-3102.rs:10:8 + | +LL | &x[10..num]; // should trigger out of bounds error + | ^^ + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/out_of_bounds_indexing/simple.rs b/src/tools/clippy/tests/ui/out_of_bounds_indexing/simple.rs new file mode 100644 index 00000000000..590e578d758 --- /dev/null +++ b/src/tools/clippy/tests/ui/out_of_bounds_indexing/simple.rs @@ -0,0 +1,22 @@ +#![warn(clippy::out_of_bounds_indexing)] +#![allow(clippy::no_effect, clippy::unnecessary_operation, const_err)] + +fn main() { + let x = [1, 2, 3, 4]; + + &x[..=4]; + &x[1..5]; + &x[5..]; + &x[..5]; + &x[5..].iter().map(|x| 2 * x).collect::<Vec<i32>>(); + &x[0..=4]; + + &x[4..]; // Ok, should not produce stderr. + &x[..4]; // Ok, should not produce stderr. + &x[..]; // Ok, should not produce stderr. + &x[1..]; // Ok, should not produce stderr. + &x[2..].iter().map(|x| 2 * x).collect::<Vec<i32>>(); // Ok, should not produce stderr. + + &x[0..].get(..3); // Ok, should not produce stderr. + &x[0..3]; // Ok, should not produce stderr. +} diff --git a/src/tools/clippy/tests/ui/out_of_bounds_indexing/simple.stderr b/src/tools/clippy/tests/ui/out_of_bounds_indexing/simple.stderr new file mode 100644 index 00000000000..3d95afcdab2 --- /dev/null +++ b/src/tools/clippy/tests/ui/out_of_bounds_indexing/simple.stderr @@ -0,0 +1,40 @@ +error: range is out of bounds + --> $DIR/simple.rs:7:11 + | +LL | &x[..=4]; + | ^ + | + = note: `-D clippy::out-of-bounds-indexing` implied by `-D warnings` + +error: range is out of bounds + --> $DIR/simple.rs:8:11 + | +LL | &x[1..5]; + | ^ + +error: range is out of bounds + --> $DIR/simple.rs:9:8 + | +LL | &x[5..]; + | ^ + +error: range is out of bounds + --> $DIR/simple.rs:10:10 + | +LL | &x[..5]; + | ^ + +error: range is out of bounds + --> $DIR/simple.rs:11:8 + | +LL | &x[5..].iter().map(|x| 2 * x).collect::<Vec<i32>>(); + | ^ + +error: range is out of bounds + --> $DIR/simple.rs:12:12 + | +LL | &x[0..=4]; + | ^ + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/outer_expn_data.fixed b/src/tools/clippy/tests/ui/outer_expn_data.fixed new file mode 100644 index 00000000000..999a19b289e --- /dev/null +++ b/src/tools/clippy/tests/ui/outer_expn_data.fixed @@ -0,0 +1,28 @@ +// run-rustfix + +#![deny(clippy::internal)] +#![feature(rustc_private)] + +extern crate rustc_hir; +extern crate rustc_lint; +extern crate rustc_middle; +#[macro_use] +extern crate rustc_session; +use rustc_hir::Expr; +use rustc_lint::{LateContext, LateLintPass}; + +declare_lint! { + pub TEST_LINT, + Warn, + "" +} + +declare_lint_pass!(Pass => [TEST_LINT]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass { + fn check_expr(&mut self, _cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) { + let _ = expr.span.ctxt().outer_expn_data(); + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/outer_expn_data.rs b/src/tools/clippy/tests/ui/outer_expn_data.rs new file mode 100644 index 00000000000..5405d475d1a --- /dev/null +++ b/src/tools/clippy/tests/ui/outer_expn_data.rs @@ -0,0 +1,28 @@ +// run-rustfix + +#![deny(clippy::internal)] +#![feature(rustc_private)] + +extern crate rustc_hir; +extern crate rustc_lint; +extern crate rustc_middle; +#[macro_use] +extern crate rustc_session; +use rustc_hir::Expr; +use rustc_lint::{LateContext, LateLintPass}; + +declare_lint! { + pub TEST_LINT, + Warn, + "" +} + +declare_lint_pass!(Pass => [TEST_LINT]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass { + fn check_expr(&mut self, _cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) { + let _ = expr.span.ctxt().outer_expn().expn_data(); + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/outer_expn_data.stderr b/src/tools/clippy/tests/ui/outer_expn_data.stderr new file mode 100644 index 00000000000..56b6ce1f78e --- /dev/null +++ b/src/tools/clippy/tests/ui/outer_expn_data.stderr @@ -0,0 +1,15 @@ +error: usage of `outer_expn().expn_data()` + --> $DIR/outer_expn_data.rs:24:34 + | +LL | let _ = expr.span.ctxt().outer_expn().expn_data(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `outer_expn_data()` + | +note: the lint level is defined here + --> $DIR/outer_expn_data.rs:3:9 + | +LL | #![deny(clippy::internal)] + | ^^^^^^^^^^^^^^^^ + = note: `#[deny(clippy::outer_expn_expn_data)]` implied by `#[deny(clippy::internal)]` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/overflow_check_conditional.rs b/src/tools/clippy/tests/ui/overflow_check_conditional.rs new file mode 100644 index 00000000000..84332040dba --- /dev/null +++ b/src/tools/clippy/tests/ui/overflow_check_conditional.rs @@ -0,0 +1,26 @@ +#![allow(clippy::many_single_char_names)] +#![warn(clippy::overflow_check_conditional)] + +fn main() { + let a: u32 = 1; + let b: u32 = 2; + let c: u32 = 3; + if a + b < a {} + if a > a + b {} + if a + b < b {} + if b > a + b {} + if a - b > b {} + if b < a - b {} + if a - b > a {} + if a < a - b {} + if a + b < c {} + if c > a + b {} + if a - b < c {} + if c > a - b {} + let i = 1.1; + let j = 2.2; + if i + j < i {} + if i - j < i {} + if i > i + j {} + if i - j < i {} +} diff --git a/src/tools/clippy/tests/ui/overflow_check_conditional.stderr b/src/tools/clippy/tests/ui/overflow_check_conditional.stderr new file mode 100644 index 00000000000..ad66135d326 --- /dev/null +++ b/src/tools/clippy/tests/ui/overflow_check_conditional.stderr @@ -0,0 +1,52 @@ +error: You are trying to use classic C overflow conditions that will fail in Rust. + --> $DIR/overflow_check_conditional.rs:8:8 + | +LL | if a + b < a {} + | ^^^^^^^^^ + | + = note: `-D clippy::overflow-check-conditional` implied by `-D warnings` + +error: You are trying to use classic C overflow conditions that will fail in Rust. + --> $DIR/overflow_check_conditional.rs:9:8 + | +LL | if a > a + b {} + | ^^^^^^^^^ + +error: You are trying to use classic C overflow conditions that will fail in Rust. + --> $DIR/overflow_check_conditional.rs:10:8 + | +LL | if a + b < b {} + | ^^^^^^^^^ + +error: You are trying to use classic C overflow conditions that will fail in Rust. + --> $DIR/overflow_check_conditional.rs:11:8 + | +LL | if b > a + b {} + | ^^^^^^^^^ + +error: You are trying to use classic C underflow conditions that will fail in Rust. + --> $DIR/overflow_check_conditional.rs:12:8 + | +LL | if a - b > b {} + | ^^^^^^^^^ + +error: You are trying to use classic C underflow conditions that will fail in Rust. + --> $DIR/overflow_check_conditional.rs:13:8 + | +LL | if b < a - b {} + | ^^^^^^^^^ + +error: You are trying to use classic C underflow conditions that will fail in Rust. + --> $DIR/overflow_check_conditional.rs:14:8 + | +LL | if a - b > a {} + | ^^^^^^^^^ + +error: You are trying to use classic C underflow conditions that will fail in Rust. + --> $DIR/overflow_check_conditional.rs:15:8 + | +LL | if a < a - b {} + | ^^^^^^^^^ + +error: aborting due to 8 previous errors + diff --git a/src/tools/clippy/tests/ui/panic.rs b/src/tools/clippy/tests/ui/panic.rs new file mode 100644 index 00000000000..6e004aa9a92 --- /dev/null +++ b/src/tools/clippy/tests/ui/panic.rs @@ -0,0 +1,61 @@ +#![warn(clippy::panic_params)] +#![allow(clippy::assertions_on_constants)] +fn missing() { + if true { + panic!("{}"); + } else if false { + panic!("{:?}"); + } else { + assert!(true, "here be missing values: {}"); + } + + panic!("{{{this}}}"); +} + +fn ok_single() { + panic!("foo bar"); +} + +fn ok_inner() { + // Test for #768 + assert!("foo bar".contains(&format!("foo {}", "bar"))); +} + +fn ok_multiple() { + panic!("{}", "This is {ok}"); +} + +fn ok_bracket() { + match 42 { + 1337 => panic!("{so is this"), + 666 => panic!("so is this}"), + _ => panic!("}so is that{"), + } +} + +const ONE: u32 = 1; + +fn ok_nomsg() { + assert!({ 1 == ONE }); + assert!(if 1 == ONE { ONE == 1 } else { false }); +} + +fn ok_escaped() { + panic!("{{ why should this not be ok? }}"); + panic!(" or {{ that ?"); + panic!(" or }} this ?"); + panic!(" {or {{ that ?"); + panic!(" }or }} this ?"); + panic!("{{ test }"); + panic!("{case }}"); +} + +fn main() { + missing(); + ok_single(); + ok_multiple(); + ok_bracket(); + ok_inner(); + ok_nomsg(); + ok_escaped(); +} diff --git a/src/tools/clippy/tests/ui/panic.stderr b/src/tools/clippy/tests/ui/panic.stderr new file mode 100644 index 00000000000..1f8ff8ccf55 --- /dev/null +++ b/src/tools/clippy/tests/ui/panic.stderr @@ -0,0 +1,28 @@ +error: you probably are missing some parameter in your format string + --> $DIR/panic.rs:5:16 + | +LL | panic!("{}"); + | ^^^^ + | + = note: `-D clippy::panic-params` implied by `-D warnings` + +error: you probably are missing some parameter in your format string + --> $DIR/panic.rs:7:16 + | +LL | panic!("{:?}"); + | ^^^^^^ + +error: you probably are missing some parameter in your format string + --> $DIR/panic.rs:9:23 + | +LL | assert!(true, "here be missing values: {}"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: you probably are missing some parameter in your format string + --> $DIR/panic.rs:12:12 + | +LL | panic!("{{{this}}}"); + | ^^^^^^^^^^^^ + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/panicking_macros.rs b/src/tools/clippy/tests/ui/panicking_macros.rs new file mode 100644 index 00000000000..dabb695368d --- /dev/null +++ b/src/tools/clippy/tests/ui/panicking_macros.rs @@ -0,0 +1,33 @@ +#![warn(clippy::unimplemented, clippy::unreachable, clippy::todo, clippy::panic)] +#![allow(clippy::assertions_on_constants)] + +fn panic() { + let a = 2; + panic!(); + let b = a + 2; +} + +fn todo() { + let a = 2; + todo!(); + let b = a + 2; +} + +fn unimplemented() { + let a = 2; + unimplemented!(); + let b = a + 2; +} + +fn unreachable() { + let a = 2; + unreachable!(); + let b = a + 2; +} + +fn main() { + panic(); + todo(); + unimplemented(); + unreachable(); +} diff --git a/src/tools/clippy/tests/ui/panicking_macros.stderr b/src/tools/clippy/tests/ui/panicking_macros.stderr new file mode 100644 index 00000000000..72319bc7e45 --- /dev/null +++ b/src/tools/clippy/tests/ui/panicking_macros.stderr @@ -0,0 +1,34 @@ +error: `panic` should not be present in production code + --> $DIR/panicking_macros.rs:6:5 + | +LL | panic!(); + | ^^^^^^^^^ + | + = note: `-D clippy::panic` implied by `-D warnings` + +error: `todo` should not be present in production code + --> $DIR/panicking_macros.rs:12:5 + | +LL | todo!(); + | ^^^^^^^^ + | + = note: `-D clippy::todo` implied by `-D warnings` + +error: `unimplemented` should not be present in production code + --> $DIR/panicking_macros.rs:18:5 + | +LL | unimplemented!(); + | ^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::unimplemented` implied by `-D warnings` + +error: `unreachable` should not be present in production code + --> $DIR/panicking_macros.rs:24:5 + | +LL | unreachable!(); + | ^^^^^^^^^^^^^^^ + | + = note: `-D clippy::unreachable` implied by `-D warnings` + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/partialeq_ne_impl.rs b/src/tools/clippy/tests/ui/partialeq_ne_impl.rs new file mode 100644 index 00000000000..1338d3c74d5 --- /dev/null +++ b/src/tools/clippy/tests/ui/partialeq_ne_impl.rs @@ -0,0 +1,26 @@ +#![allow(dead_code)] + +struct Foo; + +impl PartialEq for Foo { + fn eq(&self, _: &Foo) -> bool { + true + } + fn ne(&self, _: &Foo) -> bool { + false + } +} + +struct Bar; + +impl PartialEq for Bar { + fn eq(&self, _: &Bar) -> bool { + true + } + #[allow(clippy::partialeq_ne_impl)] + fn ne(&self, _: &Bar) -> bool { + false + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/partialeq_ne_impl.stderr b/src/tools/clippy/tests/ui/partialeq_ne_impl.stderr new file mode 100644 index 00000000000..b92da4511b4 --- /dev/null +++ b/src/tools/clippy/tests/ui/partialeq_ne_impl.stderr @@ -0,0 +1,12 @@ +error: re-implementing `PartialEq::ne` is unnecessary + --> $DIR/partialeq_ne_impl.rs:9:5 + | +LL | / fn ne(&self, _: &Foo) -> bool { +LL | | false +LL | | } + | |_____^ + | + = note: `-D clippy::partialeq-ne-impl` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/path_buf_push_overwrite.fixed b/src/tools/clippy/tests/ui/path_buf_push_overwrite.fixed new file mode 100644 index 00000000000..ef8856830fc --- /dev/null +++ b/src/tools/clippy/tests/ui/path_buf_push_overwrite.fixed @@ -0,0 +1,8 @@ +// run-rustfix +use std::path::PathBuf; + +#[warn(clippy::all, clippy::path_buf_push_overwrite)] +fn main() { + let mut x = PathBuf::from("/foo"); + x.push("bar"); +} diff --git a/src/tools/clippy/tests/ui/path_buf_push_overwrite.rs b/src/tools/clippy/tests/ui/path_buf_push_overwrite.rs new file mode 100644 index 00000000000..6e2d483f454 --- /dev/null +++ b/src/tools/clippy/tests/ui/path_buf_push_overwrite.rs @@ -0,0 +1,8 @@ +// run-rustfix +use std::path::PathBuf; + +#[warn(clippy::all, clippy::path_buf_push_overwrite)] +fn main() { + let mut x = PathBuf::from("/foo"); + x.push("/bar"); +} diff --git a/src/tools/clippy/tests/ui/path_buf_push_overwrite.stderr b/src/tools/clippy/tests/ui/path_buf_push_overwrite.stderr new file mode 100644 index 00000000000..09b18d71baf --- /dev/null +++ b/src/tools/clippy/tests/ui/path_buf_push_overwrite.stderr @@ -0,0 +1,10 @@ +error: Calling `push` with '/' or '/' (file system root) will overwrite the previous path definition + --> $DIR/path_buf_push_overwrite.rs:7:12 + | +LL | x.push("/bar"); + | ^^^^^^ help: try: `"bar"` + | + = note: `-D clippy::path-buf-push-overwrite` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/patterns.fixed b/src/tools/clippy/tests/ui/patterns.fixed new file mode 100644 index 00000000000..f2238815449 --- /dev/null +++ b/src/tools/clippy/tests/ui/patterns.fixed @@ -0,0 +1,36 @@ +// run-rustfix +#![allow(unused)] +#![warn(clippy::all)] + +fn main() { + let v = Some(true); + let s = [0, 1, 2, 3, 4]; + match v { + Some(x) => (), + y => (), + } + match v { + Some(x) => (), + y @ None => (), // no error + } + match s { + [x, inside @ .., y] => (), // no error + [..] => (), + } + + let mut mutv = vec![1, 2, 3]; + + // required "ref" left out in suggestion: #5271 + match mutv { + ref mut x => { + x.push(4); + println!("vec: {:?}", x); + }, + ref y if y == &vec![0] => (), + } + + match mutv { + ref x => println!("vec: {:?}", x), + ref y if y == &vec![0] => (), + } +} diff --git a/src/tools/clippy/tests/ui/patterns.rs b/src/tools/clippy/tests/ui/patterns.rs new file mode 100644 index 00000000000..5848ecd38d9 --- /dev/null +++ b/src/tools/clippy/tests/ui/patterns.rs @@ -0,0 +1,36 @@ +// run-rustfix +#![allow(unused)] +#![warn(clippy::all)] + +fn main() { + let v = Some(true); + let s = [0, 1, 2, 3, 4]; + match v { + Some(x) => (), + y @ _ => (), + } + match v { + Some(x) => (), + y @ None => (), // no error + } + match s { + [x, inside @ .., y] => (), // no error + [..] => (), + } + + let mut mutv = vec![1, 2, 3]; + + // required "ref" left out in suggestion: #5271 + match mutv { + ref mut x @ _ => { + x.push(4); + println!("vec: {:?}", x); + }, + ref y if y == &vec![0] => (), + } + + match mutv { + ref x @ _ => println!("vec: {:?}", x), + ref y if y == &vec![0] => (), + } +} diff --git a/src/tools/clippy/tests/ui/patterns.stderr b/src/tools/clippy/tests/ui/patterns.stderr new file mode 100644 index 00000000000..af067580688 --- /dev/null +++ b/src/tools/clippy/tests/ui/patterns.stderr @@ -0,0 +1,22 @@ +error: the `y @ _` pattern can be written as just `y` + --> $DIR/patterns.rs:10:9 + | +LL | y @ _ => (), + | ^^^^^ help: try: `y` + | + = note: `-D clippy::redundant-pattern` implied by `-D warnings` + +error: the `x @ _` pattern can be written as just `x` + --> $DIR/patterns.rs:25:9 + | +LL | ref mut x @ _ => { + | ^^^^^^^^^^^^^ help: try: `ref mut x` + +error: the `x @ _` pattern can be written as just `x` + --> $DIR/patterns.rs:33:9 + | +LL | ref x @ _ => println!("vec: {:?}", x), + | ^^^^^^^^^ help: try: `ref x` + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/precedence.fixed b/src/tools/clippy/tests/ui/precedence.fixed new file mode 100644 index 00000000000..17b1f1bd0bf --- /dev/null +++ b/src/tools/clippy/tests/ui/precedence.fixed @@ -0,0 +1,53 @@ +// run-rustfix +#![warn(clippy::precedence)] +#![allow(unused_must_use, clippy::no_effect, clippy::unnecessary_operation)] +#![allow(clippy::identity_op)] +#![allow(clippy::eq_op)] + +macro_rules! trip { + ($a:expr) => { + match $a & 0b1111_1111u8 { + 0 => println!("a is zero ({})", $a), + _ => println!("a is {}", $a), + } + }; +} + +fn main() { + 1 << (2 + 3); + (1 + 2) << 3; + 4 >> (1 + 1); + (1 + 3) >> 2; + 1 ^ (1 - 1); + 3 | (2 - 1); + 3 & (5 - 2); + -(1i32.abs()); + -(1f32.abs()); + + // These should not trigger an error + let _ = (-1i32).abs(); + let _ = (-1f32).abs(); + let _ = -(1i32).abs(); + let _ = -(1f32).abs(); + let _ = -(1i32.abs()); + let _ = -(1f32.abs()); + + // Odd functions shoud not trigger an error + let _ = -1f64.asin(); + let _ = -1f64.asinh(); + let _ = -1f64.atan(); + let _ = -1f64.atanh(); + let _ = -1f64.cbrt(); + let _ = -1f64.fract(); + let _ = -1f64.round(); + let _ = -1f64.signum(); + let _ = -1f64.sin(); + let _ = -1f64.sinh(); + let _ = -1f64.tan(); + let _ = -1f64.tanh(); + let _ = -1f64.to_degrees(); + let _ = -1f64.to_radians(); + + let b = 3; + trip!(b * 8); +} diff --git a/src/tools/clippy/tests/ui/precedence.rs b/src/tools/clippy/tests/ui/precedence.rs new file mode 100644 index 00000000000..2d0891fd3c2 --- /dev/null +++ b/src/tools/clippy/tests/ui/precedence.rs @@ -0,0 +1,53 @@ +// run-rustfix +#![warn(clippy::precedence)] +#![allow(unused_must_use, clippy::no_effect, clippy::unnecessary_operation)] +#![allow(clippy::identity_op)] +#![allow(clippy::eq_op)] + +macro_rules! trip { + ($a:expr) => { + match $a & 0b1111_1111u8 { + 0 => println!("a is zero ({})", $a), + _ => println!("a is {}", $a), + } + }; +} + +fn main() { + 1 << 2 + 3; + 1 + 2 << 3; + 4 >> 1 + 1; + 1 + 3 >> 2; + 1 ^ 1 - 1; + 3 | 2 - 1; + 3 & 5 - 2; + -1i32.abs(); + -1f32.abs(); + + // These should not trigger an error + let _ = (-1i32).abs(); + let _ = (-1f32).abs(); + let _ = -(1i32).abs(); + let _ = -(1f32).abs(); + let _ = -(1i32.abs()); + let _ = -(1f32.abs()); + + // Odd functions shoud not trigger an error + let _ = -1f64.asin(); + let _ = -1f64.asinh(); + let _ = -1f64.atan(); + let _ = -1f64.atanh(); + let _ = -1f64.cbrt(); + let _ = -1f64.fract(); + let _ = -1f64.round(); + let _ = -1f64.signum(); + let _ = -1f64.sin(); + let _ = -1f64.sinh(); + let _ = -1f64.tan(); + let _ = -1f64.tanh(); + let _ = -1f64.to_degrees(); + let _ = -1f64.to_radians(); + + let b = 3; + trip!(b * 8); +} diff --git a/src/tools/clippy/tests/ui/precedence.stderr b/src/tools/clippy/tests/ui/precedence.stderr new file mode 100644 index 00000000000..a2ed5392bfc --- /dev/null +++ b/src/tools/clippy/tests/ui/precedence.stderr @@ -0,0 +1,58 @@ +error: operator precedence can trip the unwary + --> $DIR/precedence.rs:17:5 + | +LL | 1 << 2 + 3; + | ^^^^^^^^^^ help: consider parenthesizing your expression: `1 << (2 + 3)` + | + = note: `-D clippy::precedence` implied by `-D warnings` + +error: operator precedence can trip the unwary + --> $DIR/precedence.rs:18:5 + | +LL | 1 + 2 << 3; + | ^^^^^^^^^^ help: consider parenthesizing your expression: `(1 + 2) << 3` + +error: operator precedence can trip the unwary + --> $DIR/precedence.rs:19:5 + | +LL | 4 >> 1 + 1; + | ^^^^^^^^^^ help: consider parenthesizing your expression: `4 >> (1 + 1)` + +error: operator precedence can trip the unwary + --> $DIR/precedence.rs:20:5 + | +LL | 1 + 3 >> 2; + | ^^^^^^^^^^ help: consider parenthesizing your expression: `(1 + 3) >> 2` + +error: operator precedence can trip the unwary + --> $DIR/precedence.rs:21:5 + | +LL | 1 ^ 1 - 1; + | ^^^^^^^^^ help: consider parenthesizing your expression: `1 ^ (1 - 1)` + +error: operator precedence can trip the unwary + --> $DIR/precedence.rs:22:5 + | +LL | 3 | 2 - 1; + | ^^^^^^^^^ help: consider parenthesizing your expression: `3 | (2 - 1)` + +error: operator precedence can trip the unwary + --> $DIR/precedence.rs:23:5 + | +LL | 3 & 5 - 2; + | ^^^^^^^^^ help: consider parenthesizing your expression: `3 & (5 - 2)` + +error: unary minus has lower precedence than method call + --> $DIR/precedence.rs:24:5 + | +LL | -1i32.abs(); + | ^^^^^^^^^^^ help: consider adding parentheses to clarify your intent: `-(1i32.abs())` + +error: unary minus has lower precedence than method call + --> $DIR/precedence.rs:25:5 + | +LL | -1f32.abs(); + | ^^^^^^^^^^^ help: consider adding parentheses to clarify your intent: `-(1f32.abs())` + +error: aborting due to 9 previous errors + diff --git a/src/tools/clippy/tests/ui/print.rs b/src/tools/clippy/tests/ui/print.rs new file mode 100644 index 00000000000..366ccc2b3bd --- /dev/null +++ b/src/tools/clippy/tests/ui/print.rs @@ -0,0 +1,35 @@ +#![allow(clippy::print_literal, clippy::write_literal)] +#![warn(clippy::print_stdout, clippy::use_debug)] + +use std::fmt::{Debug, Display, Formatter, Result}; + +#[allow(dead_code)] +struct Foo; + +impl Display for Foo { + fn fmt(&self, f: &mut Formatter) -> Result { + write!(f, "{:?}", 43.1415) + } +} + +impl Debug for Foo { + fn fmt(&self, f: &mut Formatter) -> Result { + // ok, we can use `Debug` formatting in `Debug` implementations + write!(f, "{:?}", 42.718) + } +} + +fn main() { + println!("Hello"); + print!("Hello"); + + print!("Hello {}", "World"); + + print!("Hello {:?}", "World"); + + print!("Hello {:#?}", "#orld"); + + assert_eq!(42, 1337); + + vec![1, 2]; +} diff --git a/src/tools/clippy/tests/ui/print.stderr b/src/tools/clippy/tests/ui/print.stderr new file mode 100644 index 00000000000..208d9532628 --- /dev/null +++ b/src/tools/clippy/tests/ui/print.stderr @@ -0,0 +1,54 @@ +error: use of `Debug`-based formatting + --> $DIR/print.rs:11:19 + | +LL | write!(f, "{:?}", 43.1415) + | ^^^^^^ + | + = note: `-D clippy::use-debug` implied by `-D warnings` + +error: use of `println!` + --> $DIR/print.rs:23:5 + | +LL | println!("Hello"); + | ^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::print-stdout` implied by `-D warnings` + +error: use of `print!` + --> $DIR/print.rs:24:5 + | +LL | print!("Hello"); + | ^^^^^^^^^^^^^^^ + +error: use of `print!` + --> $DIR/print.rs:26:5 + | +LL | print!("Hello {}", "World"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: use of `print!` + --> $DIR/print.rs:28:5 + | +LL | print!("Hello {:?}", "World"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: use of `Debug`-based formatting + --> $DIR/print.rs:28:12 + | +LL | print!("Hello {:?}", "World"); + | ^^^^^^^^^^^^ + +error: use of `print!` + --> $DIR/print.rs:30:5 + | +LL | print!("Hello {:#?}", "#orld"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: use of `Debug`-based formatting + --> $DIR/print.rs:30:12 + | +LL | print!("Hello {:#?}", "#orld"); + | ^^^^^^^^^^^^^ + +error: aborting due to 8 previous errors + diff --git a/src/tools/clippy/tests/ui/print_literal.rs b/src/tools/clippy/tests/ui/print_literal.rs new file mode 100644 index 00000000000..40ed18e9302 --- /dev/null +++ b/src/tools/clippy/tests/ui/print_literal.rs @@ -0,0 +1,38 @@ +#![warn(clippy::print_literal)] + +fn main() { + // these should be fine + print!("Hello"); + println!("Hello"); + let world = "world"; + println!("Hello {}", world); + println!("Hello {world}", world = world); + println!("3 in hex is {:X}", 3); + println!("2 + 1 = {:.4}", 3); + println!("2 + 1 = {:5.4}", 3); + println!("Debug test {:?}", "hello, world"); + println!("{0:8} {1:>8}", "hello", "world"); + println!("{1:8} {0:>8}", "hello", "world"); + println!("{foo:8} {bar:>8}", foo = "hello", bar = "world"); + println!("{bar:8} {foo:>8}", foo = "hello", bar = "world"); + println!("{number:>width$}", number = 1, width = 6); + println!("{number:>0width$}", number = 1, width = 6); + + // these should throw warnings + println!("{} of {:b} people know binary, the other half doesn't", 1, 2); + print!("Hello {}", "world"); + println!("Hello {} {}", world, "world"); + println!("Hello {}", "world"); + println!("10 / 4 is {}", 2.5); + println!("2 + 1 = {}", 3); + + // positional args don't change the fact + // that we're using a literal -- this should + // throw a warning + println!("{0} {1}", "hello", "world"); + println!("{1} {0}", "hello", "world"); + + // named args shouldn't change anything either + println!("{foo} {bar}", foo = "hello", bar = "world"); + println!("{bar} {foo}", foo = "hello", bar = "world"); +} diff --git a/src/tools/clippy/tests/ui/print_literal.stderr b/src/tools/clippy/tests/ui/print_literal.stderr new file mode 100644 index 00000000000..fc502e9f71d --- /dev/null +++ b/src/tools/clippy/tests/ui/print_literal.stderr @@ -0,0 +1,88 @@ +error: literal with an empty format string + --> $DIR/print_literal.rs:22:71 + | +LL | println!("{} of {:b} people know binary, the other half doesn't", 1, 2); + | ^ + | + = note: `-D clippy::print-literal` implied by `-D warnings` + +error: literal with an empty format string + --> $DIR/print_literal.rs:23:24 + | +LL | print!("Hello {}", "world"); + | ^^^^^^^ + +error: literal with an empty format string + --> $DIR/print_literal.rs:24:36 + | +LL | println!("Hello {} {}", world, "world"); + | ^^^^^^^ + +error: literal with an empty format string + --> $DIR/print_literal.rs:25:26 + | +LL | println!("Hello {}", "world"); + | ^^^^^^^ + +error: literal with an empty format string + --> $DIR/print_literal.rs:26:30 + | +LL | println!("10 / 4 is {}", 2.5); + | ^^^ + +error: literal with an empty format string + --> $DIR/print_literal.rs:27:28 + | +LL | println!("2 + 1 = {}", 3); + | ^ + +error: literal with an empty format string + --> $DIR/print_literal.rs:32:25 + | +LL | println!("{0} {1}", "hello", "world"); + | ^^^^^^^ + +error: literal with an empty format string + --> $DIR/print_literal.rs:32:34 + | +LL | println!("{0} {1}", "hello", "world"); + | ^^^^^^^ + +error: literal with an empty format string + --> $DIR/print_literal.rs:33:25 + | +LL | println!("{1} {0}", "hello", "world"); + | ^^^^^^^ + +error: literal with an empty format string + --> $DIR/print_literal.rs:33:34 + | +LL | println!("{1} {0}", "hello", "world"); + | ^^^^^^^ + +error: literal with an empty format string + --> $DIR/print_literal.rs:36:35 + | +LL | println!("{foo} {bar}", foo = "hello", bar = "world"); + | ^^^^^^^ + +error: literal with an empty format string + --> $DIR/print_literal.rs:36:50 + | +LL | println!("{foo} {bar}", foo = "hello", bar = "world"); + | ^^^^^^^ + +error: literal with an empty format string + --> $DIR/print_literal.rs:37:35 + | +LL | println!("{bar} {foo}", foo = "hello", bar = "world"); + | ^^^^^^^ + +error: literal with an empty format string + --> $DIR/print_literal.rs:37:50 + | +LL | println!("{bar} {foo}", foo = "hello", bar = "world"); + | ^^^^^^^ + +error: aborting due to 14 previous errors + diff --git a/src/tools/clippy/tests/ui/print_with_newline.rs b/src/tools/clippy/tests/ui/print_with_newline.rs new file mode 100644 index 00000000000..3f710540e90 --- /dev/null +++ b/src/tools/clippy/tests/ui/print_with_newline.rs @@ -0,0 +1,51 @@ +// FIXME: Ideally these suggestions would be fixed via rustfix. Blocked by rust-lang/rust#53934 +// // run-rustfix + +#![allow(clippy::print_literal)] +#![warn(clippy::print_with_newline)] + +fn main() { + print!("Hello\n"); + print!("Hello {}\n", "world"); + print!("Hello {} {}\n", "world", "#2"); + print!("{}\n", 1265); + + // these are all fine + print!(""); + print!("Hello"); + println!("Hello"); + println!("Hello\n"); + println!("Hello {}\n", "world"); + print!("Issue\n{}", 1265); + print!("{}", 1265); + print!("\n{}", 1275); + print!("\n\n"); + print!("like eof\n\n"); + print!("Hello {} {}\n\n", "world", "#2"); + println!("\ndon't\nwarn\nfor\nmultiple\nnewlines\n"); // #3126 + println!("\nbla\n\n"); // #3126 + + // Escaping + print!("\\n"); // #3514 + print!("\\\n"); // should fail + print!("\\\\n"); + + // Raw strings + print!(r"\n"); // #3778 + + // Literal newlines should also fail + print!( + " +" + ); + print!( + r" +" + ); + + // Don't warn on CRLF (#4208) + print!("\r\n"); + print!("foo\r\n"); + print!("\\r\n"); //~ ERROR + print!("foo\rbar\n") // ~ ERROR +} diff --git a/src/tools/clippy/tests/ui/print_with_newline.stderr b/src/tools/clippy/tests/ui/print_with_newline.stderr new file mode 100644 index 00000000000..05fe88915d6 --- /dev/null +++ b/src/tools/clippy/tests/ui/print_with_newline.stderr @@ -0,0 +1,110 @@ +error: using `print!()` with a format string that ends in a single newline + --> $DIR/print_with_newline.rs:8:5 + | +LL | print!("Hello/n"); + | ^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::print-with-newline` implied by `-D warnings` +help: use `println!` instead + | +LL | println!("Hello"); + | ^^^^^^^ -- + +error: using `print!()` with a format string that ends in a single newline + --> $DIR/print_with_newline.rs:9:5 + | +LL | print!("Hello {}/n", "world"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `println!` instead + | +LL | println!("Hello {}", "world"); + | ^^^^^^^ -- + +error: using `print!()` with a format string that ends in a single newline + --> $DIR/print_with_newline.rs:10:5 + | +LL | print!("Hello {} {}/n", "world", "#2"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `println!` instead + | +LL | println!("Hello {} {}", "world", "#2"); + | ^^^^^^^ -- + +error: using `print!()` with a format string that ends in a single newline + --> $DIR/print_with_newline.rs:11:5 + | +LL | print!("{}/n", 1265); + | ^^^^^^^^^^^^^^^^^^^^ + | +help: use `println!` instead + | +LL | println!("{}", 1265); + | ^^^^^^^ -- + +error: using `print!()` with a format string that ends in a single newline + --> $DIR/print_with_newline.rs:30:5 + | +LL | print!("//n"); // should fail + | ^^^^^^^^^^^^^^ + | +help: use `println!` instead + | +LL | println!("/"); // should fail + | ^^^^^^^ -- + +error: using `print!()` with a format string that ends in a single newline + --> $DIR/print_with_newline.rs:37:5 + | +LL | / print!( +LL | | " +LL | | " +LL | | ); + | |_____^ + | +help: use `println!` instead + | +LL | println!( +LL | "" + | + +error: using `print!()` with a format string that ends in a single newline + --> $DIR/print_with_newline.rs:41:5 + | +LL | / print!( +LL | | r" +LL | | " +LL | | ); + | |_____^ + | +help: use `println!` instead + | +LL | println!( +LL | r"" + | + +error: using `print!()` with a format string that ends in a single newline + --> $DIR/print_with_newline.rs:49:5 + | +LL | print!("/r/n"); //~ ERROR + | ^^^^^^^^^^^^^^^ + | +help: use `println!` instead + | +LL | println!("/r"); //~ ERROR + | ^^^^^^^ -- + +error: using `print!()` with a format string that ends in a single newline + --> $DIR/print_with_newline.rs:50:5 + | +LL | print!("foo/rbar/n") // ~ ERROR + | ^^^^^^^^^^^^^^^^^^^^ + | +help: use `println!` instead + | +LL | println!("foo/rbar") // ~ ERROR + | ^^^^^^^ -- + +error: aborting due to 9 previous errors + diff --git a/src/tools/clippy/tests/ui/println_empty_string.fixed b/src/tools/clippy/tests/ui/println_empty_string.fixed new file mode 100644 index 00000000000..2b889b62ea9 --- /dev/null +++ b/src/tools/clippy/tests/ui/println_empty_string.fixed @@ -0,0 +1,11 @@ +// run-rustfix +#![allow(clippy::match_single_binding)] + +fn main() { + println!(); + println!(); + + match "a" { + _ => println!(), + } +} diff --git a/src/tools/clippy/tests/ui/println_empty_string.rs b/src/tools/clippy/tests/ui/println_empty_string.rs new file mode 100644 index 00000000000..890f5f68476 --- /dev/null +++ b/src/tools/clippy/tests/ui/println_empty_string.rs @@ -0,0 +1,11 @@ +// run-rustfix +#![allow(clippy::match_single_binding)] + +fn main() { + println!(); + println!(""); + + match "a" { + _ => println!(""), + } +} diff --git a/src/tools/clippy/tests/ui/println_empty_string.stderr b/src/tools/clippy/tests/ui/println_empty_string.stderr new file mode 100644 index 00000000000..23112b88168 --- /dev/null +++ b/src/tools/clippy/tests/ui/println_empty_string.stderr @@ -0,0 +1,16 @@ +error: using `println!("")` + --> $DIR/println_empty_string.rs:6:5 + | +LL | println!(""); + | ^^^^^^^^^^^^ help: replace it with: `println!()` + | + = note: `-D clippy::println-empty-string` implied by `-D warnings` + +error: using `println!("")` + --> $DIR/println_empty_string.rs:9:14 + | +LL | _ => println!(""), + | ^^^^^^^^^^^^ help: replace it with: `println!()` + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/proc_macro.rs b/src/tools/clippy/tests/ui/proc_macro.rs new file mode 100644 index 00000000000..59914b8b8f6 --- /dev/null +++ b/src/tools/clippy/tests/ui/proc_macro.rs @@ -0,0 +1,26 @@ +//! Check that we correctly lint procedural macros. +#![crate_type = "proc-macro"] + +extern crate proc_macro; + +use proc_macro::TokenStream; + +#[allow(dead_code)] +fn f() { + let _x = 3.14; +} + +#[proc_macro] +pub fn mybangmacro(t: TokenStream) -> TokenStream { + t +} + +#[proc_macro_derive(MyDerivedTrait)] +pub fn myderive(t: TokenStream) -> TokenStream { + t +} + +#[proc_macro_attribute] +pub fn myattribute(t: TokenStream, a: TokenStream) -> TokenStream { + t +} diff --git a/src/tools/clippy/tests/ui/proc_macro.stderr b/src/tools/clippy/tests/ui/proc_macro.stderr new file mode 100644 index 00000000000..872cbc66af6 --- /dev/null +++ b/src/tools/clippy/tests/ui/proc_macro.stderr @@ -0,0 +1,10 @@ +error: approximate value of `f{32, 64}::consts::PI` found. Consider using it directly + --> $DIR/proc_macro.rs:10:14 + | +LL | let _x = 3.14; + | ^^^^ + | + = note: `#[deny(clippy::approx_constant)]` on by default + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/ptr_arg.rs b/src/tools/clippy/tests/ui/ptr_arg.rs new file mode 100644 index 00000000000..30f39e9b063 --- /dev/null +++ b/src/tools/clippy/tests/ui/ptr_arg.rs @@ -0,0 +1,86 @@ +#![allow(unused, clippy::many_single_char_names, clippy::redundant_clone)] +#![warn(clippy::ptr_arg)] + +use std::borrow::Cow; + +fn do_vec(x: &Vec<i64>) { + //Nothing here +} + +fn do_vec_mut(x: &mut Vec<i64>) { + // no error here + //Nothing here +} + +fn do_str(x: &String) { + //Nothing here either +} + +fn do_str_mut(x: &mut String) { + // no error here + //Nothing here either +} + +fn main() {} + +trait Foo { + type Item; + fn do_vec(x: &Vec<i64>); + fn do_item(x: &Self::Item); +} + +struct Bar; + +// no error, in trait impl (#425) +impl Foo for Bar { + type Item = Vec<u8>; + fn do_vec(x: &Vec<i64>) {} + fn do_item(x: &Vec<u8>) {} +} + +fn cloned(x: &Vec<u8>) -> Vec<u8> { + let e = x.clone(); + let f = e.clone(); // OK + let g = x; + let h = g.clone(); // Alas, we cannot reliably detect this without following data. + let i = (e).clone(); + x.clone() +} + +fn str_cloned(x: &String) -> String { + let a = x.clone(); + let b = x.clone(); + let c = b.clone(); + let d = a.clone().clone().clone(); + x.clone() +} + +fn false_positive_capacity(x: &Vec<u8>, y: &String) { + let a = x.capacity(); + let b = y.clone(); + let c = y.as_str(); +} + +fn false_positive_capacity_too(x: &String) -> String { + if x.capacity() > 1024 { + panic!("Too large!"); + } + x.clone() +} + +#[allow(dead_code)] +fn test_cow_with_ref(c: &Cow<[i32]>) {} + +#[allow(dead_code)] +fn test_cow(c: Cow<[i32]>) { + let _c = c; +} + +trait Foo2 { + fn do_string(&self); +} + +// no error for &self references where self is of type String (#2293) +impl Foo2 for String { + fn do_string(&self) {} +} diff --git a/src/tools/clippy/tests/ui/ptr_arg.stderr b/src/tools/clippy/tests/ui/ptr_arg.stderr new file mode 100644 index 00000000000..314f23497f9 --- /dev/null +++ b/src/tools/clippy/tests/ui/ptr_arg.stderr @@ -0,0 +1,89 @@ +error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices. + --> $DIR/ptr_arg.rs:6:14 + | +LL | fn do_vec(x: &Vec<i64>) { + | ^^^^^^^^^ help: change this to: `&[i64]` + | + = note: `-D clippy::ptr-arg` implied by `-D warnings` + +error: writing `&String` instead of `&str` involves a new object where a slice will do. + --> $DIR/ptr_arg.rs:15:14 + | +LL | fn do_str(x: &String) { + | ^^^^^^^ help: change this to: `&str` + +error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices. + --> $DIR/ptr_arg.rs:28:18 + | +LL | fn do_vec(x: &Vec<i64>); + | ^^^^^^^^^ help: change this to: `&[i64]` + +error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices. + --> $DIR/ptr_arg.rs:41:14 + | +LL | fn cloned(x: &Vec<u8>) -> Vec<u8> { + | ^^^^^^^^ + | +help: change this to + | +LL | fn cloned(x: &[u8]) -> Vec<u8> { + | ^^^^^ +help: change `x.clone()` to + | +LL | let e = x.to_owned(); + | ^^^^^^^^^^^^ +help: change `x.clone()` to + | +LL | x.to_owned() + | + +error: writing `&String` instead of `&str` involves a new object where a slice will do. + --> $DIR/ptr_arg.rs:50:18 + | +LL | fn str_cloned(x: &String) -> String { + | ^^^^^^^ + | +help: change this to + | +LL | fn str_cloned(x: &str) -> String { + | ^^^^ +help: change `x.clone()` to + | +LL | let a = x.to_string(); + | ^^^^^^^^^^^^^ +help: change `x.clone()` to + | +LL | let b = x.to_string(); + | ^^^^^^^^^^^^^ +help: change `x.clone()` to + | +LL | x.to_string() + | + +error: writing `&String` instead of `&str` involves a new object where a slice will do. + --> $DIR/ptr_arg.rs:58:44 + | +LL | fn false_positive_capacity(x: &Vec<u8>, y: &String) { + | ^^^^^^^ + | +help: change this to + | +LL | fn false_positive_capacity(x: &Vec<u8>, y: &str) { + | ^^^^ +help: change `y.clone()` to + | +LL | let b = y.to_string(); + | ^^^^^^^^^^^^^ +help: change `y.as_str()` to + | +LL | let c = y; + | ^ + +error: using a reference to `Cow` is not recommended. + --> $DIR/ptr_arg.rs:72:25 + | +LL | fn test_cow_with_ref(c: &Cow<[i32]>) {} + | ^^^^^^^^^^^ help: change this to: `&[i32]` + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/tests/ui/ptr_offset_with_cast.fixed b/src/tools/clippy/tests/ui/ptr_offset_with_cast.fixed new file mode 100644 index 00000000000..ebdd6c4003d --- /dev/null +++ b/src/tools/clippy/tests/ui/ptr_offset_with_cast.fixed @@ -0,0 +1,20 @@ +// run-rustfix + +fn main() { + let vec = vec![b'a', b'b', b'c']; + let ptr = vec.as_ptr(); + + let offset_u8 = 1_u8; + let offset_usize = 1_usize; + let offset_isize = 1_isize; + + unsafe { + ptr.add(offset_usize); + ptr.offset(offset_isize as isize); + ptr.offset(offset_u8 as isize); + + ptr.wrapping_add(offset_usize); + ptr.wrapping_offset(offset_isize as isize); + ptr.wrapping_offset(offset_u8 as isize); + } +} diff --git a/src/tools/clippy/tests/ui/ptr_offset_with_cast.rs b/src/tools/clippy/tests/ui/ptr_offset_with_cast.rs new file mode 100644 index 00000000000..3416c4b727a --- /dev/null +++ b/src/tools/clippy/tests/ui/ptr_offset_with_cast.rs @@ -0,0 +1,20 @@ +// run-rustfix + +fn main() { + let vec = vec![b'a', b'b', b'c']; + let ptr = vec.as_ptr(); + + let offset_u8 = 1_u8; + let offset_usize = 1_usize; + let offset_isize = 1_isize; + + unsafe { + ptr.offset(offset_usize as isize); + ptr.offset(offset_isize as isize); + ptr.offset(offset_u8 as isize); + + ptr.wrapping_offset(offset_usize as isize); + ptr.wrapping_offset(offset_isize as isize); + ptr.wrapping_offset(offset_u8 as isize); + } +} diff --git a/src/tools/clippy/tests/ui/ptr_offset_with_cast.stderr b/src/tools/clippy/tests/ui/ptr_offset_with_cast.stderr new file mode 100644 index 00000000000..b5c7a03e277 --- /dev/null +++ b/src/tools/clippy/tests/ui/ptr_offset_with_cast.stderr @@ -0,0 +1,16 @@ +error: use of `offset` with a `usize` casted to an `isize` + --> $DIR/ptr_offset_with_cast.rs:12:9 + | +LL | ptr.offset(offset_usize as isize); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr.add(offset_usize)` + | + = note: `-D clippy::ptr-offset-with-cast` implied by `-D warnings` + +error: use of `wrapping_offset` with a `usize` casted to an `isize` + --> $DIR/ptr_offset_with_cast.rs:16:9 + | +LL | ptr.wrapping_offset(offset_usize as isize); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr.wrapping_add(offset_usize)` + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/question_mark.fixed b/src/tools/clippy/tests/ui/question_mark.fixed new file mode 100644 index 00000000000..11dff94a288 --- /dev/null +++ b/src/tools/clippy/tests/ui/question_mark.fixed @@ -0,0 +1,125 @@ +// run-rustfix +#![allow(unreachable_code)] + +fn some_func(a: Option<u32>) -> Option<u32> { + a?; + + a +} + +fn some_other_func(a: Option<u32>) -> Option<u32> { + if a.is_none() { + return None; + } else { + return Some(0); + } + unreachable!() +} + +pub enum SeemsOption<T> { + Some(T), + None, +} + +impl<T> SeemsOption<T> { + pub fn is_none(&self) -> bool { + match *self { + SeemsOption::None => true, + SeemsOption::Some(_) => false, + } + } +} + +fn returns_something_similar_to_option(a: SeemsOption<u32>) -> SeemsOption<u32> { + if a.is_none() { + return SeemsOption::None; + } + + a +} + +pub struct CopyStruct { + pub opt: Option<u32>, +} + +impl CopyStruct { + #[rustfmt::skip] + pub fn func(&self) -> Option<u32> { + (self.opt)?; + + self.opt?; + + let _ = Some(self.opt?); + + let _ = self.opt?; + + self.opt + } +} + +#[derive(Clone)] +pub struct MoveStruct { + pub opt: Option<Vec<u32>>, +} + +impl MoveStruct { + pub fn ref_func(&self) -> Option<Vec<u32>> { + self.opt.as_ref()?; + + self.opt.clone() + } + + pub fn mov_func_reuse(self) -> Option<Vec<u32>> { + self.opt.as_ref()?; + + self.opt + } + + pub fn mov_func_no_use(self) -> Option<Vec<u32>> { + self.opt.as_ref()?; + Some(Vec::new()) + } + + pub fn if_let_ref_func(self) -> Option<Vec<u32>> { + let v: &Vec<_> = self.opt.as_ref()?; + + Some(v.clone()) + } + + pub fn if_let_mov_func(self) -> Option<Vec<u32>> { + let v = self.opt?; + + Some(v) + } +} + +fn func() -> Option<i32> { + fn f() -> Option<String> { + Some(String::new()) + } + + f()?; + + Some(0) +} + +fn main() { + some_func(Some(42)); + some_func(None); + some_other_func(Some(42)); + + let copy_struct = CopyStruct { opt: Some(54) }; + copy_struct.func(); + + let move_struct = MoveStruct { + opt: Some(vec![42, 1337]), + }; + move_struct.ref_func(); + move_struct.clone().mov_func_reuse(); + move_struct.mov_func_no_use(); + + let so = SeemsOption::Some(45); + returns_something_similar_to_option(so); + + func(); +} diff --git a/src/tools/clippy/tests/ui/question_mark.rs b/src/tools/clippy/tests/ui/question_mark.rs new file mode 100644 index 00000000000..1d0ee82b4f7 --- /dev/null +++ b/src/tools/clippy/tests/ui/question_mark.rs @@ -0,0 +1,155 @@ +// run-rustfix +#![allow(unreachable_code)] + +fn some_func(a: Option<u32>) -> Option<u32> { + if a.is_none() { + return None; + } + + a +} + +fn some_other_func(a: Option<u32>) -> Option<u32> { + if a.is_none() { + return None; + } else { + return Some(0); + } + unreachable!() +} + +pub enum SeemsOption<T> { + Some(T), + None, +} + +impl<T> SeemsOption<T> { + pub fn is_none(&self) -> bool { + match *self { + SeemsOption::None => true, + SeemsOption::Some(_) => false, + } + } +} + +fn returns_something_similar_to_option(a: SeemsOption<u32>) -> SeemsOption<u32> { + if a.is_none() { + return SeemsOption::None; + } + + a +} + +pub struct CopyStruct { + pub opt: Option<u32>, +} + +impl CopyStruct { + #[rustfmt::skip] + pub fn func(&self) -> Option<u32> { + if (self.opt).is_none() { + return None; + } + + if self.opt.is_none() { + return None + } + + let _ = if self.opt.is_none() { + return None; + } else { + self.opt + }; + + let _ = if let Some(x) = self.opt { + x + } else { + return None; + }; + + self.opt + } +} + +#[derive(Clone)] +pub struct MoveStruct { + pub opt: Option<Vec<u32>>, +} + +impl MoveStruct { + pub fn ref_func(&self) -> Option<Vec<u32>> { + if self.opt.is_none() { + return None; + } + + self.opt.clone() + } + + pub fn mov_func_reuse(self) -> Option<Vec<u32>> { + if self.opt.is_none() { + return None; + } + + self.opt + } + + pub fn mov_func_no_use(self) -> Option<Vec<u32>> { + if self.opt.is_none() { + return None; + } + Some(Vec::new()) + } + + pub fn if_let_ref_func(self) -> Option<Vec<u32>> { + let v: &Vec<_> = if let Some(ref v) = self.opt { + v + } else { + return None; + }; + + Some(v.clone()) + } + + pub fn if_let_mov_func(self) -> Option<Vec<u32>> { + let v = if let Some(v) = self.opt { + v + } else { + return None; + }; + + Some(v) + } +} + +fn func() -> Option<i32> { + fn f() -> Option<String> { + Some(String::new()) + } + + if f().is_none() { + return None; + } + + Some(0) +} + +fn main() { + some_func(Some(42)); + some_func(None); + some_other_func(Some(42)); + + let copy_struct = CopyStruct { opt: Some(54) }; + copy_struct.func(); + + let move_struct = MoveStruct { + opt: Some(vec![42, 1337]), + }; + move_struct.ref_func(); + move_struct.clone().mov_func_reuse(); + move_struct.mov_func_no_use(); + + let so = SeemsOption::Some(45); + returns_something_similar_to_option(so); + + func(); +} diff --git a/src/tools/clippy/tests/ui/question_mark.stderr b/src/tools/clippy/tests/ui/question_mark.stderr new file mode 100644 index 00000000000..502615fb175 --- /dev/null +++ b/src/tools/clippy/tests/ui/question_mark.stderr @@ -0,0 +1,104 @@ +error: this block may be rewritten with the `?` operator + --> $DIR/question_mark.rs:5:5 + | +LL | / if a.is_none() { +LL | | return None; +LL | | } + | |_____^ help: replace it with: `a?;` + | + = note: `-D clippy::question-mark` implied by `-D warnings` + +error: this block may be rewritten with the `?` operator + --> $DIR/question_mark.rs:50:9 + | +LL | / if (self.opt).is_none() { +LL | | return None; +LL | | } + | |_________^ help: replace it with: `(self.opt)?;` + +error: this block may be rewritten with the `?` operator + --> $DIR/question_mark.rs:54:9 + | +LL | / if self.opt.is_none() { +LL | | return None +LL | | } + | |_________^ help: replace it with: `self.opt?;` + +error: this block may be rewritten with the `?` operator + --> $DIR/question_mark.rs:58:17 + | +LL | let _ = if self.opt.is_none() { + | _________________^ +LL | | return None; +LL | | } else { +LL | | self.opt +LL | | }; + | |_________^ help: replace it with: `Some(self.opt?)` + +error: this if-let-else may be rewritten with the `?` operator + --> $DIR/question_mark.rs:64:17 + | +LL | let _ = if let Some(x) = self.opt { + | _________________^ +LL | | x +LL | | } else { +LL | | return None; +LL | | }; + | |_________^ help: replace it with: `self.opt?` + +error: this block may be rewritten with the `?` operator + --> $DIR/question_mark.rs:81:9 + | +LL | / if self.opt.is_none() { +LL | | return None; +LL | | } + | |_________^ help: replace it with: `self.opt.as_ref()?;` + +error: this block may be rewritten with the `?` operator + --> $DIR/question_mark.rs:89:9 + | +LL | / if self.opt.is_none() { +LL | | return None; +LL | | } + | |_________^ help: replace it with: `self.opt.as_ref()?;` + +error: this block may be rewritten with the `?` operator + --> $DIR/question_mark.rs:97:9 + | +LL | / if self.opt.is_none() { +LL | | return None; +LL | | } + | |_________^ help: replace it with: `self.opt.as_ref()?;` + +error: this if-let-else may be rewritten with the `?` operator + --> $DIR/question_mark.rs:104:26 + | +LL | let v: &Vec<_> = if let Some(ref v) = self.opt { + | __________________________^ +LL | | v +LL | | } else { +LL | | return None; +LL | | }; + | |_________^ help: replace it with: `self.opt.as_ref()?` + +error: this if-let-else may be rewritten with the `?` operator + --> $DIR/question_mark.rs:114:17 + | +LL | let v = if let Some(v) = self.opt { + | _________________^ +LL | | v +LL | | } else { +LL | | return None; +LL | | }; + | |_________^ help: replace it with: `self.opt?` + +error: this block may be rewritten with the `?` operator + --> $DIR/question_mark.rs:129:5 + | +LL | / if f().is_none() { +LL | | return None; +LL | | } + | |_____^ help: replace it with: `f()?;` + +error: aborting due to 11 previous errors + diff --git a/src/tools/clippy/tests/ui/range.rs b/src/tools/clippy/tests/ui/range.rs new file mode 100644 index 00000000000..628282509c1 --- /dev/null +++ b/src/tools/clippy/tests/ui/range.rs @@ -0,0 +1,16 @@ +#[warn(clippy::range_zip_with_len)] +fn main() { + let v1 = vec![1, 2, 3]; + let v2 = vec![4, 5]; + let _x = v1.iter().zip(0..v1.len()); + let _y = v1.iter().zip(0..v2.len()); // No error +} + +#[allow(unused)] +fn no_panic_with_fake_range_types() { + struct Range { + foo: i32, + } + + let _ = Range { foo: 0 }; +} diff --git a/src/tools/clippy/tests/ui/range.stderr b/src/tools/clippy/tests/ui/range.stderr new file mode 100644 index 00000000000..d53c1edecac --- /dev/null +++ b/src/tools/clippy/tests/ui/range.stderr @@ -0,0 +1,10 @@ +error: It is more idiomatic to use `v1.iter().enumerate()` + --> $DIR/range.rs:5:14 + | +LL | let _x = v1.iter().zip(0..v1.len()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::range-zip-with-len` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/range_plus_minus_one.fixed b/src/tools/clippy/tests/ui/range_plus_minus_one.fixed new file mode 100644 index 00000000000..6b402114099 --- /dev/null +++ b/src/tools/clippy/tests/ui/range_plus_minus_one.fixed @@ -0,0 +1,41 @@ +// run-rustfix + +#![allow(unused_parens)] + +fn f() -> usize { + 42 +} + +#[warn(clippy::range_plus_one)] +fn main() { + for _ in 0..2 {} + for _ in 0..=2 {} + + for _ in 0..=3 {} + for _ in 0..=3 + 1 {} + + for _ in 0..=5 {} + for _ in 0..=1 + 5 {} + + for _ in 1..=1 {} + for _ in 1..=1 + 1 {} + + for _ in 0..13 + 13 {} + for _ in 0..=13 - 7 {} + + for _ in 0..=f() {} + for _ in 0..=(1 + f()) {} + + let _ = ..11 - 1; + let _ = ..11; + let _ = ..11; + let _ = (1..=11); + let _ = ((f() + 1)..=f()); + + const ONE: usize = 1; + // integer consts are linted, too + for _ in 1..=ONE {} + + let mut vec: Vec<()> = std::vec::Vec::new(); + vec.drain(..); +} diff --git a/src/tools/clippy/tests/ui/range_plus_minus_one.rs b/src/tools/clippy/tests/ui/range_plus_minus_one.rs new file mode 100644 index 00000000000..3cfed4125b3 --- /dev/null +++ b/src/tools/clippy/tests/ui/range_plus_minus_one.rs @@ -0,0 +1,41 @@ +// run-rustfix + +#![allow(unused_parens)] + +fn f() -> usize { + 42 +} + +#[warn(clippy::range_plus_one)] +fn main() { + for _ in 0..2 {} + for _ in 0..=2 {} + + for _ in 0..3 + 1 {} + for _ in 0..=3 + 1 {} + + for _ in 0..1 + 5 {} + for _ in 0..=1 + 5 {} + + for _ in 1..1 + 1 {} + for _ in 1..=1 + 1 {} + + for _ in 0..13 + 13 {} + for _ in 0..=13 - 7 {} + + for _ in 0..(1 + f()) {} + for _ in 0..=(1 + f()) {} + + let _ = ..11 - 1; + let _ = ..=11 - 1; + let _ = ..=(11 - 1); + let _ = (1..11 + 1); + let _ = (f() + 1)..(f() + 1); + + const ONE: usize = 1; + // integer consts are linted, too + for _ in 1..ONE + ONE {} + + let mut vec: Vec<()> = std::vec::Vec::new(); + vec.drain(..); +} diff --git a/src/tools/clippy/tests/ui/range_plus_minus_one.stderr b/src/tools/clippy/tests/ui/range_plus_minus_one.stderr new file mode 100644 index 00000000000..f72943a04f2 --- /dev/null +++ b/src/tools/clippy/tests/ui/range_plus_minus_one.stderr @@ -0,0 +1,60 @@ +error: an inclusive range would be more readable + --> $DIR/range_plus_minus_one.rs:14:14 + | +LL | for _ in 0..3 + 1 {} + | ^^^^^^^^ help: use: `0..=3` + | + = note: `-D clippy::range-plus-one` implied by `-D warnings` + +error: an inclusive range would be more readable + --> $DIR/range_plus_minus_one.rs:17:14 + | +LL | for _ in 0..1 + 5 {} + | ^^^^^^^^ help: use: `0..=5` + +error: an inclusive range would be more readable + --> $DIR/range_plus_minus_one.rs:20:14 + | +LL | for _ in 1..1 + 1 {} + | ^^^^^^^^ help: use: `1..=1` + +error: an inclusive range would be more readable + --> $DIR/range_plus_minus_one.rs:26:14 + | +LL | for _ in 0..(1 + f()) {} + | ^^^^^^^^^^^^ help: use: `0..=f()` + +error: an exclusive range would be more readable + --> $DIR/range_plus_minus_one.rs:30:13 + | +LL | let _ = ..=11 - 1; + | ^^^^^^^^^ help: use: `..11` + | + = note: `-D clippy::range-minus-one` implied by `-D warnings` + +error: an exclusive range would be more readable + --> $DIR/range_plus_minus_one.rs:31:13 + | +LL | let _ = ..=(11 - 1); + | ^^^^^^^^^^^ help: use: `..11` + +error: an inclusive range would be more readable + --> $DIR/range_plus_minus_one.rs:32:13 + | +LL | let _ = (1..11 + 1); + | ^^^^^^^^^^^ help: use: `(1..=11)` + +error: an inclusive range would be more readable + --> $DIR/range_plus_minus_one.rs:33:13 + | +LL | let _ = (f() + 1)..(f() + 1); + | ^^^^^^^^^^^^^^^^^^^^ help: use: `((f() + 1)..=f())` + +error: an inclusive range would be more readable + --> $DIR/range_plus_minus_one.rs:37:14 + | +LL | for _ in 1..ONE + ONE {} + | ^^^^^^^^^^^^ help: use: `1..=ONE` + +error: aborting due to 9 previous errors + diff --git a/src/tools/clippy/tests/ui/redundant_allocation.fixed b/src/tools/clippy/tests/ui/redundant_allocation.fixed new file mode 100644 index 00000000000..26635833458 --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_allocation.fixed @@ -0,0 +1,48 @@ +// run-rustfix +#![warn(clippy::all)] +#![allow(clippy::boxed_local, clippy::needless_pass_by_value)] +#![allow(clippy::blacklisted_name, unused_variables, dead_code)] + +use std::boxed::Box; +use std::rc::Rc; + +pub struct MyStruct {} + +pub struct SubT<T> { + foo: T, +} + +pub enum MyEnum { + One, + Two, +} + +// Rc<&T> + +pub fn test1<T>(foo: &T) {} + +pub fn test2(foo: &MyStruct) {} + +pub fn test3(foo: &MyEnum) {} + +pub fn test4_neg(foo: Rc<SubT<&usize>>) {} + +// Rc<Rc<T>> + +pub fn test5(a: Rc<bool>) {} + +// Rc<Box<T>> + +pub fn test6(a: Box<bool>) {} + +// Box<&T> + +pub fn test7<T>(foo: &T) {} + +pub fn test8(foo: &MyStruct) {} + +pub fn test9(foo: &MyEnum) {} + +pub fn test10_neg(foo: Box<SubT<&usize>>) {} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/redundant_allocation.rs b/src/tools/clippy/tests/ui/redundant_allocation.rs new file mode 100644 index 00000000000..677b3e56d4d --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_allocation.rs @@ -0,0 +1,48 @@ +// run-rustfix +#![warn(clippy::all)] +#![allow(clippy::boxed_local, clippy::needless_pass_by_value)] +#![allow(clippy::blacklisted_name, unused_variables, dead_code)] + +use std::boxed::Box; +use std::rc::Rc; + +pub struct MyStruct {} + +pub struct SubT<T> { + foo: T, +} + +pub enum MyEnum { + One, + Two, +} + +// Rc<&T> + +pub fn test1<T>(foo: Rc<&T>) {} + +pub fn test2(foo: Rc<&MyStruct>) {} + +pub fn test3(foo: Rc<&MyEnum>) {} + +pub fn test4_neg(foo: Rc<SubT<&usize>>) {} + +// Rc<Rc<T>> + +pub fn test5(a: Rc<Rc<bool>>) {} + +// Rc<Box<T>> + +pub fn test6(a: Rc<Box<bool>>) {} + +// Box<&T> + +pub fn test7<T>(foo: Box<&T>) {} + +pub fn test8(foo: Box<&MyStruct>) {} + +pub fn test9(foo: Box<&MyEnum>) {} + +pub fn test10_neg(foo: Box<SubT<&usize>>) {} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/redundant_allocation.stderr b/src/tools/clippy/tests/ui/redundant_allocation.stderr new file mode 100644 index 00000000000..eaa57ce3024 --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_allocation.stderr @@ -0,0 +1,52 @@ +error: usage of `Rc<&T>` + --> $DIR/redundant_allocation.rs:22:22 + | +LL | pub fn test1<T>(foo: Rc<&T>) {} + | ^^^^^^ help: try: `&T` + | + = note: `-D clippy::redundant-allocation` implied by `-D warnings` + +error: usage of `Rc<&T>` + --> $DIR/redundant_allocation.rs:24:19 + | +LL | pub fn test2(foo: Rc<&MyStruct>) {} + | ^^^^^^^^^^^^^ help: try: `&MyStruct` + +error: usage of `Rc<&T>` + --> $DIR/redundant_allocation.rs:26:19 + | +LL | pub fn test3(foo: Rc<&MyEnum>) {} + | ^^^^^^^^^^^ help: try: `&MyEnum` + +error: usage of `Rc<Rc<T>>` + --> $DIR/redundant_allocation.rs:32:17 + | +LL | pub fn test5(a: Rc<Rc<bool>>) {} + | ^^^^^^^^^^^^ help: try: `Rc<bool>` + +error: usage of `Rc<Box<T>>` + --> $DIR/redundant_allocation.rs:36:17 + | +LL | pub fn test6(a: Rc<Box<bool>>) {} + | ^^^^^^^^^^^^^ help: try: `Box<bool>` + +error: usage of `Box<&T>` + --> $DIR/redundant_allocation.rs:40:22 + | +LL | pub fn test7<T>(foo: Box<&T>) {} + | ^^^^^^^ help: try: `&T` + +error: usage of `Box<&T>` + --> $DIR/redundant_allocation.rs:42:19 + | +LL | pub fn test8(foo: Box<&MyStruct>) {} + | ^^^^^^^^^^^^^^ help: try: `&MyStruct` + +error: usage of `Box<&T>` + --> $DIR/redundant_allocation.rs:44:19 + | +LL | pub fn test9(foo: Box<&MyEnum>) {} + | ^^^^^^^^^^^^ help: try: `&MyEnum` + +error: aborting due to 8 previous errors + diff --git a/src/tools/clippy/tests/ui/redundant_clone.fixed b/src/tools/clippy/tests/ui/redundant_clone.fixed new file mode 100644 index 00000000000..764c10a6d39 --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_clone.fixed @@ -0,0 +1,172 @@ +// run-rustfix +// rustfix-only-machine-applicable + +use std::ffi::OsString; +use std::path::Path; + +fn main() { + let _s = ["lorem", "ipsum"].join(" "); + + let s = String::from("foo"); + let _s = s; + + let s = String::from("foo"); + let _s = s; + + let s = String::from("foo"); + let _s = s; + + let _s = Path::new("/a/b/").join("c"); + + let _s = Path::new("/a/b/").join("c"); + + let _s = OsString::new(); + + let _s = OsString::new(); + + // Check that lint level works + #[allow(clippy::redundant_clone)] + let _s = String::new().to_string(); + + let tup = (String::from("foo"),); + let _t = tup.0; + + let tup_ref = &(String::from("foo"),); + let _s = tup_ref.0.clone(); // this `.clone()` cannot be removed + + { + let x = String::new(); + let y = &x; + + let _x = x.clone(); // ok; `x` is borrowed by `y` + + let _ = y.len(); + } + + let x = (String::new(),); + let _ = Some(String::new()).unwrap_or_else(|| x.0.clone()); // ok; closure borrows `x` + + with_branch(Alpha, true); + cannot_double_move(Alpha); + cannot_move_from_type_with_drop(); + borrower_propagation(); + not_consumed(); + issue_5405(); +} + +#[derive(Clone)] +struct Alpha; +fn with_branch(a: Alpha, b: bool) -> (Alpha, Alpha) { + if b { + (a.clone(), a) + } else { + (Alpha, a) + } +} + +fn cannot_double_move(a: Alpha) -> (Alpha, Alpha) { + (a.clone(), a) +} + +struct TypeWithDrop { + x: String, +} + +impl Drop for TypeWithDrop { + fn drop(&mut self) {} +} + +fn cannot_move_from_type_with_drop() -> String { + let s = TypeWithDrop { x: String::new() }; + s.x.clone() // removing this `clone()` summons E0509 +} + +fn borrower_propagation() { + let s = String::new(); + let t = String::new(); + + { + fn b() -> bool { + unimplemented!() + } + let _u = if b() { &s } else { &t }; + + // ok; `s` and `t` are possibly borrowed + let _s = s.clone(); + let _t = t.clone(); + } + + { + let _u = || s.len(); + let _v = [&t; 32]; + let _s = s.clone(); // ok + let _t = t.clone(); // ok + } + + { + let _u = { + let u = Some(&s); + let _ = s.clone(); // ok + u + }; + let _s = s.clone(); // ok + } + + { + use std::convert::identity as id; + let _u = id(id(&s)); + let _s = s.clone(); // ok, `u` borrows `s` + } + + let _s = s; + let _t = t; + + #[derive(Clone)] + struct Foo { + x: usize, + } + + { + let f = Foo { x: 123 }; + let _x = Some(f.x); + let _f = f; + } + + { + let f = Foo { x: 123 }; + let _x = &f.x; + let _f = f.clone(); // ok + } +} + +fn not_consumed() { + let x = std::path::PathBuf::from("home"); + let y = x.join("matthias"); + // join() creates a new owned PathBuf, does not take a &mut to x variable, thus the .clone() is + // redundant. (It also does not consume the PathBuf) + + println!("x: {:?}, y: {:?}", x, y); + + let mut s = String::new(); + s.clone().push_str("foo"); // OK, removing this `clone()` will change the behavior. + s.push_str("bar"); + assert_eq!(s, "bar"); + + let t = Some(s); + // OK + if let Some(x) = t.clone() { + println!("{}", x); + } + if let Some(x) = t { + println!("{}", x); + } +} + +#[allow(clippy::clone_on_copy)] +fn issue_5405() { + let a: [String; 1] = [String::from("foo")]; + let _b: String = a[0].clone(); + + let c: [usize; 2] = [2, 3]; + let _d: usize = c[1].clone(); +} diff --git a/src/tools/clippy/tests/ui/redundant_clone.rs b/src/tools/clippy/tests/ui/redundant_clone.rs new file mode 100644 index 00000000000..839747b131d --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_clone.rs @@ -0,0 +1,172 @@ +// run-rustfix +// rustfix-only-machine-applicable + +use std::ffi::OsString; +use std::path::Path; + +fn main() { + let _s = ["lorem", "ipsum"].join(" ").to_string(); + + let s = String::from("foo"); + let _s = s.clone(); + + let s = String::from("foo"); + let _s = s.to_string(); + + let s = String::from("foo"); + let _s = s.to_owned(); + + let _s = Path::new("/a/b/").join("c").to_owned(); + + let _s = Path::new("/a/b/").join("c").to_path_buf(); + + let _s = OsString::new().to_owned(); + + let _s = OsString::new().to_os_string(); + + // Check that lint level works + #[allow(clippy::redundant_clone)] + let _s = String::new().to_string(); + + let tup = (String::from("foo"),); + let _t = tup.0.clone(); + + let tup_ref = &(String::from("foo"),); + let _s = tup_ref.0.clone(); // this `.clone()` cannot be removed + + { + let x = String::new(); + let y = &x; + + let _x = x.clone(); // ok; `x` is borrowed by `y` + + let _ = y.len(); + } + + let x = (String::new(),); + let _ = Some(String::new()).unwrap_or_else(|| x.0.clone()); // ok; closure borrows `x` + + with_branch(Alpha, true); + cannot_double_move(Alpha); + cannot_move_from_type_with_drop(); + borrower_propagation(); + not_consumed(); + issue_5405(); +} + +#[derive(Clone)] +struct Alpha; +fn with_branch(a: Alpha, b: bool) -> (Alpha, Alpha) { + if b { + (a.clone(), a.clone()) + } else { + (Alpha, a) + } +} + +fn cannot_double_move(a: Alpha) -> (Alpha, Alpha) { + (a.clone(), a) +} + +struct TypeWithDrop { + x: String, +} + +impl Drop for TypeWithDrop { + fn drop(&mut self) {} +} + +fn cannot_move_from_type_with_drop() -> String { + let s = TypeWithDrop { x: String::new() }; + s.x.clone() // removing this `clone()` summons E0509 +} + +fn borrower_propagation() { + let s = String::new(); + let t = String::new(); + + { + fn b() -> bool { + unimplemented!() + } + let _u = if b() { &s } else { &t }; + + // ok; `s` and `t` are possibly borrowed + let _s = s.clone(); + let _t = t.clone(); + } + + { + let _u = || s.len(); + let _v = [&t; 32]; + let _s = s.clone(); // ok + let _t = t.clone(); // ok + } + + { + let _u = { + let u = Some(&s); + let _ = s.clone(); // ok + u + }; + let _s = s.clone(); // ok + } + + { + use std::convert::identity as id; + let _u = id(id(&s)); + let _s = s.clone(); // ok, `u` borrows `s` + } + + let _s = s.clone(); + let _t = t.clone(); + + #[derive(Clone)] + struct Foo { + x: usize, + } + + { + let f = Foo { x: 123 }; + let _x = Some(f.x); + let _f = f.clone(); + } + + { + let f = Foo { x: 123 }; + let _x = &f.x; + let _f = f.clone(); // ok + } +} + +fn not_consumed() { + let x = std::path::PathBuf::from("home"); + let y = x.clone().join("matthias"); + // join() creates a new owned PathBuf, does not take a &mut to x variable, thus the .clone() is + // redundant. (It also does not consume the PathBuf) + + println!("x: {:?}, y: {:?}", x, y); + + let mut s = String::new(); + s.clone().push_str("foo"); // OK, removing this `clone()` will change the behavior. + s.push_str("bar"); + assert_eq!(s, "bar"); + + let t = Some(s); + // OK + if let Some(x) = t.clone() { + println!("{}", x); + } + if let Some(x) = t { + println!("{}", x); + } +} + +#[allow(clippy::clone_on_copy)] +fn issue_5405() { + let a: [String; 1] = [String::from("foo")]; + let _b: String = a[0].clone(); + + let c: [usize; 2] = [2, 3]; + let _d: usize = c[1].clone(); +} diff --git a/src/tools/clippy/tests/ui/redundant_clone.stderr b/src/tools/clippy/tests/ui/redundant_clone.stderr new file mode 100644 index 00000000000..eced198283c --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_clone.stderr @@ -0,0 +1,171 @@ +error: redundant clone + --> $DIR/redundant_clone.rs:8:42 + | +LL | let _s = ["lorem", "ipsum"].join(" ").to_string(); + | ^^^^^^^^^^^^ help: remove this + | + = note: `-D clippy::redundant-clone` implied by `-D warnings` +note: this value is dropped without further use + --> $DIR/redundant_clone.rs:8:14 + | +LL | let _s = ["lorem", "ipsum"].join(" ").to_string(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: redundant clone + --> $DIR/redundant_clone.rs:11:15 + | +LL | let _s = s.clone(); + | ^^^^^^^^ help: remove this + | +note: this value is dropped without further use + --> $DIR/redundant_clone.rs:11:14 + | +LL | let _s = s.clone(); + | ^ + +error: redundant clone + --> $DIR/redundant_clone.rs:14:15 + | +LL | let _s = s.to_string(); + | ^^^^^^^^^^^^ help: remove this + | +note: this value is dropped without further use + --> $DIR/redundant_clone.rs:14:14 + | +LL | let _s = s.to_string(); + | ^ + +error: redundant clone + --> $DIR/redundant_clone.rs:17:15 + | +LL | let _s = s.to_owned(); + | ^^^^^^^^^^^ help: remove this + | +note: this value is dropped without further use + --> $DIR/redundant_clone.rs:17:14 + | +LL | let _s = s.to_owned(); + | ^ + +error: redundant clone + --> $DIR/redundant_clone.rs:19:42 + | +LL | let _s = Path::new("/a/b/").join("c").to_owned(); + | ^^^^^^^^^^^ help: remove this + | +note: this value is dropped without further use + --> $DIR/redundant_clone.rs:19:14 + | +LL | let _s = Path::new("/a/b/").join("c").to_owned(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: redundant clone + --> $DIR/redundant_clone.rs:21:42 + | +LL | let _s = Path::new("/a/b/").join("c").to_path_buf(); + | ^^^^^^^^^^^^^^ help: remove this + | +note: this value is dropped without further use + --> $DIR/redundant_clone.rs:21:14 + | +LL | let _s = Path::new("/a/b/").join("c").to_path_buf(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: redundant clone + --> $DIR/redundant_clone.rs:23:29 + | +LL | let _s = OsString::new().to_owned(); + | ^^^^^^^^^^^ help: remove this + | +note: this value is dropped without further use + --> $DIR/redundant_clone.rs:23:14 + | +LL | let _s = OsString::new().to_owned(); + | ^^^^^^^^^^^^^^^ + +error: redundant clone + --> $DIR/redundant_clone.rs:25:29 + | +LL | let _s = OsString::new().to_os_string(); + | ^^^^^^^^^^^^^^^ help: remove this + | +note: this value is dropped without further use + --> $DIR/redundant_clone.rs:25:14 + | +LL | let _s = OsString::new().to_os_string(); + | ^^^^^^^^^^^^^^^ + +error: redundant clone + --> $DIR/redundant_clone.rs:32:19 + | +LL | let _t = tup.0.clone(); + | ^^^^^^^^ help: remove this + | +note: this value is dropped without further use + --> $DIR/redundant_clone.rs:32:14 + | +LL | let _t = tup.0.clone(); + | ^^^^^ + +error: redundant clone + --> $DIR/redundant_clone.rs:61:22 + | +LL | (a.clone(), a.clone()) + | ^^^^^^^^ help: remove this + | +note: this value is dropped without further use + --> $DIR/redundant_clone.rs:61:21 + | +LL | (a.clone(), a.clone()) + | ^ + +error: redundant clone + --> $DIR/redundant_clone.rs:121:15 + | +LL | let _s = s.clone(); + | ^^^^^^^^ help: remove this + | +note: this value is dropped without further use + --> $DIR/redundant_clone.rs:121:14 + | +LL | let _s = s.clone(); + | ^ + +error: redundant clone + --> $DIR/redundant_clone.rs:122:15 + | +LL | let _t = t.clone(); + | ^^^^^^^^ help: remove this + | +note: this value is dropped without further use + --> $DIR/redundant_clone.rs:122:14 + | +LL | let _t = t.clone(); + | ^ + +error: redundant clone + --> $DIR/redundant_clone.rs:132:19 + | +LL | let _f = f.clone(); + | ^^^^^^^^ help: remove this + | +note: this value is dropped without further use + --> $DIR/redundant_clone.rs:132:18 + | +LL | let _f = f.clone(); + | ^ + +error: redundant clone + --> $DIR/redundant_clone.rs:144:14 + | +LL | let y = x.clone().join("matthias"); + | ^^^^^^^^ help: remove this + | +note: cloned value is neither consumed nor mutated + --> $DIR/redundant_clone.rs:144:13 + | +LL | let y = x.clone().join("matthias"); + | ^^^^^^^^^ + +error: aborting due to 14 previous errors + diff --git a/src/tools/clippy/tests/ui/redundant_closure_call.rs b/src/tools/clippy/tests/ui/redundant_closure_call.rs new file mode 100644 index 00000000000..bacd67db7c3 --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_closure_call.rs @@ -0,0 +1,23 @@ +// non rustfixable, see redundant_closure_call_fixable.rs + +#![warn(clippy::redundant_closure_call)] + +fn main() { + let mut i = 1; + let mut k = (|m| m + 1)(i); + + k = (|a, b| a * b)(1, 5); + + let closure = || 32; + i = closure(); + + let closure = |i| i + 1; + i = closure(3); + + i = closure(4); + + #[allow(clippy::needless_return)] + (|| return 2)(); + (|| -> Option<i32> { None? })(); + (|| -> Result<i32, i32> { Err(2)? })(); +} diff --git a/src/tools/clippy/tests/ui/redundant_closure_call.stderr b/src/tools/clippy/tests/ui/redundant_closure_call.stderr new file mode 100644 index 00000000000..68c1416bb6b --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_closure_call.stderr @@ -0,0 +1,28 @@ +error: Closure called just once immediately after it was declared + --> $DIR/redundant_closure_call.rs:12:5 + | +LL | i = closure(); + | ^^^^^^^^^^^^^ + | + = note: `-D clippy::redundant-closure-call` implied by `-D warnings` + +error: Closure called just once immediately after it was declared + --> $DIR/redundant_closure_call.rs:15:5 + | +LL | i = closure(3); + | ^^^^^^^^^^^^^^ + +error: Try not to call a closure in the expression where it is declared. + --> $DIR/redundant_closure_call.rs:7:17 + | +LL | let mut k = (|m| m + 1)(i); + | ^^^^^^^^^^^^^^ + +error: Try not to call a closure in the expression where it is declared. + --> $DIR/redundant_closure_call.rs:9:9 + | +LL | k = (|a, b| a * b)(1, 5); + | ^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.fixed b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.fixed new file mode 100644 index 00000000000..0abca6fca06 --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.fixed @@ -0,0 +1,8 @@ +// run-rustfix + +#![warn(clippy::redundant_closure_call)] +#![allow(unused)] + +fn main() { + let a = 42; +} diff --git a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.rs b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.rs new file mode 100644 index 00000000000..f8b9d37a5cc --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.rs @@ -0,0 +1,8 @@ +// run-rustfix + +#![warn(clippy::redundant_closure_call)] +#![allow(unused)] + +fn main() { + let a = (|| 42)(); +} diff --git a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.stderr b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.stderr new file mode 100644 index 00000000000..e7737f9dd85 --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.stderr @@ -0,0 +1,10 @@ +error: Try not to call a closure in the expression where it is declared. + --> $DIR/redundant_closure_call_fixable.rs:7:13 + | +LL | let a = (|| 42)(); + | ^^^^^^^^^ help: Try doing something like: : `42` + | + = note: `-D clippy::redundant-closure-call` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/redundant_field_names.fixed b/src/tools/clippy/tests/ui/redundant_field_names.fixed new file mode 100644 index 00000000000..5b4b8eeedd4 --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_field_names.fixed @@ -0,0 +1,71 @@ +// run-rustfix +#![warn(clippy::redundant_field_names)] +#![allow(clippy::no_effect, dead_code, unused_variables)] + +#[macro_use] +extern crate derive_new; + +use std::ops::{Range, RangeFrom, RangeInclusive, RangeTo, RangeToInclusive}; + +mod foo { + pub const BAR: u8 = 0; +} + +struct Person { + gender: u8, + age: u8, + name: u8, + buzz: u64, + foo: u8, +} + +#[derive(new)] +pub struct S { + v: String, +} + +fn main() { + let gender: u8 = 42; + let age = 0; + let fizz: u64 = 0; + let name: u8 = 0; + + let me = Person { + gender, + age, + + name, //should be ok + buzz: fizz, //should be ok + foo: foo::BAR, //should be ok + }; + + // Range expressions + let (start, end) = (0, 0); + + let _ = start..; + let _ = ..end; + let _ = start..end; + + let _ = ..=end; + let _ = start..=end; + + // Issue #2799 + let _: Vec<_> = (start..end).collect(); + + // hand-written Range family structs are linted + let _ = RangeFrom { start }; + let _ = RangeTo { end }; + let _ = Range { start, end }; + let _ = RangeInclusive::new(start, end); + let _ = RangeToInclusive { end }; +} + +fn issue_3476() { + fn foo<T>() {} + + struct S { + foo: fn(), + } + + S { foo: foo::<i32> }; +} diff --git a/src/tools/clippy/tests/ui/redundant_field_names.rs b/src/tools/clippy/tests/ui/redundant_field_names.rs new file mode 100644 index 00000000000..3f97b80c568 --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_field_names.rs @@ -0,0 +1,71 @@ +// run-rustfix +#![warn(clippy::redundant_field_names)] +#![allow(clippy::no_effect, dead_code, unused_variables)] + +#[macro_use] +extern crate derive_new; + +use std::ops::{Range, RangeFrom, RangeInclusive, RangeTo, RangeToInclusive}; + +mod foo { + pub const BAR: u8 = 0; +} + +struct Person { + gender: u8, + age: u8, + name: u8, + buzz: u64, + foo: u8, +} + +#[derive(new)] +pub struct S { + v: String, +} + +fn main() { + let gender: u8 = 42; + let age = 0; + let fizz: u64 = 0; + let name: u8 = 0; + + let me = Person { + gender: gender, + age: age, + + name, //should be ok + buzz: fizz, //should be ok + foo: foo::BAR, //should be ok + }; + + // Range expressions + let (start, end) = (0, 0); + + let _ = start..; + let _ = ..end; + let _ = start..end; + + let _ = ..=end; + let _ = start..=end; + + // Issue #2799 + let _: Vec<_> = (start..end).collect(); + + // hand-written Range family structs are linted + let _ = RangeFrom { start: start }; + let _ = RangeTo { end: end }; + let _ = Range { start: start, end: end }; + let _ = RangeInclusive::new(start, end); + let _ = RangeToInclusive { end: end }; +} + +fn issue_3476() { + fn foo<T>() {} + + struct S { + foo: fn(), + } + + S { foo: foo::<i32> }; +} diff --git a/src/tools/clippy/tests/ui/redundant_field_names.stderr b/src/tools/clippy/tests/ui/redundant_field_names.stderr new file mode 100644 index 00000000000..7976292df22 --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_field_names.stderr @@ -0,0 +1,46 @@ +error: redundant field names in struct initialization + --> $DIR/redundant_field_names.rs:34:9 + | +LL | gender: gender, + | ^^^^^^^^^^^^^^ help: replace it with: `gender` + | + = note: `-D clippy::redundant-field-names` implied by `-D warnings` + +error: redundant field names in struct initialization + --> $DIR/redundant_field_names.rs:35:9 + | +LL | age: age, + | ^^^^^^^^ help: replace it with: `age` + +error: redundant field names in struct initialization + --> $DIR/redundant_field_names.rs:56:25 + | +LL | let _ = RangeFrom { start: start }; + | ^^^^^^^^^^^^ help: replace it with: `start` + +error: redundant field names in struct initialization + --> $DIR/redundant_field_names.rs:57:23 + | +LL | let _ = RangeTo { end: end }; + | ^^^^^^^^ help: replace it with: `end` + +error: redundant field names in struct initialization + --> $DIR/redundant_field_names.rs:58:21 + | +LL | let _ = Range { start: start, end: end }; + | ^^^^^^^^^^^^ help: replace it with: `start` + +error: redundant field names in struct initialization + --> $DIR/redundant_field_names.rs:58:35 + | +LL | let _ = Range { start: start, end: end }; + | ^^^^^^^^ help: replace it with: `end` + +error: redundant field names in struct initialization + --> $DIR/redundant_field_names.rs:60:32 + | +LL | let _ = RangeToInclusive { end: end }; + | ^^^^^^^^ help: replace it with: `end` + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching.fixed b/src/tools/clippy/tests/ui/redundant_pattern_matching.fixed new file mode 100644 index 00000000000..fc8cb0e747c --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching.fixed @@ -0,0 +1,119 @@ +// run-rustfix + +#![warn(clippy::all)] +#![warn(clippy::redundant_pattern_matching)] +#![allow(clippy::unit_arg, unused_must_use, clippy::needless_bool, deprecated)] + +fn main() { + if Ok::<i32, i32>(42).is_ok() {} + + if Err::<i32, i32>(42).is_err() {} + + if None::<()>.is_none() {} + + if Some(42).is_some() {} + + if Some(42).is_some() { + foo(); + } else { + bar(); + } + + while Some(42).is_some() {} + + while Some(42).is_none() {} + + while None::<()>.is_none() {} + + while Ok::<i32, i32>(10).is_ok() {} + + while Ok::<i32, i32>(10).is_err() {} + + let mut v = vec![1, 2, 3]; + while v.pop().is_some() { + foo(); + } + + if Ok::<i32, i32>(42).is_ok() {} + + if Err::<i32, i32>(42).is_err() {} + + if None::<i32>.is_none() {} + + if Some(42).is_some() {} + + if let Ok(x) = Ok::<i32, i32>(42) { + println!("{}", x); + } + + Ok::<i32, i32>(42).is_ok(); + + Ok::<i32, i32>(42).is_err(); + + Err::<i32, i32>(42).is_err(); + + Err::<i32, i32>(42).is_ok(); + + Some(42).is_some(); + + None::<()>.is_none(); + + let _ = None::<()>.is_none(); + + let _ = if Ok::<usize, ()>(4).is_ok() { true } else { false }; + + let opt = Some(false); + let x = if opt.is_some() { true } else { false }; + takes_bool(x); + + issue5504(); + + let _ = if gen_opt().is_some() { + 1 + } else if gen_opt().is_none() { + 2 + } else if gen_res().is_ok() { + 3 + } else if gen_res().is_err() { + 4 + } else { + 5 + }; +} + +fn gen_opt() -> Option<()> { + None +} + +fn gen_res() -> Result<(), ()> { + Ok(()) +} + +fn takes_bool(_: bool) {} + +fn foo() {} + +fn bar() {} + +macro_rules! m { + () => { + Some(42u32) + }; +} + +fn issue5504() { + fn result_opt() -> Result<Option<i32>, i32> { + Err(42) + } + + fn try_result_opt() -> Result<i32, i32> { + while r#try!(result_opt()).is_some() {} + if r#try!(result_opt()).is_some() {} + Ok(42) + } + + try_result_opt(); + + if m!().is_some() {} + while m!().is_some() {} +} diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching.rs b/src/tools/clippy/tests/ui/redundant_pattern_matching.rs new file mode 100644 index 00000000000..51912dade03 --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching.rs @@ -0,0 +1,140 @@ +// run-rustfix + +#![warn(clippy::all)] +#![warn(clippy::redundant_pattern_matching)] +#![allow(clippy::unit_arg, unused_must_use, clippy::needless_bool, deprecated)] + +fn main() { + if let Ok(_) = Ok::<i32, i32>(42) {} + + if let Err(_) = Err::<i32, i32>(42) {} + + if let None = None::<()> {} + + if let Some(_) = Some(42) {} + + if let Some(_) = Some(42) { + foo(); + } else { + bar(); + } + + while let Some(_) = Some(42) {} + + while let None = Some(42) {} + + while let None = None::<()> {} + + while let Ok(_) = Ok::<i32, i32>(10) {} + + while let Err(_) = Ok::<i32, i32>(10) {} + + let mut v = vec![1, 2, 3]; + while let Some(_) = v.pop() { + foo(); + } + + if Ok::<i32, i32>(42).is_ok() {} + + if Err::<i32, i32>(42).is_err() {} + + if None::<i32>.is_none() {} + + if Some(42).is_some() {} + + if let Ok(x) = Ok::<i32, i32>(42) { + println!("{}", x); + } + + match Ok::<i32, i32>(42) { + Ok(_) => true, + Err(_) => false, + }; + + match Ok::<i32, i32>(42) { + Ok(_) => false, + Err(_) => true, + }; + + match Err::<i32, i32>(42) { + Ok(_) => false, + Err(_) => true, + }; + + match Err::<i32, i32>(42) { + Ok(_) => true, + Err(_) => false, + }; + + match Some(42) { + Some(_) => true, + None => false, + }; + + match None::<()> { + Some(_) => false, + None => true, + }; + + let _ = match None::<()> { + Some(_) => false, + None => true, + }; + + let _ = if let Ok(_) = Ok::<usize, ()>(4) { true } else { false }; + + let opt = Some(false); + let x = if let Some(_) = opt { true } else { false }; + takes_bool(x); + + issue5504(); + + let _ = if let Some(_) = gen_opt() { + 1 + } else if let None = gen_opt() { + 2 + } else if let Ok(_) = gen_res() { + 3 + } else if let Err(_) = gen_res() { + 4 + } else { + 5 + }; +} + +fn gen_opt() -> Option<()> { + None +} + +fn gen_res() -> Result<(), ()> { + Ok(()) +} + +fn takes_bool(_: bool) {} + +fn foo() {} + +fn bar() {} + +macro_rules! m { + () => { + Some(42u32) + }; +} + +fn issue5504() { + fn result_opt() -> Result<Option<i32>, i32> { + Err(42) + } + + fn try_result_opt() -> Result<i32, i32> { + while let Some(_) = r#try!(result_opt()) {} + if let Some(_) = r#try!(result_opt()) {} + Ok(42) + } + + try_result_opt(); + + if let Some(_) = m!() {} + while let Some(_) = m!() {} +} diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching.stderr b/src/tools/clippy/tests/ui/redundant_pattern_matching.stderr new file mode 100644 index 00000000000..b58deb7954e --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching.stderr @@ -0,0 +1,194 @@ +error: redundant pattern matching, consider using `is_ok()` + --> $DIR/redundant_pattern_matching.rs:8:12 + | +LL | if let Ok(_) = Ok::<i32, i32>(42) {} + | -------^^^^^--------------------- help: try this: `if Ok::<i32, i32>(42).is_ok()` + | + = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` + +error: redundant pattern matching, consider using `is_err()` + --> $DIR/redundant_pattern_matching.rs:10:12 + | +LL | if let Err(_) = Err::<i32, i32>(42) {} + | -------^^^^^^---------------------- help: try this: `if Err::<i32, i32>(42).is_err()` + +error: redundant pattern matching, consider using `is_none()` + --> $DIR/redundant_pattern_matching.rs:12:12 + | +LL | if let None = None::<()> {} + | -------^^^^------------- help: try this: `if None::<()>.is_none()` + +error: redundant pattern matching, consider using `is_some()` + --> $DIR/redundant_pattern_matching.rs:14:12 + | +LL | if let Some(_) = Some(42) {} + | -------^^^^^^^----------- help: try this: `if Some(42).is_some()` + +error: redundant pattern matching, consider using `is_some()` + --> $DIR/redundant_pattern_matching.rs:16:12 + | +LL | if let Some(_) = Some(42) { + | -------^^^^^^^----------- help: try this: `if Some(42).is_some()` + +error: redundant pattern matching, consider using `is_some()` + --> $DIR/redundant_pattern_matching.rs:22:15 + | +LL | while let Some(_) = Some(42) {} + | ----------^^^^^^^----------- help: try this: `while Some(42).is_some()` + +error: redundant pattern matching, consider using `is_none()` + --> $DIR/redundant_pattern_matching.rs:24:15 + | +LL | while let None = Some(42) {} + | ----------^^^^----------- help: try this: `while Some(42).is_none()` + +error: redundant pattern matching, consider using `is_none()` + --> $DIR/redundant_pattern_matching.rs:26:15 + | +LL | while let None = None::<()> {} + | ----------^^^^------------- help: try this: `while None::<()>.is_none()` + +error: redundant pattern matching, consider using `is_ok()` + --> $DIR/redundant_pattern_matching.rs:28:15 + | +LL | while let Ok(_) = Ok::<i32, i32>(10) {} + | ----------^^^^^--------------------- help: try this: `while Ok::<i32, i32>(10).is_ok()` + +error: redundant pattern matching, consider using `is_err()` + --> $DIR/redundant_pattern_matching.rs:30:15 + | +LL | while let Err(_) = Ok::<i32, i32>(10) {} + | ----------^^^^^^--------------------- help: try this: `while Ok::<i32, i32>(10).is_err()` + +error: redundant pattern matching, consider using `is_some()` + --> $DIR/redundant_pattern_matching.rs:33:15 + | +LL | while let Some(_) = v.pop() { + | ----------^^^^^^^---------- help: try this: `while v.pop().is_some()` + +error: redundant pattern matching, consider using `is_ok()` + --> $DIR/redundant_pattern_matching.rs:49:5 + | +LL | / match Ok::<i32, i32>(42) { +LL | | Ok(_) => true, +LL | | Err(_) => false, +LL | | }; + | |_____^ help: try this: `Ok::<i32, i32>(42).is_ok()` + +error: redundant pattern matching, consider using `is_err()` + --> $DIR/redundant_pattern_matching.rs:54:5 + | +LL | / match Ok::<i32, i32>(42) { +LL | | Ok(_) => false, +LL | | Err(_) => true, +LL | | }; + | |_____^ help: try this: `Ok::<i32, i32>(42).is_err()` + +error: redundant pattern matching, consider using `is_err()` + --> $DIR/redundant_pattern_matching.rs:59:5 + | +LL | / match Err::<i32, i32>(42) { +LL | | Ok(_) => false, +LL | | Err(_) => true, +LL | | }; + | |_____^ help: try this: `Err::<i32, i32>(42).is_err()` + +error: redundant pattern matching, consider using `is_ok()` + --> $DIR/redundant_pattern_matching.rs:64:5 + | +LL | / match Err::<i32, i32>(42) { +LL | | Ok(_) => true, +LL | | Err(_) => false, +LL | | }; + | |_____^ help: try this: `Err::<i32, i32>(42).is_ok()` + +error: redundant pattern matching, consider using `is_some()` + --> $DIR/redundant_pattern_matching.rs:69:5 + | +LL | / match Some(42) { +LL | | Some(_) => true, +LL | | None => false, +LL | | }; + | |_____^ help: try this: `Some(42).is_some()` + +error: redundant pattern matching, consider using `is_none()` + --> $DIR/redundant_pattern_matching.rs:74:5 + | +LL | / match None::<()> { +LL | | Some(_) => false, +LL | | None => true, +LL | | }; + | |_____^ help: try this: `None::<()>.is_none()` + +error: redundant pattern matching, consider using `is_none()` + --> $DIR/redundant_pattern_matching.rs:79:13 + | +LL | let _ = match None::<()> { + | _____________^ +LL | | Some(_) => false, +LL | | None => true, +LL | | }; + | |_____^ help: try this: `None::<()>.is_none()` + +error: redundant pattern matching, consider using `is_ok()` + --> $DIR/redundant_pattern_matching.rs:84:20 + | +LL | let _ = if let Ok(_) = Ok::<usize, ()>(4) { true } else { false }; + | -------^^^^^--------------------- help: try this: `if Ok::<usize, ()>(4).is_ok()` + +error: redundant pattern matching, consider using `is_some()` + --> $DIR/redundant_pattern_matching.rs:87:20 + | +LL | let x = if let Some(_) = opt { true } else { false }; + | -------^^^^^^^------ help: try this: `if opt.is_some()` + +error: redundant pattern matching, consider using `is_some()` + --> $DIR/redundant_pattern_matching.rs:92:20 + | +LL | let _ = if let Some(_) = gen_opt() { + | -------^^^^^^^------------ help: try this: `if gen_opt().is_some()` + +error: redundant pattern matching, consider using `is_none()` + --> $DIR/redundant_pattern_matching.rs:94:19 + | +LL | } else if let None = gen_opt() { + | -------^^^^------------ help: try this: `if gen_opt().is_none()` + +error: redundant pattern matching, consider using `is_ok()` + --> $DIR/redundant_pattern_matching.rs:96:19 + | +LL | } else if let Ok(_) = gen_res() { + | -------^^^^^------------ help: try this: `if gen_res().is_ok()` + +error: redundant pattern matching, consider using `is_err()` + --> $DIR/redundant_pattern_matching.rs:98:19 + | +LL | } else if let Err(_) = gen_res() { + | -------^^^^^^------------ help: try this: `if gen_res().is_err()` + +error: redundant pattern matching, consider using `is_some()` + --> $DIR/redundant_pattern_matching.rs:131:19 + | +LL | while let Some(_) = r#try!(result_opt()) {} + | ----------^^^^^^^----------------------- help: try this: `while r#try!(result_opt()).is_some()` + +error: redundant pattern matching, consider using `is_some()` + --> $DIR/redundant_pattern_matching.rs:132:16 + | +LL | if let Some(_) = r#try!(result_opt()) {} + | -------^^^^^^^----------------------- help: try this: `if r#try!(result_opt()).is_some()` + +error: redundant pattern matching, consider using `is_some()` + --> $DIR/redundant_pattern_matching.rs:138:12 + | +LL | if let Some(_) = m!() {} + | -------^^^^^^^------- help: try this: `if m!().is_some()` + +error: redundant pattern matching, consider using `is_some()` + --> $DIR/redundant_pattern_matching.rs:139:15 + | +LL | while let Some(_) = m!() {} + | ----------^^^^^^^------- help: try this: `while m!().is_some()` + +error: aborting due to 28 previous errors + diff --git a/src/tools/clippy/tests/ui/redundant_pub_crate.fixed b/src/tools/clippy/tests/ui/redundant_pub_crate.fixed new file mode 100644 index 00000000000..25f2fd061b8 --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_pub_crate.fixed @@ -0,0 +1,107 @@ +// run-rustfix +#![allow(dead_code)] +#![warn(clippy::redundant_pub_crate)] + +mod m1 { + fn f() {} + pub fn g() {} // private due to m1 + pub fn h() {} + + mod m1_1 { + fn f() {} + pub fn g() {} // private due to m1_1 and m1 + pub fn h() {} + } + + pub mod m1_2 { + // ^ private due to m1 + fn f() {} + pub fn g() {} // private due to m1_2 and m1 + pub fn h() {} + } + + pub mod m1_3 { + fn f() {} + pub fn g() {} // private due to m1 + pub fn h() {} + } +} + +pub(crate) mod m2 { + fn f() {} + pub fn g() {} // already crate visible due to m2 + pub fn h() {} + + mod m2_1 { + fn f() {} + pub fn g() {} // private due to m2_1 + pub fn h() {} + } + + pub mod m2_2 { + // ^ already crate visible due to m2 + fn f() {} + pub fn g() {} // already crate visible due to m2_2 and m2 + pub fn h() {} + } + + pub mod m2_3 { + fn f() {} + pub fn g() {} // already crate visible due to m2 + pub fn h() {} + } +} + +pub mod m3 { + fn f() {} + pub(crate) fn g() {} // ok: m3 is exported + pub fn h() {} + + mod m3_1 { + fn f() {} + pub fn g() {} // private due to m3_1 + pub fn h() {} + } + + pub(crate) mod m3_2 { + // ^ ok + fn f() {} + pub fn g() {} // already crate visible due to m3_2 + pub fn h() {} + } + + pub mod m3_3 { + fn f() {} + pub(crate) fn g() {} // ok: m3 and m3_3 are exported + pub fn h() {} + } +} + +mod m4 { + fn f() {} + pub fn g() {} // private: not re-exported by `pub use m4::*` + pub fn h() {} + + mod m4_1 { + fn f() {} + pub fn g() {} // private due to m4_1 + pub fn h() {} + } + + pub mod m4_2 { + // ^ private: not re-exported by `pub use m4::*` + fn f() {} + pub fn g() {} // private due to m4_2 + pub fn h() {} + } + + pub mod m4_3 { + fn f() {} + pub(crate) fn g() {} // ok: m4_3 is re-exported by `pub use m4::*` + pub fn h() {} + } +} + +pub use m4::*; + +fn main() {} diff --git a/src/tools/clippy/tests/ui/redundant_pub_crate.rs b/src/tools/clippy/tests/ui/redundant_pub_crate.rs new file mode 100644 index 00000000000..616286b4f39 --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_pub_crate.rs @@ -0,0 +1,107 @@ +// run-rustfix +#![allow(dead_code)] +#![warn(clippy::redundant_pub_crate)] + +mod m1 { + fn f() {} + pub(crate) fn g() {} // private due to m1 + pub fn h() {} + + mod m1_1 { + fn f() {} + pub(crate) fn g() {} // private due to m1_1 and m1 + pub fn h() {} + } + + pub(crate) mod m1_2 { + // ^ private due to m1 + fn f() {} + pub(crate) fn g() {} // private due to m1_2 and m1 + pub fn h() {} + } + + pub mod m1_3 { + fn f() {} + pub(crate) fn g() {} // private due to m1 + pub fn h() {} + } +} + +pub(crate) mod m2 { + fn f() {} + pub(crate) fn g() {} // already crate visible due to m2 + pub fn h() {} + + mod m2_1 { + fn f() {} + pub(crate) fn g() {} // private due to m2_1 + pub fn h() {} + } + + pub(crate) mod m2_2 { + // ^ already crate visible due to m2 + fn f() {} + pub(crate) fn g() {} // already crate visible due to m2_2 and m2 + pub fn h() {} + } + + pub mod m2_3 { + fn f() {} + pub(crate) fn g() {} // already crate visible due to m2 + pub fn h() {} + } +} + +pub mod m3 { + fn f() {} + pub(crate) fn g() {} // ok: m3 is exported + pub fn h() {} + + mod m3_1 { + fn f() {} + pub(crate) fn g() {} // private due to m3_1 + pub fn h() {} + } + + pub(crate) mod m3_2 { + // ^ ok + fn f() {} + pub(crate) fn g() {} // already crate visible due to m3_2 + pub fn h() {} + } + + pub mod m3_3 { + fn f() {} + pub(crate) fn g() {} // ok: m3 and m3_3 are exported + pub fn h() {} + } +} + +mod m4 { + fn f() {} + pub(crate) fn g() {} // private: not re-exported by `pub use m4::*` + pub fn h() {} + + mod m4_1 { + fn f() {} + pub(crate) fn g() {} // private due to m4_1 + pub fn h() {} + } + + pub(crate) mod m4_2 { + // ^ private: not re-exported by `pub use m4::*` + fn f() {} + pub(crate) fn g() {} // private due to m4_2 + pub fn h() {} + } + + pub mod m4_3 { + fn f() {} + pub(crate) fn g() {} // ok: m4_3 is re-exported by `pub use m4::*` + pub fn h() {} + } +} + +pub use m4::*; + +fn main() {} diff --git a/src/tools/clippy/tests/ui/redundant_pub_crate.stderr b/src/tools/clippy/tests/ui/redundant_pub_crate.stderr new file mode 100644 index 00000000000..6fccdaa4e20 --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_pub_crate.stderr @@ -0,0 +1,132 @@ +error: pub(crate) function inside private module + --> $DIR/redundant_pub_crate.rs:7:5 + | +LL | pub(crate) fn g() {} // private due to m1 + | ----------^^^^^ + | | + | help: consider using: `pub` + | + = note: `-D clippy::redundant-pub-crate` implied by `-D warnings` + +error: pub(crate) function inside private module + --> $DIR/redundant_pub_crate.rs:12:9 + | +LL | pub(crate) fn g() {} // private due to m1_1 and m1 + | ----------^^^^^ + | | + | help: consider using: `pub` + +error: pub(crate) module inside private module + --> $DIR/redundant_pub_crate.rs:16:5 + | +LL | pub(crate) mod m1_2 { + | ----------^^^^^^^^^ + | | + | help: consider using: `pub` + +error: pub(crate) function inside private module + --> $DIR/redundant_pub_crate.rs:19:9 + | +LL | pub(crate) fn g() {} // private due to m1_2 and m1 + | ----------^^^^^ + | | + | help: consider using: `pub` + +error: pub(crate) function inside private module + --> $DIR/redundant_pub_crate.rs:25:9 + | +LL | pub(crate) fn g() {} // private due to m1 + | ----------^^^^^ + | | + | help: consider using: `pub` + +error: pub(crate) function inside private module + --> $DIR/redundant_pub_crate.rs:32:5 + | +LL | pub(crate) fn g() {} // already crate visible due to m2 + | ----------^^^^^ + | | + | help: consider using: `pub` + +error: pub(crate) function inside private module + --> $DIR/redundant_pub_crate.rs:37:9 + | +LL | pub(crate) fn g() {} // private due to m2_1 + | ----------^^^^^ + | | + | help: consider using: `pub` + +error: pub(crate) module inside private module + --> $DIR/redundant_pub_crate.rs:41:5 + | +LL | pub(crate) mod m2_2 { + | ----------^^^^^^^^^ + | | + | help: consider using: `pub` + +error: pub(crate) function inside private module + --> $DIR/redundant_pub_crate.rs:44:9 + | +LL | pub(crate) fn g() {} // already crate visible due to m2_2 and m2 + | ----------^^^^^ + | | + | help: consider using: `pub` + +error: pub(crate) function inside private module + --> $DIR/redundant_pub_crate.rs:50:9 + | +LL | pub(crate) fn g() {} // already crate visible due to m2 + | ----------^^^^^ + | | + | help: consider using: `pub` + +error: pub(crate) function inside private module + --> $DIR/redundant_pub_crate.rs:62:9 + | +LL | pub(crate) fn g() {} // private due to m3_1 + | ----------^^^^^ + | | + | help: consider using: `pub` + +error: pub(crate) function inside private module + --> $DIR/redundant_pub_crate.rs:69:9 + | +LL | pub(crate) fn g() {} // already crate visible due to m3_2 + | ----------^^^^^ + | | + | help: consider using: `pub` + +error: pub(crate) function inside private module + --> $DIR/redundant_pub_crate.rs:82:5 + | +LL | pub(crate) fn g() {} // private: not re-exported by `pub use m4::*` + | ----------^^^^^ + | | + | help: consider using: `pub` + +error: pub(crate) function inside private module + --> $DIR/redundant_pub_crate.rs:87:9 + | +LL | pub(crate) fn g() {} // private due to m4_1 + | ----------^^^^^ + | | + | help: consider using: `pub` + +error: pub(crate) module inside private module + --> $DIR/redundant_pub_crate.rs:91:5 + | +LL | pub(crate) mod m4_2 { + | ----------^^^^^^^^^ + | | + | help: consider using: `pub` + +error: pub(crate) function inside private module + --> $DIR/redundant_pub_crate.rs:94:9 + | +LL | pub(crate) fn g() {} // private due to m4_2 + | ----------^^^^^ + | | + | help: consider using: `pub` + +error: aborting due to 16 previous errors + diff --git a/src/tools/clippy/tests/ui/redundant_static_lifetimes.fixed b/src/tools/clippy/tests/ui/redundant_static_lifetimes.fixed new file mode 100644 index 00000000000..921249606ad --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_static_lifetimes.fixed @@ -0,0 +1,56 @@ +// run-rustfix + +#![allow(unused)] + +#[derive(Debug)] +struct Foo {} + +const VAR_ONE: &str = "Test constant #1"; // ERROR Consider removing 'static. + +const VAR_TWO: &str = "Test constant #2"; // This line should not raise a warning. + +const VAR_THREE: &[&str] = &["one", "two"]; // ERROR Consider removing 'static + +const VAR_FOUR: (&str, (&str, &str), &str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static + +const VAR_SIX: &u8 = &5; + +const VAR_HEIGHT: &Foo = &Foo {}; + +const VAR_SLICE: &[u8] = b"Test constant #1"; // ERROR Consider removing 'static. + +const VAR_TUPLE: &(u8, u8) = &(1, 2); // ERROR Consider removing 'static. + +const VAR_ARRAY: &[u8; 1] = b"T"; // ERROR Consider removing 'static. + +static STATIC_VAR_ONE: &str = "Test static #1"; // ERROR Consider removing 'static. + +static STATIC_VAR_TWO: &str = "Test static #2"; // This line should not raise a warning. + +static STATIC_VAR_THREE: &[&str] = &["one", "two"]; // ERROR Consider removing 'static + +static STATIC_VAR_SIX: &u8 = &5; + +static STATIC_VAR_HEIGHT: &Foo = &Foo {}; + +static STATIC_VAR_SLICE: &[u8] = b"Test static #3"; // ERROR Consider removing 'static. + +static STATIC_VAR_TUPLE: &(u8, u8) = &(1, 2); // ERROR Consider removing 'static. + +static STATIC_VAR_ARRAY: &[u8; 1] = b"T"; // ERROR Consider removing 'static. + +fn main() { + let false_positive: &'static str = "test"; +} + +trait Bar { + const TRAIT_VAR: &'static str; +} + +impl Foo { + const IMPL_VAR: &'static str = "var"; +} + +impl Bar for Foo { + const TRAIT_VAR: &'static str = "foo"; +} diff --git a/src/tools/clippy/tests/ui/redundant_static_lifetimes.rs b/src/tools/clippy/tests/ui/redundant_static_lifetimes.rs new file mode 100644 index 00000000000..4d4b249d076 --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_static_lifetimes.rs @@ -0,0 +1,56 @@ +// run-rustfix + +#![allow(unused)] + +#[derive(Debug)] +struct Foo {} + +const VAR_ONE: &'static str = "Test constant #1"; // ERROR Consider removing 'static. + +const VAR_TWO: &str = "Test constant #2"; // This line should not raise a warning. + +const VAR_THREE: &[&'static str] = &["one", "two"]; // ERROR Consider removing 'static + +const VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static + +const VAR_SIX: &'static u8 = &5; + +const VAR_HEIGHT: &'static Foo = &Foo {}; + +const VAR_SLICE: &'static [u8] = b"Test constant #1"; // ERROR Consider removing 'static. + +const VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR Consider removing 'static. + +const VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removing 'static. + +static STATIC_VAR_ONE: &'static str = "Test static #1"; // ERROR Consider removing 'static. + +static STATIC_VAR_TWO: &str = "Test static #2"; // This line should not raise a warning. + +static STATIC_VAR_THREE: &[&'static str] = &["one", "two"]; // ERROR Consider removing 'static + +static STATIC_VAR_SIX: &'static u8 = &5; + +static STATIC_VAR_HEIGHT: &'static Foo = &Foo {}; + +static STATIC_VAR_SLICE: &'static [u8] = b"Test static #3"; // ERROR Consider removing 'static. + +static STATIC_VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR Consider removing 'static. + +static STATIC_VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removing 'static. + +fn main() { + let false_positive: &'static str = "test"; +} + +trait Bar { + const TRAIT_VAR: &'static str; +} + +impl Foo { + const IMPL_VAR: &'static str = "var"; +} + +impl Bar for Foo { + const TRAIT_VAR: &'static str = "foo"; +} diff --git a/src/tools/clippy/tests/ui/redundant_static_lifetimes.stderr b/src/tools/clippy/tests/ui/redundant_static_lifetimes.stderr new file mode 100644 index 00000000000..3c3d2eacd8d --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_static_lifetimes.stderr @@ -0,0 +1,100 @@ +error: Constants have by default a `'static` lifetime + --> $DIR/redundant_static_lifetimes.rs:8:17 + | +LL | const VAR_ONE: &'static str = "Test constant #1"; // ERROR Consider removing 'static. + | -^^^^^^^---- help: consider removing `'static`: `&str` + | + = note: `-D clippy::redundant-static-lifetimes` implied by `-D warnings` + +error: Constants have by default a `'static` lifetime + --> $DIR/redundant_static_lifetimes.rs:12:21 + | +LL | const VAR_THREE: &[&'static str] = &["one", "two"]; // ERROR Consider removing 'static + | -^^^^^^^---- help: consider removing `'static`: `&str` + +error: Constants have by default a `'static` lifetime + --> $DIR/redundant_static_lifetimes.rs:14:32 + | +LL | const VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static + | -^^^^^^^---- help: consider removing `'static`: `&str` + +error: Constants have by default a `'static` lifetime + --> $DIR/redundant_static_lifetimes.rs:14:47 + | +LL | const VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static + | -^^^^^^^---- help: consider removing `'static`: `&str` + +error: Constants have by default a `'static` lifetime + --> $DIR/redundant_static_lifetimes.rs:16:17 + | +LL | const VAR_SIX: &'static u8 = &5; + | -^^^^^^^--- help: consider removing `'static`: `&u8` + +error: Constants have by default a `'static` lifetime + --> $DIR/redundant_static_lifetimes.rs:18:20 + | +LL | const VAR_HEIGHT: &'static Foo = &Foo {}; + | -^^^^^^^---- help: consider removing `'static`: `&Foo` + +error: Constants have by default a `'static` lifetime + --> $DIR/redundant_static_lifetimes.rs:20:19 + | +LL | const VAR_SLICE: &'static [u8] = b"Test constant #1"; // ERROR Consider removing 'static. + | -^^^^^^^----- help: consider removing `'static`: `&[u8]` + +error: Constants have by default a `'static` lifetime + --> $DIR/redundant_static_lifetimes.rs:22:19 + | +LL | const VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR Consider removing 'static. + | -^^^^^^^--------- help: consider removing `'static`: `&(u8, u8)` + +error: Constants have by default a `'static` lifetime + --> $DIR/redundant_static_lifetimes.rs:24:19 + | +LL | const VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removing 'static. + | -^^^^^^^-------- help: consider removing `'static`: `&[u8; 1]` + +error: Statics have by default a `'static` lifetime + --> $DIR/redundant_static_lifetimes.rs:26:25 + | +LL | static STATIC_VAR_ONE: &'static str = "Test static #1"; // ERROR Consider removing 'static. + | -^^^^^^^---- help: consider removing `'static`: `&str` + +error: Statics have by default a `'static` lifetime + --> $DIR/redundant_static_lifetimes.rs:30:29 + | +LL | static STATIC_VAR_THREE: &[&'static str] = &["one", "two"]; // ERROR Consider removing 'static + | -^^^^^^^---- help: consider removing `'static`: `&str` + +error: Statics have by default a `'static` lifetime + --> $DIR/redundant_static_lifetimes.rs:32:25 + | +LL | static STATIC_VAR_SIX: &'static u8 = &5; + | -^^^^^^^--- help: consider removing `'static`: `&u8` + +error: Statics have by default a `'static` lifetime + --> $DIR/redundant_static_lifetimes.rs:34:28 + | +LL | static STATIC_VAR_HEIGHT: &'static Foo = &Foo {}; + | -^^^^^^^---- help: consider removing `'static`: `&Foo` + +error: Statics have by default a `'static` lifetime + --> $DIR/redundant_static_lifetimes.rs:36:27 + | +LL | static STATIC_VAR_SLICE: &'static [u8] = b"Test static #3"; // ERROR Consider removing 'static. + | -^^^^^^^----- help: consider removing `'static`: `&[u8]` + +error: Statics have by default a `'static` lifetime + --> $DIR/redundant_static_lifetimes.rs:38:27 + | +LL | static STATIC_VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR Consider removing 'static. + | -^^^^^^^--------- help: consider removing `'static`: `&(u8, u8)` + +error: Statics have by default a `'static` lifetime + --> $DIR/redundant_static_lifetimes.rs:40:27 + | +LL | static STATIC_VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removing 'static. + | -^^^^^^^-------- help: consider removing `'static`: `&[u8; 1]` + +error: aborting due to 16 previous errors + diff --git a/src/tools/clippy/tests/ui/redundant_static_lifetimes_multiple.rs b/src/tools/clippy/tests/ui/redundant_static_lifetimes_multiple.rs new file mode 100644 index 00000000000..f57dd58e230 --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_static_lifetimes_multiple.rs @@ -0,0 +1,13 @@ +// these are rustfixable, but run-rustfix tests cannot handle them + +const VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR Consider removing 'static + +const VAR_SEVEN: &[&(&str, &'static [&'static str])] = &[&("one", &["other one"])]; + +static STATIC_VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static + +static STATIC_VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR Consider removing 'static + +static STATIC_VAR_SEVEN: &[&(&str, &'static [&'static str])] = &[&("one", &["other one"])]; + +fn main() {} diff --git a/src/tools/clippy/tests/ui/redundant_static_lifetimes_multiple.stderr b/src/tools/clippy/tests/ui/redundant_static_lifetimes_multiple.stderr new file mode 100644 index 00000000000..afc853dcfce --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_static_lifetimes_multiple.stderr @@ -0,0 +1,64 @@ +error: Constants have by default a `'static` lifetime + --> $DIR/redundant_static_lifetimes_multiple.rs:3:18 + | +LL | const VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR Consider removing 'static + | -^^^^^^^------------------ help: consider removing `'static`: `&[&[&'static str]]` + | + = note: `-D clippy::redundant-static-lifetimes` implied by `-D warnings` + +error: Constants have by default a `'static` lifetime + --> $DIR/redundant_static_lifetimes_multiple.rs:3:30 + | +LL | const VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR Consider removing 'static + | -^^^^^^^---- help: consider removing `'static`: `&str` + +error: Constants have by default a `'static` lifetime + --> $DIR/redundant_static_lifetimes_multiple.rs:5:29 + | +LL | const VAR_SEVEN: &[&(&str, &'static [&'static str])] = &[&("one", &["other one"])]; + | -^^^^^^^--------------- help: consider removing `'static`: `&[&'static str]` + +error: Constants have by default a `'static` lifetime + --> $DIR/redundant_static_lifetimes_multiple.rs:5:39 + | +LL | const VAR_SEVEN: &[&(&str, &'static [&'static str])] = &[&("one", &["other one"])]; + | -^^^^^^^---- help: consider removing `'static`: `&str` + +error: Statics have by default a `'static` lifetime + --> $DIR/redundant_static_lifetimes_multiple.rs:7:40 + | +LL | static STATIC_VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static + | -^^^^^^^---- help: consider removing `'static`: `&str` + +error: Statics have by default a `'static` lifetime + --> $DIR/redundant_static_lifetimes_multiple.rs:7:55 + | +LL | static STATIC_VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static + | -^^^^^^^---- help: consider removing `'static`: `&str` + +error: Statics have by default a `'static` lifetime + --> $DIR/redundant_static_lifetimes_multiple.rs:9:26 + | +LL | static STATIC_VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR Consider removing 'static + | -^^^^^^^------------------ help: consider removing `'static`: `&[&[&'static str]]` + +error: Statics have by default a `'static` lifetime + --> $DIR/redundant_static_lifetimes_multiple.rs:9:38 + | +LL | static STATIC_VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR Consider removing 'static + | -^^^^^^^---- help: consider removing `'static`: `&str` + +error: Statics have by default a `'static` lifetime + --> $DIR/redundant_static_lifetimes_multiple.rs:11:37 + | +LL | static STATIC_VAR_SEVEN: &[&(&str, &'static [&'static str])] = &[&("one", &["other one"])]; + | -^^^^^^^--------------- help: consider removing `'static`: `&[&'static str]` + +error: Statics have by default a `'static` lifetime + --> $DIR/redundant_static_lifetimes_multiple.rs:11:47 + | +LL | static STATIC_VAR_SEVEN: &[&(&str, &'static [&'static str])] = &[&("one", &["other one"])]; + | -^^^^^^^---- help: consider removing `'static`: `&str` + +error: aborting due to 10 previous errors + diff --git a/src/tools/clippy/tests/ui/regex.rs b/src/tools/clippy/tests/ui/regex.rs new file mode 100644 index 00000000000..b523fa5b711 --- /dev/null +++ b/src/tools/clippy/tests/ui/regex.rs @@ -0,0 +1,79 @@ +#![allow(unused)] +#![warn(clippy::invalid_regex, clippy::trivial_regex, clippy::regex_macro)] + +extern crate regex; + +use regex::bytes::{Regex as BRegex, RegexBuilder as BRegexBuilder, RegexSet as BRegexSet}; +use regex::{Regex, RegexBuilder, RegexSet}; + +const OPENING_PAREN: &str = "("; +const NOT_A_REAL_REGEX: &str = "foobar"; + +fn syntax_error() { + let pipe_in_wrong_position = Regex::new("|"); + let pipe_in_wrong_position_builder = RegexBuilder::new("|"); + let wrong_char_ranice = Regex::new("[z-a]"); + let some_unicode = Regex::new("[é-è]"); + + let some_regex = Regex::new(OPENING_PAREN); + + let binary_pipe_in_wrong_position = BRegex::new("|"); + let some_binary_regex = BRegex::new(OPENING_PAREN); + let some_binary_regex_builder = BRegexBuilder::new(OPENING_PAREN); + + let closing_paren = ")"; + let not_linted = Regex::new(closing_paren); + + let set = RegexSet::new(&[r"[a-z]+@[a-z]+\.(com|org|net)", r"[a-z]+\.(com|org|net)"]); + let bset = BRegexSet::new(&[ + r"[a-z]+@[a-z]+\.(com|org|net)", + r"[a-z]+\.(com|org|net)", + r".", // regression test + ]); + + let set_error = RegexSet::new(&[OPENING_PAREN, r"[a-z]+\.(com|org|net)"]); + let bset_error = BRegexSet::new(&[OPENING_PAREN, r"[a-z]+\.(com|org|net)"]); + + let raw_string_error = Regex::new(r"[...\/...]"); + let raw_string_error = Regex::new(r#"[...\/...]"#); +} + +fn trivial_regex() { + let trivial_eq = Regex::new("^foobar$"); + + let trivial_eq_builder = RegexBuilder::new("^foobar$"); + + let trivial_starts_with = Regex::new("^foobar"); + + let trivial_ends_with = Regex::new("foobar$"); + + let trivial_contains = Regex::new("foobar"); + + let trivial_contains = Regex::new(NOT_A_REAL_REGEX); + + let trivial_backslash = Regex::new("a\\.b"); + + // unlikely corner cases + let trivial_empty = Regex::new(""); + + let trivial_empty = Regex::new("^"); + + let trivial_empty = Regex::new("^$"); + + let binary_trivial_empty = BRegex::new("^$"); + + // non-trivial regexes + let non_trivial_dot = Regex::new("a.b"); + let non_trivial_dot_builder = RegexBuilder::new("a.b"); + let non_trivial_eq = Regex::new("^foo|bar$"); + let non_trivial_starts_with = Regex::new("^foo|bar"); + let non_trivial_ends_with = Regex::new("^foo|bar"); + let non_trivial_ends_with = Regex::new("foo|bar"); + let non_trivial_binary = BRegex::new("foo|bar"); + let non_trivial_binary_builder = BRegexBuilder::new("foo|bar"); +} + +fn main() { + syntax_error(); + trivial_regex(); +} diff --git a/src/tools/clippy/tests/ui/regex.stderr b/src/tools/clippy/tests/ui/regex.stderr new file mode 100644 index 00000000000..1394a9b63bc --- /dev/null +++ b/src/tools/clippy/tests/ui/regex.stderr @@ -0,0 +1,171 @@ +error: trivial regex + --> $DIR/regex.rs:13:45 + | +LL | let pipe_in_wrong_position = Regex::new("|"); + | ^^^ + | + = note: `-D clippy::trivial-regex` implied by `-D warnings` + = help: the regex is unlikely to be useful as it is + +error: trivial regex + --> $DIR/regex.rs:14:60 + | +LL | let pipe_in_wrong_position_builder = RegexBuilder::new("|"); + | ^^^ + | + = help: the regex is unlikely to be useful as it is + +error: regex syntax error: invalid character class range, the start must be <= the end + --> $DIR/regex.rs:15:42 + | +LL | let wrong_char_ranice = Regex::new("[z-a]"); + | ^^^ + | + = note: `-D clippy::invalid-regex` implied by `-D warnings` + +error: regex syntax error: invalid character class range, the start must be <= the end + --> $DIR/regex.rs:16:37 + | +LL | let some_unicode = Regex::new("[é-è]"); + | ^^^ + +error: regex syntax error on position 0: unclosed group + --> $DIR/regex.rs:18:33 + | +LL | let some_regex = Regex::new(OPENING_PAREN); + | ^^^^^^^^^^^^^ + +error: trivial regex + --> $DIR/regex.rs:20:53 + | +LL | let binary_pipe_in_wrong_position = BRegex::new("|"); + | ^^^ + | + = help: the regex is unlikely to be useful as it is + +error: regex syntax error on position 0: unclosed group + --> $DIR/regex.rs:21:41 + | +LL | let some_binary_regex = BRegex::new(OPENING_PAREN); + | ^^^^^^^^^^^^^ + +error: regex syntax error on position 0: unclosed group + --> $DIR/regex.rs:22:56 + | +LL | let some_binary_regex_builder = BRegexBuilder::new(OPENING_PAREN); + | ^^^^^^^^^^^^^ + +error: regex syntax error on position 0: unclosed group + --> $DIR/regex.rs:34:37 + | +LL | let set_error = RegexSet::new(&[OPENING_PAREN, r"[a-z]+/.(com|org|net)"]); + | ^^^^^^^^^^^^^ + +error: regex syntax error on position 0: unclosed group + --> $DIR/regex.rs:35:39 + | +LL | let bset_error = BRegexSet::new(&[OPENING_PAREN, r"[a-z]+/.(com|org|net)"]); + | ^^^^^^^^^^^^^ + +error: regex syntax error: unrecognized escape sequence + --> $DIR/regex.rs:37:45 + | +LL | let raw_string_error = Regex::new(r"[...//...]"); + | ^^ + +error: regex syntax error: unrecognized escape sequence + --> $DIR/regex.rs:38:46 + | +LL | let raw_string_error = Regex::new(r#"[...//...]"#); + | ^^ + +error: trivial regex + --> $DIR/regex.rs:42:33 + | +LL | let trivial_eq = Regex::new("^foobar$"); + | ^^^^^^^^^^ + | + = help: consider using `==` on `str`s + +error: trivial regex + --> $DIR/regex.rs:44:48 + | +LL | let trivial_eq_builder = RegexBuilder::new("^foobar$"); + | ^^^^^^^^^^ + | + = help: consider using `==` on `str`s + +error: trivial regex + --> $DIR/regex.rs:46:42 + | +LL | let trivial_starts_with = Regex::new("^foobar"); + | ^^^^^^^^^ + | + = help: consider using `str::starts_with` + +error: trivial regex + --> $DIR/regex.rs:48:40 + | +LL | let trivial_ends_with = Regex::new("foobar$"); + | ^^^^^^^^^ + | + = help: consider using `str::ends_with` + +error: trivial regex + --> $DIR/regex.rs:50:39 + | +LL | let trivial_contains = Regex::new("foobar"); + | ^^^^^^^^ + | + = help: consider using `str::contains` + +error: trivial regex + --> $DIR/regex.rs:52:39 + | +LL | let trivial_contains = Regex::new(NOT_A_REAL_REGEX); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using `str::contains` + +error: trivial regex + --> $DIR/regex.rs:54:40 + | +LL | let trivial_backslash = Regex::new("a/.b"); + | ^^^^^^^ + | + = help: consider using `str::contains` + +error: trivial regex + --> $DIR/regex.rs:57:36 + | +LL | let trivial_empty = Regex::new(""); + | ^^ + | + = help: the regex is unlikely to be useful as it is + +error: trivial regex + --> $DIR/regex.rs:59:36 + | +LL | let trivial_empty = Regex::new("^"); + | ^^^ + | + = help: the regex is unlikely to be useful as it is + +error: trivial regex + --> $DIR/regex.rs:61:36 + | +LL | let trivial_empty = Regex::new("^$"); + | ^^^^ + | + = help: consider using `str::is_empty` + +error: trivial regex + --> $DIR/regex.rs:63:44 + | +LL | let binary_trivial_empty = BRegex::new("^$"); + | ^^^^ + | + = help: consider using `str::is_empty` + +error: aborting due to 23 previous errors + diff --git a/src/tools/clippy/tests/ui/rename.fixed b/src/tools/clippy/tests/ui/rename.fixed new file mode 100644 index 00000000000..13fbb6e2a6e --- /dev/null +++ b/src/tools/clippy/tests/ui/rename.fixed @@ -0,0 +1,19 @@ +//! Test for Clippy lint renames. +// run-rustfix + +#![allow(dead_code)] +// allow the new lint name here, to test if the new name works +#![allow(clippy::module_name_repetitions)] +#![allow(clippy::new_without_default)] +#![allow(clippy::redundant_static_lifetimes)] +// warn for the old lint name here, to test if the renaming worked +#![warn(clippy::cognitive_complexity)] + +#[warn(clippy::module_name_repetitions)] +fn main() {} + +#[warn(clippy::new_without_default)] +struct Foo; + +#[warn(clippy::redundant_static_lifetimes)] +fn foo() {} diff --git a/src/tools/clippy/tests/ui/rename.rs b/src/tools/clippy/tests/ui/rename.rs new file mode 100644 index 00000000000..cbd3b1e9166 --- /dev/null +++ b/src/tools/clippy/tests/ui/rename.rs @@ -0,0 +1,19 @@ +//! Test for Clippy lint renames. +// run-rustfix + +#![allow(dead_code)] +// allow the new lint name here, to test if the new name works +#![allow(clippy::module_name_repetitions)] +#![allow(clippy::new_without_default)] +#![allow(clippy::redundant_static_lifetimes)] +// warn for the old lint name here, to test if the renaming worked +#![warn(clippy::cyclomatic_complexity)] + +#[warn(clippy::stutter)] +fn main() {} + +#[warn(clippy::new_without_default_derive)] +struct Foo; + +#[warn(clippy::const_static_lifetime)] +fn foo() {} diff --git a/src/tools/clippy/tests/ui/rename.stderr b/src/tools/clippy/tests/ui/rename.stderr new file mode 100644 index 00000000000..a9e80394604 --- /dev/null +++ b/src/tools/clippy/tests/ui/rename.stderr @@ -0,0 +1,34 @@ +error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity` + --> $DIR/rename.rs:10:9 + | +LL | #![warn(clippy::cyclomatic_complexity)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity` + | + = note: `-D renamed-and-removed-lints` implied by `-D warnings` + +error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions` + --> $DIR/rename.rs:12:8 + | +LL | #[warn(clippy::stutter)] + | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions` + +error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default` + --> $DIR/rename.rs:15:8 + | +LL | #[warn(clippy::new_without_default_derive)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default` + +error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes` + --> $DIR/rename.rs:18:8 + | +LL | #[warn(clippy::const_static_lifetime)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes` + +error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity` + --> $DIR/rename.rs:10:9 + | +LL | #![warn(clippy::cyclomatic_complexity)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity` + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/renamed_builtin_attr.fixed b/src/tools/clippy/tests/ui/renamed_builtin_attr.fixed new file mode 100644 index 00000000000..cb91b841d2c --- /dev/null +++ b/src/tools/clippy/tests/ui/renamed_builtin_attr.fixed @@ -0,0 +1,4 @@ +// run-rustfix + +#[clippy::cognitive_complexity = "1"] +fn main() {} diff --git a/src/tools/clippy/tests/ui/renamed_builtin_attr.rs b/src/tools/clippy/tests/ui/renamed_builtin_attr.rs new file mode 100644 index 00000000000..b3ce2758067 --- /dev/null +++ b/src/tools/clippy/tests/ui/renamed_builtin_attr.rs @@ -0,0 +1,4 @@ +// run-rustfix + +#[clippy::cyclomatic_complexity = "1"] +fn main() {} diff --git a/src/tools/clippy/tests/ui/renamed_builtin_attr.stderr b/src/tools/clippy/tests/ui/renamed_builtin_attr.stderr new file mode 100644 index 00000000000..a399ff52fb8 --- /dev/null +++ b/src/tools/clippy/tests/ui/renamed_builtin_attr.stderr @@ -0,0 +1,8 @@ +error: Usage of deprecated attribute + --> $DIR/renamed_builtin_attr.rs:3:11 + | +LL | #[clippy::cyclomatic_complexity = "1"] + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `cognitive_complexity` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/repl_uninit.rs b/src/tools/clippy/tests/ui/repl_uninit.rs new file mode 100644 index 00000000000..346972b7bb4 --- /dev/null +++ b/src/tools/clippy/tests/ui/repl_uninit.rs @@ -0,0 +1,35 @@ +#![allow(deprecated, invalid_value)] +#![warn(clippy::all)] + +use std::mem; + +fn might_panic<X>(x: X) -> X { + // in practice this would be a possibly-panicky operation + x +} + +fn main() { + let mut v = vec![0i32; 4]; + // the following is UB if `might_panic` panics + unsafe { + let taken_v = mem::replace(&mut v, mem::uninitialized()); + let new_v = might_panic(taken_v); + std::mem::forget(mem::replace(&mut v, new_v)); + } + + unsafe { + let taken_v = mem::replace(&mut v, mem::zeroed()); + let new_v = might_panic(taken_v); + std::mem::forget(mem::replace(&mut v, new_v)); + } + + // this is silly but OK, because usize is a primitive type + let mut u: usize = 42; + let uref = &mut u; + let taken_u = unsafe { mem::replace(uref, mem::zeroed()) }; + *uref = taken_u + 1; + + // this is still not OK, because uninit + let taken_u = unsafe { mem::replace(uref, mem::uninitialized()) }; + *uref = taken_u + 1; +} diff --git a/src/tools/clippy/tests/ui/repl_uninit.stderr b/src/tools/clippy/tests/ui/repl_uninit.stderr new file mode 100644 index 00000000000..c1f55d7601e --- /dev/null +++ b/src/tools/clippy/tests/ui/repl_uninit.stderr @@ -0,0 +1,27 @@ +error: replacing with `mem::uninitialized()` + --> $DIR/repl_uninit.rs:15:23 + | +LL | let taken_v = mem::replace(&mut v, mem::uninitialized()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::mem-replace-with-uninit` implied by `-D warnings` + = help: consider using the `take_mut` crate instead + +error: replacing with `mem::zeroed()` + --> $DIR/repl_uninit.rs:21:23 + | +LL | let taken_v = mem::replace(&mut v, mem::zeroed()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a default value or the `take_mut` crate instead + +error: replacing with `mem::uninitialized()` + --> $DIR/repl_uninit.rs:33:28 + | +LL | let taken_u = unsafe { mem::replace(uref, mem::uninitialized()) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using the `take_mut` crate instead + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/rest_pat_in_fully_bound_structs.rs b/src/tools/clippy/tests/ui/rest_pat_in_fully_bound_structs.rs new file mode 100644 index 00000000000..38fc9969804 --- /dev/null +++ b/src/tools/clippy/tests/ui/rest_pat_in_fully_bound_structs.rs @@ -0,0 +1,42 @@ +#![warn(clippy::rest_pat_in_fully_bound_structs)] + +struct A { + a: i32, + b: i64, + c: &'static str, +} + +macro_rules! foo { + ($param:expr) => { + match $param { + A { a: 0, b: 0, c: "", .. } => {}, + _ => {}, + } + }; +} + +fn main() { + let a_struct = A { a: 5, b: 42, c: "A" }; + + match a_struct { + A { a: 5, b: 42, c: "", .. } => {}, // Lint + A { a: 0, b: 0, c: "", .. } => {}, // Lint + _ => {}, + } + + match a_struct { + A { a: 5, b: 42, .. } => {}, + A { a: 0, b: 0, c: "", .. } => {}, // Lint + _ => {}, + } + + // No lint + match a_struct { + A { a: 5, .. } => {}, + A { a: 0, b: 0, .. } => {}, + _ => {}, + } + + // No lint + foo!(a_struct); +} diff --git a/src/tools/clippy/tests/ui/rest_pat_in_fully_bound_structs.stderr b/src/tools/clippy/tests/ui/rest_pat_in_fully_bound_structs.stderr new file mode 100644 index 00000000000..57ebd47f8c7 --- /dev/null +++ b/src/tools/clippy/tests/ui/rest_pat_in_fully_bound_structs.stderr @@ -0,0 +1,27 @@ +error: unnecessary use of `..` pattern in struct binding. All fields were already bound + --> $DIR/rest_pat_in_fully_bound_structs.rs:22:9 + | +LL | A { a: 5, b: 42, c: "", .. } => {}, // Lint + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::rest-pat-in-fully-bound-structs` implied by `-D warnings` + = help: consider removing `..` from this binding + +error: unnecessary use of `..` pattern in struct binding. All fields were already bound + --> $DIR/rest_pat_in_fully_bound_structs.rs:23:9 + | +LL | A { a: 0, b: 0, c: "", .. } => {}, // Lint + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider removing `..` from this binding + +error: unnecessary use of `..` pattern in struct binding. All fields were already bound + --> $DIR/rest_pat_in_fully_bound_structs.rs:29:9 + | +LL | A { a: 0, b: 0, c: "", .. } => {}, // Lint + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider removing `..` from this binding + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/result_map_or_into_option.fixed b/src/tools/clippy/tests/ui/result_map_or_into_option.fixed new file mode 100644 index 00000000000..331531b5165 --- /dev/null +++ b/src/tools/clippy/tests/ui/result_map_or_into_option.fixed @@ -0,0 +1,19 @@ +// run-rustfix + +#![warn(clippy::result_map_or_into_option)] + +fn main() { + let opt: Result<u32, &str> = Ok(1); + let _ = opt.ok(); + + let rewrap = |s: u32| -> Option<u32> { Some(s) }; + + // A non-Some `f` arg should not emit the lint + let opt: Result<u32, &str> = Ok(1); + let _ = opt.map_or(None, rewrap); + + // A non-Some `f` closure where the argument is not used as the + // return should not emit the lint + let opt: Result<u32, &str> = Ok(1); + opt.map_or(None, |_x| Some(1)); +} diff --git a/src/tools/clippy/tests/ui/result_map_or_into_option.rs b/src/tools/clippy/tests/ui/result_map_or_into_option.rs new file mode 100644 index 00000000000..3058480e2ad --- /dev/null +++ b/src/tools/clippy/tests/ui/result_map_or_into_option.rs @@ -0,0 +1,19 @@ +// run-rustfix + +#![warn(clippy::result_map_or_into_option)] + +fn main() { + let opt: Result<u32, &str> = Ok(1); + let _ = opt.map_or(None, Some); + + let rewrap = |s: u32| -> Option<u32> { Some(s) }; + + // A non-Some `f` arg should not emit the lint + let opt: Result<u32, &str> = Ok(1); + let _ = opt.map_or(None, rewrap); + + // A non-Some `f` closure where the argument is not used as the + // return should not emit the lint + let opt: Result<u32, &str> = Ok(1); + opt.map_or(None, |_x| Some(1)); +} diff --git a/src/tools/clippy/tests/ui/result_map_or_into_option.stderr b/src/tools/clippy/tests/ui/result_map_or_into_option.stderr new file mode 100644 index 00000000000..febf32147d1 --- /dev/null +++ b/src/tools/clippy/tests/ui/result_map_or_into_option.stderr @@ -0,0 +1,10 @@ +error: called `map_or(None, Some)` on a `Result` value. This can be done more directly by calling `ok()` instead + --> $DIR/result_map_or_into_option.rs:7:13 + | +LL | let _ = opt.map_or(None, Some); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try using `ok` instead: `opt.ok()` + | + = note: `-D clippy::result-map-or-into-option` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/result_map_unit_fn_fixable.fixed b/src/tools/clippy/tests/ui/result_map_unit_fn_fixable.fixed new file mode 100644 index 00000000000..1d0a3ecd0ff --- /dev/null +++ b/src/tools/clippy/tests/ui/result_map_unit_fn_fixable.fixed @@ -0,0 +1,80 @@ +// run-rustfix + +#![warn(clippy::result_map_unit_fn)] +#![allow(unused)] + +fn do_nothing<T>(_: T) {} + +fn diverge<T>(_: T) -> ! { + panic!() +} + +fn plus_one(value: usize) -> usize { + value + 1 +} + +struct HasResult { + field: Result<usize, usize>, +} + +impl HasResult { + fn do_result_nothing(self: &Self, value: usize) {} + + fn do_result_plus_one(self: &Self, value: usize) -> usize { + value + 1 + } +} + +#[rustfmt::skip] +fn result_map_unit_fn() { + let x = HasResult { field: Ok(10) }; + + x.field.map(plus_one); + let _: Result<(), usize> = x.field.map(do_nothing); + + if let Ok(x_field) = x.field { do_nothing(x_field) } + + if let Ok(x_field) = x.field { do_nothing(x_field) } + + if let Ok(x_field) = x.field { diverge(x_field) } + + let captured = 10; + if let Ok(value) = x.field { do_nothing(value + captured) }; + let _: Result<(), usize> = x.field.map(|value| do_nothing(value + captured)); + + if let Ok(value) = x.field { x.do_result_nothing(value + captured) } + + if let Ok(value) = x.field { x.do_result_plus_one(value + captured); } + + + if let Ok(value) = x.field { do_nothing(value + captured) } + + if let Ok(value) = x.field { do_nothing(value + captured) } + + if let Ok(value) = x.field { do_nothing(value + captured); } + + if let Ok(value) = x.field { do_nothing(value + captured); } + + + if let Ok(value) = x.field { diverge(value + captured) } + + if let Ok(value) = x.field { diverge(value + captured) } + + if let Ok(value) = x.field { diverge(value + captured); } + + if let Ok(value) = x.field { diverge(value + captured); } + + + x.field.map(|value| plus_one(value + captured)); + x.field.map(|value| { plus_one(value + captured) }); + if let Ok(value) = x.field { let y = plus_one(value + captured); } + + if let Ok(value) = x.field { plus_one(value + captured); } + + if let Ok(value) = x.field { plus_one(value + captured); } + + + if let Ok(ref value) = x.field { do_nothing(value + captured) } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/result_map_unit_fn_fixable.rs b/src/tools/clippy/tests/ui/result_map_unit_fn_fixable.rs new file mode 100644 index 00000000000..2fe18f923f0 --- /dev/null +++ b/src/tools/clippy/tests/ui/result_map_unit_fn_fixable.rs @@ -0,0 +1,80 @@ +// run-rustfix + +#![warn(clippy::result_map_unit_fn)] +#![allow(unused)] + +fn do_nothing<T>(_: T) {} + +fn diverge<T>(_: T) -> ! { + panic!() +} + +fn plus_one(value: usize) -> usize { + value + 1 +} + +struct HasResult { + field: Result<usize, usize>, +} + +impl HasResult { + fn do_result_nothing(self: &Self, value: usize) {} + + fn do_result_plus_one(self: &Self, value: usize) -> usize { + value + 1 + } +} + +#[rustfmt::skip] +fn result_map_unit_fn() { + let x = HasResult { field: Ok(10) }; + + x.field.map(plus_one); + let _: Result<(), usize> = x.field.map(do_nothing); + + x.field.map(do_nothing); + + x.field.map(do_nothing); + + x.field.map(diverge); + + let captured = 10; + if let Ok(value) = x.field { do_nothing(value + captured) }; + let _: Result<(), usize> = x.field.map(|value| do_nothing(value + captured)); + + x.field.map(|value| x.do_result_nothing(value + captured)); + + x.field.map(|value| { x.do_result_plus_one(value + captured); }); + + + x.field.map(|value| do_nothing(value + captured)); + + x.field.map(|value| { do_nothing(value + captured) }); + + x.field.map(|value| { do_nothing(value + captured); }); + + x.field.map(|value| { { do_nothing(value + captured); } }); + + + x.field.map(|value| diverge(value + captured)); + + x.field.map(|value| { diverge(value + captured) }); + + x.field.map(|value| { diverge(value + captured); }); + + x.field.map(|value| { { diverge(value + captured); } }); + + + x.field.map(|value| plus_one(value + captured)); + x.field.map(|value| { plus_one(value + captured) }); + x.field.map(|value| { let y = plus_one(value + captured); }); + + x.field.map(|value| { plus_one(value + captured); }); + + x.field.map(|value| { { plus_one(value + captured); } }); + + + x.field.map(|ref value| { do_nothing(value + captured) }); +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/result_map_unit_fn_fixable.stderr b/src/tools/clippy/tests/ui/result_map_unit_fn_fixable.stderr new file mode 100644 index 00000000000..467e00263cd --- /dev/null +++ b/src/tools/clippy/tests/ui/result_map_unit_fn_fixable.stderr @@ -0,0 +1,140 @@ +error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type + --> $DIR/result_map_unit_fn_fixable.rs:35:5 + | +LL | x.field.map(do_nothing); + | ^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Ok(x_field) = x.field { do_nothing(x_field) }` + | + = note: `-D clippy::result-map-unit-fn` implied by `-D warnings` + +error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type + --> $DIR/result_map_unit_fn_fixable.rs:37:5 + | +LL | x.field.map(do_nothing); + | ^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Ok(x_field) = x.field { do_nothing(x_field) }` + +error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type + --> $DIR/result_map_unit_fn_fixable.rs:39:5 + | +LL | x.field.map(diverge); + | ^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Ok(x_field) = x.field { diverge(x_field) }` + +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type + --> $DIR/result_map_unit_fn_fixable.rs:45:5 + | +LL | x.field.map(|value| x.do_result_nothing(value + captured)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Ok(value) = x.field { x.do_result_nothing(value + captured) }` + +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type + --> $DIR/result_map_unit_fn_fixable.rs:47:5 + | +LL | x.field.map(|value| { x.do_result_plus_one(value + captured); }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Ok(value) = x.field { x.do_result_plus_one(value + captured); }` + +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type + --> $DIR/result_map_unit_fn_fixable.rs:50:5 + | +LL | x.field.map(|value| do_nothing(value + captured)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Ok(value) = x.field { do_nothing(value + captured) }` + +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type + --> $DIR/result_map_unit_fn_fixable.rs:52:5 + | +LL | x.field.map(|value| { do_nothing(value + captured) }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Ok(value) = x.field { do_nothing(value + captured) }` + +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type + --> $DIR/result_map_unit_fn_fixable.rs:54:5 + | +LL | x.field.map(|value| { do_nothing(value + captured); }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Ok(value) = x.field { do_nothing(value + captured); }` + +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type + --> $DIR/result_map_unit_fn_fixable.rs:56:5 + | +LL | x.field.map(|value| { { do_nothing(value + captured); } }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Ok(value) = x.field { do_nothing(value + captured); }` + +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type + --> $DIR/result_map_unit_fn_fixable.rs:59:5 + | +LL | x.field.map(|value| diverge(value + captured)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Ok(value) = x.field { diverge(value + captured) }` + +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type + --> $DIR/result_map_unit_fn_fixable.rs:61:5 + | +LL | x.field.map(|value| { diverge(value + captured) }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Ok(value) = x.field { diverge(value + captured) }` + +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type + --> $DIR/result_map_unit_fn_fixable.rs:63:5 + | +LL | x.field.map(|value| { diverge(value + captured); }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Ok(value) = x.field { diverge(value + captured); }` + +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type + --> $DIR/result_map_unit_fn_fixable.rs:65:5 + | +LL | x.field.map(|value| { { diverge(value + captured); } }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Ok(value) = x.field { diverge(value + captured); }` + +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type + --> $DIR/result_map_unit_fn_fixable.rs:70:5 + | +LL | x.field.map(|value| { let y = plus_one(value + captured); }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Ok(value) = x.field { let y = plus_one(value + captured); }` + +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type + --> $DIR/result_map_unit_fn_fixable.rs:72:5 + | +LL | x.field.map(|value| { plus_one(value + captured); }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Ok(value) = x.field { plus_one(value + captured); }` + +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type + --> $DIR/result_map_unit_fn_fixable.rs:74:5 + | +LL | x.field.map(|value| { { plus_one(value + captured); } }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Ok(value) = x.field { plus_one(value + captured); }` + +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type + --> $DIR/result_map_unit_fn_fixable.rs:77:5 + | +LL | x.field.map(|ref value| { do_nothing(value + captured) }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Ok(ref value) = x.field { do_nothing(value + captured) }` + +error: aborting due to 17 previous errors + diff --git a/src/tools/clippy/tests/ui/result_map_unit_fn_unfixable.rs b/src/tools/clippy/tests/ui/result_map_unit_fn_unfixable.rs new file mode 100644 index 00000000000..b197c609d7b --- /dev/null +++ b/src/tools/clippy/tests/ui/result_map_unit_fn_unfixable.rs @@ -0,0 +1,46 @@ +#![warn(clippy::result_map_unit_fn)] +#![feature(never_type)] +#![allow(unused)] + +struct HasResult { + field: Result<usize, usize>, +} + +fn do_nothing<T>(_: T) {} + +fn diverge<T>(_: T) -> ! { + panic!() +} + +fn plus_one(value: usize) -> usize { + value + 1 +} + +#[rustfmt::skip] +fn result_map_unit_fn() { + let x = HasResult { field: Ok(10) }; + + x.field.map(|value| { do_nothing(value); do_nothing(value) }); + + x.field.map(|value| if value > 0 { do_nothing(value); do_nothing(value) }); + + // Suggestion for the let block should be `{ ... }` as it's too difficult to build a + // proper suggestion for these cases + x.field.map(|value| { + do_nothing(value); + do_nothing(value) + }); + x.field.map(|value| { do_nothing(value); do_nothing(value); }); + + // The following should suggest `if let Ok(_X) ...` as it's difficult to generate a proper let variable name for them + let res: Result<!, usize> = Ok(42).map(diverge); + "12".parse::<i32>().map(diverge); + + let res: Result<(), usize> = Ok(plus_one(1)).map(do_nothing); + + // Should suggest `if let Ok(_y) ...` to not override the existing foo variable + let y: Result<usize, usize> = Ok(42); + y.map(do_nothing); +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/result_map_unit_fn_unfixable.stderr b/src/tools/clippy/tests/ui/result_map_unit_fn_unfixable.stderr new file mode 100644 index 00000000000..b23cc608621 --- /dev/null +++ b/src/tools/clippy/tests/ui/result_map_unit_fn_unfixable.stderr @@ -0,0 +1,58 @@ +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type + --> $DIR/result_map_unit_fn_unfixable.rs:23:5 + | +LL | x.field.map(|value| { do_nothing(value); do_nothing(value) }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Ok(value) = x.field { ... }` + | + = note: `-D clippy::result-map-unit-fn` implied by `-D warnings` + +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type + --> $DIR/result_map_unit_fn_unfixable.rs:25:5 + | +LL | x.field.map(|value| if value > 0 { do_nothing(value); do_nothing(value) }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Ok(value) = x.field { ... }` + +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type + --> $DIR/result_map_unit_fn_unfixable.rs:29:5 + | +LL | x.field.map(|value| { + | _____^ + | |_____| + | || +LL | || do_nothing(value); +LL | || do_nothing(value) +LL | || }); + | ||______^- help: try this: `if let Ok(value) = x.field { ... }` + | |_______| + | + +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type + --> $DIR/result_map_unit_fn_unfixable.rs:33:5 + | +LL | x.field.map(|value| { do_nothing(value); do_nothing(value); }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Ok(value) = x.field { ... }` + +error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type + --> $DIR/result_map_unit_fn_unfixable.rs:37:5 + | +LL | "12".parse::<i32>().map(diverge); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Ok(a) = "12".parse::<i32>() { diverge(a) }` + +error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type + --> $DIR/result_map_unit_fn_unfixable.rs:43:5 + | +LL | y.map(do_nothing); + | ^^^^^^^^^^^^^^^^^- + | | + | help: try this: `if let Ok(_y) = y { do_nothing(_y) }` + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/result_map_unwrap_or_else.rs b/src/tools/clippy/tests/ui/result_map_unwrap_or_else.rs new file mode 100644 index 00000000000..40751bfebe6 --- /dev/null +++ b/src/tools/clippy/tests/ui/result_map_unwrap_or_else.rs @@ -0,0 +1,23 @@ +// aux-build:option_helpers.rs + +//! Checks implementation of `RESULT_MAP_UNWRAP_OR_ELSE` + +#![warn(clippy::result_map_unwrap_or_else)] + +#[macro_use] +extern crate option_helpers; + +fn result_methods() { + let res: Result<i32, ()> = Ok(1); + + // Check RESULT_MAP_UNWRAP_OR_ELSE + // single line case + let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); // should lint even though this call is on a separate line + // multi line cases + let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); + let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); + // macro case + let _ = opt_map!(res, |x| x + 1).unwrap_or_else(|e| 0); // should not lint +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/result_map_unwrap_or_else.stderr b/src/tools/clippy/tests/ui/result_map_unwrap_or_else.stderr new file mode 100644 index 00000000000..ec7bc8f1241 --- /dev/null +++ b/src/tools/clippy/tests/ui/result_map_unwrap_or_else.stderr @@ -0,0 +1,27 @@ +error: called `map(f).unwrap_or_else(g)` on a `Result` value. This can be done more directly by calling `.map_or_else(g, f)` instead + --> $DIR/result_map_unwrap_or_else.rs:15:13 + | +LL | let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); // should lint even though this call is on a separate line + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::result-map-unwrap-or-else` implied by `-D warnings` + = note: replace `map(|x| x + 1).unwrap_or_else(|e| 0)` with `map_or_else(|e| 0, |x| x + 1)` + +error: called `map(f).unwrap_or_else(g)` on a `Result` value. This can be done more directly by calling `.map_or_else(g, f)` instead + --> $DIR/result_map_unwrap_or_else.rs:17:13 + | +LL | let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: replace `map(|x| x + 1).unwrap_or_else(|e| 0)` with `map_or_else(|e| 0, |x| x + 1)` + +error: called `map(f).unwrap_or_else(g)` on a `Result` value. This can be done more directly by calling `.map_or_else(g, f)` instead + --> $DIR/result_map_unwrap_or_else.rs:18:13 + | +LL | let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: replace `map(|x| x + 1).unwrap_or_else(|e| 0)` with `map_or_else(|e| 0, |x| x + 1)` + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/same_functions_in_if_condition.rs b/src/tools/clippy/tests/ui/same_functions_in_if_condition.rs new file mode 100644 index 00000000000..686867cf5c6 --- /dev/null +++ b/src/tools/clippy/tests/ui/same_functions_in_if_condition.rs @@ -0,0 +1,80 @@ +#![warn(clippy::same_functions_in_if_condition)] +#![allow(clippy::ifs_same_cond)] // This warning is different from `ifs_same_cond`. +#![allow(clippy::if_same_then_else, clippy::comparison_chain)] // all empty blocks + +fn function() -> bool { + true +} + +fn fn_arg(_arg: u8) -> bool { + true +} + +struct Struct; + +impl Struct { + fn method(&self) -> bool { + true + } + fn method_arg(&self, _arg: u8) -> bool { + true + } +} + +fn ifs_same_cond_fn() { + let a = 0; + let obj = Struct; + + if function() { + } else if function() { + //~ ERROR ifs same condition + } + + if fn_arg(a) { + } else if fn_arg(a) { + //~ ERROR ifs same condition + } + + if obj.method() { + } else if obj.method() { + //~ ERROR ifs same condition + } + + if obj.method_arg(a) { + } else if obj.method_arg(a) { + //~ ERROR ifs same condition + } + + let mut v = vec![1]; + if v.pop() == None { + //~ ERROR ifs same condition + } else if v.pop() == None { + } + + if v.len() == 42 { + //~ ERROR ifs same condition + } else if v.len() == 42 { + } + + if v.len() == 1 { + // ok, different conditions + } else if v.len() == 2 { + } + + if fn_arg(0) { + // ok, different arguments. + } else if fn_arg(1) { + } + + if obj.method_arg(0) { + // ok, different arguments. + } else if obj.method_arg(1) { + } + + if a == 1 { + // ok, warning is on `ifs_same_cond` behalf. + } else if a == 1 { + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/same_functions_in_if_condition.stderr b/src/tools/clippy/tests/ui/same_functions_in_if_condition.stderr new file mode 100644 index 00000000000..363a03846d2 --- /dev/null +++ b/src/tools/clippy/tests/ui/same_functions_in_if_condition.stderr @@ -0,0 +1,75 @@ +error: this `if` has the same function call as a previous `if` + --> $DIR/same_functions_in_if_condition.rs:29:15 + | +LL | } else if function() { + | ^^^^^^^^^^ + | + = note: `-D clippy::same-functions-in-if-condition` implied by `-D warnings` +note: same as this + --> $DIR/same_functions_in_if_condition.rs:28:8 + | +LL | if function() { + | ^^^^^^^^^^ + +error: this `if` has the same function call as a previous `if` + --> $DIR/same_functions_in_if_condition.rs:34:15 + | +LL | } else if fn_arg(a) { + | ^^^^^^^^^ + | +note: same as this + --> $DIR/same_functions_in_if_condition.rs:33:8 + | +LL | if fn_arg(a) { + | ^^^^^^^^^ + +error: this `if` has the same function call as a previous `if` + --> $DIR/same_functions_in_if_condition.rs:39:15 + | +LL | } else if obj.method() { + | ^^^^^^^^^^^^ + | +note: same as this + --> $DIR/same_functions_in_if_condition.rs:38:8 + | +LL | if obj.method() { + | ^^^^^^^^^^^^ + +error: this `if` has the same function call as a previous `if` + --> $DIR/same_functions_in_if_condition.rs:44:15 + | +LL | } else if obj.method_arg(a) { + | ^^^^^^^^^^^^^^^^^ + | +note: same as this + --> $DIR/same_functions_in_if_condition.rs:43:8 + | +LL | if obj.method_arg(a) { + | ^^^^^^^^^^^^^^^^^ + +error: this `if` has the same function call as a previous `if` + --> $DIR/same_functions_in_if_condition.rs:51:15 + | +LL | } else if v.pop() == None { + | ^^^^^^^^^^^^^^^ + | +note: same as this + --> $DIR/same_functions_in_if_condition.rs:49:8 + | +LL | if v.pop() == None { + | ^^^^^^^^^^^^^^^ + +error: this `if` has the same function call as a previous `if` + --> $DIR/same_functions_in_if_condition.rs:56:15 + | +LL | } else if v.len() == 42 { + | ^^^^^^^^^^^^^ + | +note: same as this + --> $DIR/same_functions_in_if_condition.rs:54:8 + | +LL | if v.len() == 42 { + | ^^^^^^^^^^^^^ + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/serde.rs b/src/tools/clippy/tests/ui/serde.rs new file mode 100644 index 00000000000..5843344eba8 --- /dev/null +++ b/src/tools/clippy/tests/ui/serde.rs @@ -0,0 +1,47 @@ +#![warn(clippy::serde_api_misuse)] +#![allow(dead_code)] + +extern crate serde; + +struct A; + +impl<'de> serde::de::Visitor<'de> for A { + type Value = (); + + fn expecting(&self, _: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + unimplemented!() + } + + fn visit_str<E>(self, _v: &str) -> Result<Self::Value, E> + where + E: serde::de::Error, + { + unimplemented!() + } + + fn visit_string<E>(self, _v: String) -> Result<Self::Value, E> + where + E: serde::de::Error, + { + unimplemented!() + } +} + +struct B; + +impl<'de> serde::de::Visitor<'de> for B { + type Value = (); + + fn expecting(&self, _: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + unimplemented!() + } + + fn visit_string<E>(self, _v: String) -> Result<Self::Value, E> + where + E: serde::de::Error, + { + unimplemented!() + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/serde.stderr b/src/tools/clippy/tests/ui/serde.stderr new file mode 100644 index 00000000000..760c9c9908a --- /dev/null +++ b/src/tools/clippy/tests/ui/serde.stderr @@ -0,0 +1,15 @@ +error: you should not implement `visit_string` without also implementing `visit_str` + --> $DIR/serde.rs:39:5 + | +LL | / fn visit_string<E>(self, _v: String) -> Result<Self::Value, E> +LL | | where +LL | | E: serde::de::Error, +LL | | { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = note: `-D clippy::serde-api-misuse` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/shadow.rs b/src/tools/clippy/tests/ui/shadow.rs new file mode 100644 index 00000000000..bd91ae4e934 --- /dev/null +++ b/src/tools/clippy/tests/ui/shadow.rs @@ -0,0 +1,53 @@ +#![warn( + clippy::all, + clippy::pedantic, + clippy::shadow_same, + clippy::shadow_reuse, + clippy::shadow_unrelated +)] +#![allow( + unused_parens, + unused_variables, + clippy::missing_docs_in_private_items, + clippy::single_match +)] + +fn id<T>(x: T) -> T { + x +} + +#[must_use] +fn first(x: (isize, isize)) -> isize { + x.0 +} + +fn main() { + let mut x = 1; + let x = &mut x; + let x = { x }; + let x = (&*x); + let x = { *x + 1 }; + let x = id(x); + let x = (1, x); + let x = first(x); + let y = 1; + let x = y; + + let x; + x = 42; + + let o = Some(1_u8); + + if let Some(p) = o { + assert_eq!(1, p); + } + match o { + Some(p) => p, // no error, because the p above is in its own scope + None => 0, + }; + + match (x, o) { + (1, Some(a)) | (a, Some(1)) => (), // no error though `a` appears twice + _ => (), + } +} diff --git a/src/tools/clippy/tests/ui/shadow.stderr b/src/tools/clippy/tests/ui/shadow.stderr new file mode 100644 index 00000000000..7fa58cf7649 --- /dev/null +++ b/src/tools/clippy/tests/ui/shadow.stderr @@ -0,0 +1,138 @@ +error: `x` is shadowed by itself in `&mut x` + --> $DIR/shadow.rs:26:5 + | +LL | let x = &mut x; + | ^^^^^^^^^^^^^^^ + | + = note: `-D clippy::shadow-same` implied by `-D warnings` +note: previous binding is here + --> $DIR/shadow.rs:25:13 + | +LL | let mut x = 1; + | ^ + +error: `x` is shadowed by itself in `{ x }` + --> $DIR/shadow.rs:27:5 + | +LL | let x = { x }; + | ^^^^^^^^^^^^^^ + | +note: previous binding is here + --> $DIR/shadow.rs:26:9 + | +LL | let x = &mut x; + | ^ + +error: `x` is shadowed by itself in `(&*x)` + --> $DIR/shadow.rs:28:5 + | +LL | let x = (&*x); + | ^^^^^^^^^^^^^^ + | +note: previous binding is here + --> $DIR/shadow.rs:27:9 + | +LL | let x = { x }; + | ^ + +error: `x` is shadowed by `{ *x + 1 }` which reuses the original value + --> $DIR/shadow.rs:29:9 + | +LL | let x = { *x + 1 }; + | ^ + | + = note: `-D clippy::shadow-reuse` implied by `-D warnings` +note: initialization happens here + --> $DIR/shadow.rs:29:13 + | +LL | let x = { *x + 1 }; + | ^^^^^^^^^^ +note: previous binding is here + --> $DIR/shadow.rs:28:9 + | +LL | let x = (&*x); + | ^ + +error: `x` is shadowed by `id(x)` which reuses the original value + --> $DIR/shadow.rs:30:9 + | +LL | let x = id(x); + | ^ + | +note: initialization happens here + --> $DIR/shadow.rs:30:13 + | +LL | let x = id(x); + | ^^^^^ +note: previous binding is here + --> $DIR/shadow.rs:29:9 + | +LL | let x = { *x + 1 }; + | ^ + +error: `x` is shadowed by `(1, x)` which reuses the original value + --> $DIR/shadow.rs:31:9 + | +LL | let x = (1, x); + | ^ + | +note: initialization happens here + --> $DIR/shadow.rs:31:13 + | +LL | let x = (1, x); + | ^^^^^^ +note: previous binding is here + --> $DIR/shadow.rs:30:9 + | +LL | let x = id(x); + | ^ + +error: `x` is shadowed by `first(x)` which reuses the original value + --> $DIR/shadow.rs:32:9 + | +LL | let x = first(x); + | ^ + | +note: initialization happens here + --> $DIR/shadow.rs:32:13 + | +LL | let x = first(x); + | ^^^^^^^^ +note: previous binding is here + --> $DIR/shadow.rs:31:9 + | +LL | let x = (1, x); + | ^ + +error: `x` is shadowed by `y` + --> $DIR/shadow.rs:34:9 + | +LL | let x = y; + | ^ + | + = note: `-D clippy::shadow-unrelated` implied by `-D warnings` +note: initialization happens here + --> $DIR/shadow.rs:34:13 + | +LL | let x = y; + | ^ +note: previous binding is here + --> $DIR/shadow.rs:32:9 + | +LL | let x = first(x); + | ^ + +error: `x` shadows a previous declaration + --> $DIR/shadow.rs:36:5 + | +LL | let x; + | ^^^^^^ + | +note: previous binding is here + --> $DIR/shadow.rs:34:9 + | +LL | let x = y; + | ^ + +error: aborting due to 9 previous errors + diff --git a/src/tools/clippy/tests/ui/short_circuit_statement.fixed b/src/tools/clippy/tests/ui/short_circuit_statement.fixed new file mode 100644 index 00000000000..af0a397bd1a --- /dev/null +++ b/src/tools/clippy/tests/ui/short_circuit_statement.fixed @@ -0,0 +1,18 @@ +// run-rustfix + +#![warn(clippy::short_circuit_statement)] +#![allow(clippy::nonminimal_bool)] + +fn main() { + if f() { g(); } + if !f() { g(); } + if !(1 == 2) { g(); } +} + +fn f() -> bool { + true +} + +fn g() -> bool { + false +} diff --git a/src/tools/clippy/tests/ui/short_circuit_statement.rs b/src/tools/clippy/tests/ui/short_circuit_statement.rs new file mode 100644 index 00000000000..73a55bf1f5e --- /dev/null +++ b/src/tools/clippy/tests/ui/short_circuit_statement.rs @@ -0,0 +1,18 @@ +// run-rustfix + +#![warn(clippy::short_circuit_statement)] +#![allow(clippy::nonminimal_bool)] + +fn main() { + f() && g(); + f() || g(); + 1 == 2 || g(); +} + +fn f() -> bool { + true +} + +fn g() -> bool { + false +} diff --git a/src/tools/clippy/tests/ui/short_circuit_statement.stderr b/src/tools/clippy/tests/ui/short_circuit_statement.stderr new file mode 100644 index 00000000000..0a3f60c3d13 --- /dev/null +++ b/src/tools/clippy/tests/ui/short_circuit_statement.stderr @@ -0,0 +1,22 @@ +error: boolean short circuit operator in statement may be clearer using an explicit test + --> $DIR/short_circuit_statement.rs:7:5 + | +LL | f() && g(); + | ^^^^^^^^^^^ help: replace it with: `if f() { g(); }` + | + = note: `-D clippy::short-circuit-statement` implied by `-D warnings` + +error: boolean short circuit operator in statement may be clearer using an explicit test + --> $DIR/short_circuit_statement.rs:8:5 + | +LL | f() || g(); + | ^^^^^^^^^^^ help: replace it with: `if !f() { g(); }` + +error: boolean short circuit operator in statement may be clearer using an explicit test + --> $DIR/short_circuit_statement.rs:9:5 + | +LL | 1 == 2 || g(); + | ^^^^^^^^^^^^^^ help: replace it with: `if !(1 == 2) { g(); }` + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/similar_names.rs b/src/tools/clippy/tests/ui/similar_names.rs new file mode 100644 index 00000000000..6796b15289e --- /dev/null +++ b/src/tools/clippy/tests/ui/similar_names.rs @@ -0,0 +1,103 @@ +#![warn(clippy::similar_names)] +#![allow(unused, clippy::println_empty_string)] + +struct Foo { + apple: i32, + bpple: i32, +} + +fn main() { + let specter: i32; + let spectre: i32; + + let apple: i32; + + let bpple: i32; + + let cpple: i32; + + let a_bar: i32; + let b_bar: i32; + let c_bar: i32; + + let items = [5]; + for item in &items { + loop {} + } + + let foo_x: i32; + let foo_y: i32; + + let rhs: i32; + let lhs: i32; + + let bla_rhs: i32; + let bla_lhs: i32; + + let blubrhs: i32; + let blublhs: i32; + + let blubx: i32; + let bluby: i32; + + let cake: i32; + let cakes: i32; + let coke: i32; + + match 5 { + cheese @ 1 => {}, + rabbit => panic!(), + } + let cheese: i32; + match (42, 43) { + (cheese1, 1) => {}, + (cheese2, 2) => panic!(), + _ => println!(""), + } + let ipv4: i32; + let ipv6: i32; + let abcd1: i32; + let abdc2: i32; + let xyz1abc: i32; + let xyz2abc: i32; + let xyzeabc: i32; + + let parser: i32; + let parsed: i32; + let parsee: i32; + + let setter: i32; + let getter: i32; + let tx1: i32; + let rx1: i32; + let tx_cake: i32; + let rx_cake: i32; +} + +fn foo() { + let Foo { apple, bpple } = unimplemented!(); + let Foo { + apple: spring, + bpple: sprang, + } = unimplemented!(); +} + +// false positive similar_names (#3057, #2651) +// clippy claimed total_reg_src_size and total_size and +// numb_reg_src_checkouts and total_bin_size were similar +#[derive(Debug, Clone)] +pub(crate) struct DirSizes { + pub(crate) total_size: u64, + pub(crate) numb_bins: u64, + pub(crate) total_bin_size: u64, + pub(crate) total_reg_size: u64, + pub(crate) total_git_db_size: u64, + pub(crate) total_git_repos_bare_size: u64, + pub(crate) numb_git_repos_bare_repos: u64, + pub(crate) numb_git_checkouts: u64, + pub(crate) total_git_chk_size: u64, + pub(crate) total_reg_cache_size: u64, + pub(crate) total_reg_src_size: u64, + pub(crate) numb_reg_cache_entries: u64, + pub(crate) numb_reg_src_checkouts: u64, +} diff --git a/src/tools/clippy/tests/ui/similar_names.stderr b/src/tools/clippy/tests/ui/similar_names.stderr new file mode 100644 index 00000000000..0256f126a94 --- /dev/null +++ b/src/tools/clippy/tests/ui/similar_names.stderr @@ -0,0 +1,107 @@ +error: binding's name is too similar to existing binding + --> $DIR/similar_names.rs:15:9 + | +LL | let bpple: i32; + | ^^^^^ + | + = note: `-D clippy::similar-names` implied by `-D warnings` +note: existing binding defined here + --> $DIR/similar_names.rs:13:9 + | +LL | let apple: i32; + | ^^^^^ +help: separate the discriminating character by an underscore like: `b_pple` + --> $DIR/similar_names.rs:15:9 + | +LL | let bpple: i32; + | ^^^^^ + +error: binding's name is too similar to existing binding + --> $DIR/similar_names.rs:17:9 + | +LL | let cpple: i32; + | ^^^^^ + | +note: existing binding defined here + --> $DIR/similar_names.rs:13:9 + | +LL | let apple: i32; + | ^^^^^ +help: separate the discriminating character by an underscore like: `c_pple` + --> $DIR/similar_names.rs:17:9 + | +LL | let cpple: i32; + | ^^^^^ + +error: binding's name is too similar to existing binding + --> $DIR/similar_names.rs:41:9 + | +LL | let bluby: i32; + | ^^^^^ + | +note: existing binding defined here + --> $DIR/similar_names.rs:40:9 + | +LL | let blubx: i32; + | ^^^^^ +help: separate the discriminating character by an underscore like: `blub_y` + --> $DIR/similar_names.rs:41:9 + | +LL | let bluby: i32; + | ^^^^^ + +error: binding's name is too similar to existing binding + --> $DIR/similar_names.rs:45:9 + | +LL | let coke: i32; + | ^^^^ + | +note: existing binding defined here + --> $DIR/similar_names.rs:43:9 + | +LL | let cake: i32; + | ^^^^ + +error: binding's name is too similar to existing binding + --> $DIR/similar_names.rs:63:9 + | +LL | let xyzeabc: i32; + | ^^^^^^^ + | +note: existing binding defined here + --> $DIR/similar_names.rs:61:9 + | +LL | let xyz1abc: i32; + | ^^^^^^^ + +error: binding's name is too similar to existing binding + --> $DIR/similar_names.rs:67:9 + | +LL | let parsee: i32; + | ^^^^^^ + | +note: existing binding defined here + --> $DIR/similar_names.rs:65:9 + | +LL | let parser: i32; + | ^^^^^^ +help: separate the discriminating character by an underscore like: `parse_e` + --> $DIR/similar_names.rs:67:9 + | +LL | let parsee: i32; + | ^^^^^^ + +error: binding's name is too similar to existing binding + --> $DIR/similar_names.rs:81:16 + | +LL | bpple: sprang, + | ^^^^^^ + | +note: existing binding defined here + --> $DIR/similar_names.rs:80:16 + | +LL | apple: spring, + | ^^^^^^ + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/tests/ui/single_char_pattern.fixed b/src/tools/clippy/tests/ui/single_char_pattern.fixed new file mode 100644 index 00000000000..3871c4f2268 --- /dev/null +++ b/src/tools/clippy/tests/ui/single_char_pattern.fixed @@ -0,0 +1,63 @@ +// run-rustfix + +#![allow(unused_must_use)] + +use std::collections::HashSet; + +fn main() { + let x = "foo"; + x.split('x'); + x.split("xx"); + x.split('x'); + + let y = "x"; + x.split(y); + // Not yet testing for multi-byte characters + // Changing `r.len() == 1` to `r.chars().count() == 1` in `lint_clippy::single_char_pattern` + // should have done this but produced an ICE + // + // We may not want to suggest changing these anyway + // See: https://github.com/rust-lang/rust-clippy/issues/650#issuecomment-184328984 + x.split("ß"); + x.split("ℝ"); + x.split("💣"); + // Can't use this lint for unicode code points which don't fit in a char + x.split("❤️"); + x.contains('x'); + x.starts_with('x'); + x.ends_with('x'); + x.find('x'); + x.rfind('x'); + x.rsplit('x'); + x.split_terminator('x'); + x.rsplit_terminator('x'); + x.splitn(0, 'x'); + x.rsplitn(0, 'x'); + x.matches('x'); + x.rmatches('x'); + x.match_indices('x'); + x.rmatch_indices('x'); + x.trim_start_matches('x'); + x.trim_end_matches('x'); + // Make sure we escape characters correctly. + x.split('\n'); + x.split('\''); + x.split('\''); + + let h = HashSet::<String>::new(); + h.contains("X"); // should not warn + + x.replace(";", ",").split(','); // issue #2978 + x.starts_with('\x03'); // issue #2996 + + // Issue #3204 + const S: &str = "#"; + x.find(S); + + // Raw string + x.split('a'); + x.split('a'); + x.split('a'); + x.split('\''); + x.split('#'); +} diff --git a/src/tools/clippy/tests/ui/single_char_pattern.rs b/src/tools/clippy/tests/ui/single_char_pattern.rs new file mode 100644 index 00000000000..32afe339cd8 --- /dev/null +++ b/src/tools/clippy/tests/ui/single_char_pattern.rs @@ -0,0 +1,63 @@ +// run-rustfix + +#![allow(unused_must_use)] + +use std::collections::HashSet; + +fn main() { + let x = "foo"; + x.split("x"); + x.split("xx"); + x.split('x'); + + let y = "x"; + x.split(y); + // Not yet testing for multi-byte characters + // Changing `r.len() == 1` to `r.chars().count() == 1` in `lint_clippy::single_char_pattern` + // should have done this but produced an ICE + // + // We may not want to suggest changing these anyway + // See: https://github.com/rust-lang/rust-clippy/issues/650#issuecomment-184328984 + x.split("ß"); + x.split("ℝ"); + x.split("💣"); + // Can't use this lint for unicode code points which don't fit in a char + x.split("❤️"); + x.contains("x"); + x.starts_with("x"); + x.ends_with("x"); + x.find("x"); + x.rfind("x"); + x.rsplit("x"); + x.split_terminator("x"); + x.rsplit_terminator("x"); + x.splitn(0, "x"); + x.rsplitn(0, "x"); + x.matches("x"); + x.rmatches("x"); + x.match_indices("x"); + x.rmatch_indices("x"); + x.trim_start_matches("x"); + x.trim_end_matches("x"); + // Make sure we escape characters correctly. + x.split("\n"); + x.split("'"); + x.split("\'"); + + let h = HashSet::<String>::new(); + h.contains("X"); // should not warn + + x.replace(";", ",").split(","); // issue #2978 + x.starts_with("\x03"); // issue #2996 + + // Issue #3204 + const S: &str = "#"; + x.find(S); + + // Raw string + x.split(r"a"); + x.split(r#"a"#); + x.split(r###"a"###); + x.split(r###"'"###); + x.split(r###"#"###); +} diff --git a/src/tools/clippy/tests/ui/single_char_pattern.stderr b/src/tools/clippy/tests/ui/single_char_pattern.stderr new file mode 100644 index 00000000000..fe7211c53f8 --- /dev/null +++ b/src/tools/clippy/tests/ui/single_char_pattern.stderr @@ -0,0 +1,166 @@ +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:9:13 + | +LL | x.split("x"); + | ^^^ help: try using a `char` instead: `'x'` + | + = note: `-D clippy::single-char-pattern` implied by `-D warnings` + +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:26:16 + | +LL | x.contains("x"); + | ^^^ help: try using a `char` instead: `'x'` + +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:27:19 + | +LL | x.starts_with("x"); + | ^^^ help: try using a `char` instead: `'x'` + +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:28:17 + | +LL | x.ends_with("x"); + | ^^^ help: try using a `char` instead: `'x'` + +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:29:12 + | +LL | x.find("x"); + | ^^^ help: try using a `char` instead: `'x'` + +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:30:13 + | +LL | x.rfind("x"); + | ^^^ help: try using a `char` instead: `'x'` + +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:31:14 + | +LL | x.rsplit("x"); + | ^^^ help: try using a `char` instead: `'x'` + +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:32:24 + | +LL | x.split_terminator("x"); + | ^^^ help: try using a `char` instead: `'x'` + +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:33:25 + | +LL | x.rsplit_terminator("x"); + | ^^^ help: try using a `char` instead: `'x'` + +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:34:17 + | +LL | x.splitn(0, "x"); + | ^^^ help: try using a `char` instead: `'x'` + +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:35:18 + | +LL | x.rsplitn(0, "x"); + | ^^^ help: try using a `char` instead: `'x'` + +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:36:15 + | +LL | x.matches("x"); + | ^^^ help: try using a `char` instead: `'x'` + +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:37:16 + | +LL | x.rmatches("x"); + | ^^^ help: try using a `char` instead: `'x'` + +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:38:21 + | +LL | x.match_indices("x"); + | ^^^ help: try using a `char` instead: `'x'` + +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:39:22 + | +LL | x.rmatch_indices("x"); + | ^^^ help: try using a `char` instead: `'x'` + +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:40:26 + | +LL | x.trim_start_matches("x"); + | ^^^ help: try using a `char` instead: `'x'` + +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:41:24 + | +LL | x.trim_end_matches("x"); + | ^^^ help: try using a `char` instead: `'x'` + +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:43:13 + | +LL | x.split("/n"); + | ^^^^ help: try using a `char` instead: `'/n'` + +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:44:13 + | +LL | x.split("'"); + | ^^^ help: try using a `char` instead: `'/''` + +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:45:13 + | +LL | x.split("/'"); + | ^^^^ help: try using a `char` instead: `'/''` + +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:50:31 + | +LL | x.replace(";", ",").split(","); // issue #2978 + | ^^^ help: try using a `char` instead: `','` + +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:51:19 + | +LL | x.starts_with("/x03"); // issue #2996 + | ^^^^^^ help: try using a `char` instead: `'/x03'` + +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:58:13 + | +LL | x.split(r"a"); + | ^^^^ help: try using a `char` instead: `'a'` + +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:59:13 + | +LL | x.split(r#"a"#); + | ^^^^^^ help: try using a `char` instead: `'a'` + +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:60:13 + | +LL | x.split(r###"a"###); + | ^^^^^^^^^^ help: try using a `char` instead: `'a'` + +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:61:13 + | +LL | x.split(r###"'"###); + | ^^^^^^^^^^ help: try using a `char` instead: `'/''` + +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:62:13 + | +LL | x.split(r###"#"###); + | ^^^^^^^^^^ help: try using a `char` instead: `'#'` + +error: aborting due to 27 previous errors + diff --git a/src/tools/clippy/tests/ui/single_component_path_imports.fixed b/src/tools/clippy/tests/ui/single_component_path_imports.fixed new file mode 100644 index 00000000000..a7a8499b58f --- /dev/null +++ b/src/tools/clippy/tests/ui/single_component_path_imports.fixed @@ -0,0 +1,21 @@ +// run-rustfix +// edition:2018 +#![warn(clippy::single_component_path_imports)] +#![allow(unused_imports)] + + +use serde as edres; +pub use serde; + +macro_rules! m { + () => { + use regex; + }; +} + +fn main() { + regex::Regex::new(r"^\d{4}-\d{2}-\d{2}$").unwrap(); + + // False positive #5154, shouldn't trigger lint. + m!(); +} diff --git a/src/tools/clippy/tests/ui/single_component_path_imports.rs b/src/tools/clippy/tests/ui/single_component_path_imports.rs new file mode 100644 index 00000000000..9a427e90ad3 --- /dev/null +++ b/src/tools/clippy/tests/ui/single_component_path_imports.rs @@ -0,0 +1,21 @@ +// run-rustfix +// edition:2018 +#![warn(clippy::single_component_path_imports)] +#![allow(unused_imports)] + +use regex; +use serde as edres; +pub use serde; + +macro_rules! m { + () => { + use regex; + }; +} + +fn main() { + regex::Regex::new(r"^\d{4}-\d{2}-\d{2}$").unwrap(); + + // False positive #5154, shouldn't trigger lint. + m!(); +} diff --git a/src/tools/clippy/tests/ui/single_component_path_imports.stderr b/src/tools/clippy/tests/ui/single_component_path_imports.stderr new file mode 100644 index 00000000000..519ada0169a --- /dev/null +++ b/src/tools/clippy/tests/ui/single_component_path_imports.stderr @@ -0,0 +1,10 @@ +error: this import is redundant + --> $DIR/single_component_path_imports.rs:6:1 + | +LL | use regex; + | ^^^^^^^^^^ help: remove it entirely + | + = note: `-D clippy::single-component-path-imports` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/single_match.rs b/src/tools/clippy/tests/ui/single_match.rs new file mode 100644 index 00000000000..1c55af5dfb6 --- /dev/null +++ b/src/tools/clippy/tests/ui/single_match.rs @@ -0,0 +1,95 @@ +#![warn(clippy::single_match)] + +fn dummy() {} + +fn single_match() { + let x = Some(1u8); + + match x { + Some(y) => { + println!("{:?}", y); + }, + _ => (), + }; + + let x = Some(1u8); + match x { + // Note the missing block braces. + // We suggest `if let Some(y) = x { .. }` because the macro + // is expanded before we can do anything. + Some(y) => println!("{:?}", y), + _ => (), + } + + let z = (1u8, 1u8); + match z { + (2..=3, 7..=9) => dummy(), + _ => {}, + }; + + // Not linted (pattern guards used) + match x { + Some(y) if y == 0 => println!("{:?}", y), + _ => (), + } + + // Not linted (no block with statements in the single arm) + match z { + (2..=3, 7..=9) => println!("{:?}", z), + _ => println!("nope"), + } +} + +enum Foo { + Bar, + Baz(u8), +} +use std::borrow::Cow; +use Foo::*; + +fn single_match_know_enum() { + let x = Some(1u8); + let y: Result<_, i8> = Ok(1i8); + + match x { + Some(y) => dummy(), + None => (), + }; + + match y { + Ok(y) => dummy(), + Err(..) => (), + }; + + let c = Cow::Borrowed(""); + + match c { + Cow::Borrowed(..) => dummy(), + Cow::Owned(..) => (), + }; + + let z = Foo::Bar; + // no warning + match z { + Bar => println!("42"), + Baz(_) => (), + } + + match z { + Baz(_) => println!("42"), + Bar => (), + } +} + +macro_rules! single_match { + ($num:literal) => { + match $num { + 15 => println!("15"), + _ => (), + } + }; +} + +fn main() { + single_match!(5); +} diff --git a/src/tools/clippy/tests/ui/single_match.stderr b/src/tools/clippy/tests/ui/single_match.stderr new file mode 100644 index 00000000000..f69554d75f9 --- /dev/null +++ b/src/tools/clippy/tests/ui/single_match.stderr @@ -0,0 +1,69 @@ +error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let` + --> $DIR/single_match.rs:8:5 + | +LL | / match x { +LL | | Some(y) => { +LL | | println!("{:?}", y); +LL | | }, +LL | | _ => (), +LL | | }; + | |_____^ + | + = note: `-D clippy::single-match` implied by `-D warnings` +help: try this + | +LL | if let Some(y) = x { +LL | println!("{:?}", y); +LL | }; + | + +error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let` + --> $DIR/single_match.rs:16:5 + | +LL | / match x { +LL | | // Note the missing block braces. +LL | | // We suggest `if let Some(y) = x { .. }` because the macro +LL | | // is expanded before we can do anything. +LL | | Some(y) => println!("{:?}", y), +LL | | _ => (), +LL | | } + | |_____^ help: try this: `if let Some(y) = x { println!("{:?}", y) }` + +error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let` + --> $DIR/single_match.rs:25:5 + | +LL | / match z { +LL | | (2..=3, 7..=9) => dummy(), +LL | | _ => {}, +LL | | }; + | |_____^ help: try this: `if let (2..=3, 7..=9) = z { dummy() }` + +error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let` + --> $DIR/single_match.rs:54:5 + | +LL | / match x { +LL | | Some(y) => dummy(), +LL | | None => (), +LL | | }; + | |_____^ help: try this: `if let Some(y) = x { dummy() }` + +error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let` + --> $DIR/single_match.rs:59:5 + | +LL | / match y { +LL | | Ok(y) => dummy(), +LL | | Err(..) => (), +LL | | }; + | |_____^ help: try this: `if let Ok(y) = y { dummy() }` + +error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let` + --> $DIR/single_match.rs:66:5 + | +LL | / match c { +LL | | Cow::Borrowed(..) => dummy(), +LL | | Cow::Owned(..) => (), +LL | | }; + | |_____^ help: try this: `if let Cow::Borrowed(..) = c { dummy() }` + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/single_match_else.rs b/src/tools/clippy/tests/ui/single_match_else.rs new file mode 100644 index 00000000000..34193be0b75 --- /dev/null +++ b/src/tools/clippy/tests/ui/single_match_else.rs @@ -0,0 +1,35 @@ +#![warn(clippy::single_match_else)] + +enum ExprNode { + ExprAddrOf, + Butterflies, + Unicorns, +} + +static NODE: ExprNode = ExprNode::Unicorns; + +fn unwrap_addr() -> Option<&'static ExprNode> { + match ExprNode::Butterflies { + ExprNode::ExprAddrOf => Some(&NODE), + _ => { + let x = 5; + None + }, + } +} + +macro_rules! unwrap_addr { + ($expression:expr) => { + match $expression { + ExprNode::ExprAddrOf => Some(&NODE), + _ => { + let x = 5; + None + }, + } + }; +} + +fn main() { + unwrap_addr!(ExprNode::Unicorns); +} diff --git a/src/tools/clippy/tests/ui/single_match_else.stderr b/src/tools/clippy/tests/ui/single_match_else.stderr new file mode 100644 index 00000000000..59861d46eb3 --- /dev/null +++ b/src/tools/clippy/tests/ui/single_match_else.stderr @@ -0,0 +1,23 @@ +error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let` + --> $DIR/single_match_else.rs:12:5 + | +LL | / match ExprNode::Butterflies { +LL | | ExprNode::ExprAddrOf => Some(&NODE), +LL | | _ => { +LL | | let x = 5; +LL | | None +LL | | }, +LL | | } + | |_____^ + | + = note: `-D clippy::single-match-else` implied by `-D warnings` +help: try this + | +LL | if let ExprNode::ExprAddrOf = ExprNode::Butterflies { Some(&NODE) } else { +LL | let x = 5; +LL | None +LL | } + | + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/skip_while_next.rs b/src/tools/clippy/tests/ui/skip_while_next.rs new file mode 100644 index 00000000000..a522c0f08b2 --- /dev/null +++ b/src/tools/clippy/tests/ui/skip_while_next.rs @@ -0,0 +1,29 @@ +// aux-build:option_helpers.rs + +#![warn(clippy::skip_while_next)] +#![allow(clippy::blacklisted_name)] + +extern crate option_helpers; +use option_helpers::IteratorFalsePositives; + +#[rustfmt::skip] +fn skip_while_next() { + let v = vec![3, 2, 1, 0, -1, -2, -3]; + + // Single-line case. + let _ = v.iter().skip_while(|&x| *x < 0).next(); + + // Multi-line case. + let _ = v.iter().skip_while(|&x| { + *x < 0 + } + ).next(); + + // Check that hat we don't lint if the caller is not an `Iterator`. + let foo = IteratorFalsePositives { foo: 0 }; + let _ = foo.skip_while().next(); +} + +fn main() { + skip_while_next(); +} diff --git a/src/tools/clippy/tests/ui/skip_while_next.stderr b/src/tools/clippy/tests/ui/skip_while_next.stderr new file mode 100644 index 00000000000..a6b7bcd63ff --- /dev/null +++ b/src/tools/clippy/tests/ui/skip_while_next.stderr @@ -0,0 +1,23 @@ +error: called `skip_while(p).next()` on an `Iterator` + --> $DIR/skip_while_next.rs:14:13 + | +LL | let _ = v.iter().skip_while(|&x| *x < 0).next(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::skip-while-next` implied by `-D warnings` + = help: this is more succinctly expressed by calling `.find(!p)` instead + +error: called `skip_while(p).next()` on an `Iterator` + --> $DIR/skip_while_next.rs:17:13 + | +LL | let _ = v.iter().skip_while(|&x| { + | _____________^ +LL | | *x < 0 +LL | | } +LL | | ).next(); + | |___________________________^ + | + = help: this is more succinctly expressed by calling `.find(!p)` instead + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/slow_vector_initialization.rs b/src/tools/clippy/tests/ui/slow_vector_initialization.rs new file mode 100644 index 00000000000..c5ae3ff769b --- /dev/null +++ b/src/tools/clippy/tests/ui/slow_vector_initialization.rs @@ -0,0 +1,63 @@ +use std::iter::repeat; + +fn main() { + resize_vector(); + extend_vector(); + mixed_extend_resize_vector(); +} + +fn extend_vector() { + // Extend with constant expression + let len = 300; + let mut vec1 = Vec::with_capacity(len); + vec1.extend(repeat(0).take(len)); + + // Extend with len expression + let mut vec2 = Vec::with_capacity(len - 10); + vec2.extend(repeat(0).take(len - 10)); + + // Extend with mismatching expression should not be warned + let mut vec3 = Vec::with_capacity(24322); + vec3.extend(repeat(0).take(2)); +} + +fn mixed_extend_resize_vector() { + // Mismatching len + let mut mismatching_len = Vec::with_capacity(30); + mismatching_len.extend(repeat(0).take(40)); + + // Slow initialization + let mut resized_vec = Vec::with_capacity(30); + resized_vec.resize(30, 0); + + let mut extend_vec = Vec::with_capacity(30); + extend_vec.extend(repeat(0).take(30)); +} + +fn resize_vector() { + // Resize with constant expression + let len = 300; + let mut vec1 = Vec::with_capacity(len); + vec1.resize(len, 0); + + // Resize mismatch len + let mut vec2 = Vec::with_capacity(200); + vec2.resize(10, 0); + + // Resize with len expression + let mut vec3 = Vec::with_capacity(len - 10); + vec3.resize(len - 10, 0); + + // Reinitialization should be warned + vec1 = Vec::with_capacity(10); + vec1.resize(10, 0); +} + +fn do_stuff(vec: &mut Vec<u8>) {} + +fn extend_vector_with_manipulations_between() { + let len = 300; + let mut vec1: Vec<u8> = Vec::with_capacity(len); + do_stuff(&mut vec1); + vec1.extend(repeat(0).take(len)); +} diff --git a/src/tools/clippy/tests/ui/slow_vector_initialization.stderr b/src/tools/clippy/tests/ui/slow_vector_initialization.stderr new file mode 100644 index 00000000000..5d2788ec260 --- /dev/null +++ b/src/tools/clippy/tests/ui/slow_vector_initialization.stderr @@ -0,0 +1,60 @@ +error: slow zero-filling initialization + --> $DIR/slow_vector_initialization.rs:13:5 + | +LL | let mut vec1 = Vec::with_capacity(len); + | ----------------------- help: consider replace allocation with: `vec![0; len]` +LL | vec1.extend(repeat(0).take(len)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::slow-vector-initialization` implied by `-D warnings` + +error: slow zero-filling initialization + --> $DIR/slow_vector_initialization.rs:17:5 + | +LL | let mut vec2 = Vec::with_capacity(len - 10); + | ---------------------------- help: consider replace allocation with: `vec![0; len - 10]` +LL | vec2.extend(repeat(0).take(len - 10)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: slow zero-filling initialization + --> $DIR/slow_vector_initialization.rs:31:5 + | +LL | let mut resized_vec = Vec::with_capacity(30); + | ---------------------- help: consider replace allocation with: `vec![0; 30]` +LL | resized_vec.resize(30, 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: slow zero-filling initialization + --> $DIR/slow_vector_initialization.rs:34:5 + | +LL | let mut extend_vec = Vec::with_capacity(30); + | ---------------------- help: consider replace allocation with: `vec![0; 30]` +LL | extend_vec.extend(repeat(0).take(30)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: slow zero-filling initialization + --> $DIR/slow_vector_initialization.rs:41:5 + | +LL | let mut vec1 = Vec::with_capacity(len); + | ----------------------- help: consider replace allocation with: `vec![0; len]` +LL | vec1.resize(len, 0); + | ^^^^^^^^^^^^^^^^^^^ + +error: slow zero-filling initialization + --> $DIR/slow_vector_initialization.rs:49:5 + | +LL | let mut vec3 = Vec::with_capacity(len - 10); + | ---------------------------- help: consider replace allocation with: `vec![0; len - 10]` +LL | vec3.resize(len - 10, 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: slow zero-filling initialization + --> $DIR/slow_vector_initialization.rs:53:5 + | +LL | vec1 = Vec::with_capacity(10); + | ---------------------- help: consider replace allocation with: `vec![0; 10]` +LL | vec1.resize(10, 0); + | ^^^^^^^^^^^^^^^^^^ + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/tests/ui/starts_ends_with.fixed b/src/tools/clippy/tests/ui/starts_ends_with.fixed new file mode 100644 index 00000000000..7dfcf9c91e4 --- /dev/null +++ b/src/tools/clippy/tests/ui/starts_ends_with.fixed @@ -0,0 +1,46 @@ +// run-rustfix +#![allow(dead_code, unused_must_use)] + +fn main() {} + +#[allow(clippy::unnecessary_operation)] +fn starts_with() { + "".starts_with(' '); + !"".starts_with(' '); +} + +fn chars_cmp_with_unwrap() { + let s = String::from("foo"); + if s.starts_with('f') { + // s.starts_with('f') + // Nothing here + } + if s.ends_with('o') { + // s.ends_with('o') + // Nothing here + } + if s.ends_with('o') { + // s.ends_with('o') + // Nothing here + } + if !s.starts_with('f') { + // !s.starts_with('f') + // Nothing here + } + if !s.ends_with('o') { + // !s.ends_with('o') + // Nothing here + } + if !s.ends_with('o') { + // !s.ends_with('o') + // Nothing here + } +} + +#[allow(clippy::unnecessary_operation)] +fn ends_with() { + "".ends_with(' '); + !"".ends_with(' '); + "".ends_with(' '); + !"".ends_with(' '); +} diff --git a/src/tools/clippy/tests/ui/starts_ends_with.rs b/src/tools/clippy/tests/ui/starts_ends_with.rs new file mode 100644 index 00000000000..e48a4246354 --- /dev/null +++ b/src/tools/clippy/tests/ui/starts_ends_with.rs @@ -0,0 +1,46 @@ +// run-rustfix +#![allow(dead_code, unused_must_use)] + +fn main() {} + +#[allow(clippy::unnecessary_operation)] +fn starts_with() { + "".chars().next() == Some(' '); + Some(' ') != "".chars().next(); +} + +fn chars_cmp_with_unwrap() { + let s = String::from("foo"); + if s.chars().next().unwrap() == 'f' { + // s.starts_with('f') + // Nothing here + } + if s.chars().next_back().unwrap() == 'o' { + // s.ends_with('o') + // Nothing here + } + if s.chars().last().unwrap() == 'o' { + // s.ends_with('o') + // Nothing here + } + if s.chars().next().unwrap() != 'f' { + // !s.starts_with('f') + // Nothing here + } + if s.chars().next_back().unwrap() != 'o' { + // !s.ends_with('o') + // Nothing here + } + if s.chars().last().unwrap() != 'o' { + // !s.ends_with('o') + // Nothing here + } +} + +#[allow(clippy::unnecessary_operation)] +fn ends_with() { + "".chars().last() == Some(' '); + Some(' ') != "".chars().last(); + "".chars().next_back() == Some(' '); + Some(' ') != "".chars().next_back(); +} diff --git a/src/tools/clippy/tests/ui/starts_ends_with.stderr b/src/tools/clippy/tests/ui/starts_ends_with.stderr new file mode 100644 index 00000000000..7c726d0e010 --- /dev/null +++ b/src/tools/clippy/tests/ui/starts_ends_with.stderr @@ -0,0 +1,78 @@ +error: you should use the `starts_with` method + --> $DIR/starts_ends_with.rs:8:5 + | +LL | "".chars().next() == Some(' '); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `"".starts_with(' ')` + | + = note: `-D clippy::chars-next-cmp` implied by `-D warnings` + +error: you should use the `starts_with` method + --> $DIR/starts_ends_with.rs:9:5 + | +LL | Some(' ') != "".chars().next(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `!"".starts_with(' ')` + +error: you should use the `starts_with` method + --> $DIR/starts_ends_with.rs:14:8 + | +LL | if s.chars().next().unwrap() == 'f' { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `s.starts_with('f')` + +error: you should use the `ends_with` method + --> $DIR/starts_ends_with.rs:18:8 + | +LL | if s.chars().next_back().unwrap() == 'o' { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `s.ends_with('o')` + | + = note: `-D clippy::chars-last-cmp` implied by `-D warnings` + +error: you should use the `ends_with` method + --> $DIR/starts_ends_with.rs:22:8 + | +LL | if s.chars().last().unwrap() == 'o' { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `s.ends_with('o')` + +error: you should use the `starts_with` method + --> $DIR/starts_ends_with.rs:26:8 + | +LL | if s.chars().next().unwrap() != 'f' { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `!s.starts_with('f')` + +error: you should use the `ends_with` method + --> $DIR/starts_ends_with.rs:30:8 + | +LL | if s.chars().next_back().unwrap() != 'o' { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `!s.ends_with('o')` + +error: you should use the `ends_with` method + --> $DIR/starts_ends_with.rs:34:8 + | +LL | if s.chars().last().unwrap() != 'o' { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `!s.ends_with('o')` + +error: you should use the `ends_with` method + --> $DIR/starts_ends_with.rs:42:5 + | +LL | "".chars().last() == Some(' '); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `"".ends_with(' ')` + +error: you should use the `ends_with` method + --> $DIR/starts_ends_with.rs:43:5 + | +LL | Some(' ') != "".chars().last(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `!"".ends_with(' ')` + +error: you should use the `ends_with` method + --> $DIR/starts_ends_with.rs:44:5 + | +LL | "".chars().next_back() == Some(' '); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `"".ends_with(' ')` + +error: you should use the `ends_with` method + --> $DIR/starts_ends_with.rs:45:5 + | +LL | Some(' ') != "".chars().next_back(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `!"".ends_with(' ')` + +error: aborting due to 12 previous errors + diff --git a/src/tools/clippy/tests/ui/string_add.rs b/src/tools/clippy/tests/ui/string_add.rs new file mode 100644 index 00000000000..30fd17c59e5 --- /dev/null +++ b/src/tools/clippy/tests/ui/string_add.rs @@ -0,0 +1,26 @@ +// aux-build:macro_rules.rs + +#[macro_use] +extern crate macro_rules; + +#[warn(clippy::string_add)] +#[allow(clippy::string_add_assign, unused)] +fn main() { + // ignores assignment distinction + let mut x = "".to_owned(); + + for _ in 1..3 { + x = x + "."; + } + + let y = "".to_owned(); + let z = y + "..."; + + assert_eq!(&x, &z); + + let mut x = 1; + x = x + 1; + assert_eq!(2, x); + + string_add!(); +} diff --git a/src/tools/clippy/tests/ui/string_add.stderr b/src/tools/clippy/tests/ui/string_add.stderr new file mode 100644 index 00000000000..3987641c75a --- /dev/null +++ b/src/tools/clippy/tests/ui/string_add.stderr @@ -0,0 +1,30 @@ +error: manual implementation of an assign operation + --> $DIR/string_add.rs:13:9 + | +LL | x = x + "."; + | ^^^^^^^^^^^ help: replace it with: `x += "."` + | + = note: `-D clippy::assign-op-pattern` implied by `-D warnings` + +error: you added something to a string. Consider using `String::push_str()` instead + --> $DIR/string_add.rs:13:13 + | +LL | x = x + "."; + | ^^^^^^^ + | + = note: `-D clippy::string-add` implied by `-D warnings` + +error: you added something to a string. Consider using `String::push_str()` instead + --> $DIR/string_add.rs:17:13 + | +LL | let z = y + "..."; + | ^^^^^^^^^ + +error: manual implementation of an assign operation + --> $DIR/string_add.rs:22:5 + | +LL | x = x + 1; + | ^^^^^^^^^ help: replace it with: `x += 1` + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/string_add_assign.fixed b/src/tools/clippy/tests/ui/string_add_assign.fixed new file mode 100644 index 00000000000..db71bab1e52 --- /dev/null +++ b/src/tools/clippy/tests/ui/string_add_assign.fixed @@ -0,0 +1,21 @@ +// run-rustfix + +#[allow(clippy::string_add, unused)] +#[warn(clippy::string_add_assign)] +fn main() { + // ignores assignment distinction + let mut x = "".to_owned(); + + for _ in 1..3 { + x += "."; + } + + let y = "".to_owned(); + let z = y + "..."; + + assert_eq!(&x, &z); + + let mut x = 1; + x += 1; + assert_eq!(2, x); +} diff --git a/src/tools/clippy/tests/ui/string_add_assign.rs b/src/tools/clippy/tests/ui/string_add_assign.rs new file mode 100644 index 00000000000..644991945cb --- /dev/null +++ b/src/tools/clippy/tests/ui/string_add_assign.rs @@ -0,0 +1,21 @@ +// run-rustfix + +#[allow(clippy::string_add, unused)] +#[warn(clippy::string_add_assign)] +fn main() { + // ignores assignment distinction + let mut x = "".to_owned(); + + for _ in 1..3 { + x = x + "."; + } + + let y = "".to_owned(); + let z = y + "..."; + + assert_eq!(&x, &z); + + let mut x = 1; + x = x + 1; + assert_eq!(2, x); +} diff --git a/src/tools/clippy/tests/ui/string_add_assign.stderr b/src/tools/clippy/tests/ui/string_add_assign.stderr new file mode 100644 index 00000000000..7676175c1b8 --- /dev/null +++ b/src/tools/clippy/tests/ui/string_add_assign.stderr @@ -0,0 +1,24 @@ +error: you assigned the result of adding something to this string. Consider using `String::push_str()` instead + --> $DIR/string_add_assign.rs:10:9 + | +LL | x = x + "."; + | ^^^^^^^^^^^ + | + = note: `-D clippy::string-add-assign` implied by `-D warnings` + +error: manual implementation of an assign operation + --> $DIR/string_add_assign.rs:10:9 + | +LL | x = x + "."; + | ^^^^^^^^^^^ help: replace it with: `x += "."` + | + = note: `-D clippy::assign-op-pattern` implied by `-D warnings` + +error: manual implementation of an assign operation + --> $DIR/string_add_assign.rs:19:5 + | +LL | x = x + 1; + | ^^^^^^^^^ help: replace it with: `x += 1` + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/string_extend.fixed b/src/tools/clippy/tests/ui/string_extend.fixed new file mode 100644 index 00000000000..1883a9f8325 --- /dev/null +++ b/src/tools/clippy/tests/ui/string_extend.fixed @@ -0,0 +1,32 @@ +// run-rustfix + +#[derive(Copy, Clone)] +struct HasChars; + +impl HasChars { + fn chars(self) -> std::str::Chars<'static> { + "HasChars".chars() + } +} + +fn main() { + let abc = "abc"; + let def = String::from("def"); + let mut s = String::new(); + + s.push_str(abc); + s.push_str(abc); + + s.push_str("abc"); + s.push_str("abc"); + + s.push_str(&def); + s.push_str(&def); + + s.extend(abc.chars().skip(1)); + s.extend("abc".chars().skip(1)); + s.extend(['a', 'b', 'c'].iter()); + + let f = HasChars; + s.extend(f.chars()); +} diff --git a/src/tools/clippy/tests/ui/string_extend.rs b/src/tools/clippy/tests/ui/string_extend.rs new file mode 100644 index 00000000000..07d0baa1be6 --- /dev/null +++ b/src/tools/clippy/tests/ui/string_extend.rs @@ -0,0 +1,32 @@ +// run-rustfix + +#[derive(Copy, Clone)] +struct HasChars; + +impl HasChars { + fn chars(self) -> std::str::Chars<'static> { + "HasChars".chars() + } +} + +fn main() { + let abc = "abc"; + let def = String::from("def"); + let mut s = String::new(); + + s.push_str(abc); + s.extend(abc.chars()); + + s.push_str("abc"); + s.extend("abc".chars()); + + s.push_str(&def); + s.extend(def.chars()); + + s.extend(abc.chars().skip(1)); + s.extend("abc".chars().skip(1)); + s.extend(['a', 'b', 'c'].iter()); + + let f = HasChars; + s.extend(f.chars()); +} diff --git a/src/tools/clippy/tests/ui/string_extend.stderr b/src/tools/clippy/tests/ui/string_extend.stderr new file mode 100644 index 00000000000..6af8c9e1662 --- /dev/null +++ b/src/tools/clippy/tests/ui/string_extend.stderr @@ -0,0 +1,22 @@ +error: calling `.extend(_.chars())` + --> $DIR/string_extend.rs:18:5 + | +LL | s.extend(abc.chars()); + | ^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.push_str(abc)` + | + = note: `-D clippy::string-extend-chars` implied by `-D warnings` + +error: calling `.extend(_.chars())` + --> $DIR/string_extend.rs:21:5 + | +LL | s.extend("abc".chars()); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.push_str("abc")` + +error: calling `.extend(_.chars())` + --> $DIR/string_extend.rs:24:5 + | +LL | s.extend(def.chars()); + | ^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.push_str(&def)` + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/string_lit_as_bytes.fixed b/src/tools/clippy/tests/ui/string_lit_as_bytes.fixed new file mode 100644 index 00000000000..7ad272ade5f --- /dev/null +++ b/src/tools/clippy/tests/ui/string_lit_as_bytes.fixed @@ -0,0 +1,22 @@ +// run-rustfix + +#![allow(dead_code, unused_variables)] +#![warn(clippy::string_lit_as_bytes)] + +fn str_lit_as_bytes() { + let bs = b"hello there"; + + let bs = br###"raw string with 3# plus " ""###; + + // no warning, because these cannot be written as byte string literals: + let ubs = "☃".as_bytes(); + let ubs = "hello there! this is a very long string".as_bytes(); + + let strify = stringify!(foobar).as_bytes(); + + let includestr = include_bytes!("entry_unfixable.rs"); + + let _ = b"string with newline\t\n"; +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/string_lit_as_bytes.rs b/src/tools/clippy/tests/ui/string_lit_as_bytes.rs new file mode 100644 index 00000000000..1bf4538b7c9 --- /dev/null +++ b/src/tools/clippy/tests/ui/string_lit_as_bytes.rs @@ -0,0 +1,22 @@ +// run-rustfix + +#![allow(dead_code, unused_variables)] +#![warn(clippy::string_lit_as_bytes)] + +fn str_lit_as_bytes() { + let bs = "hello there".as_bytes(); + + let bs = r###"raw string with 3# plus " ""###.as_bytes(); + + // no warning, because these cannot be written as byte string literals: + let ubs = "☃".as_bytes(); + let ubs = "hello there! this is a very long string".as_bytes(); + + let strify = stringify!(foobar).as_bytes(); + + let includestr = include_str!("entry_unfixable.rs").as_bytes(); + + let _ = "string with newline\t\n".as_bytes(); +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/string_lit_as_bytes.stderr b/src/tools/clippy/tests/ui/string_lit_as_bytes.stderr new file mode 100644 index 00000000000..ff6e3346dfc --- /dev/null +++ b/src/tools/clippy/tests/ui/string_lit_as_bytes.stderr @@ -0,0 +1,28 @@ +error: calling `as_bytes()` on a string literal + --> $DIR/string_lit_as_bytes.rs:7:14 + | +LL | let bs = "hello there".as_bytes(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using a byte string literal instead: `b"hello there"` + | + = note: `-D clippy::string-lit-as-bytes` implied by `-D warnings` + +error: calling `as_bytes()` on a string literal + --> $DIR/string_lit_as_bytes.rs:9:14 + | +LL | let bs = r###"raw string with 3# plus " ""###.as_bytes(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using a byte string literal instead: `br###"raw string with 3# plus " ""###` + +error: calling `as_bytes()` on `include_str!(..)` + --> $DIR/string_lit_as_bytes.rs:17:22 + | +LL | let includestr = include_str!("entry_unfixable.rs").as_bytes(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `include_bytes!(..)` instead: `include_bytes!("entry_unfixable.rs")` + +error: calling `as_bytes()` on a string literal + --> $DIR/string_lit_as_bytes.rs:19:13 + | +LL | let _ = "string with newline/t/n".as_bytes(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using a byte string literal instead: `b"string with newline/t/n"` + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/struct_excessive_bools.rs b/src/tools/clippy/tests/ui/struct_excessive_bools.rs new file mode 100644 index 00000000000..ce4fe830a0a --- /dev/null +++ b/src/tools/clippy/tests/ui/struct_excessive_bools.rs @@ -0,0 +1,44 @@ +#![warn(clippy::struct_excessive_bools)] + +macro_rules! foo { + () => { + struct MacroFoo { + a: bool, + b: bool, + c: bool, + d: bool, + } + }; +} + +foo!(); + +struct Foo { + a: bool, + b: bool, + c: bool, +} + +struct BadFoo { + a: bool, + b: bool, + c: bool, + d: bool, +} + +#[repr(C)] +struct Bar { + a: bool, + b: bool, + c: bool, + d: bool, +} + +fn main() { + struct FooFoo { + a: bool, + b: bool, + c: bool, + d: bool, + } +} diff --git a/src/tools/clippy/tests/ui/struct_excessive_bools.stderr b/src/tools/clippy/tests/ui/struct_excessive_bools.stderr new file mode 100644 index 00000000000..2941bf2983a --- /dev/null +++ b/src/tools/clippy/tests/ui/struct_excessive_bools.stderr @@ -0,0 +1,29 @@ +error: more than 3 bools in a struct + --> $DIR/struct_excessive_bools.rs:22:1 + | +LL | / struct BadFoo { +LL | | a: bool, +LL | | b: bool, +LL | | c: bool, +LL | | d: bool, +LL | | } + | |_^ + | + = note: `-D clippy::struct-excessive-bools` implied by `-D warnings` + = help: consider using a state machine or refactoring bools into two-variant enums + +error: more than 3 bools in a struct + --> $DIR/struct_excessive_bools.rs:38:5 + | +LL | / struct FooFoo { +LL | | a: bool, +LL | | b: bool, +LL | | c: bool, +LL | | d: bool, +LL | | } + | |_____^ + | + = help: consider using a state machine or refactoring bools into two-variant enums + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/suspicious_arithmetic_impl.rs b/src/tools/clippy/tests/ui/suspicious_arithmetic_impl.rs new file mode 100644 index 00000000000..1f5b9811887 --- /dev/null +++ b/src/tools/clippy/tests/ui/suspicious_arithmetic_impl.rs @@ -0,0 +1,90 @@ +#![warn(clippy::suspicious_arithmetic_impl)] +use std::ops::{Add, AddAssign, BitOrAssign, Div, DivAssign, Mul, MulAssign, Sub}; + +#[derive(Copy, Clone)] +struct Foo(u32); + +impl Add for Foo { + type Output = Foo; + + fn add(self, other: Self) -> Self { + Foo(self.0 - other.0) + } +} + +impl AddAssign for Foo { + fn add_assign(&mut self, other: Foo) { + *self = *self - other; + } +} + +impl BitOrAssign for Foo { + fn bitor_assign(&mut self, other: Foo) { + let idx = other.0; + self.0 |= 1 << idx; // OK: BinOpKind::Shl part of AssignOp as child node + } +} + +impl MulAssign for Foo { + fn mul_assign(&mut self, other: Foo) { + self.0 /= other.0; + } +} + +impl DivAssign for Foo { + fn div_assign(&mut self, other: Foo) { + self.0 /= other.0; // OK: BinOpKind::Div == DivAssign + } +} + +impl Mul for Foo { + type Output = Foo; + + fn mul(self, other: Foo) -> Foo { + Foo(self.0 * other.0 % 42) // OK: BinOpKind::Rem part of BiExpr as parent node + } +} + +impl Sub for Foo { + type Output = Foo; + + fn sub(self, other: Self) -> Self { + Foo(self.0 * other.0 - 42) // OK: BinOpKind::Mul part of BiExpr as child node + } +} + +impl Div for Foo { + type Output = Foo; + + fn div(self, other: Self) -> Self { + Foo(do_nothing(self.0 + other.0) / 42) // OK: BinOpKind::Add part of BiExpr as child node + } +} + +struct Bar(i32); + +impl Add for Bar { + type Output = Bar; + + fn add(self, other: Self) -> Self { + Bar(self.0 & !other.0) // OK: UnNot part of BiExpr as child node + } +} + +impl Sub for Bar { + type Output = Bar; + + fn sub(self, other: Self) -> Self { + if self.0 <= other.0 { + Bar(-(self.0 & other.0)) // OK: UnNeg part of BiExpr as parent node + } else { + Bar(0) + } + } +} + +fn main() {} + +fn do_nothing(x: u32) -> u32 { + x +} diff --git a/src/tools/clippy/tests/ui/suspicious_arithmetic_impl.stderr b/src/tools/clippy/tests/ui/suspicious_arithmetic_impl.stderr new file mode 100644 index 00000000000..7e42d72c30b --- /dev/null +++ b/src/tools/clippy/tests/ui/suspicious_arithmetic_impl.stderr @@ -0,0 +1,24 @@ +error: Suspicious use of binary operator in `Add` impl + --> $DIR/suspicious_arithmetic_impl.rs:11:20 + | +LL | Foo(self.0 - other.0) + | ^ + | + = note: `-D clippy::suspicious-arithmetic-impl` implied by `-D warnings` + +error: Suspicious use of binary operator in `AddAssign` impl + --> $DIR/suspicious_arithmetic_impl.rs:17:23 + | +LL | *self = *self - other; + | ^ + | + = note: `#[deny(clippy::suspicious_op_assign_impl)]` on by default + +error: Suspicious use of binary operator in `MulAssign` impl + --> $DIR/suspicious_arithmetic_impl.rs:30:16 + | +LL | self.0 /= other.0; + | ^^ + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/suspicious_map.rs b/src/tools/clippy/tests/ui/suspicious_map.rs new file mode 100644 index 00000000000..d838d8fde21 --- /dev/null +++ b/src/tools/clippy/tests/ui/suspicious_map.rs @@ -0,0 +1,5 @@ +#![warn(clippy::suspicious_map)] + +fn main() { + let _ = (0..3).map(|x| x + 2).count(); +} diff --git a/src/tools/clippy/tests/ui/suspicious_map.stderr b/src/tools/clippy/tests/ui/suspicious_map.stderr new file mode 100644 index 00000000000..e1b4ba40376 --- /dev/null +++ b/src/tools/clippy/tests/ui/suspicious_map.stderr @@ -0,0 +1,11 @@ +error: this call to `map()` won't have an effect on the call to `count()` + --> $DIR/suspicious_map.rs:4:13 + | +LL | let _ = (0..3).map(|x| x + 2).count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::suspicious-map` implied by `-D warnings` + = help: make sure you did not confuse `map` with `filter` or `for_each` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/suspicious_unary_op_formatting.rs b/src/tools/clippy/tests/ui/suspicious_unary_op_formatting.rs new file mode 100644 index 00000000000..9564e373c24 --- /dev/null +++ b/src/tools/clippy/tests/ui/suspicious_unary_op_formatting.rs @@ -0,0 +1,23 @@ +#![warn(clippy::suspicious_unary_op_formatting)] + +#[rustfmt::skip] +fn main() { + // weird binary operator formatting: + let a = 42; + + if a >- 30 {} + if a >=- 30 {} + + let b = true; + let c = false; + + if b &&! c {} + + if a >- 30 {} + + // those are ok: + if a >-30 {} + if a < -30 {} + if b && !c {} + if a > - 30 {} +} diff --git a/src/tools/clippy/tests/ui/suspicious_unary_op_formatting.stderr b/src/tools/clippy/tests/ui/suspicious_unary_op_formatting.stderr new file mode 100644 index 00000000000..581527dcff8 --- /dev/null +++ b/src/tools/clippy/tests/ui/suspicious_unary_op_formatting.stderr @@ -0,0 +1,35 @@ +error: by not having a space between `>` and `-` it looks like `>-` is a single operator + --> $DIR/suspicious_unary_op_formatting.rs:8:9 + | +LL | if a >- 30 {} + | ^^^^ + | + = note: `-D clippy::suspicious-unary-op-formatting` implied by `-D warnings` + = help: put a space between `>` and `-` and remove the space after `-` + +error: by not having a space between `>=` and `-` it looks like `>=-` is a single operator + --> $DIR/suspicious_unary_op_formatting.rs:9:9 + | +LL | if a >=- 30 {} + | ^^^^^ + | + = help: put a space between `>=` and `-` and remove the space after `-` + +error: by not having a space between `&&` and `!` it looks like `&&!` is a single operator + --> $DIR/suspicious_unary_op_formatting.rs:14:9 + | +LL | if b &&! c {} + | ^^^^^ + | + = help: put a space between `&&` and `!` and remove the space after `!` + +error: by not having a space between `>` and `-` it looks like `>-` is a single operator + --> $DIR/suspicious_unary_op_formatting.rs:16:9 + | +LL | if a >- 30 {} + | ^^^^^^ + | + = help: put a space between `>` and `-` and remove the space after `-` + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/swap.fixed b/src/tools/clippy/tests/ui/swap.fixed new file mode 100644 index 00000000000..0f8f839a0d5 --- /dev/null +++ b/src/tools/clippy/tests/ui/swap.fixed @@ -0,0 +1,83 @@ +// run-rustfix + +#![warn(clippy::all)] +#![allow( + clippy::blacklisted_name, + clippy::no_effect, + clippy::redundant_clone, + redundant_semicolons, + unused_assignments +)] + +struct Foo(u32); + +#[derive(Clone)] +struct Bar { + a: u32, + b: u32, +} + +fn field() { + let mut bar = Bar { a: 1, b: 2 }; + + let temp = bar.a; + bar.a = bar.b; + bar.b = temp; + + let mut baz = vec![bar.clone(), bar.clone()]; + let temp = baz[0].a; + baz[0].a = baz[1].a; + baz[1].a = temp; +} + +fn array() { + let mut foo = [1, 2]; + foo.swap(0, 1); + + foo.swap(0, 1); +} + +fn slice() { + let foo = &mut [1, 2]; + foo.swap(0, 1); + + foo.swap(0, 1); +} + +fn unswappable_slice() { + let foo = &mut [vec![1, 2], vec![3, 4]]; + let temp = foo[0][1]; + foo[0][1] = foo[1][0]; + foo[1][0] = temp; + + // swap(foo[0][1], foo[1][0]) would fail +} + +fn vec() { + let mut foo = vec![1, 2]; + foo.swap(0, 1); + + foo.swap(0, 1); +} + +#[rustfmt::skip] +fn main() { + field(); + array(); + slice(); + unswappable_slice(); + vec(); + + let mut a = 42; + let mut b = 1337; + + std::mem::swap(&mut a, &mut b); + + ; std::mem::swap(&mut a, &mut b); + + let mut c = Foo(42); + + std::mem::swap(&mut c.0, &mut a); + + ; std::mem::swap(&mut c.0, &mut a); +} diff --git a/src/tools/clippy/tests/ui/swap.rs b/src/tools/clippy/tests/ui/swap.rs new file mode 100644 index 00000000000..5763d9e82d4 --- /dev/null +++ b/src/tools/clippy/tests/ui/swap.rs @@ -0,0 +1,95 @@ +// run-rustfix + +#![warn(clippy::all)] +#![allow( + clippy::blacklisted_name, + clippy::no_effect, + clippy::redundant_clone, + redundant_semicolons, + unused_assignments +)] + +struct Foo(u32); + +#[derive(Clone)] +struct Bar { + a: u32, + b: u32, +} + +fn field() { + let mut bar = Bar { a: 1, b: 2 }; + + let temp = bar.a; + bar.a = bar.b; + bar.b = temp; + + let mut baz = vec![bar.clone(), bar.clone()]; + let temp = baz[0].a; + baz[0].a = baz[1].a; + baz[1].a = temp; +} + +fn array() { + let mut foo = [1, 2]; + let temp = foo[0]; + foo[0] = foo[1]; + foo[1] = temp; + + foo.swap(0, 1); +} + +fn slice() { + let foo = &mut [1, 2]; + let temp = foo[0]; + foo[0] = foo[1]; + foo[1] = temp; + + foo.swap(0, 1); +} + +fn unswappable_slice() { + let foo = &mut [vec![1, 2], vec![3, 4]]; + let temp = foo[0][1]; + foo[0][1] = foo[1][0]; + foo[1][0] = temp; + + // swap(foo[0][1], foo[1][0]) would fail +} + +fn vec() { + let mut foo = vec![1, 2]; + let temp = foo[0]; + foo[0] = foo[1]; + foo[1] = temp; + + foo.swap(0, 1); +} + +#[rustfmt::skip] +fn main() { + field(); + array(); + slice(); + unswappable_slice(); + vec(); + + let mut a = 42; + let mut b = 1337; + + a = b; + b = a; + + ; let t = a; + a = b; + b = t; + + let mut c = Foo(42); + + c.0 = a; + a = c.0; + + ; let t = c.0; + c.0 = a; + a = t; +} diff --git a/src/tools/clippy/tests/ui/swap.stderr b/src/tools/clippy/tests/ui/swap.stderr new file mode 100644 index 00000000000..f49bcfedf3a --- /dev/null +++ b/src/tools/clippy/tests/ui/swap.stderr @@ -0,0 +1,69 @@ +error: this looks like you are swapping elements of `foo` manually + --> $DIR/swap.rs:35:5 + | +LL | / let temp = foo[0]; +LL | | foo[0] = foo[1]; +LL | | foo[1] = temp; + | |_________________^ help: try: `foo.swap(0, 1)` + | + = note: `-D clippy::manual-swap` implied by `-D warnings` + +error: this looks like you are swapping elements of `foo` manually + --> $DIR/swap.rs:44:5 + | +LL | / let temp = foo[0]; +LL | | foo[0] = foo[1]; +LL | | foo[1] = temp; + | |_________________^ help: try: `foo.swap(0, 1)` + +error: this looks like you are swapping elements of `foo` manually + --> $DIR/swap.rs:62:5 + | +LL | / let temp = foo[0]; +LL | | foo[0] = foo[1]; +LL | | foo[1] = temp; + | |_________________^ help: try: `foo.swap(0, 1)` + +error: this looks like you are swapping `a` and `b` manually + --> $DIR/swap.rs:83:7 + | +LL | ; let t = a; + | _______^ +LL | | a = b; +LL | | b = t; + | |_________^ help: try: `std::mem::swap(&mut a, &mut b)` + | + = note: or maybe you should use `std::mem::replace`? + +error: this looks like you are swapping `c.0` and `a` manually + --> $DIR/swap.rs:92:7 + | +LL | ; let t = c.0; + | _______^ +LL | | c.0 = a; +LL | | a = t; + | |_________^ help: try: `std::mem::swap(&mut c.0, &mut a)` + | + = note: or maybe you should use `std::mem::replace`? + +error: this looks like you are trying to swap `a` and `b` + --> $DIR/swap.rs:80:5 + | +LL | / a = b; +LL | | b = a; + | |_________^ help: try: `std::mem::swap(&mut a, &mut b)` + | + = note: `-D clippy::almost-swapped` implied by `-D warnings` + = note: or maybe you should use `std::mem::replace`? + +error: this looks like you are trying to swap `c.0` and `a` + --> $DIR/swap.rs:89:5 + | +LL | / c.0 = a; +LL | | a = c.0; + | |___________^ help: try: `std::mem::swap(&mut c.0, &mut a)` + | + = note: or maybe you should use `std::mem::replace`? + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/tests/ui/tabs_in_doc_comments.fixed b/src/tools/clippy/tests/ui/tabs_in_doc_comments.fixed new file mode 100644 index 00000000000..4bc4bc86c76 --- /dev/null +++ b/src/tools/clippy/tests/ui/tabs_in_doc_comments.fixed @@ -0,0 +1,22 @@ +// run-rustfix + +#![warn(clippy::tabs_in_doc_comments)] +#[allow(dead_code)] + +/// +/// Struct to hold two strings: +/// - first one +/// - second one +pub struct DoubleString { + /// + /// - First String: + /// - needs to be inside here + first_string: String, + /// + /// - Second String: + /// - needs to be inside here + second_string: String, +} + +/// This is main +fn main() {} diff --git a/src/tools/clippy/tests/ui/tabs_in_doc_comments.rs b/src/tools/clippy/tests/ui/tabs_in_doc_comments.rs new file mode 100644 index 00000000000..9db3416e659 --- /dev/null +++ b/src/tools/clippy/tests/ui/tabs_in_doc_comments.rs @@ -0,0 +1,22 @@ +// run-rustfix + +#![warn(clippy::tabs_in_doc_comments)] +#[allow(dead_code)] + +/// +/// Struct to hold two strings: +/// - first one +/// - second one +pub struct DoubleString { + /// + /// - First String: + /// - needs to be inside here + first_string: String, + /// + /// - Second String: + /// - needs to be inside here + second_string: String, +} + +/// This is main +fn main() {} diff --git a/src/tools/clippy/tests/ui/tabs_in_doc_comments.stderr b/src/tools/clippy/tests/ui/tabs_in_doc_comments.stderr new file mode 100644 index 00000000000..355f2e80579 --- /dev/null +++ b/src/tools/clippy/tests/ui/tabs_in_doc_comments.stderr @@ -0,0 +1,52 @@ +error: using tabs in doc comments is not recommended + --> $DIR/tabs_in_doc_comments.rs:12:9 + | +LL | /// - First String: + | ^^^^ help: consider using four spaces per tab + | + = note: `-D clippy::tabs-in-doc-comments` implied by `-D warnings` + +error: using tabs in doc comments is not recommended + --> $DIR/tabs_in_doc_comments.rs:13:9 + | +LL | /// - needs to be inside here + | ^^^^^^^^ help: consider using four spaces per tab + +error: using tabs in doc comments is not recommended + --> $DIR/tabs_in_doc_comments.rs:16:9 + | +LL | /// - Second String: + | ^^^^ help: consider using four spaces per tab + +error: using tabs in doc comments is not recommended + --> $DIR/tabs_in_doc_comments.rs:17:9 + | +LL | /// - needs to be inside here + | ^^^^^^^^ help: consider using four spaces per tab + +error: using tabs in doc comments is not recommended + --> $DIR/tabs_in_doc_comments.rs:8:5 + | +LL | /// - first one + | ^^^^ help: consider using four spaces per tab + +error: using tabs in doc comments is not recommended + --> $DIR/tabs_in_doc_comments.rs:8:13 + | +LL | /// - first one + | ^^^^^^^^ help: consider using four spaces per tab + +error: using tabs in doc comments is not recommended + --> $DIR/tabs_in_doc_comments.rs:9:5 + | +LL | /// - second one + | ^^^^ help: consider using four spaces per tab + +error: using tabs in doc comments is not recommended + --> $DIR/tabs_in_doc_comments.rs:9:14 + | +LL | /// - second one + | ^^^^ help: consider using four spaces per tab + +error: aborting due to 8 previous errors + diff --git a/src/tools/clippy/tests/ui/temporary_assignment.rs b/src/tools/clippy/tests/ui/temporary_assignment.rs new file mode 100644 index 00000000000..c6c315d5fab --- /dev/null +++ b/src/tools/clippy/tests/ui/temporary_assignment.rs @@ -0,0 +1,75 @@ +#![warn(clippy::temporary_assignment)] + +use std::ops::{Deref, DerefMut}; + +struct TupleStruct(i32); + +struct Struct { + field: i32, +} + +struct MultiStruct { + structure: Struct, +} + +struct Wrapper<'a> { + inner: &'a mut Struct, +} + +impl<'a> Deref for Wrapper<'a> { + type Target = Struct; + fn deref(&self) -> &Struct { + self.inner + } +} + +impl<'a> DerefMut for Wrapper<'a> { + fn deref_mut(&mut self) -> &mut Struct { + self.inner + } +} + +struct ArrayStruct { + array: [i32; 1], +} + +const A: TupleStruct = TupleStruct(1); +const B: Struct = Struct { field: 1 }; +const C: MultiStruct = MultiStruct { + structure: Struct { field: 1 }, +}; +const D: ArrayStruct = ArrayStruct { array: [1] }; + +fn main() { + let mut s = Struct { field: 0 }; + let mut t = (0, 0); + + Struct { field: 0 }.field = 1; + MultiStruct { + structure: Struct { field: 0 }, + } + .structure + .field = 1; + ArrayStruct { array: [0] }.array[0] = 1; + (0, 0).0 = 1; + + A.0 = 2; + B.field = 2; + C.structure.field = 2; + D.array[0] = 2; + + // no error + s.field = 1; + t.0 = 1; + Wrapper { inner: &mut s }.field = 1; + let mut a_mut = TupleStruct(1); + a_mut.0 = 2; + let mut b_mut = Struct { field: 1 }; + b_mut.field = 2; + let mut c_mut = MultiStruct { + structure: Struct { field: 1 }, + }; + c_mut.structure.field = 2; + let mut d_mut = ArrayStruct { array: [1] }; + d_mut.array[0] = 2; +} diff --git a/src/tools/clippy/tests/ui/temporary_assignment.stderr b/src/tools/clippy/tests/ui/temporary_assignment.stderr new file mode 100644 index 00000000000..4efe2d4bb67 --- /dev/null +++ b/src/tools/clippy/tests/ui/temporary_assignment.stderr @@ -0,0 +1,56 @@ +error: assignment to temporary + --> $DIR/temporary_assignment.rs:47:5 + | +LL | Struct { field: 0 }.field = 1; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::temporary-assignment` implied by `-D warnings` + +error: assignment to temporary + --> $DIR/temporary_assignment.rs:48:5 + | +LL | / MultiStruct { +LL | | structure: Struct { field: 0 }, +LL | | } +LL | | .structure +LL | | .field = 1; + | |______________^ + +error: assignment to temporary + --> $DIR/temporary_assignment.rs:53:5 + | +LL | ArrayStruct { array: [0] }.array[0] = 1; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: assignment to temporary + --> $DIR/temporary_assignment.rs:54:5 + | +LL | (0, 0).0 = 1; + | ^^^^^^^^^^^^ + +error: assignment to temporary + --> $DIR/temporary_assignment.rs:56:5 + | +LL | A.0 = 2; + | ^^^^^^^ + +error: assignment to temporary + --> $DIR/temporary_assignment.rs:57:5 + | +LL | B.field = 2; + | ^^^^^^^^^^^ + +error: assignment to temporary + --> $DIR/temporary_assignment.rs:58:5 + | +LL | C.structure.field = 2; + | ^^^^^^^^^^^^^^^^^^^^^ + +error: assignment to temporary + --> $DIR/temporary_assignment.rs:59:5 + | +LL | D.array[0] = 2; + | ^^^^^^^^^^^^^^ + +error: aborting due to 8 previous errors + diff --git a/src/tools/clippy/tests/ui/to_digit_is_some.fixed b/src/tools/clippy/tests/ui/to_digit_is_some.fixed new file mode 100644 index 00000000000..19184df0bec --- /dev/null +++ b/src/tools/clippy/tests/ui/to_digit_is_some.fixed @@ -0,0 +1,11 @@ +//run-rustfix + +#![warn(clippy::to_digit_is_some)] + +fn main() { + let c = 'x'; + let d = &c; + + let _ = d.is_digit(10); + let _ = char::is_digit(c, 10); +} diff --git a/src/tools/clippy/tests/ui/to_digit_is_some.rs b/src/tools/clippy/tests/ui/to_digit_is_some.rs new file mode 100644 index 00000000000..45a6728ebf5 --- /dev/null +++ b/src/tools/clippy/tests/ui/to_digit_is_some.rs @@ -0,0 +1,11 @@ +//run-rustfix + +#![warn(clippy::to_digit_is_some)] + +fn main() { + let c = 'x'; + let d = &c; + + let _ = d.to_digit(10).is_some(); + let _ = char::to_digit(c, 10).is_some(); +} diff --git a/src/tools/clippy/tests/ui/to_digit_is_some.stderr b/src/tools/clippy/tests/ui/to_digit_is_some.stderr new file mode 100644 index 00000000000..177d3ccd3e2 --- /dev/null +++ b/src/tools/clippy/tests/ui/to_digit_is_some.stderr @@ -0,0 +1,16 @@ +error: use of `.to_digit(..).is_some()` + --> $DIR/to_digit_is_some.rs:9:13 + | +LL | let _ = d.to_digit(10).is_some(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `d.is_digit(10)` + | + = note: `-D clippy::to-digit-is-some` implied by `-D warnings` + +error: use of `.to_digit(..).is_some()` + --> $DIR/to_digit_is_some.rs:10:13 + | +LL | let _ = char::to_digit(c, 10).is_some(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `char::is_digit(c, 10)` + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/toplevel_ref_arg.fixed b/src/tools/clippy/tests/ui/toplevel_ref_arg.fixed new file mode 100644 index 00000000000..33605aca019 --- /dev/null +++ b/src/tools/clippy/tests/ui/toplevel_ref_arg.fixed @@ -0,0 +1,29 @@ +// run-rustfix + +#![warn(clippy::toplevel_ref_arg)] + +fn main() { + // Closures should not warn + let y = |ref x| println!("{:?}", x); + y(1u8); + + let _x = &1; + + let _y: &(&_, u8) = &(&1, 2); + + let _z = &(1 + 2); + + let _z = &mut (1 + 2); + + let (ref x, _) = (1, 2); // ok, not top level + println!("The answer is {}.", x); + + let _x = &vec![1, 2, 3]; + + // Make sure that allowing the lint works + #[allow(clippy::toplevel_ref_arg)] + let ref mut _x = 1_234_543; + + // ok + for ref _x in 0..10 {} +} diff --git a/src/tools/clippy/tests/ui/toplevel_ref_arg.rs b/src/tools/clippy/tests/ui/toplevel_ref_arg.rs new file mode 100644 index 00000000000..59759f11893 --- /dev/null +++ b/src/tools/clippy/tests/ui/toplevel_ref_arg.rs @@ -0,0 +1,29 @@ +// run-rustfix + +#![warn(clippy::toplevel_ref_arg)] + +fn main() { + // Closures should not warn + let y = |ref x| println!("{:?}", x); + y(1u8); + + let ref _x = 1; + + let ref _y: (&_, u8) = (&1, 2); + + let ref _z = 1 + 2; + + let ref mut _z = 1 + 2; + + let (ref x, _) = (1, 2); // ok, not top level + println!("The answer is {}.", x); + + let ref _x = vec![1, 2, 3]; + + // Make sure that allowing the lint works + #[allow(clippy::toplevel_ref_arg)] + let ref mut _x = 1_234_543; + + // ok + for ref _x in 0..10 {} +} diff --git a/src/tools/clippy/tests/ui/toplevel_ref_arg.stderr b/src/tools/clippy/tests/ui/toplevel_ref_arg.stderr new file mode 100644 index 00000000000..19d69496709 --- /dev/null +++ b/src/tools/clippy/tests/ui/toplevel_ref_arg.stderr @@ -0,0 +1,34 @@ +error: `ref` on an entire `let` pattern is discouraged, take a reference with `&` instead + --> $DIR/toplevel_ref_arg.rs:10:9 + | +LL | let ref _x = 1; + | ----^^^^^^----- help: try: `let _x = &1;` + | + = note: `-D clippy::toplevel-ref-arg` implied by `-D warnings` + +error: `ref` on an entire `let` pattern is discouraged, take a reference with `&` instead + --> $DIR/toplevel_ref_arg.rs:12:9 + | +LL | let ref _y: (&_, u8) = (&1, 2); + | ----^^^^^^--------------------- help: try: `let _y: &(&_, u8) = &(&1, 2);` + +error: `ref` on an entire `let` pattern is discouraged, take a reference with `&` instead + --> $DIR/toplevel_ref_arg.rs:14:9 + | +LL | let ref _z = 1 + 2; + | ----^^^^^^--------- help: try: `let _z = &(1 + 2);` + +error: `ref` on an entire `let` pattern is discouraged, take a reference with `&` instead + --> $DIR/toplevel_ref_arg.rs:16:9 + | +LL | let ref mut _z = 1 + 2; + | ----^^^^^^^^^^--------- help: try: `let _z = &mut (1 + 2);` + +error: `ref` on an entire `let` pattern is discouraged, take a reference with `&` instead + --> $DIR/toplevel_ref_arg.rs:21:9 + | +LL | let ref _x = vec![1, 2, 3]; + | ----^^^^^^----------------- help: try: `let _x = &vec![1, 2, 3];` + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/toplevel_ref_arg_non_rustfix.rs b/src/tools/clippy/tests/ui/toplevel_ref_arg_non_rustfix.rs new file mode 100644 index 00000000000..42cac2ba4de --- /dev/null +++ b/src/tools/clippy/tests/ui/toplevel_ref_arg_non_rustfix.rs @@ -0,0 +1,11 @@ +#![warn(clippy::toplevel_ref_arg)] +#![allow(unused)] + +fn the_answer(ref mut x: u8) { + *x = 42; +} + +fn main() { + let mut x = 0; + the_answer(x); +} diff --git a/src/tools/clippy/tests/ui/toplevel_ref_arg_non_rustfix.stderr b/src/tools/clippy/tests/ui/toplevel_ref_arg_non_rustfix.stderr new file mode 100644 index 00000000000..295e2f35608 --- /dev/null +++ b/src/tools/clippy/tests/ui/toplevel_ref_arg_non_rustfix.stderr @@ -0,0 +1,10 @@ +error: `ref` directly on a function argument is ignored. Consider using a reference type instead. + --> $DIR/toplevel_ref_arg_non_rustfix.rs:4:15 + | +LL | fn the_answer(ref mut x: u8) { + | ^^^^^^^^^ + | + = note: `-D clippy::toplevel-ref-arg` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/trailing_zeros.rs b/src/tools/clippy/tests/ui/trailing_zeros.rs new file mode 100644 index 00000000000..1cef8c2cfc9 --- /dev/null +++ b/src/tools/clippy/tests/ui/trailing_zeros.rs @@ -0,0 +1,9 @@ +#![allow(unused_parens)] + +fn main() { + let x: i32 = 42; + let _ = (x & 0b1111 == 0); // suggest trailing_zeros + let _ = x & 0b1_1111 == 0; // suggest trailing_zeros + let _ = x & 0b1_1010 == 0; // do not lint + let _ = x & 1 == 0; // do not lint +} diff --git a/src/tools/clippy/tests/ui/trailing_zeros.stderr b/src/tools/clippy/tests/ui/trailing_zeros.stderr new file mode 100644 index 00000000000..320d9cc3f64 --- /dev/null +++ b/src/tools/clippy/tests/ui/trailing_zeros.stderr @@ -0,0 +1,16 @@ +error: bit mask could be simplified with a call to `trailing_zeros` + --> $DIR/trailing_zeros.rs:5:13 + | +LL | let _ = (x & 0b1111 == 0); // suggest trailing_zeros + | ^^^^^^^^^^^^^^^^^ help: try: `x.trailing_zeros() >= 4` + | + = note: `-D clippy::verbose-bit-mask` implied by `-D warnings` + +error: bit mask could be simplified with a call to `trailing_zeros` + --> $DIR/trailing_zeros.rs:6:13 + | +LL | let _ = x & 0b1_1111 == 0; // suggest trailing_zeros + | ^^^^^^^^^^^^^^^^^ help: try: `x.trailing_zeros() >= 5` + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/transmute.rs b/src/tools/clippy/tests/ui/transmute.rs new file mode 100644 index 00000000000..bb853d23704 --- /dev/null +++ b/src/tools/clippy/tests/ui/transmute.rs @@ -0,0 +1,94 @@ +#![allow(dead_code)] + +extern crate core; + +use std::mem::transmute as my_transmute; +use std::vec::Vec as MyVec; + +fn my_int() -> Usize { + Usize(42) +} + +fn my_vec() -> MyVec<i32> { + vec![] +} + +#[allow(clippy::needless_lifetimes, clippy::transmute_ptr_to_ptr)] +#[warn(clippy::useless_transmute)] +unsafe fn _generic<'a, T, U: 'a>(t: &'a T) { + let _: &'a T = core::intrinsics::transmute(t); + + let _: &'a U = core::intrinsics::transmute(t); + + let _: *const T = core::intrinsics::transmute(t); + + let _: *mut T = core::intrinsics::transmute(t); + + let _: *const U = core::intrinsics::transmute(t); +} + +#[warn(clippy::useless_transmute)] +fn useless() { + unsafe { + let _: Vec<i32> = core::intrinsics::transmute(my_vec()); + + let _: Vec<i32> = core::mem::transmute(my_vec()); + + let _: Vec<i32> = std::intrinsics::transmute(my_vec()); + + let _: Vec<i32> = std::mem::transmute(my_vec()); + + let _: Vec<i32> = my_transmute(my_vec()); + + let _: *const usize = std::mem::transmute(5_isize); + + let _ = 5_isize as *const usize; + + let _: *const usize = std::mem::transmute(1 + 1usize); + + let _ = (1 + 1_usize) as *const usize; + } +} + +struct Usize(usize); + +#[warn(clippy::crosspointer_transmute)] +fn crosspointer() { + let mut int: Usize = Usize(0); + let int_const_ptr: *const Usize = &int as *const Usize; + let int_mut_ptr: *mut Usize = &mut int as *mut Usize; + + unsafe { + let _: Usize = core::intrinsics::transmute(int_const_ptr); + + let _: Usize = core::intrinsics::transmute(int_mut_ptr); + + let _: *const Usize = core::intrinsics::transmute(my_int()); + + let _: *mut Usize = core::intrinsics::transmute(my_int()); + } +} + +#[warn(clippy::transmute_int_to_char)] +fn int_to_char() { + let _: char = unsafe { std::mem::transmute(0_u32) }; + let _: char = unsafe { std::mem::transmute(0_i32) }; +} + +#[warn(clippy::transmute_int_to_bool)] +fn int_to_bool() { + let _: bool = unsafe { std::mem::transmute(0_u8) }; +} + +#[warn(clippy::transmute_int_to_float)] +fn int_to_float() { + let _: f32 = unsafe { std::mem::transmute(0_u32) }; + let _: f32 = unsafe { std::mem::transmute(0_i32) }; +} + +fn bytes_to_str(b: &[u8], mb: &mut [u8]) { + let _: &str = unsafe { std::mem::transmute(b) }; + let _: &mut str = unsafe { std::mem::transmute(mb) }; +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/transmute.stderr b/src/tools/clippy/tests/ui/transmute.stderr new file mode 100644 index 00000000000..8582080498f --- /dev/null +++ b/src/tools/clippy/tests/ui/transmute.stderr @@ -0,0 +1,146 @@ +error: transmute from a type (`&T`) to itself + --> $DIR/transmute.rs:19:20 + | +LL | let _: &'a T = core::intrinsics::transmute(t); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::useless-transmute` implied by `-D warnings` + +error: transmute from a reference to a pointer + --> $DIR/transmute.rs:23:23 + | +LL | let _: *const T = core::intrinsics::transmute(t); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T` + +error: transmute from a reference to a pointer + --> $DIR/transmute.rs:25:21 + | +LL | let _: *mut T = core::intrinsics::transmute(t); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *mut T` + +error: transmute from a reference to a pointer + --> $DIR/transmute.rs:27:23 + | +LL | let _: *const U = core::intrinsics::transmute(t); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *const U` + +error: transmute from a type (`std::vec::Vec<i32>`) to itself + --> $DIR/transmute.rs:33:27 + | +LL | let _: Vec<i32> = core::intrinsics::transmute(my_vec()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from a type (`std::vec::Vec<i32>`) to itself + --> $DIR/transmute.rs:35:27 + | +LL | let _: Vec<i32> = core::mem::transmute(my_vec()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from a type (`std::vec::Vec<i32>`) to itself + --> $DIR/transmute.rs:37:27 + | +LL | let _: Vec<i32> = std::intrinsics::transmute(my_vec()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from a type (`std::vec::Vec<i32>`) to itself + --> $DIR/transmute.rs:39:27 + | +LL | let _: Vec<i32> = std::mem::transmute(my_vec()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from a type (`std::vec::Vec<i32>`) to itself + --> $DIR/transmute.rs:41:27 + | +LL | let _: Vec<i32> = my_transmute(my_vec()); + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from an integer to a pointer + --> $DIR/transmute.rs:43:31 + | +LL | let _: *const usize = std::mem::transmute(5_isize); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `5_isize as *const usize` + +error: transmute from an integer to a pointer + --> $DIR/transmute.rs:47:31 + | +LL | let _: *const usize = std::mem::transmute(1 + 1usize); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(1 + 1usize) as *const usize` + +error: transmute from a type (`*const Usize`) to the type that it points to (`Usize`) + --> $DIR/transmute.rs:62:24 + | +LL | let _: Usize = core::intrinsics::transmute(int_const_ptr); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::crosspointer-transmute` implied by `-D warnings` + +error: transmute from a type (`*mut Usize`) to the type that it points to (`Usize`) + --> $DIR/transmute.rs:64:24 + | +LL | let _: Usize = core::intrinsics::transmute(int_mut_ptr); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from a type (`Usize`) to a pointer to that type (`*const Usize`) + --> $DIR/transmute.rs:66:31 + | +LL | let _: *const Usize = core::intrinsics::transmute(my_int()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from a type (`Usize`) to a pointer to that type (`*mut Usize`) + --> $DIR/transmute.rs:68:29 + | +LL | let _: *mut Usize = core::intrinsics::transmute(my_int()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from a `u32` to a `char` + --> $DIR/transmute.rs:74:28 + | +LL | let _: char = unsafe { std::mem::transmute(0_u32) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::char::from_u32(0_u32).unwrap()` + | + = note: `-D clippy::transmute-int-to-char` implied by `-D warnings` + +error: transmute from a `i32` to a `char` + --> $DIR/transmute.rs:75:28 + | +LL | let _: char = unsafe { std::mem::transmute(0_i32) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::char::from_u32(0_i32 as u32).unwrap()` + +error: transmute from a `u8` to a `bool` + --> $DIR/transmute.rs:80:28 + | +LL | let _: bool = unsafe { std::mem::transmute(0_u8) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `0_u8 != 0` + | + = note: `-D clippy::transmute-int-to-bool` implied by `-D warnings` + +error: transmute from a `u32` to a `f32` + --> $DIR/transmute.rs:85:27 + | +LL | let _: f32 = unsafe { std::mem::transmute(0_u32) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_u32)` + | + = note: `-D clippy::transmute-int-to-float` implied by `-D warnings` + +error: transmute from a `i32` to a `f32` + --> $DIR/transmute.rs:86:27 + | +LL | let _: f32 = unsafe { std::mem::transmute(0_i32) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_i32 as u32)` + +error: transmute from a `&[u8]` to a `&str` + --> $DIR/transmute.rs:90:28 + | +LL | let _: &str = unsafe { std::mem::transmute(b) }; + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8(b).unwrap()` + | + = note: `-D clippy::transmute-bytes-to-str` implied by `-D warnings` + +error: transmute from a `&mut [u8]` to a `&mut str` + --> $DIR/transmute.rs:91:32 + | +LL | let _: &mut str = unsafe { std::mem::transmute(mb) }; + | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_mut(mb).unwrap()` + +error: aborting due to 22 previous errors + diff --git a/src/tools/clippy/tests/ui/transmute_32bit.rs b/src/tools/clippy/tests/ui/transmute_32bit.rs new file mode 100644 index 00000000000..ffe22b12f55 --- /dev/null +++ b/src/tools/clippy/tests/ui/transmute_32bit.rs @@ -0,0 +1,14 @@ +// ignore-64bit + +#[warn(clippy::wrong_transmute)] +fn main() { + unsafe { + let _: *const usize = std::mem::transmute(6.0f32); + + let _: *mut usize = std::mem::transmute(6.0f32); + + let _: *const usize = std::mem::transmute('x'); + + let _: *mut usize = std::mem::transmute('x'); + } +} diff --git a/src/tools/clippy/tests/ui/transmute_32bit.stderr b/src/tools/clippy/tests/ui/transmute_32bit.stderr new file mode 100644 index 00000000000..040519564b9 --- /dev/null +++ b/src/tools/clippy/tests/ui/transmute_32bit.stderr @@ -0,0 +1,28 @@ +error: transmute from a `f32` to a pointer + --> $DIR/transmute_32bit.rs:6:31 + | +LL | let _: *const usize = std::mem::transmute(6.0f32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::wrong-transmute` implied by `-D warnings` + +error: transmute from a `f32` to a pointer + --> $DIR/transmute_32bit.rs:8:29 + | +LL | let _: *mut usize = std::mem::transmute(6.0f32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from a `char` to a pointer + --> $DIR/transmute_32bit.rs:10:31 + | +LL | let _: *const usize = std::mem::transmute('x'); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from a `char` to a pointer + --> $DIR/transmute_32bit.rs:12:29 + | +LL | let _: *mut usize = std::mem::transmute('x'); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/transmute_64bit.rs b/src/tools/clippy/tests/ui/transmute_64bit.rs new file mode 100644 index 00000000000..00dc0b2c360 --- /dev/null +++ b/src/tools/clippy/tests/ui/transmute_64bit.rs @@ -0,0 +1,10 @@ +// ignore-32bit + +#[warn(clippy::wrong_transmute)] +fn main() { + unsafe { + let _: *const usize = std::mem::transmute(6.0f64); + + let _: *mut usize = std::mem::transmute(6.0f64); + } +} diff --git a/src/tools/clippy/tests/ui/transmute_64bit.stderr b/src/tools/clippy/tests/ui/transmute_64bit.stderr new file mode 100644 index 00000000000..d1854c009ef --- /dev/null +++ b/src/tools/clippy/tests/ui/transmute_64bit.stderr @@ -0,0 +1,16 @@ +error: transmute from a `f64` to a pointer + --> $DIR/transmute_64bit.rs:6:31 + | +LL | let _: *const usize = std::mem::transmute(6.0f64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::wrong-transmute` implied by `-D warnings` + +error: transmute from a `f64` to a pointer + --> $DIR/transmute_64bit.rs:8:29 + | +LL | let _: *mut usize = std::mem::transmute(6.0f64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/transmute_collection.rs b/src/tools/clippy/tests/ui/transmute_collection.rs new file mode 100644 index 00000000000..cd5a7127791 --- /dev/null +++ b/src/tools/clippy/tests/ui/transmute_collection.rs @@ -0,0 +1,47 @@ +#![warn(clippy::unsound_collection_transmute)] + +use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, VecDeque}; +use std::mem::transmute; + +fn main() { + unsafe { + // wrong size + let _ = transmute::<_, Vec<u32>>(vec![0u8]); + // wrong layout + let _ = transmute::<_, Vec<[u8; 4]>>(vec![1234u32]); + + // wrong size + let _ = transmute::<_, VecDeque<u32>>(VecDeque::<u8>::new()); + // wrong layout + let _ = transmute::<_, VecDeque<u32>>(VecDeque::<[u8; 4]>::new()); + + // wrong size + let _ = transmute::<_, BinaryHeap<u32>>(BinaryHeap::<u8>::new()); + // wrong layout + let _ = transmute::<_, BinaryHeap<u32>>(BinaryHeap::<[u8; 4]>::new()); + + // wrong size + let _ = transmute::<_, BTreeSet<u32>>(BTreeSet::<u8>::new()); + // wrong layout + let _ = transmute::<_, BTreeSet<u32>>(BTreeSet::<[u8; 4]>::new()); + + // wrong size + let _ = transmute::<_, HashSet<u32>>(HashSet::<u8>::new()); + // wrong layout + let _ = transmute::<_, HashSet<u32>>(HashSet::<[u8; 4]>::new()); + + // wrong size + let _ = transmute::<_, BTreeMap<u8, u32>>(BTreeMap::<u8, u8>::new()); + let _ = transmute::<_, BTreeMap<u8, u32>>(BTreeMap::<u32, u32>::new()); + // wrong layout + let _ = transmute::<_, BTreeMap<u8, u32>>(BTreeMap::<u8, [u8; 4]>::new()); + let _ = transmute::<_, BTreeMap<u32, u32>>(BTreeMap::<[u8; 4], u32>::new()); + + // wrong size + let _ = transmute::<_, HashMap<u8, u32>>(HashMap::<u8, u8>::new()); + let _ = transmute::<_, HashMap<u8, u32>>(HashMap::<u32, u32>::new()); + // wrong layout + let _ = transmute::<_, HashMap<u8, u32>>(HashMap::<u8, [u8; 4]>::new()); + let _ = transmute::<_, HashMap<u32, u32>>(HashMap::<[u8; 4], u32>::new()); + } +} diff --git a/src/tools/clippy/tests/ui/transmute_collection.stderr b/src/tools/clippy/tests/ui/transmute_collection.stderr new file mode 100644 index 00000000000..ebc05c402ab --- /dev/null +++ b/src/tools/clippy/tests/ui/transmute_collection.stderr @@ -0,0 +1,112 @@ +error: transmute from `std::vec::Vec<u8>` to `std::vec::Vec<u32>` with mismatched layout is unsound + --> $DIR/transmute_collection.rs:9:17 + | +LL | let _ = transmute::<_, Vec<u32>>(vec![0u8]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::unsound-collection-transmute` implied by `-D warnings` + +error: transmute from `std::vec::Vec<u32>` to `std::vec::Vec<[u8; 4]>` with mismatched layout is unsound + --> $DIR/transmute_collection.rs:11:17 + | +LL | let _ = transmute::<_, Vec<[u8; 4]>>(vec![1234u32]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from `std::collections::VecDeque<u8>` to `std::collections::VecDeque<u32>` with mismatched layout is unsound + --> $DIR/transmute_collection.rs:14:17 + | +LL | let _ = transmute::<_, VecDeque<u32>>(VecDeque::<u8>::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from `std::collections::VecDeque<[u8; 4]>` to `std::collections::VecDeque<u32>` with mismatched layout is unsound + --> $DIR/transmute_collection.rs:16:17 + | +LL | let _ = transmute::<_, VecDeque<u32>>(VecDeque::<[u8; 4]>::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from `std::collections::BinaryHeap<u8>` to `std::collections::BinaryHeap<u32>` with mismatched layout is unsound + --> $DIR/transmute_collection.rs:19:17 + | +LL | let _ = transmute::<_, BinaryHeap<u32>>(BinaryHeap::<u8>::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from `std::collections::BinaryHeap<[u8; 4]>` to `std::collections::BinaryHeap<u32>` with mismatched layout is unsound + --> $DIR/transmute_collection.rs:21:17 + | +LL | let _ = transmute::<_, BinaryHeap<u32>>(BinaryHeap::<[u8; 4]>::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from `std::collections::BTreeSet<u8>` to `std::collections::BTreeSet<u32>` with mismatched layout is unsound + --> $DIR/transmute_collection.rs:24:17 + | +LL | let _ = transmute::<_, BTreeSet<u32>>(BTreeSet::<u8>::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from `std::collections::BTreeSet<[u8; 4]>` to `std::collections::BTreeSet<u32>` with mismatched layout is unsound + --> $DIR/transmute_collection.rs:26:17 + | +LL | let _ = transmute::<_, BTreeSet<u32>>(BTreeSet::<[u8; 4]>::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from `std::collections::HashSet<u8>` to `std::collections::HashSet<u32>` with mismatched layout is unsound + --> $DIR/transmute_collection.rs:29:17 + | +LL | let _ = transmute::<_, HashSet<u32>>(HashSet::<u8>::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from `std::collections::HashSet<[u8; 4]>` to `std::collections::HashSet<u32>` with mismatched layout is unsound + --> $DIR/transmute_collection.rs:31:17 + | +LL | let _ = transmute::<_, HashSet<u32>>(HashSet::<[u8; 4]>::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from `std::collections::BTreeMap<u8, u8>` to `std::collections::BTreeMap<u8, u32>` with mismatched layout is unsound + --> $DIR/transmute_collection.rs:34:17 + | +LL | let _ = transmute::<_, BTreeMap<u8, u32>>(BTreeMap::<u8, u8>::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from `std::collections::BTreeMap<u32, u32>` to `std::collections::BTreeMap<u8, u32>` with mismatched layout is unsound + --> $DIR/transmute_collection.rs:35:17 + | +LL | let _ = transmute::<_, BTreeMap<u8, u32>>(BTreeMap::<u32, u32>::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from `std::collections::BTreeMap<u8, [u8; 4]>` to `std::collections::BTreeMap<u8, u32>` with mismatched layout is unsound + --> $DIR/transmute_collection.rs:37:17 + | +LL | let _ = transmute::<_, BTreeMap<u8, u32>>(BTreeMap::<u8, [u8; 4]>::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from `std::collections::BTreeMap<[u8; 4], u32>` to `std::collections::BTreeMap<u32, u32>` with mismatched layout is unsound + --> $DIR/transmute_collection.rs:38:17 + | +LL | let _ = transmute::<_, BTreeMap<u32, u32>>(BTreeMap::<[u8; 4], u32>::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from `std::collections::HashMap<u8, u8>` to `std::collections::HashMap<u8, u32>` with mismatched layout is unsound + --> $DIR/transmute_collection.rs:41:17 + | +LL | let _ = transmute::<_, HashMap<u8, u32>>(HashMap::<u8, u8>::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from `std::collections::HashMap<u32, u32>` to `std::collections::HashMap<u8, u32>` with mismatched layout is unsound + --> $DIR/transmute_collection.rs:42:17 + | +LL | let _ = transmute::<_, HashMap<u8, u32>>(HashMap::<u32, u32>::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from `std::collections::HashMap<u8, [u8; 4]>` to `std::collections::HashMap<u8, u32>` with mismatched layout is unsound + --> $DIR/transmute_collection.rs:44:17 + | +LL | let _ = transmute::<_, HashMap<u8, u32>>(HashMap::<u8, [u8; 4]>::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute from `std::collections::HashMap<[u8; 4], u32>` to `std::collections::HashMap<u32, u32>` with mismatched layout is unsound + --> $DIR/transmute_collection.rs:45:17 + | +LL | let _ = transmute::<_, HashMap<u32, u32>>(HashMap::<[u8; 4], u32>::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 18 previous errors + diff --git a/src/tools/clippy/tests/ui/transmute_float_to_int.rs b/src/tools/clippy/tests/ui/transmute_float_to_int.rs new file mode 100644 index 00000000000..ce942751ada --- /dev/null +++ b/src/tools/clippy/tests/ui/transmute_float_to_int.rs @@ -0,0 +1,12 @@ +#[warn(clippy::transmute_float_to_int)] + +fn float_to_int() { + let _: u32 = unsafe { std::mem::transmute(1f32) }; + let _: i32 = unsafe { std::mem::transmute(1f32) }; + let _: u64 = unsafe { std::mem::transmute(1f64) }; + let _: i64 = unsafe { std::mem::transmute(1f64) }; + let _: u64 = unsafe { std::mem::transmute(1.0) }; + let _: u64 = unsafe { std::mem::transmute(-1.0) }; +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/transmute_float_to_int.stderr b/src/tools/clippy/tests/ui/transmute_float_to_int.stderr new file mode 100644 index 00000000000..eb786bb39f9 --- /dev/null +++ b/src/tools/clippy/tests/ui/transmute_float_to_int.stderr @@ -0,0 +1,40 @@ +error: transmute from a `f32` to a `u32` + --> $DIR/transmute_float_to_int.rs:4:27 + | +LL | let _: u32 = unsafe { std::mem::transmute(1f32) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f32.to_bits()` + | + = note: `-D clippy::transmute-float-to-int` implied by `-D warnings` + +error: transmute from a `f32` to a `i32` + --> $DIR/transmute_float_to_int.rs:5:27 + | +LL | let _: i32 = unsafe { std::mem::transmute(1f32) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f32.to_bits() as i32` + +error: transmute from a `f64` to a `u64` + --> $DIR/transmute_float_to_int.rs:6:27 + | +LL | let _: u64 = unsafe { std::mem::transmute(1f64) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f64.to_bits()` + +error: transmute from a `f64` to a `i64` + --> $DIR/transmute_float_to_int.rs:7:27 + | +LL | let _: i64 = unsafe { std::mem::transmute(1f64) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f64.to_bits() as i64` + +error: transmute from a `f64` to a `u64` + --> $DIR/transmute_float_to_int.rs:8:27 + | +LL | let _: u64 = unsafe { std::mem::transmute(1.0) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1.0f64.to_bits()` + +error: transmute from a `f64` to a `u64` + --> $DIR/transmute_float_to_int.rs:9:27 + | +LL | let _: u64 = unsafe { std::mem::transmute(-1.0) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(-1.0f64).to_bits()` + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.rs b/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.rs new file mode 100644 index 00000000000..0d8a322f2b2 --- /dev/null +++ b/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.rs @@ -0,0 +1,54 @@ +#![warn(clippy::transmute_ptr_to_ptr)] + +// Make sure we can modify lifetimes, which is one of the recommended uses +// of transmute + +// Make sure we can do static lifetime transmutes +unsafe fn transmute_lifetime_to_static<'a, T>(t: &'a T) -> &'static T { + std::mem::transmute::<&'a T, &'static T>(t) +} + +// Make sure we can do non-static lifetime transmutes +unsafe fn transmute_lifetime<'a, 'b, T>(t: &'a T, u: &'b T) -> &'b T { + std::mem::transmute::<&'a T, &'b T>(t) +} + +struct LifetimeParam<'a> { + s: &'a str, +} + +struct GenericParam<T> { + t: T, +} + +fn transmute_ptr_to_ptr() { + let ptr = &1u32 as *const u32; + let mut_ptr = &mut 1u32 as *mut u32; + unsafe { + // pointer-to-pointer transmutes; bad + let _: *const f32 = std::mem::transmute(ptr); + let _: *mut f32 = std::mem::transmute(mut_ptr); + // ref-ref transmutes; bad + let _: &f32 = std::mem::transmute(&1u32); + let _: &f64 = std::mem::transmute(&1f32); + // ^ this test is here because both f32 and f64 are the same TypeVariant, but they are not + // the same type + let _: &mut f32 = std::mem::transmute(&mut 1u32); + let _: &GenericParam<f32> = std::mem::transmute(&GenericParam { t: 1u32 }); + } + + // these are recommendations for solving the above; if these lint we need to update + // those suggestions + let _ = ptr as *const f32; + let _ = mut_ptr as *mut f32; + let _ = unsafe { &*(&1u32 as *const u32 as *const f32) }; + let _ = unsafe { &mut *(&mut 1u32 as *mut u32 as *mut f32) }; + + // transmute internal lifetimes, should not lint + let s = "hello world".to_owned(); + let lp = LifetimeParam { s: &s }; + let _: &LifetimeParam<'static> = unsafe { std::mem::transmute(&lp) }; + let _: &GenericParam<&LifetimeParam<'static>> = unsafe { std::mem::transmute(&GenericParam { t: &lp }) }; +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.stderr b/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.stderr new file mode 100644 index 00000000000..4d1b8fcc199 --- /dev/null +++ b/src/tools/clippy/tests/ui/transmute_ptr_to_ptr.stderr @@ -0,0 +1,40 @@ +error: transmute from a pointer to a pointer + --> $DIR/transmute_ptr_to_ptr.rs:29:29 + | +LL | let _: *const f32 = std::mem::transmute(ptr); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr as *const f32` + | + = note: `-D clippy::transmute-ptr-to-ptr` implied by `-D warnings` + +error: transmute from a pointer to a pointer + --> $DIR/transmute_ptr_to_ptr.rs:30:27 + | +LL | let _: *mut f32 = std::mem::transmute(mut_ptr); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `mut_ptr as *mut f32` + +error: transmute from a reference to a reference + --> $DIR/transmute_ptr_to_ptr.rs:32:23 + | +LL | let _: &f32 = std::mem::transmute(&1u32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(&1u32 as *const u32 as *const f32)` + +error: transmute from a reference to a reference + --> $DIR/transmute_ptr_to_ptr.rs:33:23 + | +LL | let _: &f64 = std::mem::transmute(&1f32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(&1f32 as *const f32 as *const f64)` + +error: transmute from a reference to a reference + --> $DIR/transmute_ptr_to_ptr.rs:36:27 + | +LL | let _: &mut f32 = std::mem::transmute(&mut 1u32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *(&mut 1u32 as *mut u32 as *mut f32)` + +error: transmute from a reference to a reference + --> $DIR/transmute_ptr_to_ptr.rs:37:37 + | +LL | let _: &GenericParam<f32> = std::mem::transmute(&GenericParam { t: 1u32 }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(&GenericParam { t: 1u32 } as *const GenericParam<u32> as *const GenericParam<f32>)` + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/transmute_ptr_to_ref.rs b/src/tools/clippy/tests/ui/transmute_ptr_to_ref.rs new file mode 100644 index 00000000000..ba35c6adc4d --- /dev/null +++ b/src/tools/clippy/tests/ui/transmute_ptr_to_ref.rs @@ -0,0 +1,41 @@ +#![warn(clippy::transmute_ptr_to_ref)] + +unsafe fn _ptr_to_ref<T, U>(p: *const T, m: *mut T, o: *const U, om: *mut U) { + let _: &T = std::mem::transmute(p); + let _: &T = &*p; + + let _: &mut T = std::mem::transmute(m); + let _: &mut T = &mut *m; + + let _: &T = std::mem::transmute(m); + let _: &T = &*m; + + let _: &mut T = std::mem::transmute(p as *mut T); + let _ = &mut *(p as *mut T); + + let _: &T = std::mem::transmute(o); + let _: &T = &*(o as *const T); + + let _: &mut T = std::mem::transmute(om); + let _: &mut T = &mut *(om as *mut T); + + let _: &T = std::mem::transmute(om); + let _: &T = &*(om as *const T); +} + +fn issue1231() { + struct Foo<'a, T> { + bar: &'a T, + } + + let raw = 42 as *const i32; + let _: &Foo<u8> = unsafe { std::mem::transmute::<_, &Foo<_>>(raw) }; + + let _: &Foo<&u8> = unsafe { std::mem::transmute::<_, &Foo<&_>>(raw) }; + + type Bar<'a> = &'a u8; + let raw = 42 as *const i32; + unsafe { std::mem::transmute::<_, Bar>(raw) }; +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/transmute_ptr_to_ref.stderr b/src/tools/clippy/tests/ui/transmute_ptr_to_ref.stderr new file mode 100644 index 00000000000..df0598a58cd --- /dev/null +++ b/src/tools/clippy/tests/ui/transmute_ptr_to_ref.stderr @@ -0,0 +1,64 @@ +error: transmute from a pointer type (`*const T`) to a reference type (`&T`) + --> $DIR/transmute_ptr_to_ref.rs:4:17 + | +LL | let _: &T = std::mem::transmute(p); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*p` + | + = note: `-D clippy::transmute-ptr-to-ref` implied by `-D warnings` + +error: transmute from a pointer type (`*mut T`) to a reference type (`&mut T`) + --> $DIR/transmute_ptr_to_ref.rs:7:21 + | +LL | let _: &mut T = std::mem::transmute(m); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *m` + +error: transmute from a pointer type (`*mut T`) to a reference type (`&T`) + --> $DIR/transmute_ptr_to_ref.rs:10:17 + | +LL | let _: &T = std::mem::transmute(m); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*m` + +error: transmute from a pointer type (`*mut T`) to a reference type (`&mut T`) + --> $DIR/transmute_ptr_to_ref.rs:13:21 + | +LL | let _: &mut T = std::mem::transmute(p as *mut T); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *(p as *mut T)` + +error: transmute from a pointer type (`*const U`) to a reference type (`&T`) + --> $DIR/transmute_ptr_to_ref.rs:16:17 + | +LL | let _: &T = std::mem::transmute(o); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(o as *const T)` + +error: transmute from a pointer type (`*mut U`) to a reference type (`&mut T`) + --> $DIR/transmute_ptr_to_ref.rs:19:21 + | +LL | let _: &mut T = std::mem::transmute(om); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *(om as *mut T)` + +error: transmute from a pointer type (`*mut U`) to a reference type (`&T`) + --> $DIR/transmute_ptr_to_ref.rs:22:17 + | +LL | let _: &T = std::mem::transmute(om); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(om as *const T)` + +error: transmute from a pointer type (`*const i32`) to a reference type (`&issue1231::Foo<u8>`) + --> $DIR/transmute_ptr_to_ref.rs:32:32 + | +LL | let _: &Foo<u8> = unsafe { std::mem::transmute::<_, &Foo<_>>(raw) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(raw as *const Foo<_>)` + +error: transmute from a pointer type (`*const i32`) to a reference type (`&issue1231::Foo<&u8>`) + --> $DIR/transmute_ptr_to_ref.rs:34:33 + | +LL | let _: &Foo<&u8> = unsafe { std::mem::transmute::<_, &Foo<&_>>(raw) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(raw as *const Foo<&_>)` + +error: transmute from a pointer type (`*const i32`) to a reference type (`&u8`) + --> $DIR/transmute_ptr_to_ref.rs:38:14 + | +LL | unsafe { std::mem::transmute::<_, Bar>(raw) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(raw as *const u8)` + +error: aborting due to 10 previous errors + diff --git a/src/tools/clippy/tests/ui/transmuting_null.rs b/src/tools/clippy/tests/ui/transmuting_null.rs new file mode 100644 index 00000000000..ea3ee8edc81 --- /dev/null +++ b/src/tools/clippy/tests/ui/transmuting_null.rs @@ -0,0 +1,30 @@ +#![allow(dead_code)] +#![warn(clippy::transmuting_null)] +#![allow(clippy::zero_ptr)] +#![allow(clippy::transmute_ptr_to_ref)] +#![allow(clippy::eq_op)] + +// Easy to lint because these only span one line. +fn one_liners() { + unsafe { + let _: &u64 = std::mem::transmute(0 as *const u64); + let _: &u64 = std::mem::transmute(std::ptr::null::<u64>()); + } +} + +pub const ZPTR: *const usize = 0 as *const _; +pub const NOT_ZPTR: *const usize = 1 as *const _; + +fn transmute_const() { + unsafe { + // Should raise a lint. + let _: &u64 = std::mem::transmute(ZPTR); + // Should NOT raise a lint. + let _: &u64 = std::mem::transmute(NOT_ZPTR); + } +} + +fn main() { + one_liners(); + transmute_const(); +} diff --git a/src/tools/clippy/tests/ui/transmuting_null.stderr b/src/tools/clippy/tests/ui/transmuting_null.stderr new file mode 100644 index 00000000000..05f91ee2ada --- /dev/null +++ b/src/tools/clippy/tests/ui/transmuting_null.stderr @@ -0,0 +1,22 @@ +error: transmuting a known null pointer into a reference. + --> $DIR/transmuting_null.rs:10:23 + | +LL | let _: &u64 = std::mem::transmute(0 as *const u64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::transmuting-null` implied by `-D warnings` + +error: transmuting a known null pointer into a reference. + --> $DIR/transmuting_null.rs:11:23 + | +LL | let _: &u64 = std::mem::transmute(std::ptr::null::<u64>()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmuting a known null pointer into a reference. + --> $DIR/transmuting_null.rs:21:23 + | +LL | let _: &u64 = std::mem::transmute(ZPTR); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/trivially_copy_pass_by_ref.rs b/src/tools/clippy/tests/ui/trivially_copy_pass_by_ref.rs new file mode 100644 index 00000000000..316426f1cf1 --- /dev/null +++ b/src/tools/clippy/tests/ui/trivially_copy_pass_by_ref.rs @@ -0,0 +1,114 @@ +// normalize-stderr-test "\(\d+ byte\)" -> "(N byte)" +// normalize-stderr-test "\(limit: \d+ byte\)" -> "(limit: N byte)" + +#![deny(clippy::trivially_copy_pass_by_ref)] +#![allow( + clippy::many_single_char_names, + clippy::blacklisted_name, + clippy::redundant_field_names +)] + +#[derive(Copy, Clone)] +struct Foo(u32); + +#[derive(Copy, Clone)] +struct Bar([u8; 24]); + +#[derive(Copy, Clone)] +pub struct Color { + pub r: u8, + pub g: u8, + pub b: u8, + pub a: u8, +} + +struct FooRef<'a> { + foo: &'a Foo, +} + +type Baz = u32; + +fn good(a: &mut u32, b: u32, c: &Bar) {} + +fn good_return_implicit_lt_ref(foo: &Foo) -> &u32 { + &foo.0 +} + +#[allow(clippy::needless_lifetimes)] +fn good_return_explicit_lt_ref<'a>(foo: &'a Foo) -> &'a u32 { + &foo.0 +} + +fn good_return_implicit_lt_struct(foo: &Foo) -> FooRef { + FooRef { foo } +} + +#[allow(clippy::needless_lifetimes)] +fn good_return_explicit_lt_struct<'a>(foo: &'a Foo) -> FooRef<'a> { + FooRef { foo } +} + +fn bad(x: &u32, y: &Foo, z: &Baz) {} + +impl Foo { + fn good(self, a: &mut u32, b: u32, c: &Bar) {} + + fn good2(&mut self) {} + + fn bad(&self, x: &u32, y: &Foo, z: &Baz) {} + + fn bad2(x: &u32, y: &Foo, z: &Baz) {} +} + +impl AsRef<u32> for Foo { + fn as_ref(&self) -> &u32 { + &self.0 + } +} + +impl Bar { + fn good(&self, a: &mut u32, b: u32, c: &Bar) {} + + fn bad2(x: &u32, y: &Foo, z: &Baz) {} +} + +trait MyTrait { + fn trait_method(&self, _foo: &Foo); +} + +pub trait MyTrait2 { + fn trait_method2(&self, _color: &Color); +} + +impl MyTrait for Foo { + fn trait_method(&self, _foo: &Foo) { + unimplemented!() + } +} + +#[allow(unused_variables)] +mod issue3992 { + pub trait A { + #[allow(clippy::trivially_copy_pass_by_ref)] + fn a(b: &u16) {} + } + + #[allow(clippy::trivially_copy_pass_by_ref)] + pub fn c(d: &u16) {} +} + +fn main() { + let (mut foo, bar) = (Foo(0), Bar([0; 24])); + let (mut a, b, c, x, y, z) = (0, 0, Bar([0; 24]), 0, Foo(0), 0); + good(&mut a, b, &c); + good_return_implicit_lt_ref(&y); + good_return_explicit_lt_ref(&y); + bad(&x, &y, &z); + foo.good(&mut a, b, &c); + foo.good2(); + foo.bad(&x, &y, &z); + Foo::bad2(&x, &y, &z); + bar.good(&mut a, b, &c); + Bar::bad2(&x, &y, &z); + foo.as_ref(); +} diff --git a/src/tools/clippy/tests/ui/trivially_copy_pass_by_ref.stderr b/src/tools/clippy/tests/ui/trivially_copy_pass_by_ref.stderr new file mode 100644 index 00000000000..be0914e4a79 --- /dev/null +++ b/src/tools/clippy/tests/ui/trivially_copy_pass_by_ref.stderr @@ -0,0 +1,98 @@ +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) + --> $DIR/trivially_copy_pass_by_ref.rs:51:11 + | +LL | fn bad(x: &u32, y: &Foo, z: &Baz) {} + | ^^^^ help: consider passing by value instead: `u32` + | +note: the lint level is defined here + --> $DIR/trivially_copy_pass_by_ref.rs:4:9 + | +LL | #![deny(clippy::trivially_copy_pass_by_ref)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) + --> $DIR/trivially_copy_pass_by_ref.rs:51:20 + | +LL | fn bad(x: &u32, y: &Foo, z: &Baz) {} + | ^^^^ help: consider passing by value instead: `Foo` + +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) + --> $DIR/trivially_copy_pass_by_ref.rs:51:29 + | +LL | fn bad(x: &u32, y: &Foo, z: &Baz) {} + | ^^^^ help: consider passing by value instead: `Baz` + +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) + --> $DIR/trivially_copy_pass_by_ref.rs:58:12 + | +LL | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {} + | ^^^^^ help: consider passing by value instead: `self` + +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) + --> $DIR/trivially_copy_pass_by_ref.rs:58:22 + | +LL | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {} + | ^^^^ help: consider passing by value instead: `u32` + +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) + --> $DIR/trivially_copy_pass_by_ref.rs:58:31 + | +LL | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {} + | ^^^^ help: consider passing by value instead: `Foo` + +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) + --> $DIR/trivially_copy_pass_by_ref.rs:58:40 + | +LL | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {} + | ^^^^ help: consider passing by value instead: `Baz` + +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) + --> $DIR/trivially_copy_pass_by_ref.rs:60:16 + | +LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {} + | ^^^^ help: consider passing by value instead: `u32` + +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) + --> $DIR/trivially_copy_pass_by_ref.rs:60:25 + | +LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {} + | ^^^^ help: consider passing by value instead: `Foo` + +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) + --> $DIR/trivially_copy_pass_by_ref.rs:60:34 + | +LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {} + | ^^^^ help: consider passing by value instead: `Baz` + +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) + --> $DIR/trivially_copy_pass_by_ref.rs:72:16 + | +LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {} + | ^^^^ help: consider passing by value instead: `u32` + +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) + --> $DIR/trivially_copy_pass_by_ref.rs:72:25 + | +LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {} + | ^^^^ help: consider passing by value instead: `Foo` + +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) + --> $DIR/trivially_copy_pass_by_ref.rs:72:34 + | +LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {} + | ^^^^ help: consider passing by value instead: `Baz` + +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) + --> $DIR/trivially_copy_pass_by_ref.rs:76:34 + | +LL | fn trait_method(&self, _foo: &Foo); + | ^^^^ help: consider passing by value instead: `Foo` + +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) + --> $DIR/trivially_copy_pass_by_ref.rs:80:37 + | +LL | fn trait_method2(&self, _color: &Color); + | ^^^^^^ help: consider passing by value instead: `Color` + +error: aborting due to 15 previous errors + diff --git a/src/tools/clippy/tests/ui/try_err.fixed b/src/tools/clippy/tests/ui/try_err.fixed new file mode 100644 index 00000000000..29d9139d3e3 --- /dev/null +++ b/src/tools/clippy/tests/ui/try_err.fixed @@ -0,0 +1,106 @@ +// run-rustfix +// aux-build:macro_rules.rs + +#![deny(clippy::try_err)] + +#[macro_use] +extern crate macro_rules; + +// Tests that a simple case works +// Should flag `Err(err)?` +pub fn basic_test() -> Result<i32, i32> { + let err: i32 = 1; + // To avoid warnings during rustfix + if true { + return Err(err); + } + Ok(0) +} + +// Tests that `.into()` is added when appropriate +pub fn into_test() -> Result<i32, i32> { + let err: u8 = 1; + // To avoid warnings during rustfix + if true { + return Err(err.into()); + } + Ok(0) +} + +// Tests that tries in general don't trigger the error +pub fn negative_test() -> Result<i32, i32> { + Ok(nested_error()? + 1) +} + +// Tests that `.into()` isn't added when the error type +// matches the surrounding closure's return type, even +// when it doesn't match the surrounding function's. +pub fn closure_matches_test() -> Result<i32, i32> { + let res: Result<i32, i8> = Some(1) + .into_iter() + .map(|i| { + let err: i8 = 1; + // To avoid warnings during rustfix + if true { + return Err(err); + } + Ok(i) + }) + .next() + .unwrap(); + + Ok(res?) +} + +// Tests that `.into()` isn't added when the error type +// doesn't match the surrounding closure's return type. +pub fn closure_into_test() -> Result<i32, i32> { + let res: Result<i32, i16> = Some(1) + .into_iter() + .map(|i| { + let err: i8 = 1; + // To avoid warnings during rustfix + if true { + return Err(err.into()); + } + Ok(i) + }) + .next() + .unwrap(); + + Ok(res?) +} + +fn nested_error() -> Result<i32, i32> { + Ok(1) +} + +fn main() { + basic_test().unwrap(); + into_test().unwrap(); + negative_test().unwrap(); + closure_matches_test().unwrap(); + closure_into_test().unwrap(); + + // We don't want to lint in external macros + try_err!(); +} + +macro_rules! bar { + () => { + String::from("aasdfasdfasdfa") + }; +} + +macro_rules! foo { + () => { + bar!() + }; +} + +pub fn macro_inside(fail: bool) -> Result<i32, String> { + if fail { + return Err(foo!()); + } + Ok(0) +} diff --git a/src/tools/clippy/tests/ui/try_err.rs b/src/tools/clippy/tests/ui/try_err.rs new file mode 100644 index 00000000000..5e85d091a2a --- /dev/null +++ b/src/tools/clippy/tests/ui/try_err.rs @@ -0,0 +1,106 @@ +// run-rustfix +// aux-build:macro_rules.rs + +#![deny(clippy::try_err)] + +#[macro_use] +extern crate macro_rules; + +// Tests that a simple case works +// Should flag `Err(err)?` +pub fn basic_test() -> Result<i32, i32> { + let err: i32 = 1; + // To avoid warnings during rustfix + if true { + Err(err)?; + } + Ok(0) +} + +// Tests that `.into()` is added when appropriate +pub fn into_test() -> Result<i32, i32> { + let err: u8 = 1; + // To avoid warnings during rustfix + if true { + Err(err)?; + } + Ok(0) +} + +// Tests that tries in general don't trigger the error +pub fn negative_test() -> Result<i32, i32> { + Ok(nested_error()? + 1) +} + +// Tests that `.into()` isn't added when the error type +// matches the surrounding closure's return type, even +// when it doesn't match the surrounding function's. +pub fn closure_matches_test() -> Result<i32, i32> { + let res: Result<i32, i8> = Some(1) + .into_iter() + .map(|i| { + let err: i8 = 1; + // To avoid warnings during rustfix + if true { + Err(err)?; + } + Ok(i) + }) + .next() + .unwrap(); + + Ok(res?) +} + +// Tests that `.into()` isn't added when the error type +// doesn't match the surrounding closure's return type. +pub fn closure_into_test() -> Result<i32, i32> { + let res: Result<i32, i16> = Some(1) + .into_iter() + .map(|i| { + let err: i8 = 1; + // To avoid warnings during rustfix + if true { + Err(err)?; + } + Ok(i) + }) + .next() + .unwrap(); + + Ok(res?) +} + +fn nested_error() -> Result<i32, i32> { + Ok(1) +} + +fn main() { + basic_test().unwrap(); + into_test().unwrap(); + negative_test().unwrap(); + closure_matches_test().unwrap(); + closure_into_test().unwrap(); + + // We don't want to lint in external macros + try_err!(); +} + +macro_rules! bar { + () => { + String::from("aasdfasdfasdfa") + }; +} + +macro_rules! foo { + () => { + bar!() + }; +} + +pub fn macro_inside(fail: bool) -> Result<i32, String> { + if fail { + Err(foo!())?; + } + Ok(0) +} diff --git a/src/tools/clippy/tests/ui/try_err.stderr b/src/tools/clippy/tests/ui/try_err.stderr new file mode 100644 index 00000000000..21e9d4048a5 --- /dev/null +++ b/src/tools/clippy/tests/ui/try_err.stderr @@ -0,0 +1,38 @@ +error: returning an `Err(_)` with the `?` operator + --> $DIR/try_err.rs:15:9 + | +LL | Err(err)?; + | ^^^^^^^^^ help: try this: `return Err(err)` + | +note: the lint level is defined here + --> $DIR/try_err.rs:4:9 + | +LL | #![deny(clippy::try_err)] + | ^^^^^^^^^^^^^^^ + +error: returning an `Err(_)` with the `?` operator + --> $DIR/try_err.rs:25:9 + | +LL | Err(err)?; + | ^^^^^^^^^ help: try this: `return Err(err.into())` + +error: returning an `Err(_)` with the `?` operator + --> $DIR/try_err.rs:45:17 + | +LL | Err(err)?; + | ^^^^^^^^^ help: try this: `return Err(err)` + +error: returning an `Err(_)` with the `?` operator + --> $DIR/try_err.rs:64:17 + | +LL | Err(err)?; + | ^^^^^^^^^ help: try this: `return Err(err.into())` + +error: returning an `Err(_)` with the `?` operator + --> $DIR/try_err.rs:103:9 + | +LL | Err(foo!())?; + | ^^^^^^^^^^^^ help: try this: `return Err(foo!())` + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/ty_fn_sig.rs b/src/tools/clippy/tests/ui/ty_fn_sig.rs new file mode 100644 index 00000000000..9e2753dcb18 --- /dev/null +++ b/src/tools/clippy/tests/ui/ty_fn_sig.rs @@ -0,0 +1,14 @@ +// Regression test + +pub fn retry<F: Fn()>(f: F) { + for _i in 0.. { + f(); + } +} + +fn main() { + for y in 0..4 { + let func = || (); + func(); + } +} diff --git a/src/tools/clippy/tests/ui/type_repetition_in_bounds.rs b/src/tools/clippy/tests/ui/type_repetition_in_bounds.rs new file mode 100644 index 00000000000..8b538be762b --- /dev/null +++ b/src/tools/clippy/tests/ui/type_repetition_in_bounds.rs @@ -0,0 +1,19 @@ +#[deny(clippy::type_repetition_in_bounds)] + +pub fn foo<T>(_t: T) +where + T: Copy, + T: Clone, +{ + unimplemented!(); +} + +pub fn bar<T, U>(_t: T, _u: U) +where + T: Copy, + U: Clone, +{ + unimplemented!(); +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/type_repetition_in_bounds.stderr b/src/tools/clippy/tests/ui/type_repetition_in_bounds.stderr new file mode 100644 index 00000000000..4264e2e10bf --- /dev/null +++ b/src/tools/clippy/tests/ui/type_repetition_in_bounds.stderr @@ -0,0 +1,15 @@ +error: this type has already been used as a bound predicate + --> $DIR/type_repetition_in_bounds.rs:6:5 + | +LL | T: Clone, + | ^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/type_repetition_in_bounds.rs:1:8 + | +LL | #[deny(clippy::type_repetition_in_bounds)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: consider combining the bounds: `T: Copy + Clone` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/types.fixed b/src/tools/clippy/tests/ui/types.fixed new file mode 100644 index 00000000000..417da42edf1 --- /dev/null +++ b/src/tools/clippy/tests/ui/types.fixed @@ -0,0 +1,15 @@ +// run-rustfix + +#![allow(dead_code, unused_variables)] +#![warn(clippy::cast_lossless)] + +// should not warn on lossy casting in constant types +// because not supported yet +const C: i32 = 42; +const C_I64: i64 = C as i64; + +fn main() { + // should suggest i64::from(c) + let c: i32 = 42; + let c_i64: i64 = i64::from(c); +} diff --git a/src/tools/clippy/tests/ui/types.rs b/src/tools/clippy/tests/ui/types.rs new file mode 100644 index 00000000000..b16e9e538b1 --- /dev/null +++ b/src/tools/clippy/tests/ui/types.rs @@ -0,0 +1,15 @@ +// run-rustfix + +#![allow(dead_code, unused_variables)] +#![warn(clippy::cast_lossless)] + +// should not warn on lossy casting in constant types +// because not supported yet +const C: i32 = 42; +const C_I64: i64 = C as i64; + +fn main() { + // should suggest i64::from(c) + let c: i32 = 42; + let c_i64: i64 = c as i64; +} diff --git a/src/tools/clippy/tests/ui/types.stderr b/src/tools/clippy/tests/ui/types.stderr new file mode 100644 index 00000000000..59c3e05a1aa --- /dev/null +++ b/src/tools/clippy/tests/ui/types.stderr @@ -0,0 +1,10 @@ +error: casting `i32` to `i64` may become silently lossy if you later change the type + --> $DIR/types.rs:14:22 + | +LL | let c_i64: i64 = c as i64; + | ^^^^^^^^ help: try: `i64::from(c)` + | + = note: `-D clippy::cast-lossless` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/unicode.rs b/src/tools/clippy/tests/ui/unicode.rs new file mode 100644 index 00000000000..27db9594f3b --- /dev/null +++ b/src/tools/clippy/tests/ui/unicode.rs @@ -0,0 +1,23 @@ +#[warn(clippy::zero_width_space)] +fn zero() { + print!("Here >< is a ZWS, and another"); + print!("This\u{200B}is\u{200B}fine"); +} + +#[warn(clippy::unicode_not_nfc)] +fn canon() { + print!("̀àh?"); + print!("a\u{0300}h?"); // also ok +} + +#[warn(clippy::non_ascii_literal)] +fn uni() { + print!("Üben!"); + print!("\u{DC}ben!"); // this is ok +} + +fn main() { + zero(); + uni(); + canon(); +} diff --git a/src/tools/clippy/tests/ui/unicode.stderr b/src/tools/clippy/tests/ui/unicode.stderr new file mode 100644 index 00000000000..4575a132e5b --- /dev/null +++ b/src/tools/clippy/tests/ui/unicode.stderr @@ -0,0 +1,26 @@ +error: zero-width space detected + --> $DIR/unicode.rs:3:12 + | +LL | print!("Here >< is a ZWS, and another"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider replacing the string with: `"Here >/u{200B}< is a ZWS, and /u{200B}another"` + | + = note: `-D clippy::zero-width-space` implied by `-D warnings` + +error: non-NFC Unicode sequence detected + --> $DIR/unicode.rs:9:12 + | +LL | print!("̀àh?"); + | ^^^^^ help: consider replacing the string with: `"̀àh?"` + | + = note: `-D clippy::unicode-not-nfc` implied by `-D warnings` + +error: literal non-ASCII character detected + --> $DIR/unicode.rs:15:12 + | +LL | print!("Üben!"); + | ^^^^^^^ help: consider replacing the string with: `"/u{dc}ben!"` + | + = note: `-D clippy::non-ascii-literal` implied by `-D warnings` + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/uninit.rs b/src/tools/clippy/tests/ui/uninit.rs new file mode 100644 index 00000000000..f42b884e0f0 --- /dev/null +++ b/src/tools/clippy/tests/ui/uninit.rs @@ -0,0 +1,22 @@ +#![feature(stmt_expr_attributes)] + +use std::mem::MaybeUninit; + +fn main() { + let _: usize = unsafe { MaybeUninit::uninit().assume_init() }; + + // edge case: For now we lint on empty arrays + let _: [u8; 0] = unsafe { MaybeUninit::uninit().assume_init() }; + + // edge case: For now we accept unit tuples + let _: () = unsafe { MaybeUninit::uninit().assume_init() }; + + // This is OK, because `MaybeUninit` allows uninitialized data. + let _: MaybeUninit<usize> = unsafe { MaybeUninit::uninit().assume_init() }; + + // This is OK, because all constitutent types are uninit-compatible. + let _: (MaybeUninit<usize>, MaybeUninit<bool>) = unsafe { MaybeUninit::uninit().assume_init() }; + + // This is OK, because all constitutent types are uninit-compatible. + let _: (MaybeUninit<usize>, [MaybeUninit<bool>; 2]) = unsafe { MaybeUninit::uninit().assume_init() }; +} diff --git a/src/tools/clippy/tests/ui/uninit.stderr b/src/tools/clippy/tests/ui/uninit.stderr new file mode 100644 index 00000000000..a37233ecdda --- /dev/null +++ b/src/tools/clippy/tests/ui/uninit.stderr @@ -0,0 +1,16 @@ +error: this call for this type may be undefined behavior + --> $DIR/uninit.rs:6:29 + | +LL | let _: usize = unsafe { MaybeUninit::uninit().assume_init() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[deny(clippy::uninit_assumed_init)]` on by default + +error: this call for this type may be undefined behavior + --> $DIR/uninit.rs:9:31 + | +LL | let _: [u8; 0] = unsafe { MaybeUninit::uninit().assume_init() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/unit_arg.fixed b/src/tools/clippy/tests/ui/unit_arg.fixed new file mode 100644 index 00000000000..a739cf7ad81 --- /dev/null +++ b/src/tools/clippy/tests/ui/unit_arg.fixed @@ -0,0 +1,64 @@ +// run-rustfix +#![warn(clippy::unit_arg)] +#![allow(unused_braces, clippy::no_effect, unused_must_use)] + +use std::fmt::Debug; + +fn foo<T: Debug>(t: T) { + println!("{:?}", t); +} + +fn foo3<T1: Debug, T2: Debug, T3: Debug>(t1: T1, t2: T2, t3: T3) { + println!("{:?}, {:?}, {:?}", t1, t2, t3); +} + +struct Bar; + +impl Bar { + fn bar<T: Debug>(&self, t: T) { + println!("{:?}", t); + } +} + +fn bad() { + foo(()); + foo(()); + foo(()); + foo(()); + foo3((), 2, 2); + let b = Bar; + b.bar(()); +} + +fn ok() { + foo(()); + foo(1); + foo({ 1 }); + foo3("a", 3, vec![3]); + let b = Bar; + b.bar({ 1 }); + b.bar(()); + question_mark(); +} + +fn question_mark() -> Result<(), ()> { + Ok(Ok(())?)?; + Ok(Ok(()))??; + Ok(()) +} + +#[allow(dead_code)] +mod issue_2945 { + fn unit_fn() -> Result<(), i32> { + Ok(()) + } + + fn fallible() -> Result<(), i32> { + Ok(unit_fn()?) + } +} + +fn main() { + bad(); + ok(); +} diff --git a/src/tools/clippy/tests/ui/unit_arg.rs b/src/tools/clippy/tests/ui/unit_arg.rs new file mode 100644 index 00000000000..d90c49f79de --- /dev/null +++ b/src/tools/clippy/tests/ui/unit_arg.rs @@ -0,0 +1,71 @@ +// run-rustfix +#![warn(clippy::unit_arg)] +#![allow(unused_braces, clippy::no_effect, unused_must_use)] + +use std::fmt::Debug; + +fn foo<T: Debug>(t: T) { + println!("{:?}", t); +} + +fn foo3<T1: Debug, T2: Debug, T3: Debug>(t1: T1, t2: T2, t3: T3) { + println!("{:?}, {:?}, {:?}", t1, t2, t3); +} + +struct Bar; + +impl Bar { + fn bar<T: Debug>(&self, t: T) { + println!("{:?}", t); + } +} + +fn bad() { + foo({}); + foo({ + 1; + }); + foo(foo(1)); + foo({ + foo(1); + foo(2); + }); + foo3({}, 2, 2); + let b = Bar; + b.bar({ + 1; + }); +} + +fn ok() { + foo(()); + foo(1); + foo({ 1 }); + foo3("a", 3, vec![3]); + let b = Bar; + b.bar({ 1 }); + b.bar(()); + question_mark(); +} + +fn question_mark() -> Result<(), ()> { + Ok(Ok(())?)?; + Ok(Ok(()))??; + Ok(()) +} + +#[allow(dead_code)] +mod issue_2945 { + fn unit_fn() -> Result<(), i32> { + Ok(()) + } + + fn fallible() -> Result<(), i32> { + Ok(unit_fn()?) + } +} + +fn main() { + bad(); + ok(); +} diff --git a/src/tools/clippy/tests/ui/unit_arg.stderr b/src/tools/clippy/tests/ui/unit_arg.stderr new file mode 100644 index 00000000000..21ccc684ea9 --- /dev/null +++ b/src/tools/clippy/tests/ui/unit_arg.stderr @@ -0,0 +1,79 @@ +error: passing a unit value to a function + --> $DIR/unit_arg.rs:24:9 + | +LL | foo({}); + | ^^ + | + = note: `-D clippy::unit-arg` implied by `-D warnings` +help: if you intended to pass a unit value, use a unit literal instead + | +LL | foo(()); + | ^^ + +error: passing a unit value to a function + --> $DIR/unit_arg.rs:25:9 + | +LL | foo({ + | _________^ +LL | | 1; +LL | | }); + | |_____^ + | +help: if you intended to pass a unit value, use a unit literal instead + | +LL | foo(()); + | ^^ + +error: passing a unit value to a function + --> $DIR/unit_arg.rs:28:9 + | +LL | foo(foo(1)); + | ^^^^^^ + | +help: if you intended to pass a unit value, use a unit literal instead + | +LL | foo(()); + | ^^ + +error: passing a unit value to a function + --> $DIR/unit_arg.rs:29:9 + | +LL | foo({ + | _________^ +LL | | foo(1); +LL | | foo(2); +LL | | }); + | |_____^ + | +help: if you intended to pass a unit value, use a unit literal instead + | +LL | foo(()); + | ^^ + +error: passing a unit value to a function + --> $DIR/unit_arg.rs:33:10 + | +LL | foo3({}, 2, 2); + | ^^ + | +help: if you intended to pass a unit value, use a unit literal instead + | +LL | foo3((), 2, 2); + | ^^ + +error: passing a unit value to a function + --> $DIR/unit_arg.rs:35:11 + | +LL | b.bar({ + | ___________^ +LL | | 1; +LL | | }); + | |_____^ + | +help: if you intended to pass a unit value, use a unit literal instead + | +LL | b.bar(()); + | ^^ + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/unit_cmp.rs b/src/tools/clippy/tests/ui/unit_cmp.rs new file mode 100644 index 00000000000..8d3a4eed82e --- /dev/null +++ b/src/tools/clippy/tests/ui/unit_cmp.rs @@ -0,0 +1,57 @@ +#![warn(clippy::unit_cmp)] +#![allow(clippy::no_effect, clippy::unnecessary_operation)] + +#[derive(PartialEq)] +pub struct ContainsUnit(()); // should be fine + +fn main() { + // this is fine + if true == false {} + + // this warns + if { + true; + } == { + false; + } {} + + if { + true; + } > { + false; + } {} + + assert_eq!( + { + true; + }, + { + false; + } + ); + debug_assert_eq!( + { + true; + }, + { + false; + } + ); + + assert_ne!( + { + true; + }, + { + false; + } + ); + debug_assert_ne!( + { + true; + }, + { + false; + } + ); +} diff --git a/src/tools/clippy/tests/ui/unit_cmp.stderr b/src/tools/clippy/tests/ui/unit_cmp.stderr new file mode 100644 index 00000000000..c8c0a85dfc1 --- /dev/null +++ b/src/tools/clippy/tests/ui/unit_cmp.stderr @@ -0,0 +1,82 @@ +error: ==-comparison of unit values detected. This will always be true + --> $DIR/unit_cmp.rs:12:8 + | +LL | if { + | ________^ +LL | | true; +LL | | } == { +LL | | false; +LL | | } {} + | |_____^ + | + = note: `-D clippy::unit-cmp` implied by `-D warnings` + +error: >-comparison of unit values detected. This will always be false + --> $DIR/unit_cmp.rs:18:8 + | +LL | if { + | ________^ +LL | | true; +LL | | } > { +LL | | false; +LL | | } {} + | |_____^ + +error: `assert_eq` of unit values detected. This will always succeed + --> $DIR/unit_cmp.rs:24:5 + | +LL | / assert_eq!( +LL | | { +LL | | true; +LL | | }, +... | +LL | | } +LL | | ); + | |______^ + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: `debug_assert_eq` of unit values detected. This will always succeed + --> $DIR/unit_cmp.rs:32:5 + | +LL | / debug_assert_eq!( +LL | | { +LL | | true; +LL | | }, +... | +LL | | } +LL | | ); + | |______^ + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: `assert_ne` of unit values detected. This will always fail + --> $DIR/unit_cmp.rs:41:5 + | +LL | / assert_ne!( +LL | | { +LL | | true; +LL | | }, +... | +LL | | } +LL | | ); + | |______^ + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: `debug_assert_ne` of unit values detected. This will always fail + --> $DIR/unit_cmp.rs:49:5 + | +LL | / debug_assert_ne!( +LL | | { +LL | | true; +LL | | }, +... | +LL | | } +LL | | ); + | |______^ + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/unknown_attribute.rs b/src/tools/clippy/tests/ui/unknown_attribute.rs new file mode 100644 index 00000000000..e993e63f8ed --- /dev/null +++ b/src/tools/clippy/tests/ui/unknown_attribute.rs @@ -0,0 +1,3 @@ +#[clippy::unknown] +#[clippy::cognitive_complexity = "1"] +fn main() {} diff --git a/src/tools/clippy/tests/ui/unknown_attribute.stderr b/src/tools/clippy/tests/ui/unknown_attribute.stderr new file mode 100644 index 00000000000..47e37aed246 --- /dev/null +++ b/src/tools/clippy/tests/ui/unknown_attribute.stderr @@ -0,0 +1,8 @@ +error: Usage of unknown attribute + --> $DIR/unknown_attribute.rs:1:11 + | +LL | #[clippy::unknown] + | ^^^^^^^ + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/unknown_clippy_lints.fixed b/src/tools/clippy/tests/ui/unknown_clippy_lints.fixed new file mode 100644 index 00000000000..4249ff8a958 --- /dev/null +++ b/src/tools/clippy/tests/ui/unknown_clippy_lints.fixed @@ -0,0 +1,18 @@ +// run-rustfix + +#![warn(clippy::pedantic)] +// Should suggest lowercase +#![allow(clippy::all)] +#![warn(clippy::cmp_nan)] + +// Should suggest similar clippy lint name +#[warn(clippy::if_not_else)] +#[warn(clippy::unnecessary_cast)] +#[warn(clippy::useless_transmute)] +// Shouldn't suggest rustc lint name(`dead_code`) +#[warn(clippy::drop_copy)] +// Shouldn't suggest removed/deprecated clippy lint name(`unused_collect`) +#[warn(clippy::unused_self)] +// Shouldn't suggest renamed clippy lint name(`const_static_lifetime`) +#[warn(clippy::redundant_static_lifetimes)] +fn main() {} diff --git a/src/tools/clippy/tests/ui/unknown_clippy_lints.rs b/src/tools/clippy/tests/ui/unknown_clippy_lints.rs new file mode 100644 index 00000000000..5db345f5444 --- /dev/null +++ b/src/tools/clippy/tests/ui/unknown_clippy_lints.rs @@ -0,0 +1,18 @@ +// run-rustfix + +#![warn(clippy::pedantic)] +// Should suggest lowercase +#![allow(clippy::All)] +#![warn(clippy::CMP_NAN)] + +// Should suggest similar clippy lint name +#[warn(clippy::if_not_els)] +#[warn(clippy::UNNecsaRy_cAst)] +#[warn(clippy::useles_transute)] +// Shouldn't suggest rustc lint name(`dead_code`) +#[warn(clippy::dead_cod)] +// Shouldn't suggest removed/deprecated clippy lint name(`unused_collect`) +#[warn(clippy::unused_colle)] +// Shouldn't suggest renamed clippy lint name(`const_static_lifetime`) +#[warn(clippy::const_static_lifetim)] +fn main() {} diff --git a/src/tools/clippy/tests/ui/unknown_clippy_lints.stderr b/src/tools/clippy/tests/ui/unknown_clippy_lints.stderr new file mode 100644 index 00000000000..1b859043bb5 --- /dev/null +++ b/src/tools/clippy/tests/ui/unknown_clippy_lints.stderr @@ -0,0 +1,52 @@ +error: unknown clippy lint: clippy::if_not_els + --> $DIR/unknown_clippy_lints.rs:9:8 + | +LL | #[warn(clippy::if_not_els)] + | ^^^^^^^^^^^^^^^^^^ help: did you mean: `clippy::if_not_else` + | + = note: `-D clippy::unknown-clippy-lints` implied by `-D warnings` + +error: unknown clippy lint: clippy::UNNecsaRy_cAst + --> $DIR/unknown_clippy_lints.rs:10:8 + | +LL | #[warn(clippy::UNNecsaRy_cAst)] + | ^^^^^^^^^^^^^^^^^^^^^^ help: did you mean: `clippy::unnecessary_cast` + +error: unknown clippy lint: clippy::useles_transute + --> $DIR/unknown_clippy_lints.rs:11:8 + | +LL | #[warn(clippy::useles_transute)] + | ^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean: `clippy::useless_transmute` + +error: unknown clippy lint: clippy::dead_cod + --> $DIR/unknown_clippy_lints.rs:13:8 + | +LL | #[warn(clippy::dead_cod)] + | ^^^^^^^^^^^^^^^^ help: did you mean: `clippy::drop_copy` + +error: unknown clippy lint: clippy::unused_colle + --> $DIR/unknown_clippy_lints.rs:15:8 + | +LL | #[warn(clippy::unused_colle)] + | ^^^^^^^^^^^^^^^^^^^^ help: did you mean: `clippy::unused_self` + +error: unknown clippy lint: clippy::const_static_lifetim + --> $DIR/unknown_clippy_lints.rs:17:8 + | +LL | #[warn(clippy::const_static_lifetim)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean: `clippy::redundant_static_lifetimes` + +error: unknown clippy lint: clippy::All + --> $DIR/unknown_clippy_lints.rs:5:10 + | +LL | #![allow(clippy::All)] + | ^^^^^^^^^^^ help: lowercase the lint name: `clippy::all` + +error: unknown clippy lint: clippy::CMP_NAN + --> $DIR/unknown_clippy_lints.rs:6:9 + | +LL | #![warn(clippy::CMP_NAN)] + | ^^^^^^^^^^^^^^^ help: lowercase the lint name: `clippy::cmp_nan` + +error: aborting due to 8 previous errors + diff --git a/src/tools/clippy/tests/ui/unnecessary_cast.rs b/src/tools/clippy/tests/ui/unnecessary_cast.rs new file mode 100644 index 00000000000..df9b227eeb3 --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_cast.rs @@ -0,0 +1,23 @@ +#![warn(clippy::unnecessary_cast)] +#![allow(clippy::no_effect)] + +fn main() { + // Test cast_unnecessary + 1i32 as i32; + 1f32 as f32; + false as bool; + &1i32 as &i32; + + // macro version + macro_rules! foo { + ($a:ident, $b:ident) => { + #[allow(unused)] + pub fn $a() -> $b { + 1 as $b + } + }; + } + foo!(a, i32); + foo!(b, f32); + foo!(c, f64); +} diff --git a/src/tools/clippy/tests/ui/unnecessary_cast.stderr b/src/tools/clippy/tests/ui/unnecessary_cast.stderr new file mode 100644 index 00000000000..8981d13e8ea --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_cast.stderr @@ -0,0 +1,22 @@ +error: casting to the same type is unnecessary (`i32` -> `i32`) + --> $DIR/unnecessary_cast.rs:6:5 + | +LL | 1i32 as i32; + | ^^^^^^^^^^^ + | + = note: `-D clippy::unnecessary-cast` implied by `-D warnings` + +error: casting to the same type is unnecessary (`f32` -> `f32`) + --> $DIR/unnecessary_cast.rs:7:5 + | +LL | 1f32 as f32; + | ^^^^^^^^^^^ + +error: casting to the same type is unnecessary (`bool` -> `bool`) + --> $DIR/unnecessary_cast.rs:8:5 + | +LL | false as bool; + | ^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/unnecessary_cast_fixable.fixed b/src/tools/clippy/tests/ui/unnecessary_cast_fixable.fixed new file mode 100644 index 00000000000..fb89a9fce3d --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_cast_fixable.fixed @@ -0,0 +1,23 @@ +// run-rustfix + +#![warn(clippy::unnecessary_cast)] +#![allow(clippy::no_effect, clippy::unnecessary_operation)] + +fn main() { + // casting integer literal to float is unnecessary + 100_f32; + 100_f64; + 100_f64; + // Should not trigger + #[rustfmt::skip] + let v = vec!(1); + &v as &[i32]; + 1.0 as f64; + 1 as u64; + 0x10 as f32; + 0o10 as f32; + 0b10 as f32; + 0x11 as f64; + 0o11 as f64; + 0b11 as f64; +} diff --git a/src/tools/clippy/tests/ui/unnecessary_cast_fixable.rs b/src/tools/clippy/tests/ui/unnecessary_cast_fixable.rs new file mode 100644 index 00000000000..4a0c8620dc1 --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_cast_fixable.rs @@ -0,0 +1,23 @@ +// run-rustfix + +#![warn(clippy::unnecessary_cast)] +#![allow(clippy::no_effect, clippy::unnecessary_operation)] + +fn main() { + // casting integer literal to float is unnecessary + 100 as f32; + 100 as f64; + 100_i32 as f64; + // Should not trigger + #[rustfmt::skip] + let v = vec!(1); + &v as &[i32]; + 1.0 as f64; + 1 as u64; + 0x10 as f32; + 0o10 as f32; + 0b10 as f32; + 0x11 as f64; + 0o11 as f64; + 0b11 as f64; +} diff --git a/src/tools/clippy/tests/ui/unnecessary_cast_fixable.stderr b/src/tools/clippy/tests/ui/unnecessary_cast_fixable.stderr new file mode 100644 index 00000000000..8ff1e5dea60 --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_cast_fixable.stderr @@ -0,0 +1,22 @@ +error: casting integer literal to `f32` is unnecessary + --> $DIR/unnecessary_cast_fixable.rs:8:5 + | +LL | 100 as f32; + | ^^^^^^^^^^ help: try: `100_f32` + | + = note: `-D clippy::unnecessary-cast` implied by `-D warnings` + +error: casting integer literal to `f64` is unnecessary + --> $DIR/unnecessary_cast_fixable.rs:9:5 + | +LL | 100 as f64; + | ^^^^^^^^^^ help: try: `100_f64` + +error: casting integer literal to `f64` is unnecessary + --> $DIR/unnecessary_cast_fixable.rs:10:5 + | +LL | 100_i32 as f64; + | ^^^^^^^^^^^^^^ help: try: `100_f64` + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/unnecessary_clone.rs b/src/tools/clippy/tests/ui/unnecessary_clone.rs new file mode 100644 index 00000000000..f1cc5b564c1 --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_clone.rs @@ -0,0 +1,117 @@ +// does not test any rustfixable lints + +#![warn(clippy::clone_on_ref_ptr)] +#![allow(unused, clippy::redundant_clone)] + +use std::cell::RefCell; +use std::rc::{self, Rc}; +use std::sync::{self, Arc}; + +trait SomeTrait {} +struct SomeImpl; +impl SomeTrait for SomeImpl {} + +fn main() {} + +fn is_ascii(ch: char) -> bool { + ch.is_ascii() +} + +fn clone_on_copy() { + 42.clone(); + + vec![1].clone(); // ok, not a Copy type + Some(vec![1]).clone(); // ok, not a Copy type + (&42).clone(); + + let rc = RefCell::new(0); + rc.borrow().clone(); + + // Issue #4348 + let mut x = 43; + let _ = &x.clone(); // ok, getting a ref + 'a'.clone().make_ascii_uppercase(); // ok, clone and then mutate + is_ascii('z'.clone()); + + // Issue #5436 + let mut vec = Vec::new(); + vec.push(42.clone()); +} + +fn clone_on_ref_ptr() { + let rc = Rc::new(true); + let arc = Arc::new(true); + + let rcweak = Rc::downgrade(&rc); + let arc_weak = Arc::downgrade(&arc); + + rc.clone(); + Rc::clone(&rc); + + arc.clone(); + Arc::clone(&arc); + + rcweak.clone(); + rc::Weak::clone(&rcweak); + + arc_weak.clone(); + sync::Weak::clone(&arc_weak); + + let x = Arc::new(SomeImpl); + let _: Arc<dyn SomeTrait> = x.clone(); +} + +fn clone_on_copy_generic<T: Copy>(t: T) { + t.clone(); + + Some(t).clone(); +} + +fn clone_on_double_ref() { + let x = vec![1]; + let y = &&x; + let z: &Vec<_> = y.clone(); + + println!("{:p} {:p}", *y, z); +} + +mod many_derefs { + struct A; + struct B; + struct C; + struct D; + #[derive(Copy, Clone)] + struct E; + + macro_rules! impl_deref { + ($src:ident, $dst:ident) => { + impl std::ops::Deref for $src { + type Target = $dst; + fn deref(&self) -> &Self::Target { + &$dst + } + } + }; + } + + impl_deref!(A, B); + impl_deref!(B, C); + impl_deref!(C, D); + impl std::ops::Deref for D { + type Target = &'static E; + fn deref(&self) -> &Self::Target { + &&E + } + } + + fn go1() { + let a = A; + let _: E = a.clone(); + let _: E = *****a; + } + + fn check(mut encoded: &[u8]) { + let _ = &mut encoded.clone(); + let _ = &encoded.clone(); + } +} diff --git a/src/tools/clippy/tests/ui/unnecessary_clone.stderr b/src/tools/clippy/tests/ui/unnecessary_clone.stderr new file mode 100644 index 00000000000..6176a2bc464 --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_clone.stderr @@ -0,0 +1,130 @@ +error: using `clone` on a `Copy` type + --> $DIR/unnecessary_clone.rs:21:5 + | +LL | 42.clone(); + | ^^^^^^^^^^ help: try removing the `clone` call: `42` + | + = note: `-D clippy::clone-on-copy` implied by `-D warnings` + +error: using `clone` on a `Copy` type + --> $DIR/unnecessary_clone.rs:25:5 + | +LL | (&42).clone(); + | ^^^^^^^^^^^^^ help: try dereferencing it: `*(&42)` + +error: using `clone` on a `Copy` type + --> $DIR/unnecessary_clone.rs:28:5 + | +LL | rc.borrow().clone(); + | ^^^^^^^^^^^^^^^^^^^ help: try dereferencing it: `*rc.borrow()` + +error: using `clone` on a `Copy` type + --> $DIR/unnecessary_clone.rs:34:14 + | +LL | is_ascii('z'.clone()); + | ^^^^^^^^^^^ help: try removing the `clone` call: `'z'` + +error: using `clone` on a `Copy` type + --> $DIR/unnecessary_clone.rs:38:14 + | +LL | vec.push(42.clone()); + | ^^^^^^^^^^ help: try removing the `clone` call: `42` + +error: using `.clone()` on a ref-counted pointer + --> $DIR/unnecessary_clone.rs:48:5 + | +LL | rc.clone(); + | ^^^^^^^^^^ help: try this: `Rc::<bool>::clone(&rc)` + | + = note: `-D clippy::clone-on-ref-ptr` implied by `-D warnings` + +error: using `.clone()` on a ref-counted pointer + --> $DIR/unnecessary_clone.rs:51:5 + | +LL | arc.clone(); + | ^^^^^^^^^^^ help: try this: `Arc::<bool>::clone(&arc)` + +error: using `.clone()` on a ref-counted pointer + --> $DIR/unnecessary_clone.rs:54:5 + | +LL | rcweak.clone(); + | ^^^^^^^^^^^^^^ help: try this: `Weak::<bool>::clone(&rcweak)` + +error: using `.clone()` on a ref-counted pointer + --> $DIR/unnecessary_clone.rs:57:5 + | +LL | arc_weak.clone(); + | ^^^^^^^^^^^^^^^^ help: try this: `Weak::<bool>::clone(&arc_weak)` + +error: using `.clone()` on a ref-counted pointer + --> $DIR/unnecessary_clone.rs:61:33 + | +LL | let _: Arc<dyn SomeTrait> = x.clone(); + | ^^^^^^^^^ help: try this: `Arc::<SomeImpl>::clone(&x)` + +error: using `clone` on a `Copy` type + --> $DIR/unnecessary_clone.rs:65:5 + | +LL | t.clone(); + | ^^^^^^^^^ help: try removing the `clone` call: `t` + +error: using `clone` on a `Copy` type + --> $DIR/unnecessary_clone.rs:67:5 + | +LL | Some(t).clone(); + | ^^^^^^^^^^^^^^^ help: try removing the `clone` call: `Some(t)` + +error: using `clone` on a double-reference; this will copy the reference instead of cloning the inner type + --> $DIR/unnecessary_clone.rs:73:22 + | +LL | let z: &Vec<_> = y.clone(); + | ^^^^^^^^^ + | + = note: `#[deny(clippy::clone_double_ref)]` on by default +help: try dereferencing it + | +LL | let z: &Vec<_> = &(*y).clone(); + | ^^^^^^^^^^^^^ +help: or try being explicit if you are sure, that you want to clone a reference + | +LL | let z: &Vec<_> = <&std::vec::Vec<i32>>::clone(y); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: using `clone` on a `Copy` type + --> $DIR/unnecessary_clone.rs:109:20 + | +LL | let _: E = a.clone(); + | ^^^^^^^^^ help: try dereferencing it: `*****a` + +error: using `clone` on a double-reference; this will copy the reference instead of cloning the inner type + --> $DIR/unnecessary_clone.rs:114:22 + | +LL | let _ = &mut encoded.clone(); + | ^^^^^^^^^^^^^^^ + | +help: try dereferencing it + | +LL | let _ = &mut &(*encoded).clone(); + | ^^^^^^^^^^^^^^^^^^^ +help: or try being explicit if you are sure, that you want to clone a reference + | +LL | let _ = &mut <&[u8]>::clone(encoded); + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: using `clone` on a double-reference; this will copy the reference instead of cloning the inner type + --> $DIR/unnecessary_clone.rs:115:18 + | +LL | let _ = &encoded.clone(); + | ^^^^^^^^^^^^^^^ + | +help: try dereferencing it + | +LL | let _ = &&(*encoded).clone(); + | ^^^^^^^^^^^^^^^^^^^ +help: or try being explicit if you are sure, that you want to clone a reference + | +LL | let _ = &<&[u8]>::clone(encoded); + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 16 previous errors + diff --git a/src/tools/clippy/tests/ui/unnecessary_filter_map.rs b/src/tools/clippy/tests/ui/unnecessary_filter_map.rs new file mode 100644 index 00000000000..af858e4abcf --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_filter_map.rs @@ -0,0 +1,17 @@ +fn main() { + let _ = (0..4).filter_map(|x| if x > 1 { Some(x) } else { None }); + let _ = (0..4).filter_map(|x| { + if x > 1 { + return Some(x); + }; + None + }); + let _ = (0..4).filter_map(|x| match x { + 0 | 1 => None, + _ => Some(x), + }); + + let _ = (0..4).filter_map(|x| Some(x + 1)); + + let _ = (0..4).filter_map(i32::checked_abs); +} diff --git a/src/tools/clippy/tests/ui/unnecessary_filter_map.stderr b/src/tools/clippy/tests/ui/unnecessary_filter_map.stderr new file mode 100644 index 00000000000..041829c3c78 --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_filter_map.stderr @@ -0,0 +1,38 @@ +error: this `.filter_map` can be written more simply using `.filter` + --> $DIR/unnecessary_filter_map.rs:2:13 + | +LL | let _ = (0..4).filter_map(|x| if x > 1 { Some(x) } else { None }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::unnecessary-filter-map` implied by `-D warnings` + +error: this `.filter_map` can be written more simply using `.filter` + --> $DIR/unnecessary_filter_map.rs:3:13 + | +LL | let _ = (0..4).filter_map(|x| { + | _____________^ +LL | | if x > 1 { +LL | | return Some(x); +LL | | }; +LL | | None +LL | | }); + | |______^ + +error: this `.filter_map` can be written more simply using `.filter` + --> $DIR/unnecessary_filter_map.rs:9:13 + | +LL | let _ = (0..4).filter_map(|x| match x { + | _____________^ +LL | | 0 | 1 => None, +LL | | _ => Some(x), +LL | | }); + | |______^ + +error: this `.filter_map` can be written more simply using `.map` + --> $DIR/unnecessary_filter_map.rs:14:13 + | +LL | let _ = (0..4).filter_map(|x| Some(x + 1)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/unnecessary_flat_map.fixed b/src/tools/clippy/tests/ui/unnecessary_flat_map.fixed new file mode 100644 index 00000000000..dfe3bd47e13 --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_flat_map.fixed @@ -0,0 +1,14 @@ +// run-rustfix + +#![allow(unused_imports)] +#![warn(clippy::flat_map_identity)] + +use std::convert; + +fn main() { + let iterator = [[0, 1], [2, 3], [4, 5]].iter(); + let _ = iterator.flatten(); + + let iterator = [[0, 1], [2, 3], [4, 5]].iter(); + let _ = iterator.flatten(); +} diff --git a/src/tools/clippy/tests/ui/unnecessary_flat_map.rs b/src/tools/clippy/tests/ui/unnecessary_flat_map.rs new file mode 100644 index 00000000000..393b9569255 --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_flat_map.rs @@ -0,0 +1,14 @@ +// run-rustfix + +#![allow(unused_imports)] +#![warn(clippy::flat_map_identity)] + +use std::convert; + +fn main() { + let iterator = [[0, 1], [2, 3], [4, 5]].iter(); + let _ = iterator.flat_map(|x| x); + + let iterator = [[0, 1], [2, 3], [4, 5]].iter(); + let _ = iterator.flat_map(convert::identity); +} diff --git a/src/tools/clippy/tests/ui/unnecessary_flat_map.stderr b/src/tools/clippy/tests/ui/unnecessary_flat_map.stderr new file mode 100644 index 00000000000..a1cd5745e49 --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_flat_map.stderr @@ -0,0 +1,16 @@ +error: called `flat_map(|x| x)` on an `Iterator` + --> $DIR/unnecessary_flat_map.rs:10:22 + | +LL | let _ = iterator.flat_map(|x| x); + | ^^^^^^^^^^^^^^^ help: try: `flatten()` + | + = note: `-D clippy::flat-map-identity` implied by `-D warnings` + +error: called `flat_map(std::convert::identity)` on an `Iterator` + --> $DIR/unnecessary_flat_map.rs:13:22 + | +LL | let _ = iterator.flat_map(convert::identity); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `flatten()` + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/unnecessary_fold.fixed b/src/tools/clippy/tests/ui/unnecessary_fold.fixed new file mode 100644 index 00000000000..52300a3b640 --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_fold.fixed @@ -0,0 +1,52 @@ +// run-rustfix + +#![allow(dead_code)] + +/// Calls which should trigger the `UNNECESSARY_FOLD` lint +fn unnecessary_fold() { + // Can be replaced by .any + let _ = (0..3).any(|x| x > 2); + // Can be replaced by .all + let _ = (0..3).all(|x| x > 2); + // Can be replaced by .sum + let _: i32 = (0..3).sum(); + // Can be replaced by .product + let _: i32 = (0..3).product(); +} + +/// 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); +} + +/// 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); + + // We only match against an accumulator on the left + // hand side. We could lint for .sum and .product when + // it's on the right, but don't for now (and this wouldn't + // be valid if we extended the lint to cover arbitrary numeric + // types). + let _ = (0..3).fold(false, |acc, x| x > 2 || acc); + let _ = (0..3).fold(true, |acc, x| x > 2 && acc); + let _ = (0..3).fold(0, |acc, x| x + acc); + let _ = (0..3).fold(1, |acc, x| x * acc); + + 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); +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/unnecessary_fold.rs b/src/tools/clippy/tests/ui/unnecessary_fold.rs new file mode 100644 index 00000000000..4028d80c0a3 --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_fold.rs @@ -0,0 +1,52 @@ +// run-rustfix + +#![allow(dead_code)] + +/// Calls which should trigger the `UNNECESSARY_FOLD` lint +fn unnecessary_fold() { + // Can be replaced by .any + let _ = (0..3).fold(false, |acc, x| acc || x > 2); + // Can be replaced by .all + let _ = (0..3).fold(true, |acc, x| acc && x > 2); + // Can be replaced by .sum + let _: i32 = (0..3).fold(0, |acc, x| acc + x); + // Can be replaced by .product + let _: i32 = (0..3).fold(1, |acc, x| acc * x); +} + +/// 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).fold(false, |acc, x| acc || x > 2); +} + +/// 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); + + // We only match against an accumulator on the left + // hand side. We could lint for .sum and .product when + // it's on the right, but don't for now (and this wouldn't + // be valid if we extended the lint to cover arbitrary numeric + // types). + let _ = (0..3).fold(false, |acc, x| x > 2 || acc); + let _ = (0..3).fold(true, |acc, x| x > 2 && acc); + let _ = (0..3).fold(0, |acc, x| x + acc); + let _ = (0..3).fold(1, |acc, x| x * acc); + + 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) + .fold(false, |acc, x| acc || x > 2); +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/unnecessary_fold.stderr b/src/tools/clippy/tests/ui/unnecessary_fold.stderr new file mode 100644 index 00000000000..22c44588ab7 --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_fold.stderr @@ -0,0 +1,40 @@ +error: this `.fold` can be written more succinctly using another method + --> $DIR/unnecessary_fold.rs:8:20 + | +LL | let _ = (0..3).fold(false, |acc, x| acc || x > 2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `any(|x| x > 2)` + | + = note: `-D clippy::unnecessary-fold` implied by `-D warnings` + +error: this `.fold` can be written more succinctly using another method + --> $DIR/unnecessary_fold.rs:10:20 + | +LL | let _ = (0..3).fold(true, |acc, x| acc && x > 2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `all(|x| x > 2)` + +error: this `.fold` can be written more succinctly using another method + --> $DIR/unnecessary_fold.rs:12:25 + | +LL | let _: i32 = (0..3).fold(0, |acc, x| acc + x); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `sum()` + +error: this `.fold` can be written more succinctly using another method + --> $DIR/unnecessary_fold.rs:14:25 + | +LL | let _: i32 = (0..3).fold(1, |acc, x| acc * x); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `product()` + +error: this `.fold` can be written more succinctly using another method + --> $DIR/unnecessary_fold.rs:19:41 + | +LL | let _: bool = (0..3).map(|x| 2 * x).fold(false, |acc, x| acc || x > 2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `any(|x| x > 2)` + +error: this `.fold` can be written more succinctly using another method + --> $DIR/unnecessary_fold.rs:49:10 + | +LL | .fold(false, |acc, x| acc || x > 2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `any(|x| x > 2)` + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/unnecessary_operation.fixed b/src/tools/clippy/tests/ui/unnecessary_operation.fixed new file mode 100644 index 00000000000..2fca96c4cd5 --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_operation.fixed @@ -0,0 +1,79 @@ +// run-rustfix + +#![feature(box_syntax)] +#![allow(clippy::deref_addrof, dead_code, unused, clippy::no_effect)] +#![warn(clippy::unnecessary_operation)] + +struct Tuple(i32); +struct Struct { + field: i32, +} +enum Enum { + Tuple(i32), + Struct { field: i32 }, +} +struct DropStruct { + field: i32, +} +impl Drop for DropStruct { + fn drop(&mut self) {} +} +struct DropTuple(i32); +impl Drop for DropTuple { + fn drop(&mut self) {} +} +enum DropEnum { + Tuple(i32), + Struct { field: i32 }, +} +impl Drop for DropEnum { + fn drop(&mut self) {} +} +struct FooString { + s: String, +} + +fn get_number() -> i32 { + 0 +} + +fn get_usize() -> usize { + 0 +} +fn get_struct() -> Struct { + Struct { field: 0 } +} +fn get_drop_struct() -> DropStruct { + DropStruct { field: 0 } +} + +fn main() { + get_number(); + get_number(); + get_struct(); + get_number(); + get_number(); + 5;get_number(); + get_number(); + get_number(); + 5;6;get_number(); + get_number(); + get_number(); + get_number(); + 5;get_number(); + 42;get_number(); + [42, 55];get_usize(); + 42;get_number(); + get_number(); + [42; 55];get_usize(); + get_number(); + String::from("blah"); + + // Do not warn + DropTuple(get_number()); + DropStruct { field: get_number() }; + DropStruct { field: get_number() }; + DropStruct { ..get_drop_struct() }; + DropEnum::Tuple(get_number()); + DropEnum::Struct { field: get_number() }; +} diff --git a/src/tools/clippy/tests/ui/unnecessary_operation.rs b/src/tools/clippy/tests/ui/unnecessary_operation.rs new file mode 100644 index 00000000000..08cb9ab522e --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_operation.rs @@ -0,0 +1,83 @@ +// run-rustfix + +#![feature(box_syntax)] +#![allow(clippy::deref_addrof, dead_code, unused, clippy::no_effect)] +#![warn(clippy::unnecessary_operation)] + +struct Tuple(i32); +struct Struct { + field: i32, +} +enum Enum { + Tuple(i32), + Struct { field: i32 }, +} +struct DropStruct { + field: i32, +} +impl Drop for DropStruct { + fn drop(&mut self) {} +} +struct DropTuple(i32); +impl Drop for DropTuple { + fn drop(&mut self) {} +} +enum DropEnum { + Tuple(i32), + Struct { field: i32 }, +} +impl Drop for DropEnum { + fn drop(&mut self) {} +} +struct FooString { + s: String, +} + +fn get_number() -> i32 { + 0 +} + +fn get_usize() -> usize { + 0 +} +fn get_struct() -> Struct { + Struct { field: 0 } +} +fn get_drop_struct() -> DropStruct { + DropStruct { field: 0 } +} + +fn main() { + Tuple(get_number()); + Struct { field: get_number() }; + Struct { ..get_struct() }; + Enum::Tuple(get_number()); + Enum::Struct { field: get_number() }; + 5 + get_number(); + *&get_number(); + &get_number(); + (5, 6, get_number()); + box get_number(); + get_number()..; + ..get_number(); + 5..get_number(); + [42, get_number()]; + [42, 55][get_usize()]; + (42, get_number()).1; + [get_number(); 55]; + [42; 55][get_usize()]; + { + get_number() + }; + FooString { + s: String::from("blah"), + }; + + // Do not warn + DropTuple(get_number()); + DropStruct { field: get_number() }; + DropStruct { field: get_number() }; + DropStruct { ..get_drop_struct() }; + DropEnum::Tuple(get_number()); + DropEnum::Struct { field: get_number() }; +} diff --git a/src/tools/clippy/tests/ui/unnecessary_operation.stderr b/src/tools/clippy/tests/ui/unnecessary_operation.stderr new file mode 100644 index 00000000000..f88c9f9908b --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_operation.stderr @@ -0,0 +1,128 @@ +error: statement can be reduced + --> $DIR/unnecessary_operation.rs:51:5 + | +LL | Tuple(get_number()); + | ^^^^^^^^^^^^^^^^^^^^ help: replace it with: `get_number();` + | + = note: `-D clippy::unnecessary-operation` implied by `-D warnings` + +error: statement can be reduced + --> $DIR/unnecessary_operation.rs:52:5 + | +LL | Struct { field: get_number() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `get_number();` + +error: statement can be reduced + --> $DIR/unnecessary_operation.rs:53:5 + | +LL | Struct { ..get_struct() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `get_struct();` + +error: statement can be reduced + --> $DIR/unnecessary_operation.rs:54:5 + | +LL | Enum::Tuple(get_number()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `get_number();` + +error: statement can be reduced + --> $DIR/unnecessary_operation.rs:55:5 + | +LL | Enum::Struct { field: get_number() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `get_number();` + +error: statement can be reduced + --> $DIR/unnecessary_operation.rs:56:5 + | +LL | 5 + get_number(); + | ^^^^^^^^^^^^^^^^^ help: replace it with: `5;get_number();` + +error: statement can be reduced + --> $DIR/unnecessary_operation.rs:57:5 + | +LL | *&get_number(); + | ^^^^^^^^^^^^^^^ help: replace it with: `get_number();` + +error: statement can be reduced + --> $DIR/unnecessary_operation.rs:58:5 + | +LL | &get_number(); + | ^^^^^^^^^^^^^^ help: replace it with: `get_number();` + +error: statement can be reduced + --> $DIR/unnecessary_operation.rs:59:5 + | +LL | (5, 6, get_number()); + | ^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `5;6;get_number();` + +error: statement can be reduced + --> $DIR/unnecessary_operation.rs:60:5 + | +LL | box get_number(); + | ^^^^^^^^^^^^^^^^^ help: replace it with: `get_number();` + +error: statement can be reduced + --> $DIR/unnecessary_operation.rs:61:5 + | +LL | get_number()..; + | ^^^^^^^^^^^^^^^ help: replace it with: `get_number();` + +error: statement can be reduced + --> $DIR/unnecessary_operation.rs:62:5 + | +LL | ..get_number(); + | ^^^^^^^^^^^^^^^ help: replace it with: `get_number();` + +error: statement can be reduced + --> $DIR/unnecessary_operation.rs:63:5 + | +LL | 5..get_number(); + | ^^^^^^^^^^^^^^^^ help: replace it with: `5;get_number();` + +error: statement can be reduced + --> $DIR/unnecessary_operation.rs:64:5 + | +LL | [42, get_number()]; + | ^^^^^^^^^^^^^^^^^^^ help: replace it with: `42;get_number();` + +error: statement can be reduced + --> $DIR/unnecessary_operation.rs:65:5 + | +LL | [42, 55][get_usize()]; + | ^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `[42, 55];get_usize();` + +error: statement can be reduced + --> $DIR/unnecessary_operation.rs:66:5 + | +LL | (42, get_number()).1; + | ^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `42;get_number();` + +error: statement can be reduced + --> $DIR/unnecessary_operation.rs:67:5 + | +LL | [get_number(); 55]; + | ^^^^^^^^^^^^^^^^^^^ help: replace it with: `get_number();` + +error: statement can be reduced + --> $DIR/unnecessary_operation.rs:68:5 + | +LL | [42; 55][get_usize()]; + | ^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `[42; 55];get_usize();` + +error: statement can be reduced + --> $DIR/unnecessary_operation.rs:69:5 + | +LL | / { +LL | | get_number() +LL | | }; + | |______^ help: replace it with: `get_number();` + +error: statement can be reduced + --> $DIR/unnecessary_operation.rs:72:5 + | +LL | / FooString { +LL | | s: String::from("blah"), +LL | | }; + | |______^ help: replace it with: `String::from("blah");` + +error: aborting due to 20 previous errors + diff --git a/src/tools/clippy/tests/ui/unnecessary_ref.fixed b/src/tools/clippy/tests/ui/unnecessary_ref.fixed new file mode 100644 index 00000000000..f7b94118d4e --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_ref.fixed @@ -0,0 +1,14 @@ +// run-rustfix + +#![feature(stmt_expr_attributes)] +#![allow(unused_variables)] + +struct Outer { + inner: u32, +} + +#[deny(clippy::ref_in_deref)] +fn main() { + let outer = Outer { inner: 0 }; + let inner = outer.inner; +} diff --git a/src/tools/clippy/tests/ui/unnecessary_ref.rs b/src/tools/clippy/tests/ui/unnecessary_ref.rs new file mode 100644 index 00000000000..4e585b9b96b --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_ref.rs @@ -0,0 +1,14 @@ +// run-rustfix + +#![feature(stmt_expr_attributes)] +#![allow(unused_variables)] + +struct Outer { + inner: u32, +} + +#[deny(clippy::ref_in_deref)] +fn main() { + let outer = Outer { inner: 0 }; + let inner = (&outer).inner; +} diff --git a/src/tools/clippy/tests/ui/unnecessary_ref.stderr b/src/tools/clippy/tests/ui/unnecessary_ref.stderr new file mode 100644 index 00000000000..34ba167a947 --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_ref.stderr @@ -0,0 +1,14 @@ +error: Creating a reference that is immediately dereferenced. + --> $DIR/unnecessary_ref.rs:13:17 + | +LL | let inner = (&outer).inner; + | ^^^^^^^^ help: try this: `outer` + | +note: the lint level is defined here + --> $DIR/unnecessary_ref.rs:10:8 + | +LL | #[deny(clippy::ref_in_deref)] + | ^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/unneeded_field_pattern.rs b/src/tools/clippy/tests/ui/unneeded_field_pattern.rs new file mode 100644 index 00000000000..fa639aa70d6 --- /dev/null +++ b/src/tools/clippy/tests/ui/unneeded_field_pattern.rs @@ -0,0 +1,22 @@ +#![warn(clippy::unneeded_field_pattern)] +#[allow(dead_code, unused)] + +struct Foo { + a: i32, + b: i32, + c: i32, +} + +fn main() { + let f = Foo { a: 0, b: 0, c: 0 }; + + match f { + Foo { a: _, b: 0, .. } => {}, + + Foo { a: _, b: _, c: _ } => {}, + } + match f { + Foo { b: 0, .. } => {}, // should be OK + Foo { .. } => {}, // and the Force might be with this one + } +} diff --git a/src/tools/clippy/tests/ui/unneeded_field_pattern.stderr b/src/tools/clippy/tests/ui/unneeded_field_pattern.stderr new file mode 100644 index 00000000000..e7b92ce1e19 --- /dev/null +++ b/src/tools/clippy/tests/ui/unneeded_field_pattern.stderr @@ -0,0 +1,19 @@ +error: You matched a field with a wildcard pattern. Consider using `..` instead + --> $DIR/unneeded_field_pattern.rs:14:15 + | +LL | Foo { a: _, b: 0, .. } => {}, + | ^^^^ + | + = note: `-D clippy::unneeded-field-pattern` implied by `-D warnings` + = help: Try with `Foo { b: 0, .. }` + +error: All the struct fields are matched to a wildcard pattern, consider using `..`. + --> $DIR/unneeded_field_pattern.rs:16:9 + | +LL | Foo { a: _, b: _, c: _ } => {}, + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: Try with `Foo { .. }` instead + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/unneeded_wildcard_pattern.fixed b/src/tools/clippy/tests/ui/unneeded_wildcard_pattern.fixed new file mode 100644 index 00000000000..12c3461c955 --- /dev/null +++ b/src/tools/clippy/tests/ui/unneeded_wildcard_pattern.fixed @@ -0,0 +1,45 @@ +// run-rustfix +#![feature(stmt_expr_attributes)] +#![deny(clippy::unneeded_wildcard_pattern)] + +fn main() { + let t = (0, 1, 2, 3); + + if let (0, ..) = t {}; + if let (0, ..) = t {}; + if let (.., 0) = t {}; + if let (.., 0) = t {}; + if let (0, ..) = t {}; + if let (0, ..) = t {}; + if let (_, 0, ..) = t {}; + if let (.., 0, _) = t {}; + if let (0, _, _, _) = t {}; + if let (0, ..) = t {}; + if let (.., 0) = t {}; + + #[rustfmt::skip] + { + if let (0, ..,) = t {}; + } + + struct S(usize, usize, usize, usize); + + let s = S(0, 1, 2, 3); + + if let S(0, ..) = s {}; + if let S(0, ..) = s {}; + if let S(.., 0) = s {}; + if let S(.., 0) = s {}; + if let S(0, ..) = s {}; + if let S(0, ..) = s {}; + if let S(_, 0, ..) = s {}; + if let S(.., 0, _) = s {}; + if let S(0, _, _, _) = s {}; + if let S(0, ..) = s {}; + if let S(.., 0) = s {}; + + #[rustfmt::skip] + { + if let S(0, ..,) = s {}; + } +} diff --git a/src/tools/clippy/tests/ui/unneeded_wildcard_pattern.rs b/src/tools/clippy/tests/ui/unneeded_wildcard_pattern.rs new file mode 100644 index 00000000000..4ac01d5d23b --- /dev/null +++ b/src/tools/clippy/tests/ui/unneeded_wildcard_pattern.rs @@ -0,0 +1,45 @@ +// run-rustfix +#![feature(stmt_expr_attributes)] +#![deny(clippy::unneeded_wildcard_pattern)] + +fn main() { + let t = (0, 1, 2, 3); + + if let (0, .., _) = t {}; + if let (0, _, ..) = t {}; + if let (_, .., 0) = t {}; + if let (.., _, 0) = t {}; + if let (0, _, _, ..) = t {}; + if let (0, .., _, _) = t {}; + if let (_, 0, ..) = t {}; + if let (.., 0, _) = t {}; + if let (0, _, _, _) = t {}; + if let (0, ..) = t {}; + if let (.., 0) = t {}; + + #[rustfmt::skip] + { + if let (0, .., _, _,) = t {}; + } + + struct S(usize, usize, usize, usize); + + let s = S(0, 1, 2, 3); + + if let S(0, .., _) = s {}; + if let S(0, _, ..) = s {}; + if let S(_, .., 0) = s {}; + if let S(.., _, 0) = s {}; + if let S(0, _, _, ..) = s {}; + if let S(0, .., _, _) = s {}; + if let S(_, 0, ..) = s {}; + if let S(.., 0, _) = s {}; + if let S(0, _, _, _) = s {}; + if let S(0, ..) = s {}; + if let S(.., 0) = s {}; + + #[rustfmt::skip] + { + if let S(0, .., _, _,) = s {}; + } +} diff --git a/src/tools/clippy/tests/ui/unneeded_wildcard_pattern.stderr b/src/tools/clippy/tests/ui/unneeded_wildcard_pattern.stderr new file mode 100644 index 00000000000..716d9ecff89 --- /dev/null +++ b/src/tools/clippy/tests/ui/unneeded_wildcard_pattern.stderr @@ -0,0 +1,92 @@ +error: this pattern is unneeded as the `..` pattern can match that element + --> $DIR/unneeded_wildcard_pattern.rs:8:18 + | +LL | if let (0, .., _) = t {}; + | ^^^ help: remove it + | +note: the lint level is defined here + --> $DIR/unneeded_wildcard_pattern.rs:3:9 + | +LL | #![deny(clippy::unneeded_wildcard_pattern)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this pattern is unneeded as the `..` pattern can match that element + --> $DIR/unneeded_wildcard_pattern.rs:9:16 + | +LL | if let (0, _, ..) = t {}; + | ^^^ help: remove it + +error: this pattern is unneeded as the `..` pattern can match that element + --> $DIR/unneeded_wildcard_pattern.rs:10:13 + | +LL | if let (_, .., 0) = t {}; + | ^^^ help: remove it + +error: this pattern is unneeded as the `..` pattern can match that element + --> $DIR/unneeded_wildcard_pattern.rs:11:15 + | +LL | if let (.., _, 0) = t {}; + | ^^^ help: remove it + +error: these patterns are unneeded as the `..` pattern can match those elements + --> $DIR/unneeded_wildcard_pattern.rs:12:16 + | +LL | if let (0, _, _, ..) = t {}; + | ^^^^^^ help: remove them + +error: these patterns are unneeded as the `..` pattern can match those elements + --> $DIR/unneeded_wildcard_pattern.rs:13:18 + | +LL | if let (0, .., _, _) = t {}; + | ^^^^^^ help: remove them + +error: these patterns are unneeded as the `..` pattern can match those elements + --> $DIR/unneeded_wildcard_pattern.rs:22:22 + | +LL | if let (0, .., _, _,) = t {}; + | ^^^^^^ help: remove them + +error: this pattern is unneeded as the `..` pattern can match that element + --> $DIR/unneeded_wildcard_pattern.rs:29:19 + | +LL | if let S(0, .., _) = s {}; + | ^^^ help: remove it + +error: this pattern is unneeded as the `..` pattern can match that element + --> $DIR/unneeded_wildcard_pattern.rs:30:17 + | +LL | if let S(0, _, ..) = s {}; + | ^^^ help: remove it + +error: this pattern is unneeded as the `..` pattern can match that element + --> $DIR/unneeded_wildcard_pattern.rs:31:14 + | +LL | if let S(_, .., 0) = s {}; + | ^^^ help: remove it + +error: this pattern is unneeded as the `..` pattern can match that element + --> $DIR/unneeded_wildcard_pattern.rs:32:16 + | +LL | if let S(.., _, 0) = s {}; + | ^^^ help: remove it + +error: these patterns are unneeded as the `..` pattern can match those elements + --> $DIR/unneeded_wildcard_pattern.rs:33:17 + | +LL | if let S(0, _, _, ..) = s {}; + | ^^^^^^ help: remove them + +error: these patterns are unneeded as the `..` pattern can match those elements + --> $DIR/unneeded_wildcard_pattern.rs:34:19 + | +LL | if let S(0, .., _, _) = s {}; + | ^^^^^^ help: remove them + +error: these patterns are unneeded as the `..` pattern can match those elements + --> $DIR/unneeded_wildcard_pattern.rs:43:23 + | +LL | if let S(0, .., _, _,) = s {}; + | ^^^^^^ help: remove them + +error: aborting due to 14 previous errors + diff --git a/src/tools/clippy/tests/ui/unreadable_literal.fixed b/src/tools/clippy/tests/ui/unreadable_literal.fixed new file mode 100644 index 00000000000..3f358d9ecaa --- /dev/null +++ b/src/tools/clippy/tests/ui/unreadable_literal.fixed @@ -0,0 +1,35 @@ +// run-rustfix + +#![warn(clippy::unreadable_literal)] + +struct Foo(u64); + +macro_rules! foo { + () => { + Foo(123123123123) + }; +} + +fn main() { + let _good = ( + 0b1011_i64, + 0o1_234_u32, + 0x1_234_567, + 65536, + 1_2345_6789, + 1234_f32, + 1_234.12_f32, + 1_234.123_f32, + 1.123_4_f32, + ); + let _bad = (0b11_0110_i64, 0xcafe_babe_usize, 123_456_f32, 1.234_567_f32); + let _good_sci = 1.1234e1; + let _bad_sci = 1.123_456e1; + + let _fail9 = 0x00ab_cdef; + let _fail10: u32 = 0xBAFE_BAFE; + let _fail11 = 0x0abc_deff; + let _fail12: i128 = 0x00ab_cabc_abca_bcab_cabc; + + let _ = foo!(); +} diff --git a/src/tools/clippy/tests/ui/unreadable_literal.rs b/src/tools/clippy/tests/ui/unreadable_literal.rs new file mode 100644 index 00000000000..e658a5f28c9 --- /dev/null +++ b/src/tools/clippy/tests/ui/unreadable_literal.rs @@ -0,0 +1,35 @@ +// run-rustfix + +#![warn(clippy::unreadable_literal)] + +struct Foo(u64); + +macro_rules! foo { + () => { + Foo(123123123123) + }; +} + +fn main() { + let _good = ( + 0b1011_i64, + 0o1_234_u32, + 0x1_234_567, + 65536, + 1_2345_6789, + 1234_f32, + 1_234.12_f32, + 1_234.123_f32, + 1.123_4_f32, + ); + let _bad = (0b110110_i64, 0xcafebabe_usize, 123456_f32, 1.234567_f32); + let _good_sci = 1.1234e1; + let _bad_sci = 1.123456e1; + + let _fail9 = 0xabcdef; + let _fail10: u32 = 0xBAFEBAFE; + let _fail11 = 0xabcdeff; + let _fail12: i128 = 0xabcabcabcabcabcabc; + + let _ = foo!(); +} diff --git a/src/tools/clippy/tests/ui/unreadable_literal.stderr b/src/tools/clippy/tests/ui/unreadable_literal.stderr new file mode 100644 index 00000000000..1b2ff6bff04 --- /dev/null +++ b/src/tools/clippy/tests/ui/unreadable_literal.stderr @@ -0,0 +1,58 @@ +error: long literal lacking separators + --> $DIR/unreadable_literal.rs:25:17 + | +LL | let _bad = (0b110110_i64, 0xcafebabe_usize, 123456_f32, 1.234567_f32); + | ^^^^^^^^^^^^ help: consider: `0b11_0110_i64` + | + = note: `-D clippy::unreadable-literal` implied by `-D warnings` + +error: long literal lacking separators + --> $DIR/unreadable_literal.rs:25:31 + | +LL | let _bad = (0b110110_i64, 0xcafebabe_usize, 123456_f32, 1.234567_f32); + | ^^^^^^^^^^^^^^^^ help: consider: `0xcafe_babe_usize` + +error: long literal lacking separators + --> $DIR/unreadable_literal.rs:25:49 + | +LL | let _bad = (0b110110_i64, 0xcafebabe_usize, 123456_f32, 1.234567_f32); + | ^^^^^^^^^^ help: consider: `123_456_f32` + +error: long literal lacking separators + --> $DIR/unreadable_literal.rs:25:61 + | +LL | let _bad = (0b110110_i64, 0xcafebabe_usize, 123456_f32, 1.234567_f32); + | ^^^^^^^^^^^^ help: consider: `1.234_567_f32` + +error: long literal lacking separators + --> $DIR/unreadable_literal.rs:27:20 + | +LL | let _bad_sci = 1.123456e1; + | ^^^^^^^^^^ help: consider: `1.123_456e1` + +error: long literal lacking separators + --> $DIR/unreadable_literal.rs:29:18 + | +LL | let _fail9 = 0xabcdef; + | ^^^^^^^^ help: consider: `0x00ab_cdef` + +error: long literal lacking separators + --> $DIR/unreadable_literal.rs:30:24 + | +LL | let _fail10: u32 = 0xBAFEBAFE; + | ^^^^^^^^^^ help: consider: `0xBAFE_BAFE` + +error: long literal lacking separators + --> $DIR/unreadable_literal.rs:31:19 + | +LL | let _fail11 = 0xabcdeff; + | ^^^^^^^^^ help: consider: `0x0abc_deff` + +error: long literal lacking separators + --> $DIR/unreadable_literal.rs:32:25 + | +LL | let _fail12: i128 = 0xabcabcabcabcabcabc; + | ^^^^^^^^^^^^^^^^^^^^ help: consider: `0x00ab_cabc_abca_bcab_cabc` + +error: aborting due to 9 previous errors + diff --git a/src/tools/clippy/tests/ui/unsafe_derive_deserialize.rs b/src/tools/clippy/tests/ui/unsafe_derive_deserialize.rs new file mode 100644 index 00000000000..7bee9c499e1 --- /dev/null +++ b/src/tools/clippy/tests/ui/unsafe_derive_deserialize.rs @@ -0,0 +1,60 @@ +#![warn(clippy::unsafe_derive_deserialize)] +#![allow(unused, clippy::missing_safety_doc)] + +extern crate serde; + +use serde::Deserialize; + +#[derive(Deserialize)] +pub struct A {} +impl A { + pub unsafe fn new(_a: i32, _b: i32) -> Self { + Self {} + } +} + +#[derive(Deserialize)] +pub struct B {} +impl B { + pub unsafe fn unsafe_method(&self) {} +} + +#[derive(Deserialize)] +pub struct C {} +impl C { + pub fn unsafe_block(&self) { + unsafe {} + } +} + +#[derive(Deserialize)] +pub struct D {} +impl D { + pub fn inner_unsafe_fn(&self) { + unsafe fn inner() {} + } +} + +// Does not derive `Deserialize`, should be ignored +pub struct E {} +impl E { + pub unsafe fn new(_a: i32, _b: i32) -> Self { + Self {} + } + + pub unsafe fn unsafe_method(&self) {} + + pub fn unsafe_block(&self) { + unsafe {} + } + + pub fn inner_unsafe_fn(&self) { + unsafe fn inner() {} + } +} + +// Does not have methods using `unsafe`, should be ignored +#[derive(Deserialize)] +pub struct F {} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/unsafe_derive_deserialize.stderr b/src/tools/clippy/tests/ui/unsafe_derive_deserialize.stderr new file mode 100644 index 00000000000..1978bd95a67 --- /dev/null +++ b/src/tools/clippy/tests/ui/unsafe_derive_deserialize.stderr @@ -0,0 +1,39 @@ +error: you are deriving `serde::Deserialize` on a type that has methods using `unsafe` + --> $DIR/unsafe_derive_deserialize.rs:8:10 + | +LL | #[derive(Deserialize)] + | ^^^^^^^^^^^ + | + = note: `-D clippy::unsafe-derive-deserialize` implied by `-D warnings` + = help: consider implementing `serde::Deserialize` manually. See https://serde.rs/impl-deserialize.html + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: you are deriving `serde::Deserialize` on a type that has methods using `unsafe` + --> $DIR/unsafe_derive_deserialize.rs:16:10 + | +LL | #[derive(Deserialize)] + | ^^^^^^^^^^^ + | + = help: consider implementing `serde::Deserialize` manually. See https://serde.rs/impl-deserialize.html + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: you are deriving `serde::Deserialize` on a type that has methods using `unsafe` + --> $DIR/unsafe_derive_deserialize.rs:22:10 + | +LL | #[derive(Deserialize)] + | ^^^^^^^^^^^ + | + = help: consider implementing `serde::Deserialize` manually. See https://serde.rs/impl-deserialize.html + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: you are deriving `serde::Deserialize` on a type that has methods using `unsafe` + --> $DIR/unsafe_derive_deserialize.rs:30:10 + | +LL | #[derive(Deserialize)] + | ^^^^^^^^^^^ + | + = help: consider implementing `serde::Deserialize` manually. See https://serde.rs/impl-deserialize.html + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/unsafe_removed_from_name.rs b/src/tools/clippy/tests/ui/unsafe_removed_from_name.rs new file mode 100644 index 00000000000..a1f616733bd --- /dev/null +++ b/src/tools/clippy/tests/ui/unsafe_removed_from_name.rs @@ -0,0 +1,27 @@ +#![allow(unused_imports)] +#![allow(dead_code)] +#![warn(clippy::unsafe_removed_from_name)] + +use std::cell::UnsafeCell as TotallySafeCell; + +use std::cell::UnsafeCell as TotallySafeCellAgain; + +// Shouldn't error +use std::cell::RefCell as ProbablyNotUnsafe; +use std::cell::RefCell as RefCellThatCantBeUnsafe; +use std::cell::UnsafeCell as SuperDangerousUnsafeCell; +use std::cell::UnsafeCell as Dangerunsafe; +use std::cell::UnsafeCell as Bombsawayunsafe; + +mod mod_with_some_unsafe_things { + pub struct Safe {} + pub struct Unsafe {} +} + +use mod_with_some_unsafe_things::Unsafe as LieAboutModSafety; + +// Shouldn't error +use mod_with_some_unsafe_things::Safe as IPromiseItsSafeThisTime; +use mod_with_some_unsafe_things::Unsafe as SuperUnsafeModThing; + +fn main() {} diff --git a/src/tools/clippy/tests/ui/unsafe_removed_from_name.stderr b/src/tools/clippy/tests/ui/unsafe_removed_from_name.stderr new file mode 100644 index 00000000000..4f871cbe41b --- /dev/null +++ b/src/tools/clippy/tests/ui/unsafe_removed_from_name.stderr @@ -0,0 +1,22 @@ +error: removed `unsafe` from the name of `UnsafeCell` in use as `TotallySafeCell` + --> $DIR/unsafe_removed_from_name.rs:5:1 + | +LL | use std::cell::UnsafeCell as TotallySafeCell; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::unsafe-removed-from-name` implied by `-D warnings` + +error: removed `unsafe` from the name of `UnsafeCell` in use as `TotallySafeCellAgain` + --> $DIR/unsafe_removed_from_name.rs:7:1 + | +LL | use std::cell::UnsafeCell as TotallySafeCellAgain; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: removed `unsafe` from the name of `Unsafe` in use as `LieAboutModSafety` + --> $DIR/unsafe_removed_from_name.rs:21:1 + | +LL | use mod_with_some_unsafe_things::Unsafe as LieAboutModSafety; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/unseparated_prefix_literals.fixed b/src/tools/clippy/tests/ui/unseparated_prefix_literals.fixed new file mode 100644 index 00000000000..3c422cc4fee --- /dev/null +++ b/src/tools/clippy/tests/ui/unseparated_prefix_literals.fixed @@ -0,0 +1,41 @@ +// run-rustfix + +#![warn(clippy::unseparated_literal_suffix)] +#![allow(dead_code)] + +#[macro_use] +extern crate clippy_mini_macro_test; + +// Test for proc-macro attribute +#[derive(ClippyMiniMacroTest)] +struct Foo; + +macro_rules! lit_from_macro { + () => { + 42_usize + }; +} + +fn main() { + let _ok1 = 1234_i32; + let _ok2 = 1234_isize; + let _ok3 = 0x123_isize; + let _fail1 = 1234_i32; + let _fail2 = 1234_u32; + let _fail3 = 1234_isize; + let _fail4 = 1234_usize; + let _fail5 = 0x123_isize; + + let _okf1 = 1.5_f32; + let _okf2 = 1_f32; + let _failf1 = 1.5_f32; + let _failf2 = 1_f32; + + // Test for macro + let _ = lit_from_macro!(); + + // Counter example + let _ = line!(); + // Because `assert!` contains `line!()` macro. + assert_eq!(4897_u32, 32223); +} diff --git a/src/tools/clippy/tests/ui/unseparated_prefix_literals.rs b/src/tools/clippy/tests/ui/unseparated_prefix_literals.rs new file mode 100644 index 00000000000..09608661e0e --- /dev/null +++ b/src/tools/clippy/tests/ui/unseparated_prefix_literals.rs @@ -0,0 +1,41 @@ +// run-rustfix + +#![warn(clippy::unseparated_literal_suffix)] +#![allow(dead_code)] + +#[macro_use] +extern crate clippy_mini_macro_test; + +// Test for proc-macro attribute +#[derive(ClippyMiniMacroTest)] +struct Foo; + +macro_rules! lit_from_macro { + () => { + 42usize + }; +} + +fn main() { + let _ok1 = 1234_i32; + let _ok2 = 1234_isize; + let _ok3 = 0x123_isize; + let _fail1 = 1234i32; + let _fail2 = 1234u32; + let _fail3 = 1234isize; + let _fail4 = 1234usize; + let _fail5 = 0x123isize; + + let _okf1 = 1.5_f32; + let _okf2 = 1_f32; + let _failf1 = 1.5f32; + let _failf2 = 1f32; + + // Test for macro + let _ = lit_from_macro!(); + + // Counter example + let _ = line!(); + // Because `assert!` contains `line!()` macro. + assert_eq!(4897u32, 32223); +} diff --git a/src/tools/clippy/tests/ui/unseparated_prefix_literals.stderr b/src/tools/clippy/tests/ui/unseparated_prefix_literals.stderr new file mode 100644 index 00000000000..d7dd526bcb9 --- /dev/null +++ b/src/tools/clippy/tests/ui/unseparated_prefix_literals.stderr @@ -0,0 +1,63 @@ +error: integer type suffix should be separated by an underscore + --> $DIR/unseparated_prefix_literals.rs:23:18 + | +LL | let _fail1 = 1234i32; + | ^^^^^^^ help: add an underscore: `1234_i32` + | + = note: `-D clippy::unseparated-literal-suffix` implied by `-D warnings` + +error: integer type suffix should be separated by an underscore + --> $DIR/unseparated_prefix_literals.rs:24:18 + | +LL | let _fail2 = 1234u32; + | ^^^^^^^ help: add an underscore: `1234_u32` + +error: integer type suffix should be separated by an underscore + --> $DIR/unseparated_prefix_literals.rs:25:18 + | +LL | let _fail3 = 1234isize; + | ^^^^^^^^^ help: add an underscore: `1234_isize` + +error: integer type suffix should be separated by an underscore + --> $DIR/unseparated_prefix_literals.rs:26:18 + | +LL | let _fail4 = 1234usize; + | ^^^^^^^^^ help: add an underscore: `1234_usize` + +error: integer type suffix should be separated by an underscore + --> $DIR/unseparated_prefix_literals.rs:27:18 + | +LL | let _fail5 = 0x123isize; + | ^^^^^^^^^^ help: add an underscore: `0x123_isize` + +error: float type suffix should be separated by an underscore + --> $DIR/unseparated_prefix_literals.rs:31:19 + | +LL | let _failf1 = 1.5f32; + | ^^^^^^ help: add an underscore: `1.5_f32` + +error: float type suffix should be separated by an underscore + --> $DIR/unseparated_prefix_literals.rs:32:19 + | +LL | let _failf2 = 1f32; + | ^^^^ help: add an underscore: `1_f32` + +error: integer type suffix should be separated by an underscore + --> $DIR/unseparated_prefix_literals.rs:15:9 + | +LL | 42usize + | ^^^^^^^ help: add an underscore: `42_usize` +... +LL | let _ = lit_from_macro!(); + | ----------------- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: integer type suffix should be separated by an underscore + --> $DIR/unseparated_prefix_literals.rs:40:16 + | +LL | assert_eq!(4897u32, 32223); + | ^^^^^^^ help: add an underscore: `4897_u32` + +error: aborting due to 9 previous errors + diff --git a/src/tools/clippy/tests/ui/unused_io_amount.rs b/src/tools/clippy/tests/ui/unused_io_amount.rs new file mode 100644 index 00000000000..ebaba9629db --- /dev/null +++ b/src/tools/clippy/tests/ui/unused_io_amount.rs @@ -0,0 +1,25 @@ +#![allow(dead_code)] +#![warn(clippy::unused_io_amount)] + +use std::io; + +fn question_mark<T: io::Read + io::Write>(s: &mut T) -> io::Result<()> { + s.write(b"test")?; + let mut buf = [0u8; 4]; + s.read(&mut buf)?; + Ok(()) +} + +fn unwrap<T: io::Read + io::Write>(s: &mut T) { + s.write(b"test").unwrap(); + let mut buf = [0u8; 4]; + s.read(&mut buf).unwrap(); +} + +fn vectored<T: io::Read + io::Write>(s: &mut T) -> io::Result<()> { + s.read_vectored(&mut [io::IoSliceMut::new(&mut [])])?; + s.write_vectored(&[io::IoSlice::new(&[])])?; + Ok(()) +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/unused_io_amount.stderr b/src/tools/clippy/tests/ui/unused_io_amount.stderr new file mode 100644 index 00000000000..5219d63980b --- /dev/null +++ b/src/tools/clippy/tests/ui/unused_io_amount.stderr @@ -0,0 +1,40 @@ +error: written amount is not handled. Use `Write::write_all` instead + --> $DIR/unused_io_amount.rs:7:5 + | +LL | s.write(b"test")?; + | ^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::unused-io-amount` implied by `-D warnings` + +error: read amount is not handled. Use `Read::read_exact` instead + --> $DIR/unused_io_amount.rs:9:5 + | +LL | s.read(&mut buf)?; + | ^^^^^^^^^^^^^^^^^ + +error: written amount is not handled. Use `Write::write_all` instead + --> $DIR/unused_io_amount.rs:14:5 + | +LL | s.write(b"test").unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: read amount is not handled. Use `Read::read_exact` instead + --> $DIR/unused_io_amount.rs:16:5 + | +LL | s.read(&mut buf).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: read amount is not handled + --> $DIR/unused_io_amount.rs:20:5 + | +LL | s.read_vectored(&mut [io::IoSliceMut::new(&mut [])])?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: written amount is not handled + --> $DIR/unused_io_amount.rs:21:5 + | +LL | s.write_vectored(&[io::IoSlice::new(&[])])?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/unused_self.rs b/src/tools/clippy/tests/ui/unused_self.rs new file mode 100644 index 00000000000..7a4bbdda1ab --- /dev/null +++ b/src/tools/clippy/tests/ui/unused_self.rs @@ -0,0 +1,140 @@ +#![warn(clippy::unused_self)] +#![allow(clippy::boxed_local, clippy::fn_params_excessive_bools)] + +mod unused_self { + use std::pin::Pin; + use std::sync::{Arc, Mutex}; + + struct A {} + + impl A { + fn unused_self_move(self) {} + fn unused_self_ref(&self) {} + fn unused_self_mut_ref(&mut self) {} + fn unused_self_pin_ref(self: Pin<&Self>) {} + fn unused_self_pin_mut_ref(self: Pin<&mut Self>) {} + fn unused_self_pin_nested(self: Pin<Arc<Self>>) {} + fn unused_self_box(self: Box<Self>) {} + fn unused_with_other_used_args(&self, x: u8, y: u8) -> u8 { + x + y + } + fn unused_self_class_method(&self) { + Self::static_method(); + } + + fn static_method() {} + } +} + +mod unused_self_allow { + struct A {} + + impl A { + // shouldn't trigger + #[allow(clippy::unused_self)] + fn unused_self_move(self) {} + } + + struct B {} + + // shouldn't trigger + #[allow(clippy::unused_self)] + impl B { + fn unused_self_move(self) {} + } + + struct C {} + + #[allow(clippy::unused_self)] + impl C { + #[warn(clippy::unused_self)] + fn some_fn((): ()) {} + + // shouldn't trigger + fn unused_self_move(self) {} + } +} + +mod used_self { + use std::pin::Pin; + + struct A { + x: u8, + } + + impl A { + fn used_self_move(self) -> u8 { + self.x + } + fn used_self_ref(&self) -> u8 { + self.x + } + fn used_self_mut_ref(&mut self) { + self.x += 1 + } + fn used_self_pin_ref(self: Pin<&Self>) -> u8 { + self.x + } + fn used_self_box(self: Box<Self>) -> u8 { + self.x + } + fn used_self_with_other_unused_args(&self, x: u8, y: u8) -> u8 { + self.x + } + fn used_in_nested_closure(&self) -> u8 { + let mut a = || -> u8 { self.x }; + a() + } + + #[allow(clippy::collapsible_if)] + fn used_self_method_nested_conditions(&self, a: bool, b: bool, c: bool, d: bool) { + if a { + if b { + if c { + if d { + self.used_self_ref(); + } + } + } + } + } + + fn foo(&self) -> u32 { + let mut sum = 0u32; + for i in 0..self.x { + sum += i as u32; + } + sum + } + + fn bar(&mut self, x: u8) -> u32 { + let mut y = 0u32; + for i in 0..x { + y += self.foo() + } + y + } + } +} + +mod not_applicable { + use std::fmt; + + struct A {} + + impl fmt::Debug for A { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "A") + } + } + + impl A { + fn method(x: u8, y: u8) {} + } + + trait B { + fn method(&self) {} + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/unused_self.stderr b/src/tools/clippy/tests/ui/unused_self.stderr new file mode 100644 index 00000000000..0534b40eabb --- /dev/null +++ b/src/tools/clippy/tests/ui/unused_self.stderr @@ -0,0 +1,75 @@ +error: unused `self` argument + --> $DIR/unused_self.rs:11:29 + | +LL | fn unused_self_move(self) {} + | ^^^^ + | + = note: `-D clippy::unused-self` implied by `-D warnings` + = help: consider refactoring to a associated function + +error: unused `self` argument + --> $DIR/unused_self.rs:12:28 + | +LL | fn unused_self_ref(&self) {} + | ^^^^^ + | + = help: consider refactoring to a associated function + +error: unused `self` argument + --> $DIR/unused_self.rs:13:32 + | +LL | fn unused_self_mut_ref(&mut self) {} + | ^^^^^^^^^ + | + = help: consider refactoring to a associated function + +error: unused `self` argument + --> $DIR/unused_self.rs:14:32 + | +LL | fn unused_self_pin_ref(self: Pin<&Self>) {} + | ^^^^ + | + = help: consider refactoring to a associated function + +error: unused `self` argument + --> $DIR/unused_self.rs:15:36 + | +LL | fn unused_self_pin_mut_ref(self: Pin<&mut Self>) {} + | ^^^^ + | + = help: consider refactoring to a associated function + +error: unused `self` argument + --> $DIR/unused_self.rs:16:35 + | +LL | fn unused_self_pin_nested(self: Pin<Arc<Self>>) {} + | ^^^^ + | + = help: consider refactoring to a associated function + +error: unused `self` argument + --> $DIR/unused_self.rs:17:28 + | +LL | fn unused_self_box(self: Box<Self>) {} + | ^^^^ + | + = help: consider refactoring to a associated function + +error: unused `self` argument + --> $DIR/unused_self.rs:18:40 + | +LL | fn unused_with_other_used_args(&self, x: u8, y: u8) -> u8 { + | ^^^^^ + | + = help: consider refactoring to a associated function + +error: unused `self` argument + --> $DIR/unused_self.rs:21:37 + | +LL | fn unused_self_class_method(&self) { + | ^^^^^ + | + = help: consider refactoring to a associated function + +error: aborting due to 9 previous errors + diff --git a/src/tools/clippy/tests/ui/unused_unit.fixed b/src/tools/clippy/tests/ui/unused_unit.fixed new file mode 100644 index 00000000000..3f63624720f --- /dev/null +++ b/src/tools/clippy/tests/ui/unused_unit.fixed @@ -0,0 +1,59 @@ +// run-rustfix + +// The output for humans should just highlight the whole span without showing +// the suggested replacement, but we also want to test that suggested +// replacement only removes one set of parentheses, rather than naïvely +// stripping away any starting or ending parenthesis characters—hence this +// test of the JSON error format. + +#![feature(custom_inner_attributes)] +#![rustfmt::skip] + +#![deny(clippy::unused_unit)] +#![allow(dead_code)] + +struct Unitter; +impl Unitter { + // try to disorient the lint with multiple unit returns and newlines + #[allow(clippy::no_effect)] + pub fn get_unit<F: Fn() -> (), G>(&self, f: F, _g: G) + where G: Fn() -> () { + let _y: &dyn Fn() -> () = &f; + (); // this should not lint, as it's not in return type position + } +} + +impl Into<()> for Unitter { + #[rustfmt::skip] + fn into(self) { + + } +} + +fn return_unit() { } + +#[allow(clippy::needless_return)] +#[allow(clippy::never_loop)] +#[allow(clippy::unit_cmp)] +fn main() { + let u = Unitter; + assert_eq!(u.get_unit(|| {}, return_unit), u.into()); + return_unit(); + loop { + break; + } + return; +} + +// https://github.com/rust-lang/rust-clippy/issues/4076 +fn foo() { + macro_rules! foo { + (recv($r:expr) -> $res:pat => $body:expr) => { + $body + } + } + + foo! { + recv(rx) -> _x => () + } +} diff --git a/src/tools/clippy/tests/ui/unused_unit.rs b/src/tools/clippy/tests/ui/unused_unit.rs new file mode 100644 index 00000000000..8fc072ebd69 --- /dev/null +++ b/src/tools/clippy/tests/ui/unused_unit.rs @@ -0,0 +1,60 @@ +// run-rustfix + +// The output for humans should just highlight the whole span without showing +// the suggested replacement, but we also want to test that suggested +// replacement only removes one set of parentheses, rather than naïvely +// stripping away any starting or ending parenthesis characters—hence this +// test of the JSON error format. + +#![feature(custom_inner_attributes)] +#![rustfmt::skip] + +#![deny(clippy::unused_unit)] +#![allow(dead_code)] + +struct Unitter; +impl Unitter { + // try to disorient the lint with multiple unit returns and newlines + #[allow(clippy::no_effect)] + pub fn get_unit<F: Fn() -> (), G>(&self, f: F, _g: G) -> + () + where G: Fn() -> () { + let _y: &dyn Fn() -> () = &f; + (); // this should not lint, as it's not in return type position + } +} + +impl Into<()> for Unitter { + #[rustfmt::skip] + fn into(self) -> () { + () + } +} + +fn return_unit() -> () { () } + +#[allow(clippy::needless_return)] +#[allow(clippy::never_loop)] +#[allow(clippy::unit_cmp)] +fn main() { + let u = Unitter; + assert_eq!(u.get_unit(|| {}, return_unit), u.into()); + return_unit(); + loop { + break(); + } + return(); +} + +// https://github.com/rust-lang/rust-clippy/issues/4076 +fn foo() { + macro_rules! foo { + (recv($r:expr) -> $res:pat => $body:expr) => { + $body + } + } + + foo! { + recv(rx) -> _x => () + } +} diff --git a/src/tools/clippy/tests/ui/unused_unit.stderr b/src/tools/clippy/tests/ui/unused_unit.stderr new file mode 100644 index 00000000000..a013d2b3495 --- /dev/null +++ b/src/tools/clippy/tests/ui/unused_unit.stderr @@ -0,0 +1,52 @@ +error: unneeded unit return type + --> $DIR/unused_unit.rs:19:59 + | +LL | pub fn get_unit<F: Fn() -> (), G>(&self, f: F, _g: G) -> + | ___________________________________________________________^ +LL | | () + | |__________^ help: remove the `-> ()` + | +note: the lint level is defined here + --> $DIR/unused_unit.rs:12:9 + | +LL | #![deny(clippy::unused_unit)] + | ^^^^^^^^^^^^^^^^^^^ + +error: unneeded unit return type + --> $DIR/unused_unit.rs:29:19 + | +LL | fn into(self) -> () { + | ^^^^^ help: remove the `-> ()` + +error: unneeded unit expression + --> $DIR/unused_unit.rs:30:9 + | +LL | () + | ^^ help: remove the final `()` + +error: unneeded unit return type + --> $DIR/unused_unit.rs:34:18 + | +LL | fn return_unit() -> () { () } + | ^^^^^ help: remove the `-> ()` + +error: unneeded unit expression + --> $DIR/unused_unit.rs:34:26 + | +LL | fn return_unit() -> () { () } + | ^^ help: remove the final `()` + +error: unneeded `()` + --> $DIR/unused_unit.rs:44:14 + | +LL | break(); + | ^^ help: remove the `()` + +error: unneeded `()` + --> $DIR/unused_unit.rs:46:11 + | +LL | return(); + | ^^ help: remove the `()` + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/tests/ui/unwrap.rs b/src/tools/clippy/tests/ui/unwrap.rs new file mode 100644 index 00000000000..fcd1fcd14d4 --- /dev/null +++ b/src/tools/clippy/tests/ui/unwrap.rs @@ -0,0 +1,16 @@ +#![warn(clippy::option_unwrap_used, clippy::result_unwrap_used)] + +fn unwrap_option() { + let opt = Some(0); + let _ = opt.unwrap(); +} + +fn unwrap_result() { + let res: Result<u8, ()> = Ok(0); + let _ = res.unwrap(); +} + +fn main() { + unwrap_option(); + unwrap_result(); +} diff --git a/src/tools/clippy/tests/ui/unwrap.stderr b/src/tools/clippy/tests/ui/unwrap.stderr new file mode 100644 index 00000000000..b90ce68fa97 --- /dev/null +++ b/src/tools/clippy/tests/ui/unwrap.stderr @@ -0,0 +1,20 @@ +error: used `unwrap()` on `an Option` value + --> $DIR/unwrap.rs:5:13 + | +LL | let _ = opt.unwrap(); + | ^^^^^^^^^^^^ + | + = note: `-D clippy::option-unwrap-used` implied by `-D warnings` + = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message + +error: used `unwrap()` on `a Result` value + --> $DIR/unwrap.rs:10:13 + | +LL | let _ = res.unwrap(); + | ^^^^^^^^^^^^ + | + = note: `-D clippy::result-unwrap-used` implied by `-D warnings` + = help: if you don't want to handle the `Err` case gracefully, consider using `expect()` to provide a better panic message + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/unwrap_or.rs b/src/tools/clippy/tests/ui/unwrap_or.rs new file mode 100644 index 00000000000..bfb41e43947 --- /dev/null +++ b/src/tools/clippy/tests/ui/unwrap_or.rs @@ -0,0 +1,9 @@ +#![warn(clippy::all)] + +fn main() { + let s = Some(String::from("test string")).unwrap_or("Fail".to_string()).len(); +} + +fn new_lines() { + let s = Some(String::from("test string")).unwrap_or("Fail".to_string()).len(); +} diff --git a/src/tools/clippy/tests/ui/unwrap_or.stderr b/src/tools/clippy/tests/ui/unwrap_or.stderr new file mode 100644 index 00000000000..c3a7464fd47 --- /dev/null +++ b/src/tools/clippy/tests/ui/unwrap_or.stderr @@ -0,0 +1,16 @@ +error: use of `unwrap_or` followed by a function call + --> $DIR/unwrap_or.rs:4:47 + | +LL | let s = Some(String::from("test string")).unwrap_or("Fail".to_string()).len(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| "Fail".to_string())` + | + = note: `-D clippy::or-fun-call` implied by `-D warnings` + +error: use of `unwrap_or` followed by a function call + --> $DIR/unwrap_or.rs:8:47 + | +LL | let s = Some(String::from("test string")).unwrap_or("Fail".to_string()).len(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| "Fail".to_string())` + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/update-all-references.sh b/src/tools/clippy/tests/ui/update-all-references.sh new file mode 100755 index 00000000000..30ba9188db4 --- /dev/null +++ b/src/tools/clippy/tests/ui/update-all-references.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +# A script to update the references for all tests. The idea is that +# you do a run, which will generate files in the build directory +# containing the (normalized) actual output of the compiler. You then +# run this script, which will copy those files over. If you find +# yourself manually editing a foo.stderr file, you're doing it wrong. +# +# See all `update-references.sh`, if you just want to update a single test. + +if [[ "$1" == "--help" || "$1" == "-h" ]]; then + echo "usage: $0" +fi + +CARGO_TARGET_DIR=${CARGO_TARGET_DIR:-$PWD/target} +PROFILE=${PROFILE:-debug} +BUILD_DIR=${CARGO_TARGET_DIR}/${PROFILE}/test_build_base + +MY_DIR=$(dirname "$0") +cd "$MY_DIR" || exit +find . -name '*.rs' -exec ./update-references.sh "$BUILD_DIR" {} + diff --git a/src/tools/clippy/tests/ui/update-references.sh b/src/tools/clippy/tests/ui/update-references.sh new file mode 100755 index 00000000000..2c13c327d79 --- /dev/null +++ b/src/tools/clippy/tests/ui/update-references.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +# A script to update the references for particular tests. The idea is +# that you do a run, which will generate files in the build directory +# containing the (normalized) actual output of the compiler. This +# script will then copy that output and replace the "expected output" +# files. You can then commit the changes. +# +# If you find yourself manually editing a `foo.stderr` file, you're +# doing it wrong. + +if [[ "$1" == "--help" || "$1" == "-h" || "$1" == "" || "$2" == "" ]]; then + echo "usage: $0 <build-directory> <relative-path-to-rs-files>" + echo "" + echo "For example:" + echo " $0 ../../../build/x86_64-apple-darwin/test/ui *.rs */*.rs" +fi + +MYDIR=$(dirname "$0") + +BUILD_DIR="$1" +shift + +while [[ "$1" != "" ]]; do + STDERR_NAME="${1/%.rs/.stderr}" + STDOUT_NAME="${1/%.rs/.stdout}" + FIXED_NAME="${1/%.rs/.fixed}" + shift + if [[ -f "$BUILD_DIR"/"$STDOUT_NAME" ]] && \ + ! (cmp -s -- "$BUILD_DIR"/"$STDOUT_NAME" "$MYDIR"/"$STDOUT_NAME"); then + echo updating "$MYDIR"/"$STDOUT_NAME" + cp "$BUILD_DIR"/"$STDOUT_NAME" "$MYDIR"/"$STDOUT_NAME" + fi + if [[ -f "$BUILD_DIR"/"$STDERR_NAME" ]] && \ + ! (cmp -s -- "$BUILD_DIR"/"$STDERR_NAME" "$MYDIR"/"$STDERR_NAME"); then + echo updating "$MYDIR"/"$STDERR_NAME" + cp "$BUILD_DIR"/"$STDERR_NAME" "$MYDIR"/"$STDERR_NAME" + fi + if [[ -f "$BUILD_DIR"/"$FIXED_NAME" ]] && \ + ! (cmp -s -- "$BUILD_DIR"/"$FIXED_NAME" "$MYDIR"/"$FIXED_NAME"); then + echo updating "$MYDIR"/"$FIXED_NAME" + cp "$BUILD_DIR"/"$FIXED_NAME" "$MYDIR"/"$FIXED_NAME" + fi +done diff --git a/src/tools/clippy/tests/ui/use_self.fixed b/src/tools/clippy/tests/ui/use_self.fixed new file mode 100644 index 00000000000..ebb3aa28daf --- /dev/null +++ b/src/tools/clippy/tests/ui/use_self.fixed @@ -0,0 +1,253 @@ +// run-rustfix +// edition:2018 + +#![warn(clippy::use_self)] +#![allow(dead_code)] +#![allow(clippy::should_implement_trait)] + +fn main() {} + +mod use_self { + struct Foo {} + + impl Foo { + fn new() -> Self { + Self {} + } + fn test() -> Self { + Self::new() + } + } + + impl Default for Foo { + fn default() -> Self { + Self::new() + } + } +} + +mod better { + struct Foo {} + + impl Foo { + fn new() -> Self { + Self {} + } + fn test() -> Self { + Self::new() + } + } + + impl Default for Foo { + fn default() -> Self { + Self::new() + } + } +} + +mod lifetimes { + struct Foo<'a> { + foo_str: &'a str, + } + + impl<'a> Foo<'a> { + // Cannot use `Self` as return type, because the function is actually `fn foo<'b>(s: &'b str) -> + // Foo<'b>` + fn foo(s: &str) -> Foo { + Foo { foo_str: s } + } + // cannot replace with `Self`, because that's `Foo<'a>` + fn bar() -> Foo<'static> { + Foo { foo_str: "foo" } + } + + // FIXME: the lint does not handle lifetimed struct + // `Self` should be applicable here + fn clone(&self) -> Foo<'a> { + Foo { foo_str: self.foo_str } + } + } +} + +mod issue2894 { + trait IntoBytes { + fn into_bytes(&self) -> Vec<u8>; + } + + // This should not be linted + impl IntoBytes for u8 { + fn into_bytes(&self) -> Vec<u8> { + vec![*self] + } + } +} + +mod existential { + struct Foo; + + impl Foo { + fn bad(foos: &[Self]) -> impl Iterator<Item = &Self> { + foos.iter() + } + + fn good(foos: &[Self]) -> impl Iterator<Item = &Self> { + foos.iter() + } + } +} + +mod tuple_structs { + pub struct TS(i32); + + impl TS { + pub fn ts() -> Self { + Self(0) + } + } +} + +mod macros { + macro_rules! use_self_expand { + () => { + fn new() -> Self { + Self {} + } + }; + } + + struct Foo {} + + impl Foo { + use_self_expand!(); // Should lint in local macros + } +} + +mod nesting { + struct Foo {} + impl Foo { + fn foo() { + #[allow(unused_imports)] + use self::Foo; // Can't use Self here + struct Bar { + foo: Foo, // Foo != Self + } + + impl Bar { + fn bar() -> Self { + Self { foo: Foo {} } + } + } + + // Can't use Self here + fn baz() -> Foo { + Foo {} + } + } + + // Should lint here + fn baz() -> Self { + Self {} + } + } + + enum Enum { + A, + B(u64), + C { field: bool }, + } + impl Enum { + fn method() { + #[allow(unused_imports)] + use self::Enum::*; // Issue 3425 + static STATIC: Enum = Enum::A; // Can't use Self as type + } + + fn method2() { + let _ = Self::B(42); + let _ = Self::C { field: true }; + let _ = Self::A; + } + } +} + +mod issue3410 { + + struct A; + struct B; + + trait Trait<T> { + fn a(v: T); + } + + impl Trait<Vec<A>> for Vec<B> { + fn a(_: Vec<A>) {} + } +} + +#[allow(clippy::no_effect, path_statements)] +mod rustfix { + mod nested { + pub struct A {} + } + + impl nested::A { + const A: bool = true; + + fn fun_1() {} + + fn fun_2() { + Self::fun_1(); + Self::A; + + Self {}; + } + } +} + +mod issue3567 { + struct TestStruct {} + impl TestStruct { + fn from_something() -> Self { + Self {} + } + } + + trait Test { + fn test() -> TestStruct; + } + + impl Test for TestStruct { + fn test() -> TestStruct { + Self::from_something() + } + } +} + +mod paths_created_by_lowering { + use std::ops::Range; + + struct S {} + + impl S { + const A: usize = 0; + const B: usize = 1; + + async fn g() -> Self { + Self {} + } + + fn f<'a>(&self, p: &'a [u8]) -> &'a [u8] { + &p[Self::A..Self::B] + } + } + + trait T { + fn f<'a>(&self, p: &'a [u8]) -> &'a [u8]; + } + + impl T for Range<u8> { + fn f<'a>(&self, p: &'a [u8]) -> &'a [u8] { + &p[0..1] + } + } +} diff --git a/src/tools/clippy/tests/ui/use_self.rs b/src/tools/clippy/tests/ui/use_self.rs new file mode 100644 index 00000000000..8a182192ab3 --- /dev/null +++ b/src/tools/clippy/tests/ui/use_self.rs @@ -0,0 +1,253 @@ +// run-rustfix +// edition:2018 + +#![warn(clippy::use_self)] +#![allow(dead_code)] +#![allow(clippy::should_implement_trait)] + +fn main() {} + +mod use_self { + struct Foo {} + + impl Foo { + fn new() -> Foo { + Foo {} + } + fn test() -> Foo { + Foo::new() + } + } + + impl Default for Foo { + fn default() -> Foo { + Foo::new() + } + } +} + +mod better { + struct Foo {} + + impl Foo { + fn new() -> Self { + Self {} + } + fn test() -> Self { + Self::new() + } + } + + impl Default for Foo { + fn default() -> Self { + Self::new() + } + } +} + +mod lifetimes { + struct Foo<'a> { + foo_str: &'a str, + } + + impl<'a> Foo<'a> { + // Cannot use `Self` as return type, because the function is actually `fn foo<'b>(s: &'b str) -> + // Foo<'b>` + fn foo(s: &str) -> Foo { + Foo { foo_str: s } + } + // cannot replace with `Self`, because that's `Foo<'a>` + fn bar() -> Foo<'static> { + Foo { foo_str: "foo" } + } + + // FIXME: the lint does not handle lifetimed struct + // `Self` should be applicable here + fn clone(&self) -> Foo<'a> { + Foo { foo_str: self.foo_str } + } + } +} + +mod issue2894 { + trait IntoBytes { + fn into_bytes(&self) -> Vec<u8>; + } + + // This should not be linted + impl IntoBytes for u8 { + fn into_bytes(&self) -> Vec<u8> { + vec![*self] + } + } +} + +mod existential { + struct Foo; + + impl Foo { + fn bad(foos: &[Self]) -> impl Iterator<Item = &Foo> { + foos.iter() + } + + fn good(foos: &[Self]) -> impl Iterator<Item = &Self> { + foos.iter() + } + } +} + +mod tuple_structs { + pub struct TS(i32); + + impl TS { + pub fn ts() -> Self { + TS(0) + } + } +} + +mod macros { + macro_rules! use_self_expand { + () => { + fn new() -> Foo { + Foo {} + } + }; + } + + struct Foo {} + + impl Foo { + use_self_expand!(); // Should lint in local macros + } +} + +mod nesting { + struct Foo {} + impl Foo { + fn foo() { + #[allow(unused_imports)] + use self::Foo; // Can't use Self here + struct Bar { + foo: Foo, // Foo != Self + } + + impl Bar { + fn bar() -> Bar { + Bar { foo: Foo {} } + } + } + + // Can't use Self here + fn baz() -> Foo { + Foo {} + } + } + + // Should lint here + fn baz() -> Foo { + Foo {} + } + } + + enum Enum { + A, + B(u64), + C { field: bool }, + } + impl Enum { + fn method() { + #[allow(unused_imports)] + use self::Enum::*; // Issue 3425 + static STATIC: Enum = Enum::A; // Can't use Self as type + } + + fn method2() { + let _ = Enum::B(42); + let _ = Enum::C { field: true }; + let _ = Enum::A; + } + } +} + +mod issue3410 { + + struct A; + struct B; + + trait Trait<T> { + fn a(v: T); + } + + impl Trait<Vec<A>> for Vec<B> { + fn a(_: Vec<A>) {} + } +} + +#[allow(clippy::no_effect, path_statements)] +mod rustfix { + mod nested { + pub struct A {} + } + + impl nested::A { + const A: bool = true; + + fn fun_1() {} + + fn fun_2() { + nested::A::fun_1(); + nested::A::A; + + nested::A {}; + } + } +} + +mod issue3567 { + struct TestStruct {} + impl TestStruct { + fn from_something() -> Self { + Self {} + } + } + + trait Test { + fn test() -> TestStruct; + } + + impl Test for TestStruct { + fn test() -> TestStruct { + TestStruct::from_something() + } + } +} + +mod paths_created_by_lowering { + use std::ops::Range; + + struct S {} + + impl S { + const A: usize = 0; + const B: usize = 1; + + async fn g() -> S { + S {} + } + + fn f<'a>(&self, p: &'a [u8]) -> &'a [u8] { + &p[S::A..S::B] + } + } + + trait T { + fn f<'a>(&self, p: &'a [u8]) -> &'a [u8]; + } + + impl T for Range<u8> { + fn f<'a>(&self, p: &'a [u8]) -> &'a [u8] { + &p[0..1] + } + } +} diff --git a/src/tools/clippy/tests/ui/use_self.stderr b/src/tools/clippy/tests/ui/use_self.stderr new file mode 100644 index 00000000000..b33928597c1 --- /dev/null +++ b/src/tools/clippy/tests/ui/use_self.stderr @@ -0,0 +1,164 @@ +error: unnecessary structure name repetition + --> $DIR/use_self.rs:14:21 + | +LL | fn new() -> Foo { + | ^^^ help: use the applicable keyword: `Self` + | + = note: `-D clippy::use-self` implied by `-D warnings` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:15:13 + | +LL | Foo {} + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:17:22 + | +LL | fn test() -> Foo { + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:18:13 + | +LL | Foo::new() + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:23:25 + | +LL | fn default() -> Foo { + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:24:13 + | +LL | Foo::new() + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:89:56 + | +LL | fn bad(foos: &[Self]) -> impl Iterator<Item = &Foo> { + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:104:13 + | +LL | TS(0) + | ^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:112:25 + | +LL | fn new() -> Foo { + | ^^^ help: use the applicable keyword: `Self` +... +LL | use_self_expand!(); // Should lint in local macros + | ------------------- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:113:17 + | +LL | Foo {} + | ^^^ help: use the applicable keyword: `Self` +... +LL | use_self_expand!(); // Should lint in local macros + | ------------------- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:148:21 + | +LL | fn baz() -> Foo { + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:149:13 + | +LL | Foo {} + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:136:29 + | +LL | fn bar() -> Bar { + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:137:21 + | +LL | Bar { foo: Foo {} } + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:166:21 + | +LL | let _ = Enum::B(42); + | ^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:167:21 + | +LL | let _ = Enum::C { field: true }; + | ^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:168:21 + | +LL | let _ = Enum::A; + | ^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:199:13 + | +LL | nested::A::fun_1(); + | ^^^^^^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:200:13 + | +LL | nested::A::A; + | ^^^^^^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:202:13 + | +LL | nested::A {}; + | ^^^^^^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:221:13 + | +LL | TestStruct::from_something() + | ^^^^^^^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:235:25 + | +LL | async fn g() -> S { + | ^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:236:13 + | +LL | S {} + | ^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:240:16 + | +LL | &p[S::A..S::B] + | ^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:240:22 + | +LL | &p[S::A..S::B] + | ^ help: use the applicable keyword: `Self` + +error: aborting due to 25 previous errors + diff --git a/src/tools/clippy/tests/ui/use_self_trait.fixed b/src/tools/clippy/tests/ui/use_self_trait.fixed new file mode 100644 index 00000000000..1582ae114bf --- /dev/null +++ b/src/tools/clippy/tests/ui/use_self_trait.fixed @@ -0,0 +1,114 @@ +// run-rustfix + +#![warn(clippy::use_self)] +#![allow(dead_code)] +#![allow(clippy::should_implement_trait, clippy::boxed_local)] + +use std::ops::Mul; + +trait SelfTrait { + fn refs(p1: &Self) -> &Self; + fn ref_refs<'a>(p1: &'a &'a Self) -> &'a &'a Self; + fn mut_refs(p1: &mut Self) -> &mut Self; + fn nested(p1: Box<Self>, p2: (&u8, &Self)); + fn vals(r: Self) -> Self; +} + +#[derive(Default)] +struct Bad; + +impl SelfTrait for Bad { + fn refs(p1: &Self) -> &Self { + p1 + } + + fn ref_refs<'a>(p1: &'a &'a Self) -> &'a &'a Self { + p1 + } + + fn mut_refs(p1: &mut Self) -> &mut Self { + p1 + } + + fn nested(_p1: Box<Self>, _p2: (&u8, &Self)) {} + + fn vals(_: Self) -> Self { + Self::default() + } +} + +impl Mul for Bad { + type Output = Self; + + fn mul(self, rhs: Self) -> Self { + rhs + } +} + +impl Clone for Bad { + fn clone(&self) -> Self { + Self + } +} + +#[derive(Default)] +struct Good; + +impl SelfTrait for Good { + fn refs(p1: &Self) -> &Self { + p1 + } + + fn ref_refs<'a>(p1: &'a &'a Self) -> &'a &'a Self { + p1 + } + + fn mut_refs(p1: &mut Self) -> &mut Self { + p1 + } + + fn nested(_p1: Box<Self>, _p2: (&u8, &Self)) {} + + fn vals(_: Self) -> Self { + Self::default() + } +} + +impl Mul for Good { + type Output = Self; + + fn mul(self, rhs: Self) -> Self { + rhs + } +} + +trait NameTrait { + fn refs(p1: &u8) -> &u8; + fn ref_refs<'a>(p1: &'a &'a u8) -> &'a &'a u8; + fn mut_refs(p1: &mut u8) -> &mut u8; + fn nested(p1: Box<u8>, p2: (&u8, &u8)); + fn vals(p1: u8) -> u8; +} + +// Using `Self` instead of the type name is OK +impl NameTrait for u8 { + fn refs(p1: &Self) -> &Self { + p1 + } + + fn ref_refs<'a>(p1: &'a &'a Self) -> &'a &'a Self { + p1 + } + + fn mut_refs(p1: &mut Self) -> &mut Self { + p1 + } + + fn nested(_p1: Box<Self>, _p2: (&Self, &Self)) {} + + fn vals(_: Self) -> Self { + Self::default() + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/use_self_trait.rs b/src/tools/clippy/tests/ui/use_self_trait.rs new file mode 100644 index 00000000000..70667b9797e --- /dev/null +++ b/src/tools/clippy/tests/ui/use_self_trait.rs @@ -0,0 +1,114 @@ +// run-rustfix + +#![warn(clippy::use_self)] +#![allow(dead_code)] +#![allow(clippy::should_implement_trait, clippy::boxed_local)] + +use std::ops::Mul; + +trait SelfTrait { + fn refs(p1: &Self) -> &Self; + fn ref_refs<'a>(p1: &'a &'a Self) -> &'a &'a Self; + fn mut_refs(p1: &mut Self) -> &mut Self; + fn nested(p1: Box<Self>, p2: (&u8, &Self)); + fn vals(r: Self) -> Self; +} + +#[derive(Default)] +struct Bad; + +impl SelfTrait for Bad { + fn refs(p1: &Bad) -> &Bad { + p1 + } + + fn ref_refs<'a>(p1: &'a &'a Bad) -> &'a &'a Bad { + p1 + } + + fn mut_refs(p1: &mut Bad) -> &mut Bad { + p1 + } + + fn nested(_p1: Box<Bad>, _p2: (&u8, &Bad)) {} + + fn vals(_: Bad) -> Bad { + Bad::default() + } +} + +impl Mul for Bad { + type Output = Bad; + + fn mul(self, rhs: Bad) -> Bad { + rhs + } +} + +impl Clone for Bad { + fn clone(&self) -> Self { + Bad + } +} + +#[derive(Default)] +struct Good; + +impl SelfTrait for Good { + fn refs(p1: &Self) -> &Self { + p1 + } + + fn ref_refs<'a>(p1: &'a &'a Self) -> &'a &'a Self { + p1 + } + + fn mut_refs(p1: &mut Self) -> &mut Self { + p1 + } + + fn nested(_p1: Box<Self>, _p2: (&u8, &Self)) {} + + fn vals(_: Self) -> Self { + Self::default() + } +} + +impl Mul for Good { + type Output = Self; + + fn mul(self, rhs: Self) -> Self { + rhs + } +} + +trait NameTrait { + fn refs(p1: &u8) -> &u8; + fn ref_refs<'a>(p1: &'a &'a u8) -> &'a &'a u8; + fn mut_refs(p1: &mut u8) -> &mut u8; + fn nested(p1: Box<u8>, p2: (&u8, &u8)); + fn vals(p1: u8) -> u8; +} + +// Using `Self` instead of the type name is OK +impl NameTrait for u8 { + fn refs(p1: &Self) -> &Self { + p1 + } + + fn ref_refs<'a>(p1: &'a &'a Self) -> &'a &'a Self { + p1 + } + + fn mut_refs(p1: &mut Self) -> &mut Self { + p1 + } + + fn nested(_p1: Box<Self>, _p2: (&Self, &Self)) {} + + fn vals(_: Self) -> Self { + Self::default() + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/use_self_trait.stderr b/src/tools/clippy/tests/ui/use_self_trait.stderr new file mode 100644 index 00000000000..4f2506cc119 --- /dev/null +++ b/src/tools/clippy/tests/ui/use_self_trait.stderr @@ -0,0 +1,94 @@ +error: unnecessary structure name repetition + --> $DIR/use_self_trait.rs:21:18 + | +LL | fn refs(p1: &Bad) -> &Bad { + | ^^^ help: use the applicable keyword: `Self` + | + = note: `-D clippy::use-self` implied by `-D warnings` + +error: unnecessary structure name repetition + --> $DIR/use_self_trait.rs:21:27 + | +LL | fn refs(p1: &Bad) -> &Bad { + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self_trait.rs:25:33 + | +LL | fn ref_refs<'a>(p1: &'a &'a Bad) -> &'a &'a Bad { + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self_trait.rs:25:49 + | +LL | fn ref_refs<'a>(p1: &'a &'a Bad) -> &'a &'a Bad { + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self_trait.rs:29:26 + | +LL | fn mut_refs(p1: &mut Bad) -> &mut Bad { + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self_trait.rs:29:39 + | +LL | fn mut_refs(p1: &mut Bad) -> &mut Bad { + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self_trait.rs:33:24 + | +LL | fn nested(_p1: Box<Bad>, _p2: (&u8, &Bad)) {} + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self_trait.rs:33:42 + | +LL | fn nested(_p1: Box<Bad>, _p2: (&u8, &Bad)) {} + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self_trait.rs:35:16 + | +LL | fn vals(_: Bad) -> Bad { + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self_trait.rs:35:24 + | +LL | fn vals(_: Bad) -> Bad { + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self_trait.rs:36:9 + | +LL | Bad::default() + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self_trait.rs:41:19 + | +LL | type Output = Bad; + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self_trait.rs:43:23 + | +LL | fn mul(self, rhs: Bad) -> Bad { + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self_trait.rs:43:31 + | +LL | fn mul(self, rhs: Bad) -> Bad { + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self_trait.rs:50:9 + | +LL | Bad + | ^^^ help: use the applicable keyword: `Self` + +error: aborting due to 15 previous errors + diff --git a/src/tools/clippy/tests/ui/used_underscore_binding.rs b/src/tools/clippy/tests/ui/used_underscore_binding.rs new file mode 100644 index 00000000000..8e0243c49aa --- /dev/null +++ b/src/tools/clippy/tests/ui/used_underscore_binding.rs @@ -0,0 +1,119 @@ +// edition:2018 +// aux-build:proc_macro_derive.rs + +#![feature(rustc_private)] +#![warn(clippy::all)] +#![allow(clippy::blacklisted_name)] +#![warn(clippy::used_underscore_binding)] + +#[macro_use] +extern crate proc_macro_derive; + +// This should not trigger the lint. There's underscore binding inside the external derive that +// would trigger the `used_underscore_binding` lint. +#[derive(DeriveSomething)] +struct Baz; + +macro_rules! test_macro { + () => {{ + let _foo = 42; + _foo + 1 + }}; +} + +/// Tests that we lint if we use a binding with a single leading underscore +fn prefix_underscore(_foo: u32) -> u32 { + _foo + 1 +} + +/// Tests that we lint if we use a `_`-variable defined outside within a macro expansion +fn in_macro_or_desugar(_foo: u32) { + println!("{}", _foo); + assert_eq!(_foo, _foo); + + test_macro!() + 1; +} + +// Struct for testing use of fields prefixed with an underscore +struct StructFieldTest { + _underscore_field: u32, +} + +/// Tests that we lint the use of a struct field which is prefixed with an underscore +fn in_struct_field() { + let mut s = StructFieldTest { _underscore_field: 0 }; + s._underscore_field += 1; +} + +/// Tests that we do not lint if the underscore is not a prefix +fn non_prefix_underscore(some_foo: u32) -> u32 { + some_foo + 1 +} + +/// Tests that we do not lint if we do not use the binding (simple case) +fn unused_underscore_simple(_foo: u32) -> u32 { + 1 +} + +/// Tests that we do not lint if we do not use the binding (complex case). This checks for +/// compatibility with the built-in `unused_variables` lint. +fn unused_underscore_complex(mut _foo: u32) -> u32 { + _foo += 1; + _foo = 2; + 1 +} + +/// Test that we do not lint for multiple underscores +fn multiple_underscores(__foo: u32) -> u32 { + __foo + 1 +} + +// Non-variable bindings with preceding underscore +fn _fn_test() {} +struct _StructTest; +enum _EnumTest { + _Empty, + _Value(_StructTest), +} + +/// Tests that we do not lint for non-variable bindings +fn non_variables() { + _fn_test(); + let _s = _StructTest; + let _e = match _EnumTest::_Value(_StructTest) { + _EnumTest::_Empty => 0, + _EnumTest::_Value(_st) => 1, + }; + let f = _fn_test; + f(); +} + +// Tests that we do not lint if the binding comes from await desugaring, +// but we do lint the awaited expression. See issue 5360. +async fn await_desugaring() { + async fn foo() {} + fn uses_i(_i: i32) {} + + foo().await; + ({ + let _i = 5; + uses_i(_i); + foo() + }) + .await +} + +fn main() { + let foo = 0u32; + // tests of unused_underscore lint + let _ = prefix_underscore(foo); + in_macro_or_desugar(foo); + in_struct_field(); + // possible false positives + let _ = non_prefix_underscore(foo); + let _ = unused_underscore_simple(foo); + let _ = unused_underscore_complex(foo); + let _ = multiple_underscores(foo); + non_variables(); + await_desugaring(); +} diff --git a/src/tools/clippy/tests/ui/used_underscore_binding.stderr b/src/tools/clippy/tests/ui/used_underscore_binding.stderr new file mode 100644 index 00000000000..68e96148093 --- /dev/null +++ b/src/tools/clippy/tests/ui/used_underscore_binding.stderr @@ -0,0 +1,40 @@ +error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used. + --> $DIR/used_underscore_binding.rs:26:5 + | +LL | _foo + 1 + | ^^^^ + | + = note: `-D clippy::used-underscore-binding` implied by `-D warnings` + +error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used. + --> $DIR/used_underscore_binding.rs:31:20 + | +LL | println!("{}", _foo); + | ^^^^ + +error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used. + --> $DIR/used_underscore_binding.rs:32:16 + | +LL | assert_eq!(_foo, _foo); + | ^^^^ + +error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used. + --> $DIR/used_underscore_binding.rs:32:22 + | +LL | assert_eq!(_foo, _foo); + | ^^^^ + +error: used binding `_underscore_field` which is prefixed with an underscore. A leading underscore signals that a binding will not be used. + --> $DIR/used_underscore_binding.rs:45:5 + | +LL | s._underscore_field += 1; + | ^^^^^^^^^^^^^^^^^^^ + +error: used binding `_i` which is prefixed with an underscore. A leading underscore signals that a binding will not be used. + --> $DIR/used_underscore_binding.rs:100:16 + | +LL | uses_i(_i); + | ^^ + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/useful_asref.rs b/src/tools/clippy/tests/ui/useful_asref.rs new file mode 100644 index 00000000000..a9f0170a79c --- /dev/null +++ b/src/tools/clippy/tests/ui/useful_asref.rs @@ -0,0 +1,13 @@ +#![deny(clippy::useless_asref)] + +trait Trait { + fn as_ptr(&self); +} + +impl<'a> Trait for &'a [u8] { + fn as_ptr(&self) { + self.as_ref().as_ptr(); + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/useless_asref.fixed b/src/tools/clippy/tests/ui/useless_asref.fixed new file mode 100644 index 00000000000..e356f13d087 --- /dev/null +++ b/src/tools/clippy/tests/ui/useless_asref.fixed @@ -0,0 +1,135 @@ +// run-rustfix + +#![deny(clippy::useless_asref)] + +use std::fmt::Debug; + +struct FakeAsRef; + +#[allow(clippy::should_implement_trait)] +impl FakeAsRef { + fn as_ref(&self) -> &Self { + self + } +} + +struct MoreRef; + +impl<'a, 'b, 'c> AsRef<&'a &'b &'c MoreRef> for MoreRef { + fn as_ref(&self) -> &&'a &'b &'c MoreRef { + &&&&MoreRef + } +} + +fn foo_rstr(x: &str) { + println!("{:?}", x); +} +fn foo_rslice(x: &[i32]) { + println!("{:?}", x); +} +fn foo_mrslice(x: &mut [i32]) { + println!("{:?}", x); +} +fn foo_rrrrmr(_: &&&&MoreRef) { + println!("so many refs"); +} + +fn not_ok() { + let rstr: &str = "hello"; + let mut mrslice: &mut [i32] = &mut [1, 2, 3]; + + { + let rslice: &[i32] = &*mrslice; + foo_rstr(rstr); + foo_rstr(rstr); + foo_rslice(rslice); + foo_rslice(rslice); + } + { + foo_mrslice(mrslice); + foo_mrslice(mrslice); + foo_rslice(mrslice); + foo_rslice(mrslice); + } + + { + let rrrrrstr = &&&&rstr; + let rrrrrslice = &&&&&*mrslice; + foo_rslice(rrrrrslice); + foo_rslice(rrrrrslice); + foo_rstr(rrrrrstr); + foo_rstr(rrrrrstr); + } + { + let mrrrrrslice = &mut &mut &mut &mut mrslice; + foo_mrslice(mrrrrrslice); + foo_mrslice(mrrrrrslice); + foo_rslice(mrrrrrslice); + foo_rslice(mrrrrrslice); + } + #[allow(unused_parens, clippy::double_parens)] + foo_rrrrmr((&&&&MoreRef)); + + generic_not_ok(mrslice); + generic_ok(mrslice); +} + +fn ok() { + let string = "hello".to_owned(); + let mut arr = [1, 2, 3]; + let mut vec = vec![1, 2, 3]; + + { + foo_rstr(string.as_ref()); + foo_rslice(arr.as_ref()); + foo_rslice(vec.as_ref()); + } + { + foo_mrslice(arr.as_mut()); + foo_mrslice(vec.as_mut()); + } + + { + let rrrrstring = &&&&string; + let rrrrarr = &&&&arr; + let rrrrvec = &&&&vec; + foo_rstr(rrrrstring.as_ref()); + foo_rslice(rrrrarr.as_ref()); + foo_rslice(rrrrvec.as_ref()); + } + { + let mrrrrarr = &mut &mut &mut &mut arr; + let mrrrrvec = &mut &mut &mut &mut vec; + foo_mrslice(mrrrrarr.as_mut()); + foo_mrslice(mrrrrvec.as_mut()); + } + FakeAsRef.as_ref(); + foo_rrrrmr(MoreRef.as_ref()); + + generic_not_ok(arr.as_mut()); + generic_ok(&mut arr); +} + +fn foo_mrt<T: Debug + ?Sized>(t: &mut T) { + println!("{:?}", t); +} +fn foo_rt<T: Debug + ?Sized>(t: &T) { + println!("{:?}", t); +} + +fn generic_not_ok<T: AsMut<T> + AsRef<T> + Debug + ?Sized>(mrt: &mut T) { + foo_mrt(mrt); + foo_mrt(mrt); + foo_rt(mrt); + foo_rt(mrt); +} + +fn generic_ok<U: AsMut<T> + AsRef<T> + ?Sized, T: Debug + ?Sized>(mru: &mut U) { + foo_mrt(mru.as_mut()); + foo_rt(mru.as_ref()); +} + +fn main() { + not_ok(); + ok(); +} diff --git a/src/tools/clippy/tests/ui/useless_asref.rs b/src/tools/clippy/tests/ui/useless_asref.rs new file mode 100644 index 00000000000..2a80291f5d8 --- /dev/null +++ b/src/tools/clippy/tests/ui/useless_asref.rs @@ -0,0 +1,135 @@ +// run-rustfix + +#![deny(clippy::useless_asref)] + +use std::fmt::Debug; + +struct FakeAsRef; + +#[allow(clippy::should_implement_trait)] +impl FakeAsRef { + fn as_ref(&self) -> &Self { + self + } +} + +struct MoreRef; + +impl<'a, 'b, 'c> AsRef<&'a &'b &'c MoreRef> for MoreRef { + fn as_ref(&self) -> &&'a &'b &'c MoreRef { + &&&&MoreRef + } +} + +fn foo_rstr(x: &str) { + println!("{:?}", x); +} +fn foo_rslice(x: &[i32]) { + println!("{:?}", x); +} +fn foo_mrslice(x: &mut [i32]) { + println!("{:?}", x); +} +fn foo_rrrrmr(_: &&&&MoreRef) { + println!("so many refs"); +} + +fn not_ok() { + let rstr: &str = "hello"; + let mut mrslice: &mut [i32] = &mut [1, 2, 3]; + + { + let rslice: &[i32] = &*mrslice; + foo_rstr(rstr.as_ref()); + foo_rstr(rstr); + foo_rslice(rslice.as_ref()); + foo_rslice(rslice); + } + { + foo_mrslice(mrslice.as_mut()); + foo_mrslice(mrslice); + foo_rslice(mrslice.as_ref()); + foo_rslice(mrslice); + } + + { + let rrrrrstr = &&&&rstr; + let rrrrrslice = &&&&&*mrslice; + foo_rslice(rrrrrslice.as_ref()); + foo_rslice(rrrrrslice); + foo_rstr(rrrrrstr.as_ref()); + foo_rstr(rrrrrstr); + } + { + let mrrrrrslice = &mut &mut &mut &mut mrslice; + foo_mrslice(mrrrrrslice.as_mut()); + foo_mrslice(mrrrrrslice); + foo_rslice(mrrrrrslice.as_ref()); + foo_rslice(mrrrrrslice); + } + #[allow(unused_parens, clippy::double_parens)] + foo_rrrrmr((&&&&MoreRef).as_ref()); + + generic_not_ok(mrslice); + generic_ok(mrslice); +} + +fn ok() { + let string = "hello".to_owned(); + let mut arr = [1, 2, 3]; + let mut vec = vec![1, 2, 3]; + + { + foo_rstr(string.as_ref()); + foo_rslice(arr.as_ref()); + foo_rslice(vec.as_ref()); + } + { + foo_mrslice(arr.as_mut()); + foo_mrslice(vec.as_mut()); + } + + { + let rrrrstring = &&&&string; + let rrrrarr = &&&&arr; + let rrrrvec = &&&&vec; + foo_rstr(rrrrstring.as_ref()); + foo_rslice(rrrrarr.as_ref()); + foo_rslice(rrrrvec.as_ref()); + } + { + let mrrrrarr = &mut &mut &mut &mut arr; + let mrrrrvec = &mut &mut &mut &mut vec; + foo_mrslice(mrrrrarr.as_mut()); + foo_mrslice(mrrrrvec.as_mut()); + } + FakeAsRef.as_ref(); + foo_rrrrmr(MoreRef.as_ref()); + + generic_not_ok(arr.as_mut()); + generic_ok(&mut arr); +} + +fn foo_mrt<T: Debug + ?Sized>(t: &mut T) { + println!("{:?}", t); +} +fn foo_rt<T: Debug + ?Sized>(t: &T) { + println!("{:?}", t); +} + +fn generic_not_ok<T: AsMut<T> + AsRef<T> + Debug + ?Sized>(mrt: &mut T) { + foo_mrt(mrt.as_mut()); + foo_mrt(mrt); + foo_rt(mrt.as_ref()); + foo_rt(mrt); +} + +fn generic_ok<U: AsMut<T> + AsRef<T> + ?Sized, T: Debug + ?Sized>(mru: &mut U) { + foo_mrt(mru.as_mut()); + foo_rt(mru.as_ref()); +} + +fn main() { + not_ok(); + ok(); +} diff --git a/src/tools/clippy/tests/ui/useless_asref.stderr b/src/tools/clippy/tests/ui/useless_asref.stderr new file mode 100644 index 00000000000..5876b54aca8 --- /dev/null +++ b/src/tools/clippy/tests/ui/useless_asref.stderr @@ -0,0 +1,74 @@ +error: this call to `as_ref` does nothing + --> $DIR/useless_asref.rs:43:18 + | +LL | foo_rstr(rstr.as_ref()); + | ^^^^^^^^^^^^^ help: try this: `rstr` + | +note: the lint level is defined here + --> $DIR/useless_asref.rs:3:9 + | +LL | #![deny(clippy::useless_asref)] + | ^^^^^^^^^^^^^^^^^^^^^ + +error: this call to `as_ref` does nothing + --> $DIR/useless_asref.rs:45:20 + | +LL | foo_rslice(rslice.as_ref()); + | ^^^^^^^^^^^^^^^ help: try this: `rslice` + +error: this call to `as_mut` does nothing + --> $DIR/useless_asref.rs:49:21 + | +LL | foo_mrslice(mrslice.as_mut()); + | ^^^^^^^^^^^^^^^^ help: try this: `mrslice` + +error: this call to `as_ref` does nothing + --> $DIR/useless_asref.rs:51:20 + | +LL | foo_rslice(mrslice.as_ref()); + | ^^^^^^^^^^^^^^^^ help: try this: `mrslice` + +error: this call to `as_ref` does nothing + --> $DIR/useless_asref.rs:58:20 + | +LL | foo_rslice(rrrrrslice.as_ref()); + | ^^^^^^^^^^^^^^^^^^^ help: try this: `rrrrrslice` + +error: this call to `as_ref` does nothing + --> $DIR/useless_asref.rs:60:18 + | +LL | foo_rstr(rrrrrstr.as_ref()); + | ^^^^^^^^^^^^^^^^^ help: try this: `rrrrrstr` + +error: this call to `as_mut` does nothing + --> $DIR/useless_asref.rs:65:21 + | +LL | foo_mrslice(mrrrrrslice.as_mut()); + | ^^^^^^^^^^^^^^^^^^^^ help: try this: `mrrrrrslice` + +error: this call to `as_ref` does nothing + --> $DIR/useless_asref.rs:67:20 + | +LL | foo_rslice(mrrrrrslice.as_ref()); + | ^^^^^^^^^^^^^^^^^^^^ help: try this: `mrrrrrslice` + +error: this call to `as_ref` does nothing + --> $DIR/useless_asref.rs:71:16 + | +LL | foo_rrrrmr((&&&&MoreRef).as_ref()); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `(&&&&MoreRef)` + +error: this call to `as_mut` does nothing + --> $DIR/useless_asref.rs:121:13 + | +LL | foo_mrt(mrt.as_mut()); + | ^^^^^^^^^^^^ help: try this: `mrt` + +error: this call to `as_ref` does nothing + --> $DIR/useless_asref.rs:123:12 + | +LL | foo_rt(mrt.as_ref()); + | ^^^^^^^^^^^^ help: try this: `mrt` + +error: aborting due to 11 previous errors + diff --git a/src/tools/clippy/tests/ui/useless_attribute.fixed b/src/tools/clippy/tests/ui/useless_attribute.fixed new file mode 100644 index 00000000000..b222e2f7976 --- /dev/null +++ b/src/tools/clippy/tests/ui/useless_attribute.fixed @@ -0,0 +1,61 @@ +// run-rustfix +// aux-build:proc_macro_derive.rs + +#![warn(clippy::useless_attribute)] +#![warn(unreachable_pub)] +#![feature(rustc_private)] + +#![allow(dead_code)] +#![cfg_attr(feature = "cargo-clippy", allow(dead_code))] +#[rustfmt::skip] +#[allow(unused_imports)] +#[allow(unused_extern_crates)] +#[macro_use] +extern crate rustc_middle; + +#[macro_use] +extern crate proc_macro_derive; + +// don't lint on unused_import for `use` items +#[allow(unused_imports)] +use std::collections; + +// don't lint on unused for `use` items +#[allow(unused)] +use std::option; + +// don't lint on deprecated for `use` items +mod foo { + #[deprecated] + pub struct Bar; +} +#[allow(deprecated)] +pub use foo::Bar; + +// This should not trigger the lint. There's lint level definitions inside the external derive +// that would trigger the useless_attribute lint. +#[derive(DeriveSomething)] +struct Baz; + +// don't lint on unreachable_pub for `use` items +mod a { + mod b { + #[allow(dead_code)] + #[allow(unreachable_pub)] + pub struct C {} + } + + #[allow(unreachable_pub)] + pub use self::b::C; +} + +fn test_indented_attr() { + #![allow(clippy::almost_swapped)] + use std::collections::HashSet; + + let _ = HashSet::<u32>::default(); +} + +fn main() { + test_indented_attr(); +} diff --git a/src/tools/clippy/tests/ui/useless_attribute.rs b/src/tools/clippy/tests/ui/useless_attribute.rs new file mode 100644 index 00000000000..3422eace4ab --- /dev/null +++ b/src/tools/clippy/tests/ui/useless_attribute.rs @@ -0,0 +1,61 @@ +// run-rustfix +// aux-build:proc_macro_derive.rs + +#![warn(clippy::useless_attribute)] +#![warn(unreachable_pub)] +#![feature(rustc_private)] + +#[allow(dead_code)] +#[cfg_attr(feature = "cargo-clippy", allow(dead_code))] +#[rustfmt::skip] +#[allow(unused_imports)] +#[allow(unused_extern_crates)] +#[macro_use] +extern crate rustc_middle; + +#[macro_use] +extern crate proc_macro_derive; + +// don't lint on unused_import for `use` items +#[allow(unused_imports)] +use std::collections; + +// don't lint on unused for `use` items +#[allow(unused)] +use std::option; + +// don't lint on deprecated for `use` items +mod foo { + #[deprecated] + pub struct Bar; +} +#[allow(deprecated)] +pub use foo::Bar; + +// This should not trigger the lint. There's lint level definitions inside the external derive +// that would trigger the useless_attribute lint. +#[derive(DeriveSomething)] +struct Baz; + +// don't lint on unreachable_pub for `use` items +mod a { + mod b { + #[allow(dead_code)] + #[allow(unreachable_pub)] + pub struct C {} + } + + #[allow(unreachable_pub)] + pub use self::b::C; +} + +fn test_indented_attr() { + #[allow(clippy::almost_swapped)] + use std::collections::HashSet; + + let _ = HashSet::<u32>::default(); +} + +fn main() { + test_indented_attr(); +} diff --git a/src/tools/clippy/tests/ui/useless_attribute.stderr b/src/tools/clippy/tests/ui/useless_attribute.stderr new file mode 100644 index 00000000000..57ba976730c --- /dev/null +++ b/src/tools/clippy/tests/ui/useless_attribute.stderr @@ -0,0 +1,22 @@ +error: useless lint attribute + --> $DIR/useless_attribute.rs:8:1 + | +LL | #[allow(dead_code)] + | ^^^^^^^^^^^^^^^^^^^ help: if you just forgot a `!`, use: `#![allow(dead_code)]` + | + = note: `-D clippy::useless-attribute` implied by `-D warnings` + +error: useless lint attribute + --> $DIR/useless_attribute.rs:9:1 + | +LL | #[cfg_attr(feature = "cargo-clippy", allow(dead_code))] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if you just forgot a `!`, use: `#![cfg_attr(feature = "cargo-clippy", allow(dead_code)` + +error: useless lint attribute + --> $DIR/useless_attribute.rs:53:5 + | +LL | #[allow(clippy::almost_swapped)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if you just forgot a `!`, use: `#![allow(clippy::almost_swapped)]` + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/vec.fixed b/src/tools/clippy/tests/ui/vec.fixed new file mode 100644 index 00000000000..e73a791891f --- /dev/null +++ b/src/tools/clippy/tests/ui/vec.fixed @@ -0,0 +1,55 @@ +// run-rustfix + +#![warn(clippy::useless_vec)] + +#[derive(Debug)] +struct NonCopy; + +fn on_slice(_: &[u8]) {} +#[allow(clippy::ptr_arg)] +fn on_vec(_: &Vec<u8>) {} + +struct Line { + length: usize, +} + +impl Line { + fn length(&self) -> usize { + self.length + } +} + +fn main() { + on_slice(&[]); + on_slice(&[]); + + on_slice(&[1, 2]); + on_slice(&[1, 2]); + + on_slice(&[1, 2]); + on_slice(&[1, 2]); + #[rustfmt::skip] + on_slice(&[1, 2]); + on_slice(&[1, 2]); + + on_slice(&[1; 2]); + on_slice(&[1; 2]); + + on_vec(&vec![]); + on_vec(&vec![1, 2]); + on_vec(&vec![1; 2]); + + // Now with non-constant expressions + let line = Line { length: 2 }; + + on_slice(&vec![2; line.length]); + on_slice(&vec![2; line.length()]); + + for a in &[1, 2, 3] { + println!("{:?}", a); + } + + for a in vec![NonCopy, NonCopy] { + println!("{:?}", a); + } +} diff --git a/src/tools/clippy/tests/ui/vec.rs b/src/tools/clippy/tests/ui/vec.rs new file mode 100644 index 00000000000..3eb960f53d7 --- /dev/null +++ b/src/tools/clippy/tests/ui/vec.rs @@ -0,0 +1,55 @@ +// run-rustfix + +#![warn(clippy::useless_vec)] + +#[derive(Debug)] +struct NonCopy; + +fn on_slice(_: &[u8]) {} +#[allow(clippy::ptr_arg)] +fn on_vec(_: &Vec<u8>) {} + +struct Line { + length: usize, +} + +impl Line { + fn length(&self) -> usize { + self.length + } +} + +fn main() { + on_slice(&vec![]); + on_slice(&[]); + + on_slice(&vec![1, 2]); + on_slice(&[1, 2]); + + on_slice(&vec![1, 2]); + on_slice(&[1, 2]); + #[rustfmt::skip] + on_slice(&vec!(1, 2)); + on_slice(&[1, 2]); + + on_slice(&vec![1; 2]); + on_slice(&[1; 2]); + + on_vec(&vec![]); + on_vec(&vec![1, 2]); + on_vec(&vec![1; 2]); + + // Now with non-constant expressions + let line = Line { length: 2 }; + + on_slice(&vec![2; line.length]); + on_slice(&vec![2; line.length()]); + + for a in vec![1, 2, 3] { + println!("{:?}", a); + } + + for a in vec![NonCopy, NonCopy] { + println!("{:?}", a); + } +} diff --git a/src/tools/clippy/tests/ui/vec.stderr b/src/tools/clippy/tests/ui/vec.stderr new file mode 100644 index 00000000000..37e28ebddb5 --- /dev/null +++ b/src/tools/clippy/tests/ui/vec.stderr @@ -0,0 +1,40 @@ +error: useless use of `vec!` + --> $DIR/vec.rs:23:14 + | +LL | on_slice(&vec![]); + | ^^^^^^^ help: you can use a slice directly: `&[]` + | + = note: `-D clippy::useless-vec` implied by `-D warnings` + +error: useless use of `vec!` + --> $DIR/vec.rs:26:14 + | +LL | on_slice(&vec![1, 2]); + | ^^^^^^^^^^^ help: you can use a slice directly: `&[1, 2]` + +error: useless use of `vec!` + --> $DIR/vec.rs:29:14 + | +LL | on_slice(&vec![1, 2]); + | ^^^^^^^^^^^ help: you can use a slice directly: `&[1, 2]` + +error: useless use of `vec!` + --> $DIR/vec.rs:32:14 + | +LL | on_slice(&vec!(1, 2)); + | ^^^^^^^^^^^ help: you can use a slice directly: `&[1, 2]` + +error: useless use of `vec!` + --> $DIR/vec.rs:35:14 + | +LL | on_slice(&vec![1; 2]); + | ^^^^^^^^^^^ help: you can use a slice directly: `&[1; 2]` + +error: useless use of `vec!` + --> $DIR/vec.rs:48:14 + | +LL | for a in vec![1, 2, 3] { + | ^^^^^^^^^^^^^ help: you can use a slice directly: `&[1, 2, 3]` + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/vec_box_sized.fixed b/src/tools/clippy/tests/ui/vec_box_sized.fixed new file mode 100644 index 00000000000..d0bee2460dd --- /dev/null +++ b/src/tools/clippy/tests/ui/vec_box_sized.fixed @@ -0,0 +1,38 @@ +// run-rustfix + +#![allow(dead_code)] + +struct SizedStruct(i32); +struct UnsizedStruct([i32]); +struct BigStruct([i32; 10000]); + +/// The following should trigger the lint +mod should_trigger { + use super::SizedStruct; + + struct StructWithVecBox { + sized_type: Vec<SizedStruct>, + } + + struct A(Vec<SizedStruct>); + struct B(Vec<Vec<u32>>); +} + +/// The following should not trigger the lint +mod should_not_trigger { + use super::{BigStruct, UnsizedStruct}; + + struct C(Vec<Box<UnsizedStruct>>); + struct D(Vec<Box<BigStruct>>); + + struct StructWithVecBoxButItsUnsized { + unsized_type: Vec<Box<UnsizedStruct>>, + } + + struct TraitVec<T: ?Sized> { + // Regression test for #3720. This was causing an ICE. + inner: Vec<Box<T>>, + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/vec_box_sized.rs b/src/tools/clippy/tests/ui/vec_box_sized.rs new file mode 100644 index 00000000000..500a0ae263e --- /dev/null +++ b/src/tools/clippy/tests/ui/vec_box_sized.rs @@ -0,0 +1,38 @@ +// run-rustfix + +#![allow(dead_code)] + +struct SizedStruct(i32); +struct UnsizedStruct([i32]); +struct BigStruct([i32; 10000]); + +/// The following should trigger the lint +mod should_trigger { + use super::SizedStruct; + + struct StructWithVecBox { + sized_type: Vec<Box<SizedStruct>>, + } + + struct A(Vec<Box<SizedStruct>>); + struct B(Vec<Vec<Box<(u32)>>>); +} + +/// The following should not trigger the lint +mod should_not_trigger { + use super::{BigStruct, UnsizedStruct}; + + struct C(Vec<Box<UnsizedStruct>>); + struct D(Vec<Box<BigStruct>>); + + struct StructWithVecBoxButItsUnsized { + unsized_type: Vec<Box<UnsizedStruct>>, + } + + struct TraitVec<T: ?Sized> { + // Regression test for #3720. This was causing an ICE. + inner: Vec<Box<T>>, + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/vec_box_sized.stderr b/src/tools/clippy/tests/ui/vec_box_sized.stderr new file mode 100644 index 00000000000..29bf7069e8a --- /dev/null +++ b/src/tools/clippy/tests/ui/vec_box_sized.stderr @@ -0,0 +1,22 @@ +error: `Vec<T>` is already on the heap, the boxing is unnecessary. + --> $DIR/vec_box_sized.rs:14:21 + | +LL | sized_type: Vec<Box<SizedStruct>>, + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `Vec<SizedStruct>` + | + = note: `-D clippy::vec-box` implied by `-D warnings` + +error: `Vec<T>` is already on the heap, the boxing is unnecessary. + --> $DIR/vec_box_sized.rs:17:14 + | +LL | struct A(Vec<Box<SizedStruct>>); + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `Vec<SizedStruct>` + +error: `Vec<T>` is already on the heap, the boxing is unnecessary. + --> $DIR/vec_box_sized.rs:18:18 + | +LL | struct B(Vec<Vec<Box<(u32)>>>); + | ^^^^^^^^^^^^^^^ help: try: `Vec<u32>` + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/verbose_file_reads.rs b/src/tools/clippy/tests/ui/verbose_file_reads.rs new file mode 100644 index 00000000000..e0065e05ade --- /dev/null +++ b/src/tools/clippy/tests/ui/verbose_file_reads.rs @@ -0,0 +1,28 @@ +#![warn(clippy::verbose_file_reads)] +use std::env::temp_dir; +use std::fs::File; +use std::io::Read; + +struct Struct; +// To make sure we only warn on File::{read_to_end, read_to_string} calls +impl Struct { + pub fn read_to_end(&self) {} + + pub fn read_to_string(&self) {} +} + +fn main() -> std::io::Result<()> { + let path = "foo.txt"; + // Lint shouldn't catch this + let s = Struct; + s.read_to_end(); + s.read_to_string(); + // Should catch this + let mut f = File::open(&path)?; + let mut buffer = Vec::new(); + f.read_to_end(&mut buffer)?; + // ...and this + let mut string_buffer = String::new(); + f.read_to_string(&mut string_buffer)?; + Ok(()) +} diff --git a/src/tools/clippy/tests/ui/verbose_file_reads.stderr b/src/tools/clippy/tests/ui/verbose_file_reads.stderr new file mode 100644 index 00000000000..550b6ab679f --- /dev/null +++ b/src/tools/clippy/tests/ui/verbose_file_reads.stderr @@ -0,0 +1,19 @@ +error: use of `File::read_to_end` + --> $DIR/verbose_file_reads.rs:23:5 + | +LL | f.read_to_end(&mut buffer)?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::verbose-file-reads` implied by `-D warnings` + = help: consider using `fs::read` instead + +error: use of `File::read_to_string` + --> $DIR/verbose_file_reads.rs:26:5 + | +LL | f.read_to_string(&mut string_buffer)?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using `fs::read_to_string` instead + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/vtable_address_comparisons.rs b/src/tools/clippy/tests/ui/vtable_address_comparisons.rs new file mode 100644 index 00000000000..c91d96ee18a --- /dev/null +++ b/src/tools/clippy/tests/ui/vtable_address_comparisons.rs @@ -0,0 +1,42 @@ +use std::fmt::Debug; +use std::ptr; +use std::rc::Rc; +use std::sync::Arc; + +#[warn(clippy::vtable_address_comparisons)] +fn main() { + let a: *const dyn Debug = &1 as &dyn Debug; + let b: *const dyn Debug = &1 as &dyn Debug; + + // These should fail: + let _ = a == b; + let _ = a != b; + let _ = a < b; + let _ = a <= b; + let _ = a > b; + let _ = a >= b; + ptr::eq(a, b); + + let a = &1 as &dyn Debug; + let b = &1 as &dyn Debug; + ptr::eq(a, b); + + let a: Rc<dyn Debug> = Rc::new(1); + Rc::ptr_eq(&a, &a); + + let a: Arc<dyn Debug> = Arc::new(1); + Arc::ptr_eq(&a, &a); + + // These should be fine: + let a = &1; + ptr::eq(a, a); + + let a = Rc::new(1); + Rc::ptr_eq(&a, &a); + + let a = Arc::new(1); + Arc::ptr_eq(&a, &a); + + let a: &[u8] = b""; + ptr::eq(a, a); +} diff --git a/src/tools/clippy/tests/ui/vtable_address_comparisons.stderr b/src/tools/clippy/tests/ui/vtable_address_comparisons.stderr new file mode 100644 index 00000000000..76bd57217d7 --- /dev/null +++ b/src/tools/clippy/tests/ui/vtable_address_comparisons.stderr @@ -0,0 +1,83 @@ +error: comparing trait object pointers compares a non-unique vtable address + --> $DIR/vtable_address_comparisons.rs:12:13 + | +LL | let _ = a == b; + | ^^^^^^ + | + = note: `-D clippy::vtable-address-comparisons` implied by `-D warnings` + = help: consider extracting and comparing data pointers only + +error: comparing trait object pointers compares a non-unique vtable address + --> $DIR/vtable_address_comparisons.rs:13:13 + | +LL | let _ = a != b; + | ^^^^^^ + | + = help: consider extracting and comparing data pointers only + +error: comparing trait object pointers compares a non-unique vtable address + --> $DIR/vtable_address_comparisons.rs:14:13 + | +LL | let _ = a < b; + | ^^^^^ + | + = help: consider extracting and comparing data pointers only + +error: comparing trait object pointers compares a non-unique vtable address + --> $DIR/vtable_address_comparisons.rs:15:13 + | +LL | let _ = a <= b; + | ^^^^^^ + | + = help: consider extracting and comparing data pointers only + +error: comparing trait object pointers compares a non-unique vtable address + --> $DIR/vtable_address_comparisons.rs:16:13 + | +LL | let _ = a > b; + | ^^^^^ + | + = help: consider extracting and comparing data pointers only + +error: comparing trait object pointers compares a non-unique vtable address + --> $DIR/vtable_address_comparisons.rs:17:13 + | +LL | let _ = a >= b; + | ^^^^^^ + | + = help: consider extracting and comparing data pointers only + +error: comparing trait object pointers compares a non-unique vtable address + --> $DIR/vtable_address_comparisons.rs:18:5 + | +LL | ptr::eq(a, b); + | ^^^^^^^^^^^^^ + | + = help: consider extracting and comparing data pointers only + +error: comparing trait object pointers compares a non-unique vtable address + --> $DIR/vtable_address_comparisons.rs:22:5 + | +LL | ptr::eq(a, b); + | ^^^^^^^^^^^^^ + | + = help: consider extracting and comparing data pointers only + +error: comparing trait object pointers compares a non-unique vtable address + --> $DIR/vtable_address_comparisons.rs:25:5 + | +LL | Rc::ptr_eq(&a, &a); + | ^^^^^^^^^^^^^^^^^^ + | + = help: consider extracting and comparing data pointers only + +error: comparing trait object pointers compares a non-unique vtable address + --> $DIR/vtable_address_comparisons.rs:28:5 + | +LL | Arc::ptr_eq(&a, &a); + | ^^^^^^^^^^^^^^^^^^^ + | + = help: consider extracting and comparing data pointers only + +error: aborting due to 10 previous errors + diff --git a/src/tools/clippy/tests/ui/while_let_loop.rs b/src/tools/clippy/tests/ui/while_let_loop.rs new file mode 100644 index 00000000000..3ce699f551b --- /dev/null +++ b/src/tools/clippy/tests/ui/while_let_loop.rs @@ -0,0 +1,119 @@ +#![warn(clippy::while_let_loop)] + +fn main() { + let y = Some(true); + loop { + if let Some(_x) = y { + let _v = 1; + } else { + break; + } + } + + #[allow(clippy::never_loop)] + loop { + // no error, break is not in else clause + if let Some(_x) = y { + let _v = 1; + } + break; + } + + loop { + match y { + Some(_x) => true, + None => break, + }; + } + + loop { + let x = match y { + Some(x) => x, + None => break, + }; + let _x = x; + let _str = "foo"; + } + + loop { + let x = match y { + Some(x) => x, + None => break, + }; + { + let _a = "bar"; + }; + { + let _b = "foobar"; + } + } + + loop { + // no error, else branch does something other than break + match y { + Some(_x) => true, + _ => { + let _z = 1; + break; + }, + }; + } + + while let Some(x) = y { + // no error, obviously + println!("{}", x); + } + + // #675, this used to have a wrong suggestion + loop { + let (e, l) = match "".split_whitespace().next() { + Some(word) => (word.is_empty(), word.len()), + None => break, + }; + + let _ = (e, l); + } +} + +fn issue771() { + let mut a = 100; + let b = Some(true); + loop { + if a > 10 { + break; + } + + match b { + Some(_) => a = 0, + None => break, + } + } +} + +fn issue1017() { + let r: Result<u32, u32> = Ok(42); + let mut len = 1337; + + loop { + match r { + Err(_) => len = 0, + Ok(length) => { + len = length; + break; + }, + } + } +} + +#[allow(clippy::never_loop)] +fn issue1948() { + // should not trigger clippy::while_let_loop lint because break passes an expression + let a = Some(10); + let b = loop { + if let Some(c) = a { + break Some(c); + } else { + break None; + } + }; +} diff --git a/src/tools/clippy/tests/ui/while_let_loop.stderr b/src/tools/clippy/tests/ui/while_let_loop.stderr new file mode 100644 index 00000000000..13dd0ee224c --- /dev/null +++ b/src/tools/clippy/tests/ui/while_let_loop.stderr @@ -0,0 +1,63 @@ +error: this loop could be written as a `while let` loop + --> $DIR/while_let_loop.rs:5:5 + | +LL | / loop { +LL | | if let Some(_x) = y { +LL | | let _v = 1; +LL | | } else { +LL | | break; +LL | | } +LL | | } + | |_____^ help: try: `while let Some(_x) = y { .. }` + | + = note: `-D clippy::while-let-loop` implied by `-D warnings` + +error: this loop could be written as a `while let` loop + --> $DIR/while_let_loop.rs:22:5 + | +LL | / loop { +LL | | match y { +LL | | Some(_x) => true, +LL | | None => break, +LL | | }; +LL | | } + | |_____^ help: try: `while let Some(_x) = y { .. }` + +error: this loop could be written as a `while let` loop + --> $DIR/while_let_loop.rs:29:5 + | +LL | / loop { +LL | | let x = match y { +LL | | Some(x) => x, +LL | | None => break, +... | +LL | | let _str = "foo"; +LL | | } + | |_____^ help: try: `while let Some(x) = y { .. }` + +error: this loop could be written as a `while let` loop + --> $DIR/while_let_loop.rs:38:5 + | +LL | / loop { +LL | | let x = match y { +LL | | Some(x) => x, +LL | | None => break, +... | +LL | | } +LL | | } + | |_____^ help: try: `while let Some(x) = y { .. }` + +error: this loop could be written as a `while let` loop + --> $DIR/while_let_loop.rs:68:5 + | +LL | / loop { +LL | | let (e, l) = match "".split_whitespace().next() { +LL | | Some(word) => (word.is_empty(), word.len()), +LL | | None => break, +... | +LL | | let _ = (e, l); +LL | | } + | |_____^ help: try: `while let Some(word) = "".split_whitespace().next() { .. }` + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/while_let_on_iterator.fixed b/src/tools/clippy/tests/ui/while_let_on_iterator.fixed new file mode 100644 index 00000000000..f5fcabf63fd --- /dev/null +++ b/src/tools/clippy/tests/ui/while_let_on_iterator.fixed @@ -0,0 +1,160 @@ +// run-rustfix + +#![warn(clippy::while_let_on_iterator)] +#![allow(clippy::never_loop, unreachable_code, unused_mut)] + +fn base() { + let mut iter = 1..20; + for x in iter { + println!("{}", x); + } + + let mut iter = 1..20; + for x in iter { + println!("{}", x); + } + + let mut iter = 1..20; + for _ in iter {} + + let mut iter = 1..20; + while let None = iter.next() {} // this is fine (if nonsensical) + + let mut iter = 1..20; + if let Some(x) = iter.next() { + // also fine + println!("{}", x) + } + + // the following shouldn't warn because it can't be written with a for loop + let mut iter = 1u32..20; + while let Some(_) = iter.next() { + println!("next: {:?}", iter.next()) + } + + // neither can this + let mut iter = 1u32..20; + while let Some(_) = iter.next() { + println!("next: {:?}", iter.next()); + } + + // or this + let mut iter = 1u32..20; + while let Some(_) = iter.next() { + break; + } + println!("Remaining iter {:?}", iter); + + // or this + let mut iter = 1u32..20; + while let Some(_) = iter.next() { + iter = 1..20; + } +} + +// Issue #1188 +fn refutable() { + let a = [42, 1337]; + let mut b = a.iter(); + + // consume all the 42s + while let Some(&42) = b.next() {} + + let a = [(1, 2, 3)]; + let mut b = a.iter(); + + while let Some(&(1, 2, 3)) = b.next() {} + + let a = [Some(42)]; + let mut b = a.iter(); + + while let Some(&None) = b.next() {} + + /* This gives “refutable pattern in `for` loop binding: `&_` not covered” + for &42 in b {} + for &(1, 2, 3) in b {} + for &Option::None in b.next() {} + // */ +} + +fn nested_loops() { + let a = [42, 1337]; + let mut y = a.iter(); + loop { + // x is reused, so don't lint here + while let Some(_) = y.next() {} + } + + let mut y = a.iter(); + for _ in 0..2 { + while let Some(_) = y.next() { + // y is reused, don't lint + } + } + + loop { + let mut y = a.iter(); + for _ in y { + // use a for loop here + } + } +} + +fn issue1121() { + use std::collections::HashSet; + let mut values = HashSet::new(); + values.insert(1); + + while let Some(&value) = values.iter().next() { + values.remove(&value); + } +} + +fn issue2965() { + // This should not cause an ICE and suggest: + // + // for _ in values.iter() {} + // + use std::collections::HashSet; + let mut values = HashSet::new(); + values.insert(1); + + while let Some(..) = values.iter().next() {} +} + +fn issue3670() { + let array = [Some(0), None, Some(1)]; + let mut iter = array.iter(); + + while let Some(elem) = iter.next() { + let _ = elem.or_else(|| *iter.next()?); + } +} + +fn issue1654() { + // should not lint if the iterator is generated on every iteration + use std::collections::HashSet; + let mut values = HashSet::new(); + values.insert(1); + + while let Some(..) = values.iter().next() { + values.remove(&1); + } + + while let Some(..) = values.iter().map(|x| x + 1).next() {} + + let chars = "Hello, World!".char_indices(); + while let Some((i, ch)) = chars.clone().next() { + println!("{}: {}", i, ch); + } +} + +fn main() { + base(); + refutable(); + nested_loops(); + issue1121(); + issue2965(); + issue3670(); + issue1654(); +} diff --git a/src/tools/clippy/tests/ui/while_let_on_iterator.rs b/src/tools/clippy/tests/ui/while_let_on_iterator.rs new file mode 100644 index 00000000000..04dce8a0289 --- /dev/null +++ b/src/tools/clippy/tests/ui/while_let_on_iterator.rs @@ -0,0 +1,160 @@ +// run-rustfix + +#![warn(clippy::while_let_on_iterator)] +#![allow(clippy::never_loop, unreachable_code, unused_mut)] + +fn base() { + let mut iter = 1..20; + while let Option::Some(x) = iter.next() { + println!("{}", x); + } + + let mut iter = 1..20; + while let Some(x) = iter.next() { + println!("{}", x); + } + + let mut iter = 1..20; + while let Some(_) = iter.next() {} + + let mut iter = 1..20; + while let None = iter.next() {} // this is fine (if nonsensical) + + let mut iter = 1..20; + if let Some(x) = iter.next() { + // also fine + println!("{}", x) + } + + // the following shouldn't warn because it can't be written with a for loop + let mut iter = 1u32..20; + while let Some(_) = iter.next() { + println!("next: {:?}", iter.next()) + } + + // neither can this + let mut iter = 1u32..20; + while let Some(_) = iter.next() { + println!("next: {:?}", iter.next()); + } + + // or this + let mut iter = 1u32..20; + while let Some(_) = iter.next() { + break; + } + println!("Remaining iter {:?}", iter); + + // or this + let mut iter = 1u32..20; + while let Some(_) = iter.next() { + iter = 1..20; + } +} + +// Issue #1188 +fn refutable() { + let a = [42, 1337]; + let mut b = a.iter(); + + // consume all the 42s + while let Some(&42) = b.next() {} + + let a = [(1, 2, 3)]; + let mut b = a.iter(); + + while let Some(&(1, 2, 3)) = b.next() {} + + let a = [Some(42)]; + let mut b = a.iter(); + + while let Some(&None) = b.next() {} + + /* This gives “refutable pattern in `for` loop binding: `&_` not covered” + for &42 in b {} + for &(1, 2, 3) in b {} + for &Option::None in b.next() {} + // */ +} + +fn nested_loops() { + let a = [42, 1337]; + let mut y = a.iter(); + loop { + // x is reused, so don't lint here + while let Some(_) = y.next() {} + } + + let mut y = a.iter(); + for _ in 0..2 { + while let Some(_) = y.next() { + // y is reused, don't lint + } + } + + loop { + let mut y = a.iter(); + while let Some(_) = y.next() { + // use a for loop here + } + } +} + +fn issue1121() { + use std::collections::HashSet; + let mut values = HashSet::new(); + values.insert(1); + + while let Some(&value) = values.iter().next() { + values.remove(&value); + } +} + +fn issue2965() { + // This should not cause an ICE and suggest: + // + // for _ in values.iter() {} + // + use std::collections::HashSet; + let mut values = HashSet::new(); + values.insert(1); + + while let Some(..) = values.iter().next() {} +} + +fn issue3670() { + let array = [Some(0), None, Some(1)]; + let mut iter = array.iter(); + + while let Some(elem) = iter.next() { + let _ = elem.or_else(|| *iter.next()?); + } +} + +fn issue1654() { + // should not lint if the iterator is generated on every iteration + use std::collections::HashSet; + let mut values = HashSet::new(); + values.insert(1); + + while let Some(..) = values.iter().next() { + values.remove(&1); + } + + while let Some(..) = values.iter().map(|x| x + 1).next() {} + + let chars = "Hello, World!".char_indices(); + while let Some((i, ch)) = chars.clone().next() { + println!("{}: {}", i, ch); + } +} + +fn main() { + base(); + refutable(); + nested_loops(); + issue1121(); + issue2965(); + issue3670(); + issue1654(); +} diff --git a/src/tools/clippy/tests/ui/while_let_on_iterator.stderr b/src/tools/clippy/tests/ui/while_let_on_iterator.stderr new file mode 100644 index 00000000000..6de138d7227 --- /dev/null +++ b/src/tools/clippy/tests/ui/while_let_on_iterator.stderr @@ -0,0 +1,28 @@ +error: this loop could be written as a `for` loop + --> $DIR/while_let_on_iterator.rs:8:5 + | +LL | while let Option::Some(x) = iter.next() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in iter` + | + = note: `-D clippy::while-let-on-iterator` implied by `-D warnings` + +error: this loop could be written as a `for` loop + --> $DIR/while_let_on_iterator.rs:13:5 + | +LL | while let Some(x) = iter.next() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in iter` + +error: this loop could be written as a `for` loop + --> $DIR/while_let_on_iterator.rs:18:5 + | +LL | while let Some(_) = iter.next() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in iter` + +error: this loop could be written as a `for` loop + --> $DIR/while_let_on_iterator.rs:97:9 + | +LL | while let Some(_) = y.next() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in y` + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/wild_in_or_pats.rs b/src/tools/clippy/tests/ui/wild_in_or_pats.rs new file mode 100644 index 00000000000..ad600f12577 --- /dev/null +++ b/src/tools/clippy/tests/ui/wild_in_or_pats.rs @@ -0,0 +1,36 @@ +#![warn(clippy::wildcard_in_or_patterns)] + +fn main() { + match "foo" { + "a" => { + dbg!("matched a"); + }, + "bar" | _ => { + dbg!("matched (bar or) wild"); + }, + }; + match "foo" { + "a" => { + dbg!("matched a"); + }, + "bar" | "bar2" | _ => { + dbg!("matched (bar or bar2 or) wild"); + }, + }; + match "foo" { + "a" => { + dbg!("matched a"); + }, + _ | "bar" | _ => { + dbg!("matched (bar or) wild"); + }, + }; + match "foo" { + "a" => { + dbg!("matched a"); + }, + _ | "bar" => { + dbg!("matched (bar or) wild"); + }, + }; +} diff --git a/src/tools/clippy/tests/ui/wild_in_or_pats.stderr b/src/tools/clippy/tests/ui/wild_in_or_pats.stderr new file mode 100644 index 00000000000..33c34cbbd40 --- /dev/null +++ b/src/tools/clippy/tests/ui/wild_in_or_pats.stderr @@ -0,0 +1,35 @@ +error: wildcard pattern covers any other pattern as it will match anyway. + --> $DIR/wild_in_or_pats.rs:8:9 + | +LL | "bar" | _ => { + | ^^^^^^^^^ + | + = note: `-D clippy::wildcard-in-or-patterns` implied by `-D warnings` + = help: Consider handling `_` separately. + +error: wildcard pattern covers any other pattern as it will match anyway. + --> $DIR/wild_in_or_pats.rs:16:9 + | +LL | "bar" | "bar2" | _ => { + | ^^^^^^^^^^^^^^^^^^ + | + = help: Consider handling `_` separately. + +error: wildcard pattern covers any other pattern as it will match anyway. + --> $DIR/wild_in_or_pats.rs:24:9 + | +LL | _ | "bar" | _ => { + | ^^^^^^^^^^^^^ + | + = help: Consider handling `_` separately. + +error: wildcard pattern covers any other pattern as it will match anyway. + --> $DIR/wild_in_or_pats.rs:32:9 + | +LL | _ | "bar" => { + | ^^^^^^^^^ + | + = help: Consider handling `_` separately. + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/wildcard_enum_match_arm.fixed b/src/tools/clippy/tests/ui/wildcard_enum_match_arm.fixed new file mode 100644 index 00000000000..2aa24ea1156 --- /dev/null +++ b/src/tools/clippy/tests/ui/wildcard_enum_match_arm.fixed @@ -0,0 +1,101 @@ +// run-rustfix + +#![deny(clippy::wildcard_enum_match_arm)] +#![allow( + unreachable_code, + unused_variables, + dead_code, + clippy::single_match, + clippy::wildcard_in_or_patterns +)] + +use std::io::ErrorKind; + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +enum Color { + Red, + Green, + Blue, + Rgb(u8, u8, u8), + Cyan, +} + +impl Color { + fn is_monochrome(self) -> bool { + match self { + Color::Red | Color::Green | Color::Blue => true, + Color::Rgb(r, g, b) => r | g == 0 || r | b == 0 || g | b == 0, + Color::Cyan => false, + } + } +} + +fn main() { + let color = Color::Rgb(0, 0, 127); + match color { + Color::Red => println!("Red"), + Color::Green | Color::Blue | Color::Rgb(..) | Color::Cyan => eprintln!("Not red"), + }; + match color { + Color::Red => println!("Red"), + _not_red @ Color::Green | _not_red @ Color::Blue | _not_red @ Color::Rgb(..) | _not_red @ Color::Cyan => eprintln!("Not red"), + }; + let _str = match color { + Color::Red => "Red".to_owned(), + not_red @ Color::Green | not_red @ Color::Blue | not_red @ Color::Rgb(..) | not_red @ Color::Cyan => format!("{:?}", not_red), + }; + match color { + Color::Red => {}, + Color::Green => {}, + Color::Blue => {}, + Color::Cyan => {}, + c if c.is_monochrome() => {}, + Color::Rgb(_, _, _) => {}, + }; + let _str = match color { + Color::Red => "Red", + c @ Color::Green | c @ Color::Blue | c @ Color::Rgb(_, _, _) | c @ Color::Cyan => "Not red", + }; + match color { + Color::Rgb(r, _, _) if r > 0 => "Some red", + Color::Red | Color::Green | Color::Blue | Color::Rgb(..) | Color::Cyan => "No red", + }; + match color { + Color::Red | Color::Green | Color::Blue | Color::Cyan => {}, + Color::Rgb(..) => {}, + }; + let x: u8 = unimplemented!(); + match x { + 0 => {}, + 140 => {}, + _ => {}, + }; + // We need to use an enum not defined in this test because non_exhaustive is ignored for the + // purposes of dead code analysis within a crate. + let error_kind = ErrorKind::NotFound; + match error_kind { + ErrorKind::NotFound => {}, + std::io::ErrorKind::PermissionDenied | std::io::ErrorKind::ConnectionRefused | std::io::ErrorKind::ConnectionReset | std::io::ErrorKind::ConnectionAborted | std::io::ErrorKind::NotConnected | std::io::ErrorKind::AddrInUse | std::io::ErrorKind::AddrNotAvailable | std::io::ErrorKind::BrokenPipe | std::io::ErrorKind::AlreadyExists | std::io::ErrorKind::WouldBlock | std::io::ErrorKind::InvalidInput | std::io::ErrorKind::InvalidData | std::io::ErrorKind::TimedOut | std::io::ErrorKind::WriteZero | std::io::ErrorKind::Interrupted | std::io::ErrorKind::Other | std::io::ErrorKind::UnexpectedEof | _ => {}, + } + match error_kind { + ErrorKind::NotFound => {}, + ErrorKind::PermissionDenied => {}, + ErrorKind::ConnectionRefused => {}, + ErrorKind::ConnectionReset => {}, + ErrorKind::ConnectionAborted => {}, + ErrorKind::NotConnected => {}, + ErrorKind::AddrInUse => {}, + ErrorKind::AddrNotAvailable => {}, + ErrorKind::BrokenPipe => {}, + ErrorKind::AlreadyExists => {}, + ErrorKind::WouldBlock => {}, + ErrorKind::InvalidInput => {}, + ErrorKind::InvalidData => {}, + ErrorKind::TimedOut => {}, + ErrorKind::WriteZero => {}, + ErrorKind::Interrupted => {}, + ErrorKind::Other => {}, + ErrorKind::UnexpectedEof => {}, + _ => {}, + } +} diff --git a/src/tools/clippy/tests/ui/wildcard_enum_match_arm.rs b/src/tools/clippy/tests/ui/wildcard_enum_match_arm.rs new file mode 100644 index 00000000000..07c93feaf28 --- /dev/null +++ b/src/tools/clippy/tests/ui/wildcard_enum_match_arm.rs @@ -0,0 +1,101 @@ +// run-rustfix + +#![deny(clippy::wildcard_enum_match_arm)] +#![allow( + unreachable_code, + unused_variables, + dead_code, + clippy::single_match, + clippy::wildcard_in_or_patterns +)] + +use std::io::ErrorKind; + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +enum Color { + Red, + Green, + Blue, + Rgb(u8, u8, u8), + Cyan, +} + +impl Color { + fn is_monochrome(self) -> bool { + match self { + Color::Red | Color::Green | Color::Blue => true, + Color::Rgb(r, g, b) => r | g == 0 || r | b == 0 || g | b == 0, + Color::Cyan => false, + } + } +} + +fn main() { + let color = Color::Rgb(0, 0, 127); + match color { + Color::Red => println!("Red"), + _ => eprintln!("Not red"), + }; + match color { + Color::Red => println!("Red"), + _not_red => eprintln!("Not red"), + }; + let _str = match color { + Color::Red => "Red".to_owned(), + not_red => format!("{:?}", not_red), + }; + match color { + Color::Red => {}, + Color::Green => {}, + Color::Blue => {}, + Color::Cyan => {}, + c if c.is_monochrome() => {}, + Color::Rgb(_, _, _) => {}, + }; + let _str = match color { + Color::Red => "Red", + c @ Color::Green | c @ Color::Blue | c @ Color::Rgb(_, _, _) | c @ Color::Cyan => "Not red", + }; + match color { + Color::Rgb(r, _, _) if r > 0 => "Some red", + _ => "No red", + }; + match color { + Color::Red | Color::Green | Color::Blue | Color::Cyan => {}, + Color::Rgb(..) => {}, + }; + let x: u8 = unimplemented!(); + match x { + 0 => {}, + 140 => {}, + _ => {}, + }; + // We need to use an enum not defined in this test because non_exhaustive is ignored for the + // purposes of dead code analysis within a crate. + let error_kind = ErrorKind::NotFound; + match error_kind { + ErrorKind::NotFound => {}, + _ => {}, + } + match error_kind { + ErrorKind::NotFound => {}, + ErrorKind::PermissionDenied => {}, + ErrorKind::ConnectionRefused => {}, + ErrorKind::ConnectionReset => {}, + ErrorKind::ConnectionAborted => {}, + ErrorKind::NotConnected => {}, + ErrorKind::AddrInUse => {}, + ErrorKind::AddrNotAvailable => {}, + ErrorKind::BrokenPipe => {}, + ErrorKind::AlreadyExists => {}, + ErrorKind::WouldBlock => {}, + ErrorKind::InvalidInput => {}, + ErrorKind::InvalidData => {}, + ErrorKind::TimedOut => {}, + ErrorKind::WriteZero => {}, + ErrorKind::Interrupted => {}, + ErrorKind::Other => {}, + ErrorKind::UnexpectedEof => {}, + _ => {}, + } +} diff --git a/src/tools/clippy/tests/ui/wildcard_enum_match_arm.stderr b/src/tools/clippy/tests/ui/wildcard_enum_match_arm.stderr new file mode 100644 index 00000000000..7151a5c5770 --- /dev/null +++ b/src/tools/clippy/tests/ui/wildcard_enum_match_arm.stderr @@ -0,0 +1,38 @@ +error: wildcard match will miss any future added variants + --> $DIR/wildcard_enum_match_arm.rs:37:9 + | +LL | _ => eprintln!("Not red"), + | ^ help: try this: `Color::Green | Color::Blue | Color::Rgb(..) | Color::Cyan` + | +note: the lint level is defined here + --> $DIR/wildcard_enum_match_arm.rs:3:9 + | +LL | #![deny(clippy::wildcard_enum_match_arm)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: wildcard match will miss any future added variants + --> $DIR/wildcard_enum_match_arm.rs:41:9 + | +LL | _not_red => eprintln!("Not red"), + | ^^^^^^^^ help: try this: `_not_red @ Color::Green | _not_red @ Color::Blue | _not_red @ Color::Rgb(..) | _not_red @ Color::Cyan` + +error: wildcard match will miss any future added variants + --> $DIR/wildcard_enum_match_arm.rs:45:9 + | +LL | not_red => format!("{:?}", not_red), + | ^^^^^^^ help: try this: `not_red @ Color::Green | not_red @ Color::Blue | not_red @ Color::Rgb(..) | not_red @ Color::Cyan` + +error: wildcard match will miss any future added variants + --> $DIR/wildcard_enum_match_arm.rs:61:9 + | +LL | _ => "No red", + | ^ help: try this: `Color::Red | Color::Green | Color::Blue | Color::Rgb(..) | Color::Cyan` + +error: match on non-exhaustive enum doesn't explicitly match all known variants + --> $DIR/wildcard_enum_match_arm.rs:78:9 + | +LL | _ => {}, + | ^ help: try this: `std::io::ErrorKind::PermissionDenied | std::io::ErrorKind::ConnectionRefused | std::io::ErrorKind::ConnectionReset | std::io::ErrorKind::ConnectionAborted | std::io::ErrorKind::NotConnected | std::io::ErrorKind::AddrInUse | std::io::ErrorKind::AddrNotAvailable | std::io::ErrorKind::BrokenPipe | std::io::ErrorKind::AlreadyExists | std::io::ErrorKind::WouldBlock | std::io::ErrorKind::InvalidInput | std::io::ErrorKind::InvalidData | std::io::ErrorKind::TimedOut | std::io::ErrorKind::WriteZero | std::io::ErrorKind::Interrupted | std::io::ErrorKind::Other | std::io::ErrorKind::UnexpectedEof | _` + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/wildcard_imports.fixed b/src/tools/clippy/tests/ui/wildcard_imports.fixed new file mode 100644 index 00000000000..ed6cc00ef04 --- /dev/null +++ b/src/tools/clippy/tests/ui/wildcard_imports.fixed @@ -0,0 +1,157 @@ +// run-rustfix +// aux-build:wildcard_imports_helper.rs + +#![warn(clippy::wildcard_imports)] +//#![allow(clippy::redundant_pub_crate)] +#![allow(unused)] +#![warn(unused_imports)] + +extern crate wildcard_imports_helper; + +use crate::fn_mod::foo; +use crate::mod_mod::inner_mod; +use crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod}; +#[macro_use] +use crate::struct_mod::{A, inner_struct_mod}; + +#[allow(unused_imports)] +use wildcard_imports_helper::inner::inner_for_self_import; +use wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar; +use wildcard_imports_helper::{ExternA, extern_foo}; + +use std::io::prelude::*; + +struct ReadFoo; + +impl Read for ReadFoo { + fn read(&mut self, _buf: &mut [u8]) -> std::io::Result<usize> { + Ok(0) + } +} + +mod fn_mod { + pub fn foo() {} +} + +mod mod_mod { + pub mod inner_mod { + pub fn foo() {} + } +} + +mod multi_fn_mod { + pub fn multi_foo() {} + pub fn multi_bar() {} + pub fn multi_baz() {} + pub mod multi_inner_mod { + pub fn foo() {} + } +} + +mod struct_mod { + pub struct A; + pub struct B; + pub mod inner_struct_mod { + pub struct C; + } + + #[macro_export] + macro_rules! double_struct_import_test { + () => { + let _ = A; + }; + } +} + +fn main() { + foo(); + multi_foo(); + multi_bar(); + multi_inner_mod::foo(); + inner_mod::foo(); + extern_foo(); + inner_extern_bar(); + + let _ = A; + let _ = inner_struct_mod::C; + let _ = ExternA; + + double_struct_import_test!(); + double_struct_import_test!(); +} + +mod in_fn_test { + pub use self::inner_exported::*; + #[allow(unused_imports)] + pub(crate) use self::inner_exported2::*; + + fn test_intern() { + use crate::fn_mod::foo; + + foo(); + } + + fn test_extern() { + use wildcard_imports_helper::inner::inner_for_self_import::{self, inner_extern_foo}; + use wildcard_imports_helper::{ExternA, extern_foo}; + + inner_for_self_import::inner_extern_foo(); + inner_extern_foo(); + + extern_foo(); + + let _ = ExternA; + } + + fn test_inner_nested() { + use self::{inner::inner_foo, inner2::inner_bar}; + + inner_foo(); + inner_bar(); + } + + fn test_extern_reexported() { + use wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}; + + extern_exported(); + let _ = ExternExportedStruct; + let _ = ExternExportedEnum::A; + } + + mod inner_exported { + pub fn exported() {} + pub struct ExportedStruct; + pub enum ExportedEnum { + A, + } + } + + mod inner_exported2 { + pub(crate) fn exported2() {} + } + + mod inner { + pub fn inner_foo() {} + } + + mod inner2 { + pub fn inner_bar() {} + } +} + +fn test_reexported() { + use crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}; + + exported(); + let _ = ExportedStruct; + let _ = ExportedEnum::A; +} + +#[rustfmt::skip] +fn test_weird_formatting() { + use crate:: in_fn_test::exported; + use crate:: fn_mod::foo; + + exported(); + foo(); +} diff --git a/src/tools/clippy/tests/ui/wildcard_imports.rs b/src/tools/clippy/tests/ui/wildcard_imports.rs new file mode 100644 index 00000000000..c6d6efaece8 --- /dev/null +++ b/src/tools/clippy/tests/ui/wildcard_imports.rs @@ -0,0 +1,158 @@ +// run-rustfix +// aux-build:wildcard_imports_helper.rs + +#![warn(clippy::wildcard_imports)] +//#![allow(clippy::redundant_pub_crate)] +#![allow(unused)] +#![warn(unused_imports)] + +extern crate wildcard_imports_helper; + +use crate::fn_mod::*; +use crate::mod_mod::*; +use crate::multi_fn_mod::*; +#[macro_use] +use crate::struct_mod::*; + +#[allow(unused_imports)] +use wildcard_imports_helper::inner::inner_for_self_import; +use wildcard_imports_helper::inner::inner_for_self_import::*; +use wildcard_imports_helper::*; + +use std::io::prelude::*; + +struct ReadFoo; + +impl Read for ReadFoo { + fn read(&mut self, _buf: &mut [u8]) -> std::io::Result<usize> { + Ok(0) + } +} + +mod fn_mod { + pub fn foo() {} +} + +mod mod_mod { + pub mod inner_mod { + pub fn foo() {} + } +} + +mod multi_fn_mod { + pub fn multi_foo() {} + pub fn multi_bar() {} + pub fn multi_baz() {} + pub mod multi_inner_mod { + pub fn foo() {} + } +} + +mod struct_mod { + pub struct A; + pub struct B; + pub mod inner_struct_mod { + pub struct C; + } + + #[macro_export] + macro_rules! double_struct_import_test { + () => { + let _ = A; + }; + } +} + +fn main() { + foo(); + multi_foo(); + multi_bar(); + multi_inner_mod::foo(); + inner_mod::foo(); + extern_foo(); + inner_extern_bar(); + + let _ = A; + let _ = inner_struct_mod::C; + let _ = ExternA; + + double_struct_import_test!(); + double_struct_import_test!(); +} + +mod in_fn_test { + pub use self::inner_exported::*; + #[allow(unused_imports)] + pub(crate) use self::inner_exported2::*; + + fn test_intern() { + use crate::fn_mod::*; + + foo(); + } + + fn test_extern() { + use wildcard_imports_helper::inner::inner_for_self_import::{self, *}; + use wildcard_imports_helper::*; + + inner_for_self_import::inner_extern_foo(); + inner_extern_foo(); + + extern_foo(); + + let _ = ExternA; + } + + fn test_inner_nested() { + use self::{inner::*, inner2::*}; + + inner_foo(); + inner_bar(); + } + + fn test_extern_reexported() { + use wildcard_imports_helper::*; + + extern_exported(); + let _ = ExternExportedStruct; + let _ = ExternExportedEnum::A; + } + + mod inner_exported { + pub fn exported() {} + pub struct ExportedStruct; + pub enum ExportedEnum { + A, + } + } + + mod inner_exported2 { + pub(crate) fn exported2() {} + } + + mod inner { + pub fn inner_foo() {} + } + + mod inner2 { + pub fn inner_bar() {} + } +} + +fn test_reexported() { + use crate::in_fn_test::*; + + exported(); + let _ = ExportedStruct; + let _ = ExportedEnum::A; +} + +#[rustfmt::skip] +fn test_weird_formatting() { + use crate:: in_fn_test:: * ; + use crate:: fn_mod:: + *; + + exported(); + foo(); +} diff --git a/src/tools/clippy/tests/ui/wildcard_imports.stderr b/src/tools/clippy/tests/ui/wildcard_imports.stderr new file mode 100644 index 00000000000..050e4c6304f --- /dev/null +++ b/src/tools/clippy/tests/ui/wildcard_imports.stderr @@ -0,0 +1,96 @@ +error: usage of wildcard import + --> $DIR/wildcard_imports.rs:11:5 + | +LL | use crate::fn_mod::*; + | ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo` + | + = note: `-D clippy::wildcard-imports` implied by `-D warnings` + +error: usage of wildcard import + --> $DIR/wildcard_imports.rs:12:5 + | +LL | use crate::mod_mod::*; + | ^^^^^^^^^^^^^^^^^ help: try: `crate::mod_mod::inner_mod` + +error: usage of wildcard import + --> $DIR/wildcard_imports.rs:13:5 + | +LL | use crate::multi_fn_mod::*; + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod}` + +error: usage of wildcard import + --> $DIR/wildcard_imports.rs:15:5 + | +LL | use crate::struct_mod::*; + | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::struct_mod::{A, inner_struct_mod}` + +error: usage of wildcard import + --> $DIR/wildcard_imports.rs:19:5 + | +LL | use wildcard_imports_helper::inner::inner_for_self_import::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar` + +error: usage of wildcard import + --> $DIR/wildcard_imports.rs:20:5 + | +LL | use wildcard_imports_helper::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` + +error: usage of wildcard import + --> $DIR/wildcard_imports.rs:89:13 + | +LL | use crate::fn_mod::*; + | ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo` + +error: usage of wildcard import + --> $DIR/wildcard_imports.rs:95:75 + | +LL | use wildcard_imports_helper::inner::inner_for_self_import::{self, *}; + | ^ help: try: `inner_extern_foo` + +error: usage of wildcard import + --> $DIR/wildcard_imports.rs:96:13 + | +LL | use wildcard_imports_helper::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` + +error: usage of wildcard import + --> $DIR/wildcard_imports.rs:107:20 + | +LL | use self::{inner::*, inner2::*}; + | ^^^^^^^^ help: try: `inner::inner_foo` + +error: usage of wildcard import + --> $DIR/wildcard_imports.rs:107:30 + | +LL | use self::{inner::*, inner2::*}; + | ^^^^^^^^^ help: try: `inner2::inner_bar` + +error: usage of wildcard import + --> $DIR/wildcard_imports.rs:114:13 + | +LL | use wildcard_imports_helper::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}` + +error: usage of wildcard import + --> $DIR/wildcard_imports.rs:143:9 + | +LL | use crate::in_fn_test::*; + | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}` + +error: usage of wildcard import + --> $DIR/wildcard_imports.rs:152:9 + | +LL | use crate:: in_fn_test:: * ; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate:: in_fn_test::exported` + +error: usage of wildcard import + --> $DIR/wildcard_imports.rs:153:9 + | +LL | use crate:: fn_mod:: + | _________^ +LL | | *; + | |_________^ help: try: `crate:: fn_mod::foo` + +error: aborting due to 15 previous errors + diff --git a/src/tools/clippy/tests/ui/write_literal.rs b/src/tools/clippy/tests/ui/write_literal.rs new file mode 100644 index 00000000000..d8205c5eb67 --- /dev/null +++ b/src/tools/clippy/tests/ui/write_literal.rs @@ -0,0 +1,43 @@ +#![allow(unused_must_use)] +#![warn(clippy::write_literal)] + +use std::io::Write; + +fn main() { + let mut v = Vec::new(); + + // these should be fine + write!(&mut v, "Hello"); + writeln!(&mut v, "Hello"); + let world = "world"; + writeln!(&mut v, "Hello {}", world); + writeln!(&mut v, "Hello {world}", world = world); + writeln!(&mut v, "3 in hex is {:X}", 3); + writeln!(&mut v, "2 + 1 = {:.4}", 3); + writeln!(&mut v, "2 + 1 = {:5.4}", 3); + writeln!(&mut v, "Debug test {:?}", "hello, world"); + writeln!(&mut v, "{0:8} {1:>8}", "hello", "world"); + writeln!(&mut v, "{1:8} {0:>8}", "hello", "world"); + writeln!(&mut v, "{foo:8} {bar:>8}", foo = "hello", bar = "world"); + writeln!(&mut v, "{bar:8} {foo:>8}", foo = "hello", bar = "world"); + writeln!(&mut v, "{number:>width$}", number = 1, width = 6); + writeln!(&mut v, "{number:>0width$}", number = 1, width = 6); + + // these should throw warnings + writeln!(&mut v, "{} of {:b} people know binary, the other half doesn't", 1, 2); + write!(&mut v, "Hello {}", "world"); + writeln!(&mut v, "Hello {} {}", world, "world"); + writeln!(&mut v, "Hello {}", "world"); + writeln!(&mut v, "10 / 4 is {}", 2.5); + writeln!(&mut v, "2 + 1 = {}", 3); + + // positional args don't change the fact + // that we're using a literal -- this should + // throw a warning + writeln!(&mut v, "{0} {1}", "hello", "world"); + writeln!(&mut v, "{1} {0}", "hello", "world"); + + // named args shouldn't change anything either + writeln!(&mut v, "{foo} {bar}", foo = "hello", bar = "world"); + writeln!(&mut v, "{bar} {foo}", foo = "hello", bar = "world"); +} diff --git a/src/tools/clippy/tests/ui/write_literal.stderr b/src/tools/clippy/tests/ui/write_literal.stderr new file mode 100644 index 00000000000..54a787fe555 --- /dev/null +++ b/src/tools/clippy/tests/ui/write_literal.stderr @@ -0,0 +1,88 @@ +error: literal with an empty format string + --> $DIR/write_literal.rs:27:79 + | +LL | writeln!(&mut v, "{} of {:b} people know binary, the other half doesn't", 1, 2); + | ^ + | + = note: `-D clippy::write-literal` implied by `-D warnings` + +error: literal with an empty format string + --> $DIR/write_literal.rs:28:32 + | +LL | write!(&mut v, "Hello {}", "world"); + | ^^^^^^^ + +error: literal with an empty format string + --> $DIR/write_literal.rs:29:44 + | +LL | writeln!(&mut v, "Hello {} {}", world, "world"); + | ^^^^^^^ + +error: literal with an empty format string + --> $DIR/write_literal.rs:30:34 + | +LL | writeln!(&mut v, "Hello {}", "world"); + | ^^^^^^^ + +error: literal with an empty format string + --> $DIR/write_literal.rs:31:38 + | +LL | writeln!(&mut v, "10 / 4 is {}", 2.5); + | ^^^ + +error: literal with an empty format string + --> $DIR/write_literal.rs:32:36 + | +LL | writeln!(&mut v, "2 + 1 = {}", 3); + | ^ + +error: literal with an empty format string + --> $DIR/write_literal.rs:37:33 + | +LL | writeln!(&mut v, "{0} {1}", "hello", "world"); + | ^^^^^^^ + +error: literal with an empty format string + --> $DIR/write_literal.rs:37:42 + | +LL | writeln!(&mut v, "{0} {1}", "hello", "world"); + | ^^^^^^^ + +error: literal with an empty format string + --> $DIR/write_literal.rs:38:33 + | +LL | writeln!(&mut v, "{1} {0}", "hello", "world"); + | ^^^^^^^ + +error: literal with an empty format string + --> $DIR/write_literal.rs:38:42 + | +LL | writeln!(&mut v, "{1} {0}", "hello", "world"); + | ^^^^^^^ + +error: literal with an empty format string + --> $DIR/write_literal.rs:41:43 + | +LL | writeln!(&mut v, "{foo} {bar}", foo = "hello", bar = "world"); + | ^^^^^^^ + +error: literal with an empty format string + --> $DIR/write_literal.rs:41:58 + | +LL | writeln!(&mut v, "{foo} {bar}", foo = "hello", bar = "world"); + | ^^^^^^^ + +error: literal with an empty format string + --> $DIR/write_literal.rs:42:43 + | +LL | writeln!(&mut v, "{bar} {foo}", foo = "hello", bar = "world"); + | ^^^^^^^ + +error: literal with an empty format string + --> $DIR/write_literal.rs:42:58 + | +LL | writeln!(&mut v, "{bar} {foo}", foo = "hello", bar = "world"); + | ^^^^^^^ + +error: aborting due to 14 previous errors + diff --git a/src/tools/clippy/tests/ui/write_with_newline.rs b/src/tools/clippy/tests/ui/write_with_newline.rs new file mode 100644 index 00000000000..93afd73d111 --- /dev/null +++ b/src/tools/clippy/tests/ui/write_with_newline.rs @@ -0,0 +1,58 @@ +// FIXME: Ideally these suggestions would be fixed via rustfix. Blocked by rust-lang/rust#53934 +// // run-rustfix + +#![allow(clippy::write_literal)] +#![warn(clippy::write_with_newline)] + +use std::io::Write; + +fn main() { + let mut v = Vec::new(); + + // These should fail + write!(&mut v, "Hello\n"); + write!(&mut v, "Hello {}\n", "world"); + write!(&mut v, "Hello {} {}\n", "world", "#2"); + write!(&mut v, "{}\n", 1265); + + // These should be fine + write!(&mut v, ""); + write!(&mut v, "Hello"); + writeln!(&mut v, "Hello"); + writeln!(&mut v, "Hello\n"); + writeln!(&mut v, "Hello {}\n", "world"); + write!(&mut v, "Issue\n{}", 1265); + write!(&mut v, "{}", 1265); + write!(&mut v, "\n{}", 1275); + write!(&mut v, "\n\n"); + write!(&mut v, "like eof\n\n"); + write!(&mut v, "Hello {} {}\n\n", "world", "#2"); + writeln!(&mut v, "\ndon't\nwarn\nfor\nmultiple\nnewlines\n"); // #3126 + writeln!(&mut v, "\nbla\n\n"); // #3126 + + // Escaping + write!(&mut v, "\\n"); // #3514 + write!(&mut v, "\\\n"); // should fail + write!(&mut v, "\\\\n"); + + // Raw strings + write!(&mut v, r"\n"); // #3778 + + // Literal newlines should also fail + write!( + &mut v, + " +" + ); + write!( + &mut v, + r" +" + ); + + // Don't warn on CRLF (#4208) + write!(&mut v, "\r\n"); + write!(&mut v, "foo\r\n"); + write!(&mut v, "\\r\n"); //~ ERROR + write!(&mut v, "foo\rbar\n"); +} diff --git a/src/tools/clippy/tests/ui/write_with_newline.stderr b/src/tools/clippy/tests/ui/write_with_newline.stderr new file mode 100644 index 00000000000..2473329ca72 --- /dev/null +++ b/src/tools/clippy/tests/ui/write_with_newline.stderr @@ -0,0 +1,114 @@ +error: using `write!()` with a format string that ends in a single newline + --> $DIR/write_with_newline.rs:13:5 + | +LL | write!(&mut v, "Hello/n"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::write-with-newline` implied by `-D warnings` +help: use `writeln!()` instead + | +LL | writeln!(&mut v, "Hello"); + | ^^^^^^^ -- + +error: using `write!()` with a format string that ends in a single newline + --> $DIR/write_with_newline.rs:14:5 + | +LL | write!(&mut v, "Hello {}/n", "world"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `writeln!()` instead + | +LL | writeln!(&mut v, "Hello {}", "world"); + | ^^^^^^^ -- + +error: using `write!()` with a format string that ends in a single newline + --> $DIR/write_with_newline.rs:15:5 + | +LL | write!(&mut v, "Hello {} {}/n", "world", "#2"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `writeln!()` instead + | +LL | writeln!(&mut v, "Hello {} {}", "world", "#2"); + | ^^^^^^^ -- + +error: using `write!()` with a format string that ends in a single newline + --> $DIR/write_with_newline.rs:16:5 + | +LL | write!(&mut v, "{}/n", 1265); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `writeln!()` instead + | +LL | writeln!(&mut v, "{}", 1265); + | ^^^^^^^ -- + +error: using `write!()` with a format string that ends in a single newline + --> $DIR/write_with_newline.rs:35:5 + | +LL | write!(&mut v, "//n"); // should fail + | ^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `writeln!()` instead + | +LL | writeln!(&mut v, "/"); // should fail + | ^^^^^^^ -- + +error: using `write!()` with a format string that ends in a single newline + --> $DIR/write_with_newline.rs:42:5 + | +LL | / write!( +LL | | &mut v, +LL | | " +LL | | " +LL | | ); + | |_____^ + | +help: use `writeln!()` instead + | +LL | writeln!( +LL | &mut v, +LL | "" + | + +error: using `write!()` with a format string that ends in a single newline + --> $DIR/write_with_newline.rs:47:5 + | +LL | / write!( +LL | | &mut v, +LL | | r" +LL | | " +LL | | ); + | |_____^ + | +help: use `writeln!()` instead + | +LL | writeln!( +LL | &mut v, +LL | r"" + | + +error: using `write!()` with a format string that ends in a single newline + --> $DIR/write_with_newline.rs:56:5 + | +LL | write!(&mut v, "/r/n"); //~ ERROR + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `writeln!()` instead + | +LL | writeln!(&mut v, "/r"); //~ ERROR + | ^^^^^^^ -- + +error: using `write!()` with a format string that ends in a single newline + --> $DIR/write_with_newline.rs:57:5 + | +LL | write!(&mut v, "foo/rbar/n"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `writeln!()` instead + | +LL | writeln!(&mut v, "foo/rbar"); + | ^^^^^^^ -- + +error: aborting due to 9 previous errors + diff --git a/src/tools/clippy/tests/ui/writeln_empty_string.fixed b/src/tools/clippy/tests/ui/writeln_empty_string.fixed new file mode 100644 index 00000000000..c3ac15b0375 --- /dev/null +++ b/src/tools/clippy/tests/ui/writeln_empty_string.fixed @@ -0,0 +1,20 @@ +// run-rustfix + +#![allow(unused_must_use)] +#![warn(clippy::writeln_empty_string)] +use std::io::Write; + +fn main() { + let mut v = Vec::new(); + + // These should fail + writeln!(&mut v); + + let mut suggestion = Vec::new(); + writeln!(&mut suggestion); + + // These should be fine + writeln!(&mut v); + writeln!(&mut v, " "); + write!(&mut v, ""); +} diff --git a/src/tools/clippy/tests/ui/writeln_empty_string.rs b/src/tools/clippy/tests/ui/writeln_empty_string.rs new file mode 100644 index 00000000000..9a8894b6c0d --- /dev/null +++ b/src/tools/clippy/tests/ui/writeln_empty_string.rs @@ -0,0 +1,20 @@ +// run-rustfix + +#![allow(unused_must_use)] +#![warn(clippy::writeln_empty_string)] +use std::io::Write; + +fn main() { + let mut v = Vec::new(); + + // These should fail + writeln!(&mut v, ""); + + let mut suggestion = Vec::new(); + writeln!(&mut suggestion, ""); + + // These should be fine + writeln!(&mut v); + writeln!(&mut v, " "); + write!(&mut v, ""); +} diff --git a/src/tools/clippy/tests/ui/writeln_empty_string.stderr b/src/tools/clippy/tests/ui/writeln_empty_string.stderr new file mode 100644 index 00000000000..99635229b3e --- /dev/null +++ b/src/tools/clippy/tests/ui/writeln_empty_string.stderr @@ -0,0 +1,16 @@ +error: using `writeln!(&mut v, "")` + --> $DIR/writeln_empty_string.rs:11:5 + | +LL | writeln!(&mut v, ""); + | ^^^^^^^^^^^^^^^^^^^^ help: replace it with: `writeln!(&mut v)` + | + = note: `-D clippy::writeln-empty-string` implied by `-D warnings` + +error: using `writeln!(&mut suggestion, "")` + --> $DIR/writeln_empty_string.rs:14:5 + | +LL | writeln!(&mut suggestion, ""); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `writeln!(&mut suggestion)` + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/wrong_self_convention.rs b/src/tools/clippy/tests/ui/wrong_self_convention.rs new file mode 100644 index 00000000000..99652ca4470 --- /dev/null +++ b/src/tools/clippy/tests/ui/wrong_self_convention.rs @@ -0,0 +1,77 @@ +#![warn(clippy::wrong_self_convention)] +#![warn(clippy::wrong_pub_self_convention)] +#![allow(dead_code)] + +fn main() {} + +#[derive(Clone, Copy)] +struct Foo; + +impl Foo { + fn as_i32(self) {} + fn as_u32(&self) {} + fn into_i32(self) {} + fn is_i32(self) {} + fn is_u32(&self) {} + fn to_i32(self) {} + fn from_i32(self) {} + + pub fn as_i64(self) {} + pub fn into_i64(self) {} + pub fn is_i64(self) {} + pub fn to_i64(self) {} + pub fn from_i64(self) {} + // check whether the lint can be allowed at the function level + #[allow(clippy::wrong_self_convention)] + pub fn from_cake(self) {} + + fn as_x<F: AsRef<Self>>(_: F) {} + fn as_y<F: AsRef<Foo>>(_: F) {} +} + +struct Bar; + +impl Bar { + fn as_i32(self) {} + fn as_u32(&self) {} + fn into_i32(&self) {} + fn into_u32(self) {} + fn is_i32(self) {} + fn is_u32(&self) {} + fn to_i32(self) {} + fn to_u32(&self) {} + fn from_i32(self) {} + + pub fn as_i64(self) {} + pub fn into_i64(&self) {} + pub fn is_i64(self) {} + pub fn to_i64(self) {} + pub fn from_i64(self) {} + + // test for false positives + fn as_(self) {} + fn into_(&self) {} + fn is_(self) {} + fn to_(self) {} + fn from_(self) {} + fn to_mut(&mut self) {} +} + +// Allow Box<Self>, Rc<Self>, Arc<Self> for methods that take conventionally take Self by value +#[allow(clippy::boxed_local)] +mod issue4293 { + use std::rc::Rc; + use std::sync::Arc; + + struct T; + + impl T { + fn into_s1(self: Box<Self>) {} + fn into_s2(self: Rc<Self>) {} + fn into_s3(self: Arc<Self>) {} + + fn into_t1(self: Box<T>) {} + fn into_t2(self: Rc<T>) {} + fn into_t3(self: Arc<T>) {} + } +} diff --git a/src/tools/clippy/tests/ui/wrong_self_convention.stderr b/src/tools/clippy/tests/ui/wrong_self_convention.stderr new file mode 100644 index 00000000000..0d0eb19cd07 --- /dev/null +++ b/src/tools/clippy/tests/ui/wrong_self_convention.stderr @@ -0,0 +1,76 @@ +error: methods called `from_*` usually take no self; consider choosing a less ambiguous name + --> $DIR/wrong_self_convention.rs:17:17 + | +LL | fn from_i32(self) {} + | ^^^^ + | + = note: `-D clippy::wrong-self-convention` implied by `-D warnings` + +error: methods called `from_*` usually take no self; consider choosing a less ambiguous name + --> $DIR/wrong_self_convention.rs:23:21 + | +LL | pub fn from_i64(self) {} + | ^^^^ + +error: methods called `as_*` usually take self by reference or self by mutable reference; consider choosing a less ambiguous name + --> $DIR/wrong_self_convention.rs:35:15 + | +LL | fn as_i32(self) {} + | ^^^^ + +error: methods called `into_*` usually take self by value; consider choosing a less ambiguous name + --> $DIR/wrong_self_convention.rs:37:17 + | +LL | fn into_i32(&self) {} + | ^^^^^ + +error: methods called `is_*` usually take self by reference or no self; consider choosing a less ambiguous name + --> $DIR/wrong_self_convention.rs:39:15 + | +LL | fn is_i32(self) {} + | ^^^^ + +error: methods called `to_*` usually take self by reference; consider choosing a less ambiguous name + --> $DIR/wrong_self_convention.rs:41:15 + | +LL | fn to_i32(self) {} + | ^^^^ + +error: methods called `from_*` usually take no self; consider choosing a less ambiguous name + --> $DIR/wrong_self_convention.rs:43:17 + | +LL | fn from_i32(self) {} + | ^^^^ + +error: methods called `as_*` usually take self by reference or self by mutable reference; consider choosing a less ambiguous name + --> $DIR/wrong_self_convention.rs:45:19 + | +LL | pub fn as_i64(self) {} + | ^^^^ + +error: methods called `into_*` usually take self by value; consider choosing a less ambiguous name + --> $DIR/wrong_self_convention.rs:46:21 + | +LL | pub fn into_i64(&self) {} + | ^^^^^ + +error: methods called `is_*` usually take self by reference or no self; consider choosing a less ambiguous name + --> $DIR/wrong_self_convention.rs:47:19 + | +LL | pub fn is_i64(self) {} + | ^^^^ + +error: methods called `to_*` usually take self by reference; consider choosing a less ambiguous name + --> $DIR/wrong_self_convention.rs:48:19 + | +LL | pub fn to_i64(self) {} + | ^^^^ + +error: methods called `from_*` usually take no self; consider choosing a less ambiguous name + --> $DIR/wrong_self_convention.rs:49:21 + | +LL | pub fn from_i64(self) {} + | ^^^^ + +error: aborting due to 12 previous errors + diff --git a/src/tools/clippy/tests/ui/zero_div_zero.rs b/src/tools/clippy/tests/ui/zero_div_zero.rs new file mode 100644 index 00000000000..09db130a764 --- /dev/null +++ b/src/tools/clippy/tests/ui/zero_div_zero.rs @@ -0,0 +1,13 @@ +#[allow(unused_variables)] +#[warn(clippy::zero_divided_by_zero)] +fn main() { + let nan = 0.0 / 0.0; + let f64_nan = 0.0 / 0.0f64; + let other_f64_nan = 0.0f64 / 0.0; + let one_more_f64_nan = 0.0f64 / 0.0f64; + let zero = 0.0; + let other_zero = 0.0; + let other_nan = zero / other_zero; // fine - this lint doesn't propegate constants. + let not_nan = 2.0 / 0.0; // not an error: 2/0 = inf + let also_not_nan = 0.0 / 2.0; // not an error: 0/2 = 0 +} diff --git a/src/tools/clippy/tests/ui/zero_div_zero.stderr b/src/tools/clippy/tests/ui/zero_div_zero.stderr new file mode 100644 index 00000000000..d0e88f3c5a5 --- /dev/null +++ b/src/tools/clippy/tests/ui/zero_div_zero.stderr @@ -0,0 +1,61 @@ +error: equal expressions as operands to `/` + --> $DIR/zero_div_zero.rs:4:15 + | +LL | let nan = 0.0 / 0.0; + | ^^^^^^^^^ + | + = note: `#[deny(clippy::eq_op)]` on by default + +error: constant division of `0.0` with `0.0` will always result in NaN + --> $DIR/zero_div_zero.rs:4:15 + | +LL | let nan = 0.0 / 0.0; + | ^^^^^^^^^ + | + = note: `-D clippy::zero-divided-by-zero` implied by `-D warnings` + = help: Consider using `f64::NAN` if you would like a constant representing NaN + +error: equal expressions as operands to `/` + --> $DIR/zero_div_zero.rs:5:19 + | +LL | let f64_nan = 0.0 / 0.0f64; + | ^^^^^^^^^^^^ + +error: constant division of `0.0` with `0.0` will always result in NaN + --> $DIR/zero_div_zero.rs:5:19 + | +LL | let f64_nan = 0.0 / 0.0f64; + | ^^^^^^^^^^^^ + | + = help: Consider using `f64::NAN` if you would like a constant representing NaN + +error: equal expressions as operands to `/` + --> $DIR/zero_div_zero.rs:6:25 + | +LL | let other_f64_nan = 0.0f64 / 0.0; + | ^^^^^^^^^^^^ + +error: constant division of `0.0` with `0.0` will always result in NaN + --> $DIR/zero_div_zero.rs:6:25 + | +LL | let other_f64_nan = 0.0f64 / 0.0; + | ^^^^^^^^^^^^ + | + = help: Consider using `f64::NAN` if you would like a constant representing NaN + +error: equal expressions as operands to `/` + --> $DIR/zero_div_zero.rs:7:28 + | +LL | let one_more_f64_nan = 0.0f64 / 0.0f64; + | ^^^^^^^^^^^^^^^ + +error: constant division of `0.0` with `0.0` will always result in NaN + --> $DIR/zero_div_zero.rs:7:28 + | +LL | let one_more_f64_nan = 0.0f64 / 0.0f64; + | ^^^^^^^^^^^^^^^ + | + = help: Consider using `f64::NAN` if you would like a constant representing NaN + +error: aborting due to 8 previous errors + diff --git a/src/tools/clippy/tests/ui/zero_offset.rs b/src/tools/clippy/tests/ui/zero_offset.rs new file mode 100644 index 00000000000..2de904376ad --- /dev/null +++ b/src/tools/clippy/tests/ui/zero_offset.rs @@ -0,0 +1,12 @@ +fn main() { + unsafe { + let x = &() as *const (); + x.offset(0); + x.wrapping_add(0); + x.sub(0); + x.wrapping_sub(0); + + let y = &1 as *const u8; + y.offset(0); + } +} diff --git a/src/tools/clippy/tests/ui/zero_offset.stderr b/src/tools/clippy/tests/ui/zero_offset.stderr new file mode 100644 index 00000000000..cfcd7de2b3d --- /dev/null +++ b/src/tools/clippy/tests/ui/zero_offset.stderr @@ -0,0 +1,9 @@ +error[E0606]: casting `&i32` as `*const u8` is invalid + --> $DIR/zero_offset.rs:9:17 + | +LL | let y = &1 as *const u8; + | ^^^^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0606`. diff --git a/src/tools/clippy/tests/ui/zero_ptr.fixed b/src/tools/clippy/tests/ui/zero_ptr.fixed new file mode 100644 index 00000000000..489aa4121a3 --- /dev/null +++ b/src/tools/clippy/tests/ui/zero_ptr.fixed @@ -0,0 +1,14 @@ +// run-rustfix +pub fn foo(_const: *const f32, _mut: *mut i64) {} + +fn main() { + let _ = std::ptr::null::<usize>(); + let _ = std::ptr::null_mut::<f64>(); + let _: *const u8 = std::ptr::null(); + + foo(0 as _, 0 as _); + foo(std::ptr::null(), std::ptr::null_mut()); + + let z = 0; + let _ = z as *const usize; // this is currently not caught +} diff --git a/src/tools/clippy/tests/ui/zero_ptr.rs b/src/tools/clippy/tests/ui/zero_ptr.rs new file mode 100644 index 00000000000..c3b55ef9ebd --- /dev/null +++ b/src/tools/clippy/tests/ui/zero_ptr.rs @@ -0,0 +1,14 @@ +// run-rustfix +pub fn foo(_const: *const f32, _mut: *mut i64) {} + +fn main() { + let _ = 0 as *const usize; + let _ = 0 as *mut f64; + let _: *const u8 = 0 as *const _; + + foo(0 as _, 0 as _); + foo(0 as *const _, 0 as *mut _); + + let z = 0; + let _ = z as *const usize; // this is currently not caught +} diff --git a/src/tools/clippy/tests/ui/zero_ptr.stderr b/src/tools/clippy/tests/ui/zero_ptr.stderr new file mode 100644 index 00000000000..4ee5e9a2616 --- /dev/null +++ b/src/tools/clippy/tests/ui/zero_ptr.stderr @@ -0,0 +1,34 @@ +error: `0 as *const _` detected + --> $DIR/zero_ptr.rs:5:13 + | +LL | let _ = 0 as *const usize; + | ^^^^^^^^^^^^^^^^^ help: try: `std::ptr::null::<usize>()` + | + = note: `-D clippy::zero-ptr` implied by `-D warnings` + +error: `0 as *mut _` detected + --> $DIR/zero_ptr.rs:6:13 + | +LL | let _ = 0 as *mut f64; + | ^^^^^^^^^^^^^ help: try: `std::ptr::null_mut::<f64>()` + +error: `0 as *const _` detected + --> $DIR/zero_ptr.rs:7:24 + | +LL | let _: *const u8 = 0 as *const _; + | ^^^^^^^^^^^^^ help: try: `std::ptr::null()` + +error: `0 as *const _` detected + --> $DIR/zero_ptr.rs:10:9 + | +LL | foo(0 as *const _, 0 as *mut _); + | ^^^^^^^^^^^^^ help: try: `std::ptr::null()` + +error: `0 as *mut _` detected + --> $DIR/zero_ptr.rs:10:24 + | +LL | foo(0 as *const _, 0 as *mut _); + | ^^^^^^^^^^^ help: try: `std::ptr::null_mut()` + +error: aborting due to 5 previous errors + |
