From c70f1e0f8f3419f67dc31b5bbbc47db1e829128c Mon Sep 17 00:00:00 2001
From: Peter Jaszkowiak
Date: Fri, 1 Apr 2022 22:36:30 -0600
Subject: [PATCH 001/128] ignore `&x | &y` in unnested_or_patterns
replacing it with `&(x | y)` is actually more characters
---
clippy_lints/src/unnested_or_patterns.rs | 12 ++++---
tests/ui/unnested_or_patterns.fixed | 7 +++--
tests/ui/unnested_or_patterns.rs | 7 +++--
tests/ui/unnested_or_patterns.stderr | 40 ++++++++++++------------
4 files changed, 38 insertions(+), 28 deletions(-)
diff --git a/clippy_lints/src/unnested_or_patterns.rs b/clippy_lints/src/unnested_or_patterns.rs
index 1d4fe9cfd3cf..790ffe618d71 100644
--- a/clippy_lints/src/unnested_or_patterns.rs
+++ b/clippy_lints/src/unnested_or_patterns.rs
@@ -5,7 +5,7 @@ use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::{meets_msrv, msrvs, over};
use rustc_ast::mut_visit::*;
use rustc_ast::ptr::P;
-use rustc_ast::{self as ast, Pat, PatKind, PatKind::*, DUMMY_NODE_ID};
+use rustc_ast::{self as ast, Mutability, Pat, PatKind, PatKind::*, DUMMY_NODE_ID};
use rustc_ast_pretty::pprust;
use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass};
@@ -230,6 +230,10 @@ fn transform_with_focus_on_idx(alternatives: &mut Vec>, focus_idx: usize)
// with which a pattern `C(p_0)` may be formed,
// which we would want to join with other `C(p_j)`s.
Ident(.., None) | Lit(_) | Wild | Path(..) | Range(..) | Rest | MacCall(_)
+ // Skip immutable refs, as grouping them saves few characters,
+ // and almost always requires adding parens (increasing noisiness).
+ // In the case of only two patterns, replacement adds net characters.
+ | Ref(_, Mutability::Not)
// Dealt with elsewhere.
| Or(_) | Paren(_) => false,
// Transform `box x | ... | box y` into `box (x | y)`.
@@ -241,10 +245,10 @@ fn transform_with_focus_on_idx(alternatives: &mut Vec
>, focus_idx: usize)
|k| matches!(k, Box(_)),
|k| always_pat!(k, Box(p) => p),
),
- // Transform `&m x | ... | &m y` into `&m (x | y)`.
- Ref(target, m1) => extend_with_matching(
+ // Transform `&mut x | ... | &mut y` into `&mut (x | y)`.
+ Ref(target, Mutability::Mut) => extend_with_matching(
target, start, alternatives,
- |k| matches!(k, Ref(_, m2) if m1 == m2), // Mutabilities must match.
+ |k| matches!(k, Ref(_, Mutability::Mut)),
|k| always_pat!(k, Ref(p, _) => p),
),
// Transform `b @ p0 | ... b @ p1` into `b @ (p0 | p1)`.
diff --git a/tests/ui/unnested_or_patterns.fixed b/tests/ui/unnested_or_patterns.fixed
index 46463a29e9b2..c223b5bc711b 100644
--- a/tests/ui/unnested_or_patterns.fixed
+++ b/tests/ui/unnested_or_patterns.fixed
@@ -6,10 +6,13 @@
#![allow(unreachable_patterns, irrefutable_let_patterns, unused_variables)]
fn main() {
+ // Should be ignored by this lint, as nesting requires more characters.
+ if let &0 | &2 = &0 {}
+
if let box (0 | 2) = Box::new(0) {}
if let box (0 | 1 | 2 | 3 | 4) = Box::new(0) {}
- const C0: &u8 = &1;
- if let &(0 | 2) | C0 = &0 {}
+ const C0: Option = Some(1);
+ if let Some(1 | 2) | C0 = None {}
if let &mut (0 | 2) = &mut 0 {}
if let x @ (0 | 2) = 0 {}
if let (0, 1 | 2 | 3) = (0, 0) {}
diff --git a/tests/ui/unnested_or_patterns.rs b/tests/ui/unnested_or_patterns.rs
index 8ce0738bfc27..04cd11036e4e 100644
--- a/tests/ui/unnested_or_patterns.rs
+++ b/tests/ui/unnested_or_patterns.rs
@@ -6,10 +6,13 @@
#![allow(unreachable_patterns, irrefutable_let_patterns, unused_variables)]
fn main() {
+ // Should be ignored by this lint, as nesting requires more characters.
+ if let &0 | &2 = &0 {}
+
if let box 0 | box 2 = Box::new(0) {}
if let box ((0 | 1)) | box (2 | 3) | box 4 = Box::new(0) {}
- const C0: &u8 = &1;
- if let &0 | C0 | &2 = &0 {}
+ const C0: Option = Some(1);
+ if let Some(1) | C0 | Some(2) = None {}
if let &mut 0 | &mut 2 = &mut 0 {}
if let x @ 0 | x @ 2 = 0 {}
if let (0, 1) | (0, 2) | (0, 3) = (0, 0) {}
diff --git a/tests/ui/unnested_or_patterns.stderr b/tests/ui/unnested_or_patterns.stderr
index de424c3fdb8f..453c66cbba8f 100644
--- a/tests/ui/unnested_or_patterns.stderr
+++ b/tests/ui/unnested_or_patterns.stderr
@@ -1,5 +1,5 @@
error: unnested or-patterns
- --> $DIR/unnested_or_patterns.rs:9:12
+ --> $DIR/unnested_or_patterns.rs:12:12
|
LL | if let box 0 | box 2 = Box::new(0) {}
| ^^^^^^^^^^^^^
@@ -11,7 +11,7 @@ LL | if let box (0 | 2) = Box::new(0) {}
| ~~~~~~~~~~~
error: unnested or-patterns
- --> $DIR/unnested_or_patterns.rs:10:12
+ --> $DIR/unnested_or_patterns.rs:13:12
|
LL | if let box ((0 | 1)) | box (2 | 3) | box 4 = Box::new(0) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -22,18 +22,18 @@ LL | if let box (0 | 1 | 2 | 3 | 4) = Box::new(0) {}
| ~~~~~~~~~~~~~~~~~~~~~~~
error: unnested or-patterns
- --> $DIR/unnested_or_patterns.rs:12:12
+ --> $DIR/unnested_or_patterns.rs:15:12
|
-LL | if let &0 | C0 | &2 = &0 {}
- | ^^^^^^^^^^^^
+LL | if let Some(1) | C0 | Some(2) = None {}
+ | ^^^^^^^^^^^^^^^^^^^^^^
|
help: nest the patterns
|
-LL | if let &(0 | 2) | C0 = &0 {}
- | ~~~~~~~~~~~~~
+LL | if let Some(1 | 2) | C0 = None {}
+ | ~~~~~~~~~~~~~~~~
error: unnested or-patterns
- --> $DIR/unnested_or_patterns.rs:13:12
+ --> $DIR/unnested_or_patterns.rs:16:12
|
LL | if let &mut 0 | &mut 2 = &mut 0 {}
| ^^^^^^^^^^^^^^^
@@ -44,7 +44,7 @@ LL | if let &mut (0 | 2) = &mut 0 {}
| ~~~~~~~~~~~~
error: unnested or-patterns
- --> $DIR/unnested_or_patterns.rs:14:12
+ --> $DIR/unnested_or_patterns.rs:17:12
|
LL | if let x @ 0 | x @ 2 = 0 {}
| ^^^^^^^^^^^^^
@@ -55,7 +55,7 @@ LL | if let x @ (0 | 2) = 0 {}
| ~~~~~~~~~~~
error: unnested or-patterns
- --> $DIR/unnested_or_patterns.rs:15:12
+ --> $DIR/unnested_or_patterns.rs:18:12
|
LL | if let (0, 1) | (0, 2) | (0, 3) = (0, 0) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^
@@ -66,7 +66,7 @@ LL | if let (0, 1 | 2 | 3) = (0, 0) {}
| ~~~~~~~~~~~~~~
error: unnested or-patterns
- --> $DIR/unnested_or_patterns.rs:16:12
+ --> $DIR/unnested_or_patterns.rs:19:12
|
LL | if let (1, 0) | (2, 0) | (3, 0) = (0, 0) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^
@@ -77,7 +77,7 @@ LL | if let (1 | 2 | 3, 0) = (0, 0) {}
| ~~~~~~~~~~~~~~
error: unnested or-patterns
- --> $DIR/unnested_or_patterns.rs:17:12
+ --> $DIR/unnested_or_patterns.rs:20:12
|
LL | if let (x, ..) | (x, 1) | (x, 2) = (0, 1) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -88,7 +88,7 @@ LL | if let (x, ..) | (x, 1 | 2) = (0, 1) {}
| ~~~~~~~~~~~~~~~~~~~~
error: unnested or-patterns
- --> $DIR/unnested_or_patterns.rs:18:12
+ --> $DIR/unnested_or_patterns.rs:21:12
|
LL | if let [0] | [1] = [0] {}
| ^^^^^^^^^
@@ -99,7 +99,7 @@ LL | if let [0 | 1] = [0] {}
| ~~~~~~~
error: unnested or-patterns
- --> $DIR/unnested_or_patterns.rs:19:12
+ --> $DIR/unnested_or_patterns.rs:22:12
|
LL | if let [x, 0] | [x, 1] = [0, 1] {}
| ^^^^^^^^^^^^^^^
@@ -110,7 +110,7 @@ LL | if let [x, 0 | 1] = [0, 1] {}
| ~~~~~~~~~~
error: unnested or-patterns
- --> $DIR/unnested_or_patterns.rs:20:12
+ --> $DIR/unnested_or_patterns.rs:23:12
|
LL | if let [x, 0] | [x, 1] | [x, 2] = [0, 1] {}
| ^^^^^^^^^^^^^^^^^^^^^^^^
@@ -121,7 +121,7 @@ LL | if let [x, 0 | 1 | 2] = [0, 1] {}
| ~~~~~~~~~~~~~~
error: unnested or-patterns
- --> $DIR/unnested_or_patterns.rs:21:12
+ --> $DIR/unnested_or_patterns.rs:24:12
|
LL | if let [x, ..] | [x, 1] | [x, 2] = [0, 1] {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -132,7 +132,7 @@ LL | if let [x, ..] | [x, 1 | 2] = [0, 1] {}
| ~~~~~~~~~~~~~~~~~~~~
error: unnested or-patterns
- --> $DIR/unnested_or_patterns.rs:23:12
+ --> $DIR/unnested_or_patterns.rs:26:12
|
LL | if let TS(0, x) | TS(1, x) = TS(0, 0) {}
| ^^^^^^^^^^^^^^^^^^^
@@ -143,7 +143,7 @@ LL | if let TS(0 | 1, x) = TS(0, 0) {}
| ~~~~~~~~~~~~
error: unnested or-patterns
- --> $DIR/unnested_or_patterns.rs:24:12
+ --> $DIR/unnested_or_patterns.rs:27:12
|
LL | if let TS(1, 0) | TS(2, 0) | TS(3, 0) = TS(0, 0) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -154,7 +154,7 @@ LL | if let TS(1 | 2 | 3, 0) = TS(0, 0) {}
| ~~~~~~~~~~~~~~~~
error: unnested or-patterns
- --> $DIR/unnested_or_patterns.rs:25:12
+ --> $DIR/unnested_or_patterns.rs:28:12
|
LL | if let TS(x, ..) | TS(x, 1) | TS(x, 2) = TS(0, 0) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -165,7 +165,7 @@ LL | if let TS(x, ..) | TS(x, 1 | 2) = TS(0, 0) {}
| ~~~~~~~~~~~~~~~~~~~~~~~~
error: unnested or-patterns
- --> $DIR/unnested_or_patterns.rs:30:12
+ --> $DIR/unnested_or_patterns.rs:33:12
|
LL | if let S { x: 0, y } | S { y, x: 1 } = (S { x: 0, y: 1 }) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
From bca346b90b3423de3d333960f864d379b3cd7c0f Mon Sep 17 00:00:00 2001
From: b-naber
Date: Fri, 25 Mar 2022 10:06:10 +0100
Subject: [PATCH 002/128] rebase and use ty::Const in patterns again
---
clippy_lints/src/matches/overlapping_arms.rs | 17 +++++++----------
clippy_lints/src/neg_multiply.rs | 2 +-
clippy_utils/src/consts.rs | 4 ++--
3 files changed, 10 insertions(+), 13 deletions(-)
diff --git a/clippy_lints/src/matches/overlapping_arms.rs b/clippy_lints/src/matches/overlapping_arms.rs
index 7e6581266902..b5fa847451d1 100644
--- a/clippy_lints/src/matches/overlapping_arms.rs
+++ b/clippy_lints/src/matches/overlapping_arms.rs
@@ -1,4 +1,4 @@
-use clippy_utils::consts::{constant, constant_full_int, miri_to_const, FullInt};
+use clippy_utils::consts::{constant, constant_full_int, FullInt};
use clippy_utils::diagnostics::span_lint_and_note;
use core::cmp::Ordering;
use rustc_hir::{Arm, Expr, PatKind, RangeEnd};
@@ -32,18 +32,15 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>)
.filter_map(|arm| {
if let Arm { pat, guard: None, .. } = *arm {
if let PatKind::Range(ref lhs, ref rhs, range_end) = pat.kind {
- let lhs_const = match lhs {
- Some(lhs) => constant(cx, cx.typeck_results(), lhs)?.0,
- None => miri_to_const(ty.numeric_min_val(cx.tcx)?)?,
+ let lhs_val = match lhs {
+ Some(lhs) => constant(cx, cx.typeck_results(), lhs)?.0.int_value(cx, ty)?,
+ None => FullInt::U(ty.numeric_min_val(cx.tcx)?),
};
- let rhs_const = match rhs {
- Some(rhs) => constant(cx, cx.typeck_results(), rhs)?.0,
- None => miri_to_const(ty.numeric_max_val(cx.tcx)?)?,
+ let rhs_val = match rhs {
+ Some(rhs) => constant(cx, cx.typeck_results(), rhs)?.0.int_value(cx, ty)?,
+ None => FullInt::U(ty.numeric_max_val(cx.tcx)?),
};
- let lhs_val = lhs_const.int_value(cx, ty)?;
- let rhs_val = rhs_const.int_value(cx, ty)?;
-
let rhs_bound = match range_end {
RangeEnd::Included => EndBound::Included(rhs_val),
RangeEnd::Excluded => EndBound::Excluded(rhs_val),
diff --git a/clippy_lints/src/neg_multiply.rs b/clippy_lints/src/neg_multiply.rs
index 0d05c83ffe45..6ba9ba0753d4 100644
--- a/clippy_lints/src/neg_multiply.rs
+++ b/clippy_lints/src/neg_multiply.rs
@@ -53,7 +53,7 @@ impl<'tcx> LateLintPass<'tcx> for NegMultiply {
fn check_mul(cx: &LateContext<'_>, span: Span, lit: &Expr<'_>, exp: &Expr<'_>) {
if_chain! {
if let ExprKind::Lit(ref l) = lit.kind;
- if consts::lit_to_constant(&l.node, cx.typeck_results().expr_ty_opt(lit)) == Constant::Int(1);
+ if consts::lit_to_mir_constant(&l.node, cx.typeck_results().expr_ty_opt(lit)) == Constant::Int(1);
if cx.typeck_results().expr_ty(exp).is_integral();
then {
diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs
index 1d6f7acab139..be46b791aa4b 100644
--- a/clippy_utils/src/consts.rs
+++ b/clippy_utils/src/consts.rs
@@ -179,7 +179,7 @@ impl Constant {
}
/// Parses a `LitKind` to a `Constant`.
-pub fn lit_to_constant(lit: &LitKind, ty: Option>) -> Constant {
+pub fn lit_to_mir_constant(lit: &LitKind, ty: Option>) -> Constant {
match *lit {
LitKind::Str(ref is, _) => Constant::Str(is.to_string()),
LitKind::Byte(b) => Constant::Int(u128::from(b)),
@@ -301,7 +301,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
if is_direct_expn_of(e.span, "cfg").is_some() {
None
} else {
- Some(lit_to_constant(&lit.node, self.typeck_results.expr_ty_opt(e)))
+ Some(lit_to_mir_constant(&lit.node, self.typeck_results.expr_ty_opt(e)))
}
},
ExprKind::Array(vec) => self.multi(vec).map(Constant::Vec),
From 422741151359b8ad4b4f1f4a545aef27f5722c82 Mon Sep 17 00:00:00 2001
From: Jason Newcomb
Date: Sat, 2 Apr 2022 16:57:09 -0400
Subject: [PATCH 003/128] Generate deprecated lints test
---
clippy_dev/src/update_lints.rs | 12 ++++++++++++
tests/ui/deprecated.rs | 4 ++++
tests/ui/deprecated.stderr | 32 ++++++++++++++++----------------
3 files changed, 32 insertions(+), 16 deletions(-)
diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs
index 4e48b670457e..b010149626cf 100644
--- a/clippy_dev/src/update_lints.rs
+++ b/clippy_dev/src/update_lints.rs
@@ -107,6 +107,9 @@ pub fn run(update_mode: UpdateMode) {
&content,
);
}
+
+ let content = gen_deprecated_lints_test(&deprecated_lints);
+ process_file("tests/ui/deprecated.rs", update_mode, &content);
}
pub fn print_lints() {
@@ -276,6 +279,15 @@ fn gen_register_lint_list<'a>(
output
}
+fn gen_deprecated_lints_test(lints: &[DeprecatedLint]) -> String {
+ let mut res: String = GENERATED_FILE_COMMENT.into();
+ for lint in lints {
+ writeln!(res, "#![warn(clippy::{})]", lint.name).unwrap();
+ }
+ res.push_str("\nfn main() {}\n");
+ res
+}
+
/// Gathers all lints defined in `clippy_lints/src`
fn gather_all() -> (Vec, Vec) {
let mut lints = Vec::with_capacity(1000);
diff --git a/tests/ui/deprecated.rs b/tests/ui/deprecated.rs
index 39a2601fee9a..07270bd76362 100644
--- a/tests/ui/deprecated.rs
+++ b/tests/ui/deprecated.rs
@@ -1,3 +1,7 @@
+// This file was generated by `cargo dev update_lints`.
+// Use that command to update this file and do not edit by hand.
+// Manual edits will be overwritten.
+
#![warn(clippy::should_assert_eq)]
#![warn(clippy::extend_from_slice)]
#![warn(clippy::range_step_by_zero)]
diff --git a/tests/ui/deprecated.stderr b/tests/ui/deprecated.stderr
index 6095f134d55e..0e142ac8f20e 100644
--- a/tests/ui/deprecated.stderr
+++ b/tests/ui/deprecated.stderr
@@ -1,5 +1,5 @@
error: lint `clippy::should_assert_eq` has been removed: `assert!()` will be more flexible with RFC 2011
- --> $DIR/deprecated.rs:1:9
+ --> $DIR/deprecated.rs:5:9
|
LL | #![warn(clippy::should_assert_eq)]
| ^^^^^^^^^^^^^^^^^^^^^^^^
@@ -7,91 +7,91 @@ LL | #![warn(clippy::should_assert_eq)]
= note: `-D renamed-and-removed-lints` implied by `-D warnings`
error: lint `clippy::extend_from_slice` has been removed: `.extend_from_slice(_)` is a faster way to extend a Vec by a slice
- --> $DIR/deprecated.rs:2:9
+ --> $DIR/deprecated.rs:6:9
|
LL | #![warn(clippy::extend_from_slice)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
error: lint `clippy::range_step_by_zero` has been removed: `iterator.step_by(0)` panics nowadays
- --> $DIR/deprecated.rs:3:9
+ --> $DIR/deprecated.rs:7:9
|
LL | #![warn(clippy::range_step_by_zero)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
error: lint `clippy::unstable_as_slice` has been removed: `Vec::as_slice` has been stabilized in 1.7
- --> $DIR/deprecated.rs:4:9
+ --> $DIR/deprecated.rs:8:9
|
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:5:9
+ --> $DIR/deprecated.rs:9:9
|
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:6:9
+ --> $DIR/deprecated.rs:10:9
|
LL | #![warn(clippy::misaligned_transmute)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: lint `clippy::assign_ops` has been removed: using compound assignment operators (e.g., `+=`) is harmless
- --> $DIR/deprecated.rs:7:9
+ --> $DIR/deprecated.rs:11:9
|
LL | #![warn(clippy::assign_ops)]
| ^^^^^^^^^^^^^^^^^^
error: lint `clippy::if_let_redundant_pattern_matching` has been removed: this lint has been changed to redundant_pattern_matching
- --> $DIR/deprecated.rs:8:9
+ --> $DIR/deprecated.rs:12:9
|
LL | #![warn(clippy::if_let_redundant_pattern_matching)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: lint `clippy::unsafe_vector_initialization` has been removed: the replacement suggested by this lint had substantially different behavior
- --> $DIR/deprecated.rs:9:9
+ --> $DIR/deprecated.rs:13:9
|
LL | #![warn(clippy::unsafe_vector_initialization)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
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:10:9
+ --> $DIR/deprecated.rs:14:9
|
LL | #![warn(clippy::unused_collect)]
| ^^^^^^^^^^^^^^^^^^^^^^
error: lint `clippy::replace_consts` has been removed: associated-constants `MIN`/`MAX` of integers are preferred to `{min,max}_value()` and module constants
- --> $DIR/deprecated.rs:11:9
+ --> $DIR/deprecated.rs:15:9
|
LL | #![warn(clippy::replace_consts)]
| ^^^^^^^^^^^^^^^^^^^^^^
error: lint `clippy::regex_macro` has been removed: the regex! macro has been removed from the regex crate in 2018
- --> $DIR/deprecated.rs:12:9
+ --> $DIR/deprecated.rs:16:9
|
LL | #![warn(clippy::regex_macro)]
| ^^^^^^^^^^^^^^^^^^^
error: lint `clippy::find_map` has been removed: this lint has been replaced by `manual_find_map`, a more specific lint
- --> $DIR/deprecated.rs:13:9
+ --> $DIR/deprecated.rs:17:9
|
LL | #![warn(clippy::find_map)]
| ^^^^^^^^^^^^^^^^
error: lint `clippy::filter_map` has been removed: this lint has been replaced by `manual_filter_map`, a more specific lint
- --> $DIR/deprecated.rs:14:9
+ --> $DIR/deprecated.rs:18:9
|
LL | #![warn(clippy::filter_map)]
| ^^^^^^^^^^^^^^^^^^
error: lint `clippy::pub_enum_variant_names` has been removed: set the `avoid-breaking-exported-api` config option to `false` to enable the `enum_variant_names` lint for public items
- --> $DIR/deprecated.rs:15:9
+ --> $DIR/deprecated.rs:19:9
|
LL | #![warn(clippy::pub_enum_variant_names)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: lint `clippy::wrong_pub_self_convention` has been removed: set the `avoid-breaking-exported-api` config option to `false` to enable the `wrong_self_convention` lint for public items
- --> $DIR/deprecated.rs:16:9
+ --> $DIR/deprecated.rs:20:9
|
LL | #![warn(clippy::wrong_pub_self_convention)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
From d5ef542d376877380fda93ac7c457b5b8ba66833 Mon Sep 17 00:00:00 2001
From: Jason Newcomb
Date: Sun, 3 Apr 2022 08:51:17 -0400
Subject: [PATCH 004/128] Generate renamed lint test
---
clippy_dev/src/update_lints.rs | 73 +++++++++++++++++++++++++++----
clippy_lints/src/lib.rs | 42 +++---------------
clippy_lints/src/renamed_lints.rs | 37 ++++++++++++++++
tests/ui/rename.fixed | 15 +++----
tests/ui/rename.rs | 15 +++----
tests/ui/rename.stderr | 32 +++++++-------
6 files changed, 136 insertions(+), 78 deletions(-)
create mode 100644 clippy_lints/src/renamed_lints.rs
diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs
index b010149626cf..f15b00ecad1e 100644
--- a/clippy_dev/src/update_lints.rs
+++ b/clippy_dev/src/update_lints.rs
@@ -1,7 +1,7 @@
use core::fmt::Write;
use itertools::Itertools;
use rustc_lexer::{tokenize, unescape, LiteralKind, TokenKind};
-use std::collections::HashMap;
+use std::collections::{HashMap, HashSet};
use std::ffi::OsStr;
use std::fs;
use std::path::Path;
@@ -32,7 +32,7 @@ pub enum UpdateMode {
/// Panics if a file path could not read from or then written to
#[allow(clippy::too_many_lines)]
pub fn run(update_mode: UpdateMode) {
- let (lints, deprecated_lints) = gather_all();
+ let (lints, deprecated_lints, renamed_lints) = gather_all();
let internal_lints = Lint::internal_lints(&lints);
let usable_lints = Lint::usable_lints(&lints);
@@ -110,10 +110,13 @@ pub fn run(update_mode: UpdateMode) {
let content = gen_deprecated_lints_test(&deprecated_lints);
process_file("tests/ui/deprecated.rs", update_mode, &content);
+
+ let content = gen_renamed_lints_test(&renamed_lints);
+ process_file("tests/ui/rename.rs", update_mode, &content);
}
pub fn print_lints() {
- let (lint_list, _) = gather_all();
+ let (lint_list, _, _) = gather_all();
let usable_lints = Lint::usable_lints(&lint_list);
let usable_lint_count = usable_lints.len();
let grouped_by_lint_group = Lint::by_lint_group(usable_lints.into_iter());
@@ -213,6 +216,19 @@ impl DeprecatedLint {
}
}
+struct RenamedLint {
+ old_name: String,
+ new_name: String,
+}
+impl RenamedLint {
+ fn new(old_name: &str, new_name: &str) -> Self {
+ Self {
+ old_name: remove_line_splices(old_name),
+ new_name: remove_line_splices(new_name),
+ }
+ }
+}
+
/// Generates the code for registering a group
fn gen_lint_group_list<'a>(group_name: &str, lints: impl Iterator- ) -> String {
let mut details: Vec<_> = lints.map(|l| (&l.module, l.name.to_uppercase())).collect();
@@ -288,10 +304,30 @@ fn gen_deprecated_lints_test(lints: &[DeprecatedLint]) -> String {
res
}
+fn gen_renamed_lints_test(lints: &[RenamedLint]) -> String {
+ let mut seen_lints = HashSet::new();
+ let mut res: String = GENERATED_FILE_COMMENT.into();
+ res.push_str("// run-rustfix\n\n");
+ for lint in lints {
+ if seen_lints.insert(&lint.new_name) {
+ writeln!(res, "#![allow({})]", lint.new_name).unwrap();
+ }
+ }
+ seen_lints.clear();
+ for lint in lints {
+ if seen_lints.insert(&lint.old_name) {
+ writeln!(res, "#![warn({})]", lint.old_name).unwrap();
+ }
+ }
+ res.push_str("\nfn main() {}\n");
+ res
+}
+
/// Gathers all lints defined in `clippy_lints/src`
-fn gather_all() -> (Vec
, Vec) {
+fn gather_all() -> (Vec, Vec, Vec) {
let mut lints = Vec::with_capacity(1000);
let mut deprecated_lints = Vec::with_capacity(50);
+ let mut renamed_lints = Vec::with_capacity(50);
let root_path = clippy_project_root().join("clippy_lints/src");
for (rel_path, file) in WalkDir::new(&root_path)
@@ -317,13 +353,13 @@ fn gather_all() -> (Vec, Vec) {
module.strip_suffix(".rs").unwrap_or(&module)
};
- if module == "deprecated_lints" {
- parse_deprecated_contents(&contents, &mut deprecated_lints);
- } else {
- parse_contents(&contents, module, &mut lints);
+ match module {
+ "deprecated_lints" => parse_deprecated_contents(&contents, &mut deprecated_lints),
+ "renamed_lints" => parse_renamed_contents(&contents, &mut renamed_lints),
+ _ => parse_contents(&contents, module, &mut lints),
}
}
- (lints, deprecated_lints)
+ (lints, deprecated_lints, renamed_lints)
}
macro_rules! match_tokens {
@@ -406,6 +442,25 @@ fn parse_deprecated_contents(contents: &str, lints: &mut Vec) {
}
}
+fn parse_renamed_contents(contents: &str, lints: &mut Vec) {
+ for line in contents.lines() {
+ let mut offset = 0usize;
+ let mut iter = tokenize(line).map(|t| {
+ let range = offset..offset + t.len;
+ offset = range.end;
+ (t.kind, &line[range])
+ });
+ let (old_name, new_name) = match_tokens!(
+ iter,
+ // ("old_name",
+ Whitespace OpenParen Literal{kind: LiteralKind::Str{..},..}(old_name) Comma
+ // "new_name"),
+ Whitespace Literal{kind: LiteralKind::Str{..},..}(new_name) CloseParen Comma
+ );
+ lints.push(RenamedLint::new(old_name, new_name));
+ }
+}
+
/// Removes the line splices and surrounding quotes from a string literal
fn remove_line_splices(s: &str) -> String {
let s = s
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index c8b57956b1b6..9812cfde3ec7 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -162,6 +162,8 @@ mod deprecated_lints;
#[cfg_attr(feature = "internal", allow(clippy::missing_clippy_version_attribute))]
mod utils;
+mod renamed_lints;
+
// begin lints modules, do not remove this comment, it’s used in `update_lints`
mod absurd_extreme_comparisons;
mod approx_const;
@@ -920,43 +922,9 @@ fn register_removed_non_tool_lints(store: &mut rustc_lint::LintStore) {
///
/// Used in `./src/driver.rs`.
pub fn register_renamed(ls: &mut rustc_lint::LintStore) {
- // NOTE: when renaming a lint, add a corresponding test to tests/ui/rename.rs
- ls.register_renamed("clippy::stutter", "clippy::module_name_repetitions");
- ls.register_renamed("clippy::new_without_default_derive", "clippy::new_without_default");
- ls.register_renamed("clippy::cyclomatic_complexity", "clippy::cognitive_complexity");
- ls.register_renamed("clippy::const_static_lifetime", "clippy::redundant_static_lifetimes");
- ls.register_renamed("clippy::option_and_then_some", "clippy::bind_instead_of_map");
- ls.register_renamed("clippy::box_vec", "clippy::box_collection");
- ls.register_renamed("clippy::block_in_if_condition_expr", "clippy::blocks_in_if_conditions");
- ls.register_renamed("clippy::block_in_if_condition_stmt", "clippy::blocks_in_if_conditions");
- ls.register_renamed("clippy::option_map_unwrap_or", "clippy::map_unwrap_or");
- ls.register_renamed("clippy::option_map_unwrap_or_else", "clippy::map_unwrap_or");
- ls.register_renamed("clippy::result_map_unwrap_or_else", "clippy::map_unwrap_or");
- ls.register_renamed("clippy::option_unwrap_used", "clippy::unwrap_used");
- ls.register_renamed("clippy::result_unwrap_used", "clippy::unwrap_used");
- ls.register_renamed("clippy::option_expect_used", "clippy::expect_used");
- ls.register_renamed("clippy::result_expect_used", "clippy::expect_used");
- ls.register_renamed("clippy::for_loop_over_option", "clippy::for_loops_over_fallibles");
- ls.register_renamed("clippy::for_loop_over_result", "clippy::for_loops_over_fallibles");
- ls.register_renamed("clippy::identity_conversion", "clippy::useless_conversion");
- ls.register_renamed("clippy::zero_width_space", "clippy::invisible_characters");
- ls.register_renamed("clippy::single_char_push_str", "clippy::single_char_add_str");
- ls.register_renamed("clippy::if_let_some_result", "clippy::match_result_ok");
- ls.register_renamed("clippy::disallowed_type", "clippy::disallowed_types");
- ls.register_renamed("clippy::disallowed_method", "clippy::disallowed_methods");
- ls.register_renamed("clippy::ref_in_deref", "clippy::needless_borrow");
- ls.register_renamed("clippy::to_string_in_display", "clippy::recursive_format_impl");
-
- // uplifted lints
- ls.register_renamed("clippy::invalid_ref", "invalid_value");
- ls.register_renamed("clippy::into_iter_on_array", "array_into_iter");
- ls.register_renamed("clippy::unused_label", "unused_labels");
- ls.register_renamed("clippy::drop_bounds", "drop_bounds");
- ls.register_renamed("clippy::temporary_cstring_as_ptr", "temporary_cstring_as_ptr");
- ls.register_renamed("clippy::panic_params", "non_fmt_panics");
- ls.register_renamed("clippy::unknown_clippy_lints", "unknown_lints");
- ls.register_renamed("clippy::invalid_atomic_ordering", "invalid_atomic_ordering");
- ls.register_renamed("clippy::mem_discriminant_non_enum", "enum_intrinsics_non_enums");
+ for (old_name, new_name) in renamed_lints::RENAMED_LINTS {
+ ls.register_renamed(old_name, new_name);
+ }
}
// only exists to let the dogfood integration test works.
diff --git a/clippy_lints/src/renamed_lints.rs b/clippy_lints/src/renamed_lints.rs
new file mode 100644
index 000000000000..e10dc0e1bfeb
--- /dev/null
+++ b/clippy_lints/src/renamed_lints.rs
@@ -0,0 +1,37 @@
+pub static RENAMED_LINTS: &[(&str, &str)] = &[
+ ("clippy::stutter", "clippy::module_name_repetitions"),
+ ("clippy::new_without_default_derive", "clippy::new_without_default"),
+ ("clippy::cyclomatic_complexity", "clippy::cognitive_complexity"),
+ ("clippy::const_static_lifetime", "clippy::redundant_static_lifetimes"),
+ ("clippy::option_and_then_some", "clippy::bind_instead_of_map"),
+ ("clippy::box_vec", "clippy::box_collection"),
+ ("clippy::block_in_if_condition_expr", "clippy::blocks_in_if_conditions"),
+ ("clippy::block_in_if_condition_stmt", "clippy::blocks_in_if_conditions"),
+ ("clippy::option_map_unwrap_or", "clippy::map_unwrap_or"),
+ ("clippy::option_map_unwrap_or_else", "clippy::map_unwrap_or"),
+ ("clippy::result_map_unwrap_or_else", "clippy::map_unwrap_or"),
+ ("clippy::option_unwrap_used", "clippy::unwrap_used"),
+ ("clippy::result_unwrap_used", "clippy::unwrap_used"),
+ ("clippy::option_expect_used", "clippy::expect_used"),
+ ("clippy::result_expect_used", "clippy::expect_used"),
+ ("clippy::for_loop_over_option", "clippy::for_loops_over_fallibles"),
+ ("clippy::for_loop_over_result", "clippy::for_loops_over_fallibles"),
+ ("clippy::identity_conversion", "clippy::useless_conversion"),
+ ("clippy::zero_width_space", "clippy::invisible_characters"),
+ ("clippy::single_char_push_str", "clippy::single_char_add_str"),
+ ("clippy::if_let_some_result", "clippy::match_result_ok"),
+ ("clippy::disallowed_type", "clippy::disallowed_types"),
+ ("clippy::disallowed_method", "clippy::disallowed_methods"),
+ ("clippy::ref_in_deref", "clippy::needless_borrow"),
+ ("clippy::to_string_in_display", "clippy::recursive_format_impl"),
+ // uplifted lints
+ ("clippy::invalid_ref", "invalid_value"),
+ ("clippy::into_iter_on_array", "array_into_iter"),
+ ("clippy::unused_label", "unused_labels"),
+ ("clippy::drop_bounds", "drop_bounds"),
+ ("clippy::temporary_cstring_as_ptr", "temporary_cstring_as_ptr"),
+ ("clippy::panic_params", "non_fmt_panics"),
+ ("clippy::unknown_clippy_lints", "unknown_lints"),
+ ("clippy::invalid_atomic_ordering", "invalid_atomic_ordering"),
+ ("clippy::mem_discriminant_non_enum", "enum_intrinsics_non_enums"),
+];
diff --git a/tests/ui/rename.fixed b/tests/ui/rename.fixed
index 24a0c8122919..325f63a64dd9 100644
--- a/tests/ui/rename.fixed
+++ b/tests/ui/rename.fixed
@@ -1,12 +1,13 @@
-//! Test for Clippy lint renames.
+// This file was generated by `cargo dev update_lints`.
+// Use that command to update this file and do not edit by hand.
+// Manual edits will be overwritten.
+
// 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)]
#![allow(clippy::cognitive_complexity)]
+#![allow(clippy::redundant_static_lifetimes)]
#![allow(clippy::bind_instead_of_map)]
#![allow(clippy::box_collection)]
#![allow(clippy::blocks_in_if_conditions)]
@@ -20,8 +21,8 @@
#![allow(clippy::match_result_ok)]
#![allow(clippy::disallowed_types)]
#![allow(clippy::disallowed_methods)]
+#![allow(clippy::needless_borrow)]
#![allow(clippy::recursive_format_impl)]
-// uplifted lints
#![allow(invalid_value)]
#![allow(array_into_iter)]
#![allow(unused_labels)]
@@ -31,11 +32,10 @@
#![allow(unknown_lints)]
#![allow(invalid_atomic_ordering)]
#![allow(enum_intrinsics_non_enums)]
-// warn for the old lint name here, to test if the renaming worked
#![warn(clippy::module_name_repetitions)]
#![warn(clippy::new_without_default)]
-#![warn(clippy::redundant_static_lifetimes)]
#![warn(clippy::cognitive_complexity)]
+#![warn(clippy::redundant_static_lifetimes)]
#![warn(clippy::bind_instead_of_map)]
#![warn(clippy::box_collection)]
#![warn(clippy::blocks_in_if_conditions)]
@@ -57,7 +57,6 @@
#![warn(clippy::disallowed_methods)]
#![warn(clippy::needless_borrow)]
#![warn(clippy::recursive_format_impl)]
-// uplifted lints
#![warn(invalid_value)]
#![warn(array_into_iter)]
#![warn(unused_labels)]
diff --git a/tests/ui/rename.rs b/tests/ui/rename.rs
index ea64234c680d..a21b4a242880 100644
--- a/tests/ui/rename.rs
+++ b/tests/ui/rename.rs
@@ -1,12 +1,13 @@
-//! Test for Clippy lint renames.
+// This file was generated by `cargo dev update_lints`.
+// Use that command to update this file and do not edit by hand.
+// Manual edits will be overwritten.
+
// 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)]
#![allow(clippy::cognitive_complexity)]
+#![allow(clippy::redundant_static_lifetimes)]
#![allow(clippy::bind_instead_of_map)]
#![allow(clippy::box_collection)]
#![allow(clippy::blocks_in_if_conditions)]
@@ -20,8 +21,8 @@
#![allow(clippy::match_result_ok)]
#![allow(clippy::disallowed_types)]
#![allow(clippy::disallowed_methods)]
+#![allow(clippy::needless_borrow)]
#![allow(clippy::recursive_format_impl)]
-// uplifted lints
#![allow(invalid_value)]
#![allow(array_into_iter)]
#![allow(unused_labels)]
@@ -31,11 +32,10 @@
#![allow(unknown_lints)]
#![allow(invalid_atomic_ordering)]
#![allow(enum_intrinsics_non_enums)]
-// warn for the old lint name here, to test if the renaming worked
#![warn(clippy::stutter)]
#![warn(clippy::new_without_default_derive)]
-#![warn(clippy::const_static_lifetime)]
#![warn(clippy::cyclomatic_complexity)]
+#![warn(clippy::const_static_lifetime)]
#![warn(clippy::option_and_then_some)]
#![warn(clippy::box_vec)]
#![warn(clippy::block_in_if_condition_expr)]
@@ -57,7 +57,6 @@
#![warn(clippy::disallowed_method)]
#![warn(clippy::ref_in_deref)]
#![warn(clippy::to_string_in_display)]
-// uplifted lints
#![warn(clippy::invalid_ref)]
#![warn(clippy::into_iter_on_array)]
#![warn(clippy::unused_label)]
diff --git a/tests/ui/rename.stderr b/tests/ui/rename.stderr
index 8b132a783847..54e12d5fae5e 100644
--- a/tests/ui/rename.stderr
+++ b/tests/ui/rename.stderr
@@ -12,18 +12,18 @@ error: lint `clippy::new_without_default_derive` has been renamed to `clippy::ne
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:37:9
- |
-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:38:9
+ --> $DIR/rename.rs:37:9
|
LL | #![warn(clippy::cyclomatic_complexity)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity`
+error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes`
+ --> $DIR/rename.rs:38:9
+ |
+LL | #![warn(clippy::const_static_lifetime)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes`
+
error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map`
--> $DIR/rename.rs:39:9
|
@@ -151,55 +151,55 @@ LL | #![warn(clippy::to_string_in_display)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl`
error: lint `clippy::invalid_ref` has been renamed to `invalid_value`
- --> $DIR/rename.rs:61:9
+ --> $DIR/rename.rs:60:9
|
LL | #![warn(clippy::invalid_ref)]
| ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value`
error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter`
- --> $DIR/rename.rs:62:9
+ --> $DIR/rename.rs:61:9
|
LL | #![warn(clippy::into_iter_on_array)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter`
error: lint `clippy::unused_label` has been renamed to `unused_labels`
- --> $DIR/rename.rs:63:9
+ --> $DIR/rename.rs:62:9
|
LL | #![warn(clippy::unused_label)]
| ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels`
error: lint `clippy::drop_bounds` has been renamed to `drop_bounds`
- --> $DIR/rename.rs:64:9
+ --> $DIR/rename.rs:63:9
|
LL | #![warn(clippy::drop_bounds)]
| ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds`
error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr`
- --> $DIR/rename.rs:65:9
+ --> $DIR/rename.rs:64:9
|
LL | #![warn(clippy::temporary_cstring_as_ptr)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr`
error: lint `clippy::panic_params` has been renamed to `non_fmt_panics`
- --> $DIR/rename.rs:66:9
+ --> $DIR/rename.rs:65:9
|
LL | #![warn(clippy::panic_params)]
| ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics`
error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints`
- --> $DIR/rename.rs:67:9
+ --> $DIR/rename.rs:66:9
|
LL | #![warn(clippy::unknown_clippy_lints)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints`
error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering`
- --> $DIR/rename.rs:68:9
+ --> $DIR/rename.rs:67:9
|
LL | #![warn(clippy::invalid_atomic_ordering)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering`
error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums`
- --> $DIR/rename.rs:69:9
+ --> $DIR/rename.rs:68:9
|
LL | #![warn(clippy::mem_discriminant_non_enum)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums`
From 2f780758099bd649e8b9434449ec5e303e31e1aa Mon Sep 17 00:00:00 2001
From: b-naber
Date: Wed, 6 Apr 2022 10:12:42 +0200
Subject: [PATCH 005/128] get rid of visit_constant in thir visitor
---
clippy_lints/src/matches/overlapping_arms.rs | 17 +++++++++--------
1 file changed, 9 insertions(+), 8 deletions(-)
diff --git a/clippy_lints/src/matches/overlapping_arms.rs b/clippy_lints/src/matches/overlapping_arms.rs
index b5fa847451d1..c0b3e95b1852 100644
--- a/clippy_lints/src/matches/overlapping_arms.rs
+++ b/clippy_lints/src/matches/overlapping_arms.rs
@@ -1,4 +1,4 @@
-use clippy_utils::consts::{constant, constant_full_int, FullInt};
+use clippy_utils::consts::{constant, constant_full_int, miri_to_const, FullInt};
use clippy_utils::diagnostics::span_lint_and_note;
use core::cmp::Ordering;
use rustc_hir::{Arm, Expr, PatKind, RangeEnd};
@@ -32,15 +32,16 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>)
.filter_map(|arm| {
if let Arm { pat, guard: None, .. } = *arm {
if let PatKind::Range(ref lhs, ref rhs, range_end) = pat.kind {
- let lhs_val = match lhs {
- Some(lhs) => constant(cx, cx.typeck_results(), lhs)?.0.int_value(cx, ty)?,
- None => FullInt::U(ty.numeric_min_val(cx.tcx)?),
+ let lhs_const = match lhs {
+ Some(lhs) => constant(cx, cx.typeck_results(), lhs)?.0,
+ None => miri_to_const(ty.numeric_min_val(cx.tcx)?)?,
};
- let rhs_val = match rhs {
- Some(rhs) => constant(cx, cx.typeck_results(), rhs)?.0.int_value(cx, ty)?,
- None => FullInt::U(ty.numeric_max_val(cx.tcx)?),
+ let rhs_const = match rhs {
+ Some(rhs) => constant(cx, cx.typeck_results(), rhs)?.0,
+ None => miri_to_const(ty.numeric_max_val(cx.tcx)?)?,
};
-
+ let lhs_val = lhs_const.int_value(cx, ty)?;
+ let rhs_val = rhs_const.int_value(cx, ty)?;
let rhs_bound = match range_end {
RangeEnd::Included => EndBound::Included(rhs_val),
RangeEnd::Excluded => EndBound::Excluded(rhs_val),
From 6e20a634e7a87a8c0234397e9fdcd8d8dea4e0f4 Mon Sep 17 00:00:00 2001
From: Jason Newcomb
Date: Wed, 6 Apr 2022 09:26:59 -0400
Subject: [PATCH 006/128] Don't lint `manual_non_exhaustive` when the enum
variant is used
---
clippy_lints/src/lib.rs | 3 +-
clippy_lints/src/manual_non_exhaustive.rs | 177 ++++++++++++------
tests/ui/manual_non_exhaustive_enum.rs | 78 ++++++++
tests/ui/manual_non_exhaustive_enum.stderr | 41 ++++
...ive.rs => manual_non_exhaustive_struct.rs} | 63 -------
...rr => manual_non_exhaustive_struct.stderr} | 58 +-----
6 files changed, 246 insertions(+), 174 deletions(-)
create mode 100644 tests/ui/manual_non_exhaustive_enum.rs
create mode 100644 tests/ui/manual_non_exhaustive_enum.stderr
rename tests/ui/{manual_non_exhaustive.rs => manual_non_exhaustive_struct.rs} (58%)
rename tests/ui/{manual_non_exhaustive.stderr => manual_non_exhaustive_struct.stderr} (53%)
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index 8dab039f24fe..b4a70988286a 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -575,7 +575,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(move || Box::new(approx_const::ApproxConstant::new(msrv)));
store.register_late_pass(move || Box::new(methods::Methods::new(avoid_breaking_exported_api, msrv)));
store.register_late_pass(move || Box::new(matches::Matches::new(msrv)));
- store.register_early_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustive::new(msrv)));
+ store.register_early_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustiveStruct::new(msrv)));
+ store.register_late_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustiveEnum::new(msrv)));
store.register_late_pass(move || Box::new(manual_strip::ManualStrip::new(msrv)));
store.register_early_pass(move || Box::new(redundant_static_lifetimes::RedundantStaticLifetimes::new(msrv)));
store.register_early_pass(move || Box::new(redundant_field_names::RedundantFieldNames::new(msrv)));
diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs
index 33d1bb2985f4..7b4b8d6bffa0 100644
--- a/clippy_lints/src/manual_non_exhaustive.rs
+++ b/clippy_lints/src/manual_non_exhaustive.rs
@@ -1,13 +1,18 @@
use clippy_utils::attrs::is_doc_hidden;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::snippet_opt;
-use clippy_utils::{meets_msrv, msrvs};
+use clippy_utils::{is_lint_allowed, meets_msrv, msrvs};
use if_chain::if_chain;
-use rustc_ast::ast::{FieldDef, Item, ItemKind, Variant, VariantData, VisibilityKind};
+use rustc_ast::ast::{self, FieldDef, VisibilityKind};
+use rustc_data_structures::fx::FxHashSet;
use rustc_errors::Applicability;
-use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
+use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
+use rustc_hir::{self as hir, Expr, ExprKind, QPath};
+use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
+use rustc_middle::ty::DefIdTree;
use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::def_id::{DefId, LocalDefId};
use rustc_span::{sym, Span};
declare_clippy_lint! {
@@ -58,82 +63,59 @@ declare_clippy_lint! {
"manual implementations of the non-exhaustive pattern can be simplified using #[non_exhaustive]"
}
-#[derive(Clone)]
-pub struct ManualNonExhaustive {
+#[allow(clippy::module_name_repetitions)]
+pub struct ManualNonExhaustiveStruct {
msrv: Option,
}
-impl ManualNonExhaustive {
+impl ManualNonExhaustiveStruct {
#[must_use]
pub fn new(msrv: Option) -> Self {
Self { msrv }
}
}
-impl_lint_pass!(ManualNonExhaustive => [MANUAL_NON_EXHAUSTIVE]);
+impl_lint_pass!(ManualNonExhaustiveStruct => [MANUAL_NON_EXHAUSTIVE]);
-impl EarlyLintPass for ManualNonExhaustive {
- fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
+#[allow(clippy::module_name_repetitions)]
+pub struct ManualNonExhaustiveEnum {
+ msrv: Option,
+ constructed_enum_variants: FxHashSet<(DefId, DefId)>,
+ potential_enums: Vec<(LocalDefId, LocalDefId, Span, Span)>,
+}
+
+impl ManualNonExhaustiveEnum {
+ #[must_use]
+ pub fn new(msrv: Option) -> Self {
+ Self {
+ msrv,
+ constructed_enum_variants: FxHashSet::default(),
+ potential_enums: Vec::new(),
+ }
+ }
+}
+
+impl_lint_pass!(ManualNonExhaustiveEnum => [MANUAL_NON_EXHAUSTIVE]);
+
+impl EarlyLintPass for ManualNonExhaustiveStruct {
+ fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
if !meets_msrv(self.msrv.as_ref(), &msrvs::NON_EXHAUSTIVE) {
return;
}
- match &item.kind {
- ItemKind::Enum(def, _) => {
- check_manual_non_exhaustive_enum(cx, item, &def.variants);
- },
- ItemKind::Struct(variant_data, _) => {
- if let VariantData::Unit(..) = variant_data {
- return;
- }
+ if let ast::ItemKind::Struct(variant_data, _) = &item.kind {
+ if let ast::VariantData::Unit(..) = variant_data {
+ return;
+ }
- check_manual_non_exhaustive_struct(cx, item, variant_data);
- },
- _ => {},
+ check_manual_non_exhaustive_struct(cx, item, variant_data);
}
}
extract_msrv_attr!(EarlyContext);
}
-fn check_manual_non_exhaustive_enum(cx: &EarlyContext<'_>, item: &Item, variants: &[Variant]) {
- fn is_non_exhaustive_marker(variant: &Variant) -> bool {
- matches!(variant.data, VariantData::Unit(_))
- && variant.ident.as_str().starts_with('_')
- && is_doc_hidden(&variant.attrs)
- }
-
- let mut markers = variants.iter().filter(|v| is_non_exhaustive_marker(v));
- if_chain! {
- if let Some(marker) = markers.next();
- if markers.count() == 0 && variants.len() > 1;
- then {
- span_lint_and_then(
- cx,
- MANUAL_NON_EXHAUSTIVE,
- item.span,
- "this seems like a manual implementation of the non-exhaustive pattern",
- |diag| {
- if_chain! {
- if !item.attrs.iter().any(|attr| attr.has_name(sym::non_exhaustive));
- let header_span = cx.sess().source_map().span_until_char(item.span, '{');
- if let Some(snippet) = snippet_opt(cx, header_span);
- then {
- diag.span_suggestion(
- header_span,
- "add the attribute",
- format!("#[non_exhaustive] {}", snippet),
- Applicability::Unspecified,
- );
- }
- }
- diag.span_help(marker.span, "remove this variant");
- });
- }
- }
-}
-
-fn check_manual_non_exhaustive_struct(cx: &EarlyContext<'_>, item: &Item, data: &VariantData) {
+fn check_manual_non_exhaustive_struct(cx: &EarlyContext<'_>, item: &ast::Item, data: &ast::VariantData) {
fn is_private(field: &FieldDef) -> bool {
matches!(field.vis.kind, VisibilityKind::Inherited)
}
@@ -142,11 +124,11 @@ fn check_manual_non_exhaustive_struct(cx: &EarlyContext<'_>, item: &Item, data:
is_private(field) && field.ty.kind.is_unit() && field.ident.map_or(true, |n| n.as_str().starts_with('_'))
}
- fn find_header_span(cx: &EarlyContext<'_>, item: &Item, data: &VariantData) -> Span {
+ fn find_header_span(cx: &EarlyContext<'_>, item: &ast::Item, data: &ast::VariantData) -> Span {
let delimiter = match data {
- VariantData::Struct(..) => '{',
- VariantData::Tuple(..) => '(',
- VariantData::Unit(_) => unreachable!("`VariantData::Unit` is already handled above"),
+ ast::VariantData::Struct(..) => '{',
+ ast::VariantData::Tuple(..) => '(',
+ ast::VariantData::Unit(_) => unreachable!("`VariantData::Unit` is already handled above"),
};
cx.sess().source_map().span_until_char(item.span, delimiter)
@@ -184,3 +166,74 @@ fn check_manual_non_exhaustive_struct(cx: &EarlyContext<'_>, item: &Item, data:
}
}
}
+
+impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustiveEnum {
+ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
+ if !meets_msrv(self.msrv.as_ref(), &msrvs::NON_EXHAUSTIVE) {
+ return;
+ }
+
+ if let hir::ItemKind::Enum(def, _) = &item.kind
+ && def.variants.len() > 1
+ {
+ let mut iter = def.variants.iter().filter_map(|v| {
+ let id = cx.tcx.hir().local_def_id(v.id);
+ (matches!(v.data, hir::VariantData::Unit(_))
+ && v.ident.as_str().starts_with('_')
+ && is_doc_hidden(cx.tcx.get_attrs(id.to_def_id())))
+ .then(|| (id, v.span))
+ });
+ if let Some((id, span)) = iter.next()
+ && iter.next().is_none()
+ {
+ self.potential_enums.push((item.def_id, id, item.span, span));
+ }
+ }
+ }
+
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
+ if let ExprKind::Path(QPath::Resolved(None, p)) = &e.kind
+ && let [.., name] = p.segments
+ && let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), id) = p.res
+ && name.ident.as_str().starts_with('_')
+ && let Some(variant_id) = cx.tcx.parent(id)
+ && let Some(enum_id) = cx.tcx.parent(variant_id)
+ {
+ self.constructed_enum_variants.insert((enum_id, variant_id));
+ }
+ }
+
+ fn check_crate_post(&mut self, cx: &LateContext<'tcx>) {
+ for &(enum_id, _, enum_span, variant_span) in
+ self.potential_enums.iter().filter(|&&(enum_id, variant_id, _, _)| {
+ !self
+ .constructed_enum_variants
+ .contains(&(enum_id.to_def_id(), variant_id.to_def_id()))
+ && !is_lint_allowed(cx, MANUAL_NON_EXHAUSTIVE, cx.tcx.hir().local_def_id_to_hir_id(enum_id))
+ })
+ {
+ span_lint_and_then(
+ cx,
+ MANUAL_NON_EXHAUSTIVE,
+ enum_span,
+ "this seems like a manual implementation of the non-exhaustive pattern",
+ |diag| {
+ if !cx.tcx.adt_def(enum_id).is_variant_list_non_exhaustive()
+ && let header_span = cx.sess().source_map().span_until_char(enum_span, '{')
+ && let Some(snippet) = snippet_opt(cx, header_span)
+ {
+ diag.span_suggestion(
+ header_span,
+ "add the attribute",
+ format!("#[non_exhaustive] {}", snippet),
+ Applicability::Unspecified,
+ );
+ }
+ diag.span_help(variant_span, "remove this variant");
+ },
+ );
+ }
+ }
+
+ extract_msrv_attr!(LateContext);
+}
diff --git a/tests/ui/manual_non_exhaustive_enum.rs b/tests/ui/manual_non_exhaustive_enum.rs
new file mode 100644
index 000000000000..f23c6d69b4c6
--- /dev/null
+++ b/tests/ui/manual_non_exhaustive_enum.rs
@@ -0,0 +1,78 @@
+#![warn(clippy::manual_non_exhaustive)]
+#![allow(unused)]
+
+enum E {
+ A,
+ B,
+ #[doc(hidden)]
+ _C,
+}
+
+// user forgot to remove the marker
+#[non_exhaustive]
+enum Ep {
+ A,
+ B,
+ #[doc(hidden)]
+ _C,
+}
+
+// marker variant does not have doc hidden attribute, should be ignored
+enum NoDocHidden {
+ A,
+ B,
+ _C,
+}
+
+// name of variant with doc hidden does not start with underscore, should be ignored
+enum NoUnderscore {
+ A,
+ B,
+ #[doc(hidden)]
+ C,
+}
+
+// variant with doc hidden is not unit, should be ignored
+enum NotUnit {
+ A,
+ B,
+ #[doc(hidden)]
+ _C(bool),
+}
+
+// variant with doc hidden is the only one, should be ignored
+enum OnlyMarker {
+ #[doc(hidden)]
+ _A,
+}
+
+// variant with multiple markers, should be ignored
+enum MultipleMarkers {
+ A,
+ #[doc(hidden)]
+ _B,
+ #[doc(hidden)]
+ _C,
+}
+
+// already non_exhaustive and no markers, should be ignored
+#[non_exhaustive]
+enum NonExhaustive {
+ A,
+ B,
+}
+
+// marked is used, don't lint
+enum UsedHidden {
+ #[doc(hidden)]
+ _A,
+ B,
+ C,
+}
+fn foo(x: &mut UsedHidden) {
+ if matches!(*x, UsedHidden::B) {
+ *x = UsedHidden::_A;
+ }
+}
+
+fn main() {}
diff --git a/tests/ui/manual_non_exhaustive_enum.stderr b/tests/ui/manual_non_exhaustive_enum.stderr
new file mode 100644
index 000000000000..317a45d2cbd5
--- /dev/null
+++ b/tests/ui/manual_non_exhaustive_enum.stderr
@@ -0,0 +1,41 @@
+error: this seems like a manual implementation of the non-exhaustive pattern
+ --> $DIR/manual_non_exhaustive_enum.rs:4:1
+ |
+LL | enum E {
+ | ^-----
+ | |
+ | _help: add the attribute: `#[non_exhaustive] enum E`
+ | |
+LL | | A,
+LL | | B,
+LL | | #[doc(hidden)]
+LL | | _C,
+LL | | }
+ | |_^
+ |
+ = note: `-D clippy::manual-non-exhaustive` implied by `-D warnings`
+help: remove this variant
+ --> $DIR/manual_non_exhaustive_enum.rs:8:5
+ |
+LL | _C,
+ | ^^
+
+error: this seems like a manual implementation of the non-exhaustive pattern
+ --> $DIR/manual_non_exhaustive_enum.rs:13:1
+ |
+LL | / enum Ep {
+LL | | A,
+LL | | B,
+LL | | #[doc(hidden)]
+LL | | _C,
+LL | | }
+ | |_^
+ |
+help: remove this variant
+ --> $DIR/manual_non_exhaustive_enum.rs:17:5
+ |
+LL | _C,
+ | ^^
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/manual_non_exhaustive.rs b/tests/ui/manual_non_exhaustive_struct.rs
similarity index 58%
rename from tests/ui/manual_non_exhaustive.rs
rename to tests/ui/manual_non_exhaustive_struct.rs
index 7a788f485207..498eee4447b8 100644
--- a/tests/ui/manual_non_exhaustive.rs
+++ b/tests/ui/manual_non_exhaustive_struct.rs
@@ -1,69 +1,6 @@
#![warn(clippy::manual_non_exhaustive)]
#![allow(unused)]
-mod enums {
- enum E {
- A,
- B,
- #[doc(hidden)]
- _C,
- }
-
- // user forgot to remove the marker
- #[non_exhaustive]
- enum Ep {
- A,
- B,
- #[doc(hidden)]
- _C,
- }
-
- // marker variant does not have doc hidden attribute, should be ignored
- enum NoDocHidden {
- A,
- B,
- _C,
- }
-
- // name of variant with doc hidden does not start with underscore, should be ignored
- enum NoUnderscore {
- A,
- B,
- #[doc(hidden)]
- C,
- }
-
- // variant with doc hidden is not unit, should be ignored
- enum NotUnit {
- A,
- B,
- #[doc(hidden)]
- _C(bool),
- }
-
- // variant with doc hidden is the only one, should be ignored
- enum OnlyMarker {
- #[doc(hidden)]
- _A,
- }
-
- // variant with multiple markers, should be ignored
- enum MultipleMarkers {
- A,
- #[doc(hidden)]
- _B,
- #[doc(hidden)]
- _C,
- }
-
- // already non_exhaustive and no markers, should be ignored
- #[non_exhaustive]
- enum NonExhaustive {
- A,
- B,
- }
-}
-
mod structs {
struct S {
pub a: i32,
diff --git a/tests/ui/manual_non_exhaustive.stderr b/tests/ui/manual_non_exhaustive_struct.stderr
similarity index 53%
rename from tests/ui/manual_non_exhaustive.stderr
rename to tests/ui/manual_non_exhaustive_struct.stderr
index 613c5e8ca1d4..e0766c17b758 100644
--- a/tests/ui/manual_non_exhaustive.stderr
+++ b/tests/ui/manual_non_exhaustive_struct.stderr
@@ -1,44 +1,5 @@
error: this seems like a manual implementation of the non-exhaustive pattern
- --> $DIR/manual_non_exhaustive.rs:5:5
- |
-LL | enum E {
- | ^-----
- | |
- | _____help: add the attribute: `#[non_exhaustive] enum E`
- | |
-LL | | A,
-LL | | B,
-LL | | #[doc(hidden)]
-LL | | _C,
-LL | | }
- | |_____^
- |
- = note: `-D clippy::manual-non-exhaustive` implied by `-D warnings`
-help: remove this variant
- --> $DIR/manual_non_exhaustive.rs:9:9
- |
-LL | _C,
- | ^^
-
-error: this seems like a manual implementation of the non-exhaustive pattern
- --> $DIR/manual_non_exhaustive.rs:14:5
- |
-LL | / enum Ep {
-LL | | A,
-LL | | B,
-LL | | #[doc(hidden)]
-LL | | _C,
-LL | | }
- | |_____^
- |
-help: remove this variant
- --> $DIR/manual_non_exhaustive.rs:18:9
- |
-LL | _C,
- | ^^
-
-error: this seems like a manual implementation of the non-exhaustive pattern
- --> $DIR/manual_non_exhaustive.rs:68:5
+ --> $DIR/manual_non_exhaustive_struct.rs:5:5
|
LL | struct S {
| ^-------
@@ -51,14 +12,15 @@ LL | | _c: (),
LL | | }
| |_____^
|
+ = note: `-D clippy::manual-non-exhaustive` implied by `-D warnings`
help: remove this field
- --> $DIR/manual_non_exhaustive.rs:71:9
+ --> $DIR/manual_non_exhaustive_struct.rs:8:9
|
LL | _c: (),
| ^^^^^^
error: this seems like a manual implementation of the non-exhaustive pattern
- --> $DIR/manual_non_exhaustive.rs:76:5
+ --> $DIR/manual_non_exhaustive_struct.rs:13:5
|
LL | / struct Sp {
LL | | pub a: i32,
@@ -68,13 +30,13 @@ LL | | }
| |_____^
|
help: remove this field
- --> $DIR/manual_non_exhaustive.rs:79:9
+ --> $DIR/manual_non_exhaustive_struct.rs:16:9
|
LL | _c: (),
| ^^^^^^
error: this seems like a manual implementation of the non-exhaustive pattern
- --> $DIR/manual_non_exhaustive.rs:117:5
+ --> $DIR/manual_non_exhaustive_struct.rs:54:5
|
LL | struct T(pub i32, pub i32, ());
| --------^^^^^^^^^^^^^^^^^^^^^^^
@@ -82,22 +44,22 @@ LL | struct T(pub i32, pub i32, ());
| help: add the attribute: `#[non_exhaustive] struct T`
|
help: remove this field
- --> $DIR/manual_non_exhaustive.rs:117:32
+ --> $DIR/manual_non_exhaustive_struct.rs:54:32
|
LL | struct T(pub i32, pub i32, ());
| ^^
error: this seems like a manual implementation of the non-exhaustive pattern
- --> $DIR/manual_non_exhaustive.rs:121:5
+ --> $DIR/manual_non_exhaustive_struct.rs:58:5
|
LL | struct Tp(pub i32, pub i32, ());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: remove this field
- --> $DIR/manual_non_exhaustive.rs:121:33
+ --> $DIR/manual_non_exhaustive_struct.rs:58:33
|
LL | struct Tp(pub i32, pub i32, ());
| ^^
-error: aborting due to 6 previous errors
+error: aborting due to 4 previous errors
From b0f8a3155159286af233577aa0f50d933770dc47 Mon Sep 17 00:00:00 2001
From: Jason Newcomb
Date: Wed, 6 Apr 2022 09:53:20 -0400
Subject: [PATCH 007/128] Cleanup `manual_non_exhaustive`
---
clippy_lints/src/manual_non_exhaustive.rs | 83 +++++++++--------------
1 file changed, 32 insertions(+), 51 deletions(-)
diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs
index 7b4b8d6bffa0..2501dd5bed4b 100644
--- a/clippy_lints/src/manual_non_exhaustive.rs
+++ b/clippy_lints/src/manual_non_exhaustive.rs
@@ -2,8 +2,7 @@ use clippy_utils::attrs::is_doc_hidden;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::snippet_opt;
use clippy_utils::{is_lint_allowed, meets_msrv, msrvs};
-use if_chain::if_chain;
-use rustc_ast::ast::{self, FieldDef, VisibilityKind};
+use rustc_ast::ast::{self, VisibilityKind};
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::Applicability;
use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
@@ -104,55 +103,34 @@ impl EarlyLintPass for ManualNonExhaustiveStruct {
}
if let ast::ItemKind::Struct(variant_data, _) = &item.kind {
- if let ast::VariantData::Unit(..) = variant_data {
+ let (fields, delimiter) = match variant_data {
+ ast::VariantData::Struct(fields, _) => (&**fields, '{'),
+ ast::VariantData::Tuple(fields, _) => (&**fields, '('),
+ ast::VariantData::Unit(_) => return,
+ };
+ if fields.len() <= 1 {
return;
}
-
- check_manual_non_exhaustive_struct(cx, item, variant_data);
- }
- }
-
- extract_msrv_attr!(EarlyContext);
-}
-
-fn check_manual_non_exhaustive_struct(cx: &EarlyContext<'_>, item: &ast::Item, data: &ast::VariantData) {
- fn is_private(field: &FieldDef) -> bool {
- matches!(field.vis.kind, VisibilityKind::Inherited)
- }
-
- fn is_non_exhaustive_marker(field: &FieldDef) -> bool {
- is_private(field) && field.ty.kind.is_unit() && field.ident.map_or(true, |n| n.as_str().starts_with('_'))
- }
-
- fn find_header_span(cx: &EarlyContext<'_>, item: &ast::Item, data: &ast::VariantData) -> Span {
- let delimiter = match data {
- ast::VariantData::Struct(..) => '{',
- ast::VariantData::Tuple(..) => '(',
- ast::VariantData::Unit(_) => unreachable!("`VariantData::Unit` is already handled above"),
- };
-
- cx.sess().source_map().span_until_char(item.span, delimiter)
- }
-
- let fields = data.fields();
- let private_fields = fields.iter().filter(|f| is_private(f)).count();
- let public_fields = fields.iter().filter(|f| f.vis.kind.is_pub()).count();
-
- if_chain! {
- if private_fields == 1 && public_fields >= 1 && public_fields == fields.len() - 1;
- if let Some(marker) = fields.iter().find(|f| is_non_exhaustive_marker(f));
- then {
- span_lint_and_then(
- cx,
- MANUAL_NON_EXHAUSTIVE,
- item.span,
- "this seems like a manual implementation of the non-exhaustive pattern",
- |diag| {
- if_chain! {
- if !item.attrs.iter().any(|attr| attr.has_name(sym::non_exhaustive));
- let header_span = find_header_span(cx, item, data);
- if let Some(snippet) = snippet_opt(cx, header_span);
- then {
+ let mut iter = fields.iter().filter_map(|f| match f.vis.kind {
+ VisibilityKind::Public => None,
+ VisibilityKind::Inherited => Some(Ok(f)),
+ _ => Some(Err(())),
+ });
+ if let Some(Ok(field)) = iter.next()
+ && iter.next().is_none()
+ && field.ty.kind.is_unit()
+ && field.ident.map_or(true, |name| name.as_str().starts_with('_'))
+ {
+ span_lint_and_then(
+ cx,
+ MANUAL_NON_EXHAUSTIVE,
+ item.span,
+ "this seems like a manual implementation of the non-exhaustive pattern",
+ |diag| {
+ if !item.attrs.iter().any(|attr| attr.has_name(sym::non_exhaustive))
+ && let header_span = cx.sess().source_map().span_until_char(item.span, delimiter)
+ && let Some(snippet) = snippet_opt(cx, header_span)
+ {
diag.span_suggestion(
header_span,
"add the attribute",
@@ -160,11 +138,14 @@ fn check_manual_non_exhaustive_struct(cx: &EarlyContext<'_>, item: &ast::Item, d
Applicability::Unspecified,
);
}
+ diag.span_help(field.span, "remove this field");
}
- diag.span_help(marker.span, "remove this field");
- });
+ );
+ }
}
}
+
+ extract_msrv_attr!(EarlyContext);
}
impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustiveEnum {
From ddd3af227329236db174dbfb920b27037e138279 Mon Sep 17 00:00:00 2001
From: Jason Newcomb
Date: Wed, 6 Apr 2022 10:56:06 -0400
Subject: [PATCH 008/128] Only lint `mut_from_ref` when unsafe code is used
---
clippy_lints/src/ptr.rs | 56 +++++++++++++++++++-----------------
clippy_utils/src/visitors.rs | 34 +++++++++++++++++++++-
tests/ui/mut_from_ref.rs | 20 +++++++++----
tests/ui/mut_from_ref.stderr | 14 ++++++++-
4 files changed, 89 insertions(+), 35 deletions(-)
diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs
index ba1997e70e13..4ecde8f49581 100644
--- a/clippy_lints/src/ptr.rs
+++ b/clippy_lints/src/ptr.rs
@@ -3,6 +3,7 @@
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
use clippy_utils::source::snippet_opt;
use clippy_utils::ty::expr_sig;
+use clippy_utils::visitors::contains_unsafe_block;
use clippy_utils::{get_expr_use_or_unification_node, is_lint_allowed, path_def_id, path_to_local, paths};
use if_chain::if_chain;
use rustc_errors::Applicability;
@@ -10,9 +11,9 @@ use rustc_hir::def_id::DefId;
use rustc_hir::hir_id::HirIdMap;
use rustc_hir::intravisit::{walk_expr, Visitor};
use rustc_hir::{
- self as hir, AnonConst, BinOpKind, BindingAnnotation, Body, Expr, ExprKind, FnDecl, FnRetTy, GenericArg,
+ self as hir, AnonConst, BinOpKind, BindingAnnotation, Body, Expr, ExprKind, FnRetTy, FnSig, GenericArg,
ImplItemKind, ItemKind, Lifetime, LifetimeName, Mutability, Node, Param, ParamName, PatKind, QPath, TraitFn,
- TraitItem, TraitItemKind, TyKind,
+ TraitItem, TraitItemKind, TyKind, Unsafety,
};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::hir::nested_filter;
@@ -145,7 +146,7 @@ impl<'tcx> LateLintPass<'tcx> for Ptr {
return;
}
- check_mut_from_ref(cx, sig.decl);
+ check_mut_from_ref(cx, sig, None);
for arg in check_fn_args(
cx,
cx.tcx.fn_sig(item.def_id).skip_binder().inputs(),
@@ -170,10 +171,10 @@ impl<'tcx> LateLintPass<'tcx> for Ptr {
fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) {
let hir = cx.tcx.hir();
let mut parents = hir.parent_iter(body.value.hir_id);
- let (item_id, decl, is_trait_item) = match parents.next() {
+ let (item_id, sig, is_trait_item) = match parents.next() {
Some((_, Node::Item(i))) => {
if let ItemKind::Fn(sig, ..) = &i.kind {
- (i.def_id, sig.decl, false)
+ (i.def_id, sig, false)
} else {
return;
}
@@ -185,14 +186,14 @@ impl<'tcx> LateLintPass<'tcx> for Ptr {
return;
}
if let ImplItemKind::Fn(sig, _) = &i.kind {
- (i.def_id, sig.decl, false)
+ (i.def_id, sig, false)
} else {
return;
}
},
Some((_, Node::TraitItem(i))) => {
if let TraitItemKind::Fn(sig, _) = &i.kind {
- (i.def_id, sig.decl, true)
+ (i.def_id, sig, true)
} else {
return;
}
@@ -200,7 +201,8 @@ impl<'tcx> LateLintPass<'tcx> for Ptr {
_ => return,
};
- check_mut_from_ref(cx, decl);
+ check_mut_from_ref(cx, sig, Some(body));
+ let decl = sig.decl;
let sig = cx.tcx.fn_sig(item_id).skip_binder();
let lint_args: Vec<_> = check_fn_args(cx, sig.inputs(), decl.inputs, body.params)
.filter(|arg| !is_trait_item || arg.mutability() == Mutability::Not)
@@ -478,31 +480,31 @@ fn check_fn_args<'cx, 'tcx: 'cx>(
})
}
-fn check_mut_from_ref(cx: &LateContext<'_>, decl: &FnDecl<'_>) {
- if let FnRetTy::Return(ty) = decl.output {
- if let Some((out, Mutability::Mut, _)) = get_rptr_lm(ty) {
- let mut immutables = vec![];
- for (_, mutbl, argspan) in decl
- .inputs
- .iter()
- .filter_map(get_rptr_lm)
- .filter(|&(lt, _, _)| lt.name == out.name)
- {
- if mutbl == Mutability::Mut {
- return;
- }
- immutables.push(argspan);
- }
- if immutables.is_empty() {
- return;
- }
+fn check_mut_from_ref<'tcx>(cx: &LateContext<'tcx>, sig: &FnSig<'_>, body: Option<&'tcx Body<'_>>) {
+ if let FnRetTy::Return(ty) = sig.decl.output
+ && let Some((out, Mutability::Mut, _)) = get_rptr_lm(ty)
+ {
+ let args: Option> = sig
+ .decl
+ .inputs
+ .iter()
+ .filter_map(get_rptr_lm)
+ .filter(|&(lt, _, _)| lt.name == out.name)
+ .map(|(_, mutability, span)| (mutability == Mutability::Not).then(|| span))
+ .collect();
+ if let Some(args) = args
+ && !args.is_empty()
+ && body.map_or(true, |body| {
+ sig.header.unsafety == Unsafety::Unsafe || contains_unsafe_block(cx, &body.value)
+ })
+ {
span_lint_and_then(
cx,
MUT_FROM_REF,
ty.span,
"mutable borrow from immutable input(s)",
|diag| {
- let ms = MultiSpan::from_spans(immutables);
+ let ms = MultiSpan::from_spans(args);
diag.span_note(ms, "immutable borrow here");
},
);
diff --git a/clippy_utils/src/visitors.rs b/clippy_utils/src/visitors.rs
index 40451b17a9c6..3db64b253539 100644
--- a/clippy_utils/src/visitors.rs
+++ b/clippy_utils/src/visitors.rs
@@ -3,7 +3,8 @@ use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::intravisit::{self, walk_block, walk_expr, Visitor};
use rustc_hir::{
- Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId, ItemKind, Stmt, UnOp, Unsafety,
+ Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId, ItemKind, Stmt, UnOp, UnsafeSource,
+ Unsafety,
};
use rustc_lint::LateContext;
use rustc_middle::hir::map::Map;
@@ -370,3 +371,34 @@ pub fn is_expr_unsafe<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
v.visit_expr(e);
v.is_unsafe
}
+
+/// Checks if the given expression contains an unsafe block
+pub fn contains_unsafe_block<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool {
+ struct V<'cx, 'tcx> {
+ cx: &'cx LateContext<'tcx>,
+ found_unsafe: bool,
+ }
+ impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
+ type NestedFilter = nested_filter::OnlyBodies;
+ fn nested_visit_map(&mut self) -> Self::Map {
+ self.cx.tcx.hir()
+ }
+
+ fn visit_block(&mut self, b: &'tcx Block<'_>) {
+ if self.found_unsafe {
+ return;
+ }
+ if b.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) {
+ self.found_unsafe = true;
+ return;
+ }
+ walk_block(self, b);
+ }
+ }
+ let mut v = V {
+ cx,
+ found_unsafe: false,
+ };
+ v.visit_expr(e);
+ v.found_unsafe
+}
diff --git a/tests/ui/mut_from_ref.rs b/tests/ui/mut_from_ref.rs
index a9a04c8f56b9..370dbd588216 100644
--- a/tests/ui/mut_from_ref.rs
+++ b/tests/ui/mut_from_ref.rs
@@ -5,7 +5,7 @@ struct Foo;
impl Foo {
fn this_wont_hurt_a_bit(&self) -> &mut Foo {
- unimplemented!()
+ unsafe { unimplemented!() }
}
}
@@ -15,29 +15,37 @@ trait Ouch {
impl Ouch for Foo {
fn ouch(x: &Foo) -> &mut Foo {
- unimplemented!()
+ unsafe { unimplemented!() }
}
}
fn fail(x: &u32) -> &mut u16 {
- unimplemented!()
+ unsafe { unimplemented!() }
}
fn fail_lifetime<'a>(x: &'a u32, y: &mut u32) -> &'a mut u32 {
- unimplemented!()
+ unsafe { unimplemented!() }
}
fn fail_double<'a, 'b>(x: &'a u32, y: &'a u32, z: &'b mut u32) -> &'a mut u32 {
- unimplemented!()
+ unsafe { unimplemented!() }
}
// this is OK, because the result borrows y
fn works<'a>(x: &u32, y: &'a mut u32) -> &'a mut u32 {
- unimplemented!()
+ unsafe { 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 {
+ unsafe { unimplemented!() }
+}
+
+unsafe fn also_broken(x: &u32) -> &mut u32 {
+ unimplemented!()
+}
+
+fn without_unsafe(x: &u32) -> &mut u32 {
unimplemented!()
}
diff --git a/tests/ui/mut_from_ref.stderr b/tests/ui/mut_from_ref.stderr
index 4787999920bc..b76d6a13ffb9 100644
--- a/tests/ui/mut_from_ref.stderr
+++ b/tests/ui/mut_from_ref.stderr
@@ -59,5 +59,17 @@ note: immutable borrow here
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
+error: mutable borrow from immutable input(s)
+ --> $DIR/mut_from_ref.rs:44:35
+ |
+LL | unsafe fn also_broken(x: &u32) -> &mut u32 {
+ | ^^^^^^^^
+ |
+note: immutable borrow here
+ --> $DIR/mut_from_ref.rs:44:26
+ |
+LL | unsafe fn also_broken(x: &u32) -> &mut u32 {
+ | ^^^^
+
+error: aborting due to 6 previous errors
From d5e887c6f005347c5c243fe8691e2b7d7d594203 Mon Sep 17 00:00:00 2001
From: Jason Newcomb
Date: Wed, 6 Apr 2022 11:57:55 -0400
Subject: [PATCH 009/128] Better check size and alignment requirements in
`unsound_collection_transmute`
---
clippy_lints/src/transmute/utils.rs | 18 +++++++-----------
tests/ui/transmute_collection.rs | 5 ++++-
2 files changed, 11 insertions(+), 12 deletions(-)
diff --git a/clippy_lints/src/transmute/utils.rs b/clippy_lints/src/transmute/utils.rs
index f359b606e454..0e07a0b0392e 100644
--- a/clippy_lints/src/transmute/utils.rs
+++ b/clippy_lints/src/transmute/utils.rs
@@ -1,10 +1,9 @@
use clippy_utils::last_path_segment;
use clippy_utils::source::snippet;
-use clippy_utils::ty::is_normalizable;
use if_chain::if_chain;
use rustc_hir::{Expr, GenericArg, QPath, TyKind};
use rustc_lint::LateContext;
-use rustc_middle::ty::{self, cast::CastKind, Ty};
+use rustc_middle::ty::{cast::CastKind, Ty};
use rustc_span::DUMMY_SP;
use rustc_typeck::check::{cast::CastCheck, FnCtxt, Inherited};
@@ -34,15 +33,12 @@ pub(super) fn get_type_snippet(cx: &LateContext<'_>, path: &QPath<'_>, to_ref_ty
// check if the component types of the transmuted collection and the result have different ABI,
// size or alignment
pub(super) fn is_layout_incompatible<'tcx>(cx: &LateContext<'tcx>, from: Ty<'tcx>, to: Ty<'tcx>) -> bool {
- let empty_param_env = ty::ParamEnv::empty();
- // check if `from` and `to` are normalizable to avoid ICE (#4968)
- if !(is_normalizable(cx, empty_param_env, from) && is_normalizable(cx, empty_param_env, to)) {
- return false;
- }
- let from_ty_layout = cx.tcx.layout_of(empty_param_env.and(from));
- let to_ty_layout = cx.tcx.layout_of(empty_param_env.and(to));
- if let (Ok(from_layout), Ok(to_layout)) = (from_ty_layout, to_ty_layout) {
- from_layout.size != to_layout.size || from_layout.align != to_layout.align || from_layout.abi != to_layout.abi
+ if let Ok(from) = cx.tcx.try_normalize_erasing_regions(cx.param_env, from)
+ && let Ok(to) = cx.tcx.try_normalize_erasing_regions(cx.param_env, to)
+ && let Ok(from_layout) = cx.tcx.layout_of(cx.param_env.and(from))
+ && let Ok(to_layout) = cx.tcx.layout_of(cx.param_env.and(to))
+ {
+ from_layout.size != to_layout.size || from_layout.align.abi != to_layout.align.abi
} else {
// no idea about layout, so don't lint
false
diff --git a/tests/ui/transmute_collection.rs b/tests/ui/transmute_collection.rs
index cd5a7127791a..5a431bee04a4 100644
--- a/tests/ui/transmute_collection.rs
+++ b/tests/ui/transmute_collection.rs
@@ -1,7 +1,7 @@
#![warn(clippy::unsound_collection_transmute)]
use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, VecDeque};
-use std::mem::transmute;
+use std::mem::{transmute, MaybeUninit};
fn main() {
unsafe {
@@ -43,5 +43,8 @@ fn main() {
// wrong layout
let _ = transmute::<_, HashMap>(HashMap::::new());
let _ = transmute::<_, HashMap>(HashMap::<[u8; 4], u32>::new());
+
+ let _ = transmute::<_, Vec>(Vec::>::new());
+ let _ = transmute::<_, Vec<*mut u32>>(Vec::>::new());
}
}
From 06cfeb90c156d70c2318774081b37db809f6d840 Mon Sep 17 00:00:00 2001
From: Peter Jaszkowiak
Date: Sat, 2 Apr 2022 15:01:07 -0600
Subject: [PATCH 010/128] New lint `is_digit_ascii_radix`
---
CHANGELOG.md | 1 +
...se_sensitive_file_extension_comparisons.rs | 4 +-
clippy_lints/src/doc.rs | 2 +-
clippy_lints/src/lib.register_all.rs | 1 +
clippy_lints/src/lib.register_lints.rs | 1 +
clippy_lints/src/lib.register_style.rs | 1 +
.../src/methods/is_digit_ascii_radix.rs | 50 +++++++++++++++++++
clippy_lints/src/methods/mod.rs | 33 ++++++++++++
clippy_lints/src/misc_early/mod.rs | 2 +-
clippy_lints/src/non_expressive_names.rs | 2 +-
clippy_utils/src/msrvs.rs | 1 +
clippy_utils/src/numeric_literal.rs | 2 +-
tests/ui/is_digit_ascii_radix.fixed | 18 +++++++
tests/ui/is_digit_ascii_radix.rs | 18 +++++++
tests/ui/is_digit_ascii_radix.stderr | 22 ++++++++
tests/ui/to_digit_is_some.fixed | 4 +-
tests/ui/to_digit_is_some.rs | 4 +-
tests/ui/to_digit_is_some.stderr | 8 +--
18 files changed, 160 insertions(+), 14 deletions(-)
create mode 100644 clippy_lints/src/methods/is_digit_ascii_radix.rs
create mode 100644 tests/ui/is_digit_ascii_radix.fixed
create mode 100644 tests/ui/is_digit_ascii_radix.rs
create mode 100644 tests/ui/is_digit_ascii_radix.stderr
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b4097ea86a51..c5c93b113278 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3356,6 +3356,7 @@ Released 2018-09-13
[`invalid_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_regex
[`invalid_upcast_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_upcast_comparisons
[`invisible_characters`]: https://rust-lang.github.io/rust-clippy/master/index.html#invisible_characters
+[`is_digit_ascii_radix`]: https://rust-lang.github.io/rust-clippy/master/index.html#is_digit_ascii_radix
[`items_after_statements`]: https://rust-lang.github.io/rust-clippy/master/index.html#items_after_statements
[`iter_cloned_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_cloned_collect
[`iter_count`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_count
diff --git a/clippy_lints/src/case_sensitive_file_extension_comparisons.rs b/clippy_lints/src/case_sensitive_file_extension_comparisons.rs
index df780747a0c7..e3e31c5863ed 100644
--- a/clippy_lints/src/case_sensitive_file_extension_comparisons.rs
+++ b/clippy_lints/src/case_sensitive_file_extension_comparisons.rs
@@ -43,8 +43,8 @@ fn check_case_sensitive_file_extension_comparison(ctx: &LateContext<'_>, expr: &
if let ExprKind::Lit(Spanned { node: LitKind::Str(ext_literal, ..), ..}) = extension.kind;
if (2..=6).contains(&ext_literal.as_str().len());
if ext_literal.as_str().starts_with('.');
- if ext_literal.as_str().chars().skip(1).all(|c| c.is_uppercase() || c.is_digit(10))
- || ext_literal.as_str().chars().skip(1).all(|c| c.is_lowercase() || c.is_digit(10));
+ if ext_literal.as_str().chars().skip(1).all(|c| c.is_uppercase() || c.is_ascii_digit())
+ || ext_literal.as_str().chars().skip(1).all(|c| c.is_lowercase() || c.is_ascii_digit());
then {
let mut ty = ctx.typeck_results().expr_ty(obj);
ty = match ty.kind() {
diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs
index 28d0c75fde6b..e72031e48778 100644
--- a/clippy_lints/src/doc.rs
+++ b/clippy_lints/src/doc.rs
@@ -739,7 +739,7 @@ fn check_word(cx: &LateContext<'_>, word: &str, span: Span) {
/// letters (`Clippy` is ok) and one lower-case letter (`NASA` is ok).
/// Plurals are also excluded (`IDs` is ok).
fn is_camel_case(s: &str) -> bool {
- if s.starts_with(|c: char| c.is_digit(10)) {
+ if s.starts_with(|c: char| c.is_ascii_digit()) {
return false;
}
diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs
index 14ca93b5f3c1..aa8e3b535b15 100644
--- a/clippy_lints/src/lib.register_all.rs
+++ b/clippy_lints/src/lib.register_all.rs
@@ -165,6 +165,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
LintId::of(methods::FLAT_MAP_IDENTITY),
LintId::of(methods::INSPECT_FOR_EACH),
LintId::of(methods::INTO_ITER_ON_REF),
+ LintId::of(methods::IS_DIGIT_ASCII_RADIX),
LintId::of(methods::ITERATOR_STEP_BY_ZERO),
LintId::of(methods::ITER_CLONED_COLLECT),
LintId::of(methods::ITER_COUNT),
diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs
index 532590aaa5a3..7436f31125b1 100644
--- a/clippy_lints/src/lib.register_lints.rs
+++ b/clippy_lints/src/lib.register_lints.rs
@@ -302,6 +302,7 @@ store.register_lints(&[
methods::INEFFICIENT_TO_STRING,
methods::INSPECT_FOR_EACH,
methods::INTO_ITER_ON_REF,
+ methods::IS_DIGIT_ASCII_RADIX,
methods::ITERATOR_STEP_BY_ZERO,
methods::ITER_CLONED_COLLECT,
methods::ITER_COUNT,
diff --git a/clippy_lints/src/lib.register_style.rs b/clippy_lints/src/lib.register_style.rs
index 3114afac8863..2d98802cb140 100644
--- a/clippy_lints/src/lib.register_style.rs
+++ b/clippy_lints/src/lib.register_style.rs
@@ -61,6 +61,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
LintId::of(methods::CHARS_NEXT_CMP),
LintId::of(methods::ERR_EXPECT),
LintId::of(methods::INTO_ITER_ON_REF),
+ LintId::of(methods::IS_DIGIT_ASCII_RADIX),
LintId::of(methods::ITER_CLONED_COLLECT),
LintId::of(methods::ITER_NEXT_SLICE),
LintId::of(methods::ITER_NTH_ZERO),
diff --git a/clippy_lints/src/methods/is_digit_ascii_radix.rs b/clippy_lints/src/methods/is_digit_ascii_radix.rs
new file mode 100644
index 000000000000..ad333df2f2d5
--- /dev/null
+++ b/clippy_lints/src/methods/is_digit_ascii_radix.rs
@@ -0,0 +1,50 @@
+//! Lint for `c.is_digit(10)`
+
+use super::IS_DIGIT_ASCII_RADIX;
+use clippy_utils::{
+ consts::constant_full_int, consts::FullInt, diagnostics::span_lint_and_sugg, meets_msrv, msrvs,
+ source::snippet_with_applicability,
+};
+use rustc_errors::Applicability;
+use rustc_hir::Expr;
+use rustc_lint::LateContext;
+use rustc_semver::RustcVersion;
+
+pub(super) fn check<'tcx>(
+ cx: &LateContext<'tcx>,
+ expr: &'tcx Expr<'_>,
+ self_arg: &'tcx Expr<'_>,
+ radix: &'tcx Expr<'_>,
+ msrv: Option<&RustcVersion>,
+) {
+ if !meets_msrv(msrv, &msrvs::IS_ASCII_DIGIT) {
+ return;
+ }
+
+ if !cx.typeck_results().expr_ty_adjusted(self_arg).peel_refs().is_char() {
+ return;
+ }
+
+ if let Some(radix_val) = constant_full_int(cx, cx.typeck_results(), radix) {
+ let (num, replacement) = match radix_val {
+ FullInt::S(10) | FullInt::U(10) => (10, "is_ascii_digit"),
+ FullInt::S(16) | FullInt::U(16) => (16, "is_ascii_hexdigit"),
+ _ => return,
+ };
+ let mut applicability = Applicability::MachineApplicable;
+
+ span_lint_and_sugg(
+ cx,
+ IS_DIGIT_ASCII_RADIX,
+ expr.span,
+ &format!("use of `char::is_digit` with literal radix of {}", num),
+ "try",
+ format!(
+ "{}.{}()",
+ snippet_with_applicability(cx, self_arg.span, "..", &mut applicability),
+ replacement
+ ),
+ applicability,
+ );
+ }
+}
diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs
index 70d021a1668e..6a7841a90dfa 100644
--- a/clippy_lints/src/methods/mod.rs
+++ b/clippy_lints/src/methods/mod.rs
@@ -26,6 +26,7 @@ mod implicit_clone;
mod inefficient_to_string;
mod inspect_for_each;
mod into_iter_on_ref;
+mod is_digit_ascii_radix;
mod iter_cloned_collect;
mod iter_count;
mod iter_next_slice;
@@ -2131,6 +2132,36 @@ declare_clippy_lint! {
"no-op use of `deref` or `deref_mut` method to `Option`."
}
+declare_clippy_lint! {
+ /// ### What it does
+ /// Finds usages of [`char::is_digit`]
+ /// (https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_digit) that
+ /// can be replaced with [`is_ascii_digit`]
+ /// (https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_ascii_digit) or
+ /// [`is_ascii_hexdigit`]
+ /// (https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_ascii_hexdigit).
+ ///
+ /// ### Why is this bad?
+ /// `is_digit(..)` is slower and requires specifying the radix.
+ ///
+ /// ### Example
+ /// ```rust
+ /// let c: char = '6';
+ /// c.is_digit(10);
+ /// c.is_digit(16);
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// let c: char = '6';
+ /// c.is_ascii_digit();
+ /// c.is_ascii_hexdigit();
+ /// ```
+ #[clippy::version = "1.61.0"]
+ pub IS_DIGIT_ASCII_RADIX,
+ style,
+ "use of `char::is_digit(..)` with literal radix of 10 or 16"
+}
+
pub struct Methods {
avoid_breaking_exported_api: bool,
msrv: Option,
@@ -2219,6 +2250,7 @@ impl_lint_pass!(Methods => [
UNNECESSARY_JOIN,
ERR_EXPECT,
NEEDLESS_OPTION_AS_DEREF,
+ IS_DIGIT_ASCII_RADIX,
]);
/// Extracts a method call name, args, and `Span` of the method name.
@@ -2516,6 +2548,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
},
("get_or_insert_with", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "get_or_insert"),
("is_file", []) => filetype_is_file::check(cx, expr, recv),
+ ("is_digit", [radix]) => is_digit_ascii_radix::check(cx, expr, recv, radix, msrv),
("is_none", []) => check_is_some_is_none(cx, expr, recv, false),
("is_some", []) => check_is_some_is_none(cx, expr, recv, true),
("join", [join_arg]) => {
diff --git a/clippy_lints/src/misc_early/mod.rs b/clippy_lints/src/misc_early/mod.rs
index d955fad7d41a..6860b60acbdb 100644
--- a/clippy_lints/src/misc_early/mod.rs
+++ b/clippy_lints/src/misc_early/mod.rs
@@ -361,7 +361,7 @@ impl MiscEarlyLints {
// See for a regression.
// FIXME: Find a better way to detect those cases.
let lit_snip = match snippet_opt(cx, lit.span) {
- Some(snip) if snip.chars().next().map_or(false, |c| c.is_digit(10)) => snip,
+ Some(snip) if snip.chars().next().map_or(false, |c| c.is_ascii_digit()) => snip,
_ => return,
};
diff --git a/clippy_lints/src/non_expressive_names.rs b/clippy_lints/src/non_expressive_names.rs
index 0d0c88b02c78..e3bc40c4b491 100644
--- a/clippy_lints/src/non_expressive_names.rs
+++ b/clippy_lints/src/non_expressive_names.rs
@@ -197,7 +197,7 @@ impl<'a, 'tcx, 'b> SimilarNamesNameVisitor<'a, 'tcx, 'b> {
if interned_name.chars().any(char::is_uppercase) {
return;
}
- if interned_name.chars().all(|c| c.is_digit(10) || c == '_') {
+ if interned_name.chars().all(|c| c.is_ascii_digit() || c == '_') {
span_lint(
self.0.cx,
JUST_UNDERSCORES_AND_DIGITS,
diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs
index 0424e0672026..134fd1ce505a 100644
--- a/clippy_utils/src/msrvs.rs
+++ b/clippy_utils/src/msrvs.rs
@@ -32,4 +32,5 @@ msrv_aliases! {
1,28,0 { FROM_BOOL }
1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST, EXPECT_ERR }
1,16,0 { STR_REPEAT }
+ 1,24,0 { IS_ASCII_DIGIT }
}
diff --git a/clippy_utils/src/numeric_literal.rs b/clippy_utils/src/numeric_literal.rs
index 908ff822712f..b92d42e83232 100644
--- a/clippy_utils/src/numeric_literal.rs
+++ b/clippy_utils/src/numeric_literal.rs
@@ -57,7 +57,7 @@ impl<'a> NumericLiteral<'a> {
.trim_start()
.chars()
.next()
- .map_or(false, |c| c.is_digit(10))
+ .map_or(false, |c| c.is_ascii_digit())
{
let (unsuffixed, suffix) = split_suffix(src, lit_kind);
let float = matches!(lit_kind, LitKind::Float(..));
diff --git a/tests/ui/is_digit_ascii_radix.fixed b/tests/ui/is_digit_ascii_radix.fixed
new file mode 100644
index 000000000000..c0ba647d7079
--- /dev/null
+++ b/tests/ui/is_digit_ascii_radix.fixed
@@ -0,0 +1,18 @@
+// run-rustfix
+
+#![warn(clippy::is_digit_ascii_radix)]
+
+const TEN: u32 = 10;
+
+fn main() {
+ let c: char = '6';
+
+ // Should trigger the lint.
+ let _ = c.is_ascii_digit();
+ let _ = c.is_ascii_hexdigit();
+ let _ = c.is_ascii_hexdigit();
+
+ // Should not trigger the lint.
+ let _ = c.is_digit(11);
+ let _ = c.is_digit(TEN);
+}
diff --git a/tests/ui/is_digit_ascii_radix.rs b/tests/ui/is_digit_ascii_radix.rs
new file mode 100644
index 000000000000..68e3f3243d96
--- /dev/null
+++ b/tests/ui/is_digit_ascii_radix.rs
@@ -0,0 +1,18 @@
+// run-rustfix
+
+#![warn(clippy::is_digit_ascii_radix)]
+
+const TEN: u32 = 10;
+
+fn main() {
+ let c: char = '6';
+
+ // Should trigger the lint.
+ let _ = c.is_digit(10);
+ let _ = c.is_digit(16);
+ let _ = c.is_digit(0x10);
+
+ // Should not trigger the lint.
+ let _ = c.is_digit(11);
+ let _ = c.is_digit(TEN);
+}
diff --git a/tests/ui/is_digit_ascii_radix.stderr b/tests/ui/is_digit_ascii_radix.stderr
new file mode 100644
index 000000000000..dc5cb2913ae1
--- /dev/null
+++ b/tests/ui/is_digit_ascii_radix.stderr
@@ -0,0 +1,22 @@
+error: use of `char::is_digit` with literal radix of 10
+ --> $DIR/is_digit_ascii_radix.rs:11:13
+ |
+LL | let _ = c.is_digit(10);
+ | ^^^^^^^^^^^^^^ help: try: `c.is_ascii_digit()`
+ |
+ = note: `-D clippy::is-digit-ascii-radix` implied by `-D warnings`
+
+error: use of `char::is_digit` with literal radix of 16
+ --> $DIR/is_digit_ascii_radix.rs:12:13
+ |
+LL | let _ = c.is_digit(16);
+ | ^^^^^^^^^^^^^^ help: try: `c.is_ascii_hexdigit()`
+
+error: use of `char::is_digit` with literal radix of 16
+ --> $DIR/is_digit_ascii_radix.rs:13:13
+ |
+LL | let _ = c.is_digit(0x10);
+ | ^^^^^^^^^^^^^^^^ help: try: `c.is_ascii_hexdigit()`
+
+error: aborting due to 3 previous errors
+
diff --git a/tests/ui/to_digit_is_some.fixed b/tests/ui/to_digit_is_some.fixed
index 19184df0becb..3c5e96427146 100644
--- a/tests/ui/to_digit_is_some.fixed
+++ b/tests/ui/to_digit_is_some.fixed
@@ -6,6 +6,6 @@ fn main() {
let c = 'x';
let d = &c;
- let _ = d.is_digit(10);
- let _ = char::is_digit(c, 10);
+ let _ = d.is_digit(8);
+ let _ = char::is_digit(c, 8);
}
diff --git a/tests/ui/to_digit_is_some.rs b/tests/ui/to_digit_is_some.rs
index 45a6728ebf57..4f247c06ceed 100644
--- a/tests/ui/to_digit_is_some.rs
+++ b/tests/ui/to_digit_is_some.rs
@@ -6,6 +6,6 @@ fn main() {
let c = 'x';
let d = &c;
- let _ = d.to_digit(10).is_some();
- let _ = char::to_digit(c, 10).is_some();
+ let _ = d.to_digit(8).is_some();
+ let _ = char::to_digit(c, 8).is_some();
}
diff --git a/tests/ui/to_digit_is_some.stderr b/tests/ui/to_digit_is_some.stderr
index 177d3ccd3e23..10a1b393a390 100644
--- a/tests/ui/to_digit_is_some.stderr
+++ b/tests/ui/to_digit_is_some.stderr
@@ -1,16 +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)`
+LL | let _ = d.to_digit(8).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `d.is_digit(8)`
|
= 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)`
+LL | let _ = char::to_digit(c, 8).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `char::is_digit(c, 8)`
error: aborting due to 2 previous errors
From 19054251d350ccc81d4fe803821a7695b4518094 Mon Sep 17 00:00:00 2001
From: Jason Newcomb
Date: Fri, 8 Apr 2022 00:36:09 -0400
Subject: [PATCH 011/128] Update documentation for `mut_from_ref`
---
clippy_lints/src/ptr.rs | 25 ++++++++++++++++---------
1 file changed, 16 insertions(+), 9 deletions(-)
diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs
index 4ecde8f49581..aa7222266eb5 100644
--- a/clippy_lints/src/ptr.rs
+++ b/clippy_lints/src/ptr.rs
@@ -89,19 +89,26 @@ declare_clippy_lint! {
declare_clippy_lint! {
/// ### What it does
- /// This lint checks for functions that take immutable
- /// references and return mutable ones.
+ /// This lint checks for functions that take immutable references and return
+ /// mutable ones. This will not trigger if no unsafe code exists as there
+ /// are multiple safe functions which will do this transformation
+ ///
+ /// To be on the conservative side, if there's at least one mutable
+ /// reference with the output lifetime, this lint will not trigger.
///
/// ### Why is this bad?
- /// This is trivially unsound, as one can create two
- /// mutable references from the same (immutable!) source.
- /// This [error](https://github.com/rust-lang/rust/issues/39465)
- /// actually lead to an interim Rust release 1.15.1.
+ /// Creating a mutable reference which can be repeatably derived from an
+ /// immutable reference is unsound as it allows creating multiple live
+ /// mutable references to the same object.
+ ///
+ /// This [error](https://github.com/rust-lang/rust/issues/39465) actually
+ /// lead to an interim Rust release 1.15.1.
///
/// ### Known problems
- /// To be on the conservative side, if there's at least one
- /// mutable reference with the output lifetime, this lint will not trigger.
- /// In practice, this case is unlikely anyway.
+ /// This pattern is used by memory allocators to allow allocating multiple
+ /// objects while returning mutable references to each one. So long as
+ /// different mutable references are returned each time such a function may
+ /// be safe.
///
/// ### Example
/// ```ignore
From 71131351de6dae8b2baa39ab2a0b0fd20fa5e4a6 Mon Sep 17 00:00:00 2001
From: flip1995
Date: Thu, 7 Apr 2022 18:39:59 +0100
Subject: [PATCH 012/128] Merge commit
'984330a6ee3c4d15626685d6dc8b7b759ff630bd' into clippyup
---
CHANGELOG.md | 146 +++-
Cargo.toml | 9 +-
clippy_dev/Cargo.toml | 8 +-
clippy_dev/src/bless.rs | 13 +-
clippy_dev/src/lib.rs | 17 +
clippy_dev/src/lint.rs | 65 +-
clippy_dev/src/main.rs | 20 +-
clippy_dev/src/new_lint.rs | 24 +-
clippy_dev/src/update_lints.rs | 656 ++++++++----------
clippy_lints/Cargo.toml | 2 +-
.../src/casts/cast_abs_to_unsigned.rs | 42 ++
clippy_lints/src/casts/cast_ptr_alignment.rs | 100 ++-
clippy_lints/src/casts/mod.rs | 27 +-
clippy_lints/src/casts/unnecessary_cast.rs | 14 +-
clippy_lints/src/crate_in_macro_def.rs | 125 ++++
clippy_lints/src/doc.rs | 4 +-
clippy_lints/src/drop_forget_ref.rs | 181 +++--
.../src/empty_structs_with_brackets.rs | 99 +++
clippy_lints/src/identity_op.rs | 37 +-
clippy_lints/src/indexing_slicing.rs | 8 +
clippy_lints/src/lib.register_all.rs | 9 +-
clippy_lints/src/lib.register_complexity.rs | 2 +-
clippy_lints/src/lib.register_correctness.rs | 2 +-
clippy_lints/src/lib.register_lints.rs | 10 +-
clippy_lints/src/lib.register_restriction.rs | 1 +
clippy_lints/src/lib.register_style.rs | 1 +
clippy_lints/src/lib.register_suspicious.rs | 4 +
clippy_lints/src/lib.rs | 11 +-
clippy_lints/src/loops/single_element_loop.rs | 70 +-
clippy_lints/src/map_unit_fn.rs | 23 +-
clippy_lints/src/matches/mod.rs | 2 +-
clippy_lints/src/matches/needless_match.rs | 160 +++--
clippy_lints/src/methods/bytes_nth.rs | 2 +-
clippy_lints/src/methods/err_expect.rs | 60 ++
clippy_lints/src/methods/implicit_clone.rs | 12 +-
.../src/methods/iter_overeager_cloned.rs | 16 +-
clippy_lints/src/methods/map_identity.rs | 3 +-
clippy_lints/src/methods/mod.rs | 66 +-
.../src/methods/needless_option_as_deref.rs | 37 +
clippy_lints/src/module_style.rs | 66 +-
clippy_lints/src/needless_option_as_deref.rs | 65 --
clippy_lints/src/panic_unimplemented.rs | 4 +
clippy_lints/src/ptr.rs | 4 +-
clippy_lints/src/transmute/mod.rs | 9 +-
.../src/transmute/transmute_int_to_char.rs | 3 +-
.../src/transmute/transmute_ref_to_ref.rs | 14 +-
.../src/undocumented_unsafe_blocks.rs | 323 ++++-----
clippy_lints/src/undropped_manually_drops.rs | 59 --
clippy_lints/src/use_self.rs | 4 +-
clippy_lints/src/utils/conf.rs | 2 +-
clippy_lints/src/utils/internal_lints.rs | 15 +-
.../internal_lints/metadata_collector.rs | 2 +-
clippy_utils/Cargo.toml | 2 +-
clippy_utils/src/lib.rs | 17 +-
clippy_utils/src/msrvs.rs | 4 +-
clippy_utils/src/paths.rs | 2 +
doc/release.md | 21 +
rust-toolchain | 2 +-
src/driver.rs | 3 +-
tests/{fmt.rs => check-fmt.rs} | 0
.../module_style/fail_mod/src/main.stderr | 8 +-
.../module_style/fail_no_mod/src/main.stderr | 4 +-
.../check_clippy_version_attribute.stderr | 5 -
tests/ui-toml/struct_excessive_bools/test.rs | 2 +-
tests/ui/auxiliary/proc_macro_unsafe.rs | 18 +
tests/ui/bytes_nth.stderr | 6 +-
...se_sensitive_file_extension_comparisons.rs | 2 +-
tests/ui/cast.rs | 2 +-
tests/ui/cast_abs_to_unsigned.fixed | 8 +
tests/ui/cast_abs_to_unsigned.rs | 8 +
tests/ui/cast_abs_to_unsigned.stderr | 10 +
tests/ui/cast_alignment.rs | 14 +
tests/ui/cast_alignment.stderr | 8 +-
tests/ui/crashes/ice-2774.rs | 2 +-
tests/ui/crashes/ice-6179.rs | 2 +-
tests/ui/crashes/ice-6792.rs | 2 +-
tests/ui/crashes/ice-7868.stderr | 6 +-
.../crashes/needless_lifetimes_impl_trait.rs | 2 +-
tests/ui/crashes/regressions.rs | 2 +-
tests/ui/crate_in_macro_def.fixed | 56 ++
tests/ui/crate_in_macro_def.rs | 56 ++
tests/ui/crate_in_macro_def.stderr | 10 +
tests/ui/default_numeric_fallback_f64.fixed | 2 +-
tests/ui/default_numeric_fallback_f64.rs | 2 +-
tests/ui/default_numeric_fallback_i32.fixed | 2 +-
tests/ui/default_numeric_fallback_i32.rs | 2 +-
tests/ui/drop_forget_copy.rs | 2 +-
tests/ui/drop_forget_copy.stderr | 12 +-
tests/ui/drop_non_drop.rs | 40 ++
tests/ui/drop_non_drop.stderr | 27 +
tests/ui/drop_ref.rs | 2 +-
tests/ui/empty_structs_with_brackets.fixed | 25 +
tests/ui/empty_structs_with_brackets.rs | 25 +
tests/ui/empty_structs_with_brackets.stderr | 19 +
tests/ui/err_expect.fixed | 14 +
tests/ui/err_expect.rs | 14 +
tests/ui/err_expect.stderr | 10 +
tests/ui/fn_params_excessive_bools.rs | 2 +-
tests/ui/forget_non_drop.rs | 27 +
tests/ui/forget_non_drop.stderr | 27 +
tests/ui/forget_ref.rs | 2 +-
tests/ui/identity_op.rs | 9 +
tests/ui/identity_op.stderr | 32 +-
tests/ui/implicit_clone.rs | 4 +-
tests/ui/indexing_slicing_index.rs | 24 +-
tests/ui/indexing_slicing_index.stderr | 27 +-
tests/ui/iter_nth_zero.fixed | 2 +-
tests/ui/iter_nth_zero.rs | 2 +-
tests/ui/iter_overeager_cloned.fixed | 6 +
tests/ui/iter_overeager_cloned.rs | 6 +
tests/ui/iter_overeager_cloned.stderr | 14 +-
tests/ui/large_types_passed_by_value.rs | 2 +-
tests/ui/let_and_return.rs | 2 +-
tests/ui/let_underscore_must_use.rs | 2 +-
tests/ui/manual_async_fn.fixed | 2 +-
tests/ui/manual_async_fn.rs | 2 +-
tests/ui/manual_unwrap_or.fixed | 2 +-
tests/ui/manual_unwrap_or.rs | 2 +-
tests/ui/map_identity.fixed | 2 +
tests/ui/map_identity.rs | 2 +
tests/ui/map_identity.stderr | 8 +-
tests/ui/map_unit_fn.rs | 2 +-
tests/ui/min_rust_version_attr.rs | 14 +-
tests/ui/min_rust_version_attr.stderr | 36 +-
tests/ui/missing_inline.rs | 6 +-
tests/ui/module_name_repetitions.rs | 2 +-
tests/ui/module_name_repetitions.stderr | 4 +-
tests/ui/modulo_arithmetic_integral_const.rs | 7 +-
.../modulo_arithmetic_integral_const.stderr | 34 +-
.../needless_arbitrary_self_type_unfixable.rs | 4 +-
tests/ui/needless_lifetimes.rs | 2 +-
tests/ui/needless_match.fixed | 176 ++++-
tests/ui/needless_match.rs | 222 ++++--
tests/ui/needless_match.stderr | 87 ++-
tests/ui/needless_option_as_deref.fixed | 30 +-
tests/ui/needless_option_as_deref.rs | 30 +-
tests/ui/needless_option_as_deref.stderr | 12 +-
tests/ui/no_effect.rs | 2 +-
tests/ui/option_map_unit_fn_fixable.fixed | 5 +-
tests/ui/option_map_unit_fn_fixable.rs | 5 +-
tests/ui/option_map_unit_fn_fixable.stderr | 12 +-
tests/ui/or_then_unwrap.fixed | 4 +-
tests/ui/or_then_unwrap.rs | 4 +-
tests/ui/panicking_macros.rs | 17 +-
tests/ui/panicking_macros.stderr | 32 +-
tests/ui/ptr_arg.rs | 2 +-
tests/ui/recursive_format_impl.rs | 24 +-
tests/ui/redundant_allocation.rs | 2 +-
tests/ui/redundant_allocation_fixable.fixed | 2 +-
tests/ui/redundant_allocation_fixable.rs | 2 +-
tests/ui/redundant_clone.fixed | 2 +-
tests/ui/redundant_clone.rs | 2 +-
tests/ui/redundant_static_lifetimes.fixed | 2 +-
tests/ui/redundant_static_lifetimes.rs | 2 +-
tests/ui/result_map_unit_fn_fixable.fixed | 2 +
tests/ui/result_map_unit_fn_fixable.rs | 2 +
tests/ui/result_map_unit_fn_fixable.stderr | 10 +-
tests/ui/same_item_push.rs | 2 +-
tests/ui/single_element_loop.fixed | 24 +-
tests/ui/single_element_loop.rs | 20 +-
tests/ui/single_element_loop.stderr | 74 +-
tests/ui/trait_duplication_in_bounds.rs | 4 +-
tests/ui/transmute.rs | 11 +-
tests/ui/transmute.stderr | 54 +-
tests/ui/undocumented_unsafe_blocks.rs | 55 +-
tests/ui/undocumented_unsafe_blocks.stderr | 160 ++---
tests/ui/unnecessary_cast.rs | 6 +
tests/ui/unnecessary_cast_fixable.fixed | 5 +
tests/ui/unnecessary_cast_fixable.rs | 5 +
tests/ui/unsafe_derive_deserialize.rs | 14 +-
tests/ui/unsafe_removed_from_name.rs | 4 +-
tests/ui/unused_self.rs | 10 +-
tests/ui/use_self.fixed | 22 +-
tests/ui/use_self.rs | 22 +-
tests/ui/useless_attribute.fixed | 2 +-
tests/ui/useless_attribute.rs | 2 +-
tests/versioncheck.rs | 34 +-
177 files changed, 3155 insertions(+), 1528 deletions(-)
create mode 100644 clippy_lints/src/casts/cast_abs_to_unsigned.rs
create mode 100644 clippy_lints/src/crate_in_macro_def.rs
create mode 100644 clippy_lints/src/empty_structs_with_brackets.rs
create mode 100644 clippy_lints/src/methods/err_expect.rs
create mode 100644 clippy_lints/src/methods/needless_option_as_deref.rs
delete mode 100644 clippy_lints/src/needless_option_as_deref.rs
delete mode 100644 clippy_lints/src/undropped_manually_drops.rs
rename tests/{fmt.rs => check-fmt.rs} (100%)
create mode 100644 tests/ui/auxiliary/proc_macro_unsafe.rs
create mode 100644 tests/ui/cast_abs_to_unsigned.fixed
create mode 100644 tests/ui/cast_abs_to_unsigned.rs
create mode 100644 tests/ui/cast_abs_to_unsigned.stderr
create mode 100644 tests/ui/crate_in_macro_def.fixed
create mode 100644 tests/ui/crate_in_macro_def.rs
create mode 100644 tests/ui/crate_in_macro_def.stderr
create mode 100644 tests/ui/drop_non_drop.rs
create mode 100644 tests/ui/drop_non_drop.stderr
create mode 100644 tests/ui/empty_structs_with_brackets.fixed
create mode 100644 tests/ui/empty_structs_with_brackets.rs
create mode 100644 tests/ui/empty_structs_with_brackets.stderr
create mode 100644 tests/ui/err_expect.fixed
create mode 100644 tests/ui/err_expect.rs
create mode 100644 tests/ui/err_expect.stderr
create mode 100644 tests/ui/forget_non_drop.rs
create mode 100644 tests/ui/forget_non_drop.stderr
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 88f71931d92b..b4097ea86a51 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,11 +6,143 @@ document.
## Unreleased / In Rust Nightly
-[0eff589...master](https://github.com/rust-lang/rust-clippy/compare/0eff589...master)
+[57b3c4b...master](https://github.com/rust-lang/rust-clippy/compare/57b3c4b...master)
-## Rust 1.59 (beta)
+## Rust 1.60
-Current beta, release 2022-02-24
+Current stable, released 2022-04-07
+
+[0eff589...57b3c4b](https://github.com/rust-lang/rust-clippy/compare/0eff589...57b3c4b)
+
+### New Lints
+
+* [`single_char_lifetime_names`]
+ [#8236](https://github.com/rust-lang/rust-clippy/pull/8236)
+* [`iter_overeager_cloned`]
+ [#8203](https://github.com/rust-lang/rust-clippy/pull/8203)
+* [`transmute_undefined_repr`]
+ [#8398](https://github.com/rust-lang/rust-clippy/pull/8398)
+* [`default_union_representation`]
+ [#8289](https://github.com/rust-lang/rust-clippy/pull/8289)
+* [`manual_bits`]
+ [#8213](https://github.com/rust-lang/rust-clippy/pull/8213)
+* [`borrow_as_ptr`]
+ [#8210](https://github.com/rust-lang/rust-clippy/pull/8210)
+
+### Moves and Deprecations
+
+* Moved [`disallowed_methods`] and [`disallowed_types`] to `style` (now warn-by-default)
+ [#8261](https://github.com/rust-lang/rust-clippy/pull/8261)
+* Rename `ref_in_deref` to [`needless_borrow`]
+ [#8217](https://github.com/rust-lang/rust-clippy/pull/8217)
+* Moved [`mutex_atomic`] to `nursery` (now allow-by-default)
+ [#8260](https://github.com/rust-lang/rust-clippy/pull/8260)
+
+### Enhancements
+
+* [`ptr_arg`]: Now takes the argument usage into account and lints for mutable references
+ [#8271](https://github.com/rust-lang/rust-clippy/pull/8271)
+* [`unused_io_amount`]: Now supports async read and write traits
+ [#8179](https://github.com/rust-lang/rust-clippy/pull/8179)
+* [`while_let_on_iterator`]: Improved detection to catch more cases
+ [#8221](https://github.com/rust-lang/rust-clippy/pull/8221)
+* [`trait_duplication_in_bounds`]: Now covers trait functions with `Self` bounds
+ [#8252](https://github.com/rust-lang/rust-clippy/pull/8252)
+* [`unwrap_used`]: Now works for `.get(i).unwrap()` and `.get_mut(i).unwrap()`
+ [#8372](https://github.com/rust-lang/rust-clippy/pull/8372)
+* [`map_clone`]: The suggestion takes `msrv` into account
+ [#8280](https://github.com/rust-lang/rust-clippy/pull/8280)
+* [`manual_bits`] and [`borrow_as_ptr`]: Now track the `clippy::msrv` attribute
+ [#8280](https://github.com/rust-lang/rust-clippy/pull/8280)
+* [`disallowed_methods`]: Now works for methods on primitive types
+ [#8112](https://github.com/rust-lang/rust-clippy/pull/8112)
+* [`not_unsafe_ptr_arg_deref`]: Now works for type aliases
+ [#8273](https://github.com/rust-lang/rust-clippy/pull/8273)
+* [`needless_question_mark`]: Now works for async functions
+ [#8311](https://github.com/rust-lang/rust-clippy/pull/8311)
+* [`iter_not_returning_iterator`]: Now handles type projections
+ [#8228](https://github.com/rust-lang/rust-clippy/pull/8228)
+* [`wrong_self_convention`]: Now detects wrong `self` references in more cases
+ [#8208](https://github.com/rust-lang/rust-clippy/pull/8208)
+* [`single_match`]: Now works for `match` statements with tuples
+ [#8322](https://github.com/rust-lang/rust-clippy/pull/8322)
+
+### False Positive Fixes
+
+* [`erasing_op`]: No longer triggers if the output type changes
+ [#8204](https://github.com/rust-lang/rust-clippy/pull/8204)
+* [`if_same_then_else`]: No longer triggers for `if let` statements
+ [#8297](https://github.com/rust-lang/rust-clippy/pull/8297)
+* [`manual_memcpy`]: No longer lints on `VecDeque`
+ [#8226](https://github.com/rust-lang/rust-clippy/pull/8226)
+* [`trait_duplication_in_bounds`]: Now takes path segments into account
+ [#8315](https://github.com/rust-lang/rust-clippy/pull/8315)
+* [`deref_addrof`]: No longer lints when the dereference or borrow occurs in different a context
+ [#8268](https://github.com/rust-lang/rust-clippy/pull/8268)
+* [`type_repetition_in_bounds`]: Now checks for full equality to prevent false positives
+ [#8224](https://github.com/rust-lang/rust-clippy/pull/8224)
+* [`ptr_arg`]: No longer lint for mutable references in traits
+ [#8369](https://github.com/rust-lang/rust-clippy/pull/8369)
+* [`implicit_clone`]: No longer lints for double references
+ [#8231](https://github.com/rust-lang/rust-clippy/pull/8231)
+* [`needless_lifetimes`]: No longer lints lifetimes for explicit `self` types
+ [#8278](https://github.com/rust-lang/rust-clippy/pull/8278)
+* [`op_ref`]: No longer lints in `BinOp` impl if that can cause recursion
+ [#8298](https://github.com/rust-lang/rust-clippy/pull/8298)
+* [`enum_variant_names`]: No longer triggers for empty variant names
+ [#8329](https://github.com/rust-lang/rust-clippy/pull/8329)
+* [`redundant_closure`]: No longer lints for `Arc` or `Rc`
+ [#8193](https://github.com/rust-lang/rust-clippy/pull/8193)
+* [`iter_not_returning_iterator`]: No longer lints on trait implementations but therefore on trait definitions
+ [#8228](https://github.com/rust-lang/rust-clippy/pull/8228)
+* [`single_match`]: No longer lints on exhaustive enum patterns without a wildcard
+ [#8322](https://github.com/rust-lang/rust-clippy/pull/8322)
+* [`manual_swap`]: No longer lints on cases that involve automatic dereferences
+ [#8220](https://github.com/rust-lang/rust-clippy/pull/8220)
+* [`useless_format`]: Now works for implicit named arguments
+ [#8295](https://github.com/rust-lang/rust-clippy/pull/8295)
+
+### Suggestion Fixes/Improvements
+
+* [`needless_borrow`]: Prevent mutable borrows being moved and suggest removing the borrow on method calls
+ [#8217](https://github.com/rust-lang/rust-clippy/pull/8217)
+* [`chars_next_cmp`]: Correctly excapes the suggestion
+ [#8376](https://github.com/rust-lang/rust-clippy/pull/8376)
+* [`explicit_write`]: Add suggestions for `write!`s with format arguments
+ [#8365](https://github.com/rust-lang/rust-clippy/pull/8365)
+* [`manual_memcpy`]: Suggests `copy_from_slice` when applicable
+ [#8226](https://github.com/rust-lang/rust-clippy/pull/8226)
+* [`or_fun_call`]: Improved suggestion display for long arguments
+ [#8292](https://github.com/rust-lang/rust-clippy/pull/8292)
+* [`unnecessary_cast`]: Now correctly includes the sign
+ [#8350](https://github.com/rust-lang/rust-clippy/pull/8350)
+* [`cmp_owned`]: No longer flips the comparison order
+ [#8299](https://github.com/rust-lang/rust-clippy/pull/8299)
+* [`explicit_counter_loop`]: Now correctly suggests `iter()` on references
+ [#8382](https://github.com/rust-lang/rust-clippy/pull/8382)
+
+### ICE Fixes
+
+* [`manual_split_once`]
+ [#8250](https://github.com/rust-lang/rust-clippy/pull/8250)
+
+### Documentation Improvements
+
+* [`map_flatten`]: Add documentation for the `Option` type
+ [#8354](https://github.com/rust-lang/rust-clippy/pull/8354)
+* Document that Clippy's driver might use a different code generation than rustc
+ [#8037](https://github.com/rust-lang/rust-clippy/pull/8037)
+* Clippy's lint list will now automatically focus the search box
+ [#8343](https://github.com/rust-lang/rust-clippy/pull/8343)
+
+### Others
+
+* Clippy now warns if we find multiple Clippy config files exist
+ [#8326](https://github.com/rust-lang/rust-clippy/pull/8326)
+
+## Rust 1.59
+
+Released 2022-02-24
[e181011...0eff589](https://github.com/rust-lang/rust-clippy/compare/e181011...0eff589)
@@ -174,7 +306,7 @@ Current beta, release 2022-02-24
## Rust 1.58
-Current stable, released 2022-01-13
+Released 2022-01-13
[00e31fa...e181011](https://github.com/rust-lang/rust-clippy/compare/00e31fa...e181011)
@@ -3069,6 +3201,7 @@ Released 2018-09-13
[`bytes_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#bytes_nth
[`cargo_common_metadata`]: https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata
[`case_sensitive_file_extension_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#case_sensitive_file_extension_comparisons
+[`cast_abs_to_unsigned`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_abs_to_unsigned
[`cast_enum_constructor`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_enum_constructor
[`cast_enum_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_enum_truncation
[`cast_lossless`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_lossless
@@ -3097,6 +3230,7 @@ Released 2018-09-13
[`comparison_chain`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_chain
[`comparison_to_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_to_empty
[`copy_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#copy_iterator
+[`crate_in_macro_def`]: https://rust-lang.github.io/rust-clippy/master/index.html#crate_in_macro_def
[`create_dir`]: https://rust-lang.github.io/rust-clippy/master/index.html#create_dir
[`crosspointer_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#crosspointer_transmute
[`dbg_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#dbg_macro
@@ -3123,6 +3257,7 @@ Released 2018-09-13
[`double_neg`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_neg
[`double_parens`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_parens
[`drop_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_copy
+[`drop_non_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_non_drop
[`drop_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_ref
[`duplicate_underscore_argument`]: https://rust-lang.github.io/rust-clippy/master/index.html#duplicate_underscore_argument
[`duration_subsec`]: https://rust-lang.github.io/rust-clippy/master/index.html#duration_subsec
@@ -3130,12 +3265,14 @@ Released 2018-09-13
[`empty_enum`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_enum
[`empty_line_after_outer_attr`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_line_after_outer_attr
[`empty_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_loop
+[`empty_structs_with_brackets`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_structs_with_brackets
[`enum_clike_unportable_variant`]: https://rust-lang.github.io/rust-clippy/master/index.html#enum_clike_unportable_variant
[`enum_glob_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#enum_glob_use
[`enum_variant_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#enum_variant_names
[`eq_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#eq_op
[`equatable_if_let`]: https://rust-lang.github.io/rust-clippy/master/index.html#equatable_if_let
[`erasing_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#erasing_op
+[`err_expect`]: https://rust-lang.github.io/rust-clippy/master/index.html#err_expect
[`eval_order_dependence`]: https://rust-lang.github.io/rust-clippy/master/index.html#eval_order_dependence
[`excessive_precision`]: https://rust-lang.github.io/rust-clippy/master/index.html#excessive_precision
[`exhaustive_enums`]: https://rust-lang.github.io/rust-clippy/master/index.html#exhaustive_enums
@@ -3174,6 +3311,7 @@ Released 2018-09-13
[`for_kv_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_kv_map
[`for_loops_over_fallibles`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loops_over_fallibles
[`forget_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_copy
+[`forget_non_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_non_drop
[`forget_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_ref
[`format_in_format_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#format_in_format_args
[`from_iter_instead_of_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_iter_instead_of_collect
diff --git a/Cargo.toml b/Cargo.toml
index 123af23881b6..dd6518d5241b 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "clippy"
-version = "0.1.61"
+version = "0.1.62"
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
repository = "https://github.com/rust-lang/rust-clippy"
readme = "README.md"
@@ -21,13 +21,12 @@ name = "clippy-driver"
path = "src/driver.rs"
[dependencies]
-clippy_lints = { version = "0.1", path = "clippy_lints" }
+clippy_lints = { path = "clippy_lints" }
semver = "1.0"
-rustc_tools_util = { version = "0.2", path = "rustc_tools_util" }
+rustc_tools_util = { path = "rustc_tools_util" }
tempfile = { version = "3.2", optional = true }
[dev-dependencies]
-cargo_metadata = "0.14"
compiletest_rs = { version = "0.7.1", features = ["tmp"] }
tester = "0.9"
regex = "1.5"
@@ -45,7 +44,7 @@ derive-new = "0.5"
if_chain = "1.0"
itertools = "0.10.1"
quote = "1.0"
-serde = { version = "1.0", features = ["derive"] }
+serde = { version = "1.0.125", features = ["derive"] }
syn = { version = "1.0", features = ["full"] }
futures = "0.3"
parking_lot = "0.11.2"
diff --git a/clippy_dev/Cargo.toml b/clippy_dev/Cargo.toml
index d133e8cddabc..c2ebba0683ca 100644
--- a/clippy_dev/Cargo.toml
+++ b/clippy_dev/Cargo.toml
@@ -4,15 +4,17 @@ version = "0.0.1"
edition = "2021"
[dependencies]
-bytecount = "0.6"
clap = "2.33"
indoc = "1.0"
itertools = "0.10.1"
opener = "0.5"
-regex = "1.5"
shell-escape = "0.1"
+tempfile = "3.3"
walkdir = "2.3"
-cargo_metadata = "0.14"
[features]
deny-warnings = []
+
+[package.metadata.rust-analyzer]
+# This package uses #[feature(rustc_private)]
+rustc_private = true
diff --git a/clippy_dev/src/bless.rs b/clippy_dev/src/bless.rs
index b0fb39e81699..8e5c739afe05 100644
--- a/clippy_dev/src/bless.rs
+++ b/clippy_dev/src/bless.rs
@@ -1,22 +1,15 @@
//! `bless` updates the reference files in the repo with changed output files
//! from the last test run.
+use crate::cargo_clippy_path;
use std::ffi::OsStr;
use std::fs;
use std::lazy::SyncLazy;
use std::path::{Path, PathBuf};
use walkdir::{DirEntry, WalkDir};
-#[cfg(not(windows))]
-static CARGO_CLIPPY_EXE: &str = "cargo-clippy";
-#[cfg(windows)]
-static CARGO_CLIPPY_EXE: &str = "cargo-clippy.exe";
-
-static CLIPPY_BUILD_TIME: SyncLazy> = SyncLazy::new(|| {
- let mut path = std::env::current_exe().unwrap();
- path.set_file_name(CARGO_CLIPPY_EXE);
- fs::metadata(path).ok()?.modified().ok()
-});
+static CLIPPY_BUILD_TIME: SyncLazy > =
+ SyncLazy::new(|| cargo_clippy_path().metadata().ok()?.modified().ok());
/// # Panics
///
diff --git a/clippy_dev/src/lib.rs b/clippy_dev/src/lib.rs
index 59fde4475471..9c6d754b400f 100644
--- a/clippy_dev/src/lib.rs
+++ b/clippy_dev/src/lib.rs
@@ -1,8 +1,12 @@
+#![feature(let_else)]
#![feature(once_cell)]
+#![feature(rustc_private)]
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
// warn on lints, that are included in `rust-lang/rust`s bootstrap
#![warn(rust_2018_idioms, unused_lifetimes)]
+extern crate rustc_lexer;
+
use std::path::PathBuf;
pub mod bless;
@@ -13,6 +17,19 @@ pub mod serve;
pub mod setup;
pub mod update_lints;
+#[cfg(not(windows))]
+static CARGO_CLIPPY_EXE: &str = "cargo-clippy";
+#[cfg(windows)]
+static CARGO_CLIPPY_EXE: &str = "cargo-clippy.exe";
+
+/// Returns the path to the `cargo-clippy` binary
+#[must_use]
+pub fn cargo_clippy_path() -> PathBuf {
+ let mut path = std::env::current_exe().expect("failed to get current executable name");
+ path.set_file_name(CARGO_CLIPPY_EXE);
+ path
+}
+
/// Returns the path to the Clippy project directory
///
/// # Panics
diff --git a/clippy_dev/src/lint.rs b/clippy_dev/src/lint.rs
index b8287980a4ba..1bc1a39542db 100644
--- a/clippy_dev/src/lint.rs
+++ b/clippy_dev/src/lint.rs
@@ -1,19 +1,52 @@
-use std::process::{self, Command};
+use crate::cargo_clippy_path;
+use std::process::{self, Command, ExitStatus};
+use std::{fs, io};
-pub fn run(filename: &str) {
- let code = Command::new("cargo")
- .args(["run", "--bin", "clippy-driver", "--"])
- .args(["-L", "./target/debug"])
- .args(["-Z", "no-codegen"])
- .args(["--edition", "2021"])
- .arg(filename)
- .status()
- .expect("failed to run cargo")
- .code();
-
- if code.is_none() {
- eprintln!("Killed by signal");
+fn exit_if_err(status: io::Result) {
+ match status.expect("failed to run command").code() {
+ Some(0) => {},
+ Some(n) => process::exit(n),
+ None => {
+ eprintln!("Killed by signal");
+ process::exit(1);
+ },
+ }
+}
+
+pub fn run(path: &str) {
+ let is_file = match fs::metadata(path) {
+ Ok(metadata) => metadata.is_file(),
+ Err(e) => {
+ eprintln!("Failed to read {path}: {e:?}");
+ process::exit(1);
+ },
+ };
+
+ if is_file {
+ exit_if_err(
+ Command::new("cargo")
+ .args(["run", "--bin", "clippy-driver", "--"])
+ .args(["-L", "./target/debug"])
+ .args(["-Z", "no-codegen"])
+ .args(["--edition", "2021"])
+ .arg(path)
+ .status(),
+ );
+ } else {
+ exit_if_err(Command::new("cargo").arg("build").status());
+
+ // Run in a tempdir as changes to clippy do not retrigger linting
+ let target = tempfile::Builder::new()
+ .prefix("clippy")
+ .tempdir()
+ .expect("failed to create tempdir");
+
+ let status = Command::new(cargo_clippy_path())
+ .current_dir(path)
+ .env("CARGO_TARGET_DIR", target.as_ref())
+ .status();
+
+ target.close().expect("failed to remove tempdir");
+ exit_if_err(status);
}
-
- process::exit(code.unwrap_or(1));
}
diff --git a/clippy_dev/src/main.rs b/clippy_dev/src/main.rs
index 30a241c8ba15..b1fe35a0243f 100644
--- a/clippy_dev/src/main.rs
+++ b/clippy_dev/src/main.rs
@@ -4,6 +4,7 @@
use clap::{App, AppSettings, Arg, ArgMatches, SubCommand};
use clippy_dev::{bless, fmt, lint, new_lint, serve, setup, update_lints};
+use indoc::indoc;
fn main() {
let matches = get_clap_config();
@@ -56,8 +57,8 @@ fn main() {
serve::run(port, lint);
},
("lint", Some(matches)) => {
- let filename = matches.value_of("filename").unwrap();
- lint::run(filename);
+ let path = matches.value_of("path").unwrap();
+ lint::run(path);
},
_ => {},
}
@@ -225,11 +226,20 @@ fn get_clap_config<'a>() -> ArgMatches<'a> {
)
.subcommand(
SubCommand::with_name("lint")
- .about("Manually run clippy on a file")
+ .about("Manually run clippy on a file or package")
+ .after_help(indoc! {"
+ EXAMPLES
+ Lint a single file:
+ cargo dev lint tests/ui/attrs.rs
+
+ Lint a package directory:
+ cargo dev lint tests/ui-cargo/wildcard_dependencies/fail
+ cargo dev lint ~/my-project
+ "})
.arg(
- Arg::with_name("filename")
+ Arg::with_name("path")
.required(true)
- .help("The path to a file to lint"),
+ .help("The path to a file or package directory to lint"),
),
)
.get_matches()
diff --git a/clippy_dev/src/new_lint.rs b/clippy_dev/src/new_lint.rs
index 59658b42c79b..7a3fd1317619 100644
--- a/clippy_dev/src/new_lint.rs
+++ b/clippy_dev/src/new_lint.rs
@@ -133,15 +133,23 @@ fn to_camel_case(name: &str) -> String {
}
fn get_stabilisation_version() -> String {
- let mut command = cargo_metadata::MetadataCommand::new();
- command.no_deps();
- if let Ok(metadata) = command.exec() {
- if let Some(pkg) = metadata.packages.iter().find(|pkg| pkg.name == "clippy") {
- return format!("{}.{}.0", pkg.version.minor, pkg.version.patch);
- }
+ fn parse_manifest(contents: &str) -> Option {
+ let version = contents
+ .lines()
+ .filter_map(|l| l.split_once('='))
+ .find_map(|(k, v)| (k.trim() == "version").then(|| v.trim()))?;
+ let Some(("0", version)) = version.get(1..version.len() - 1)?.split_once('.') else {
+ return None;
+ };
+ let (minor, patch) = version.split_once('.')?;
+ Some(format!(
+ "{}.{}.0",
+ minor.parse::().ok()?,
+ patch.parse::().ok()?
+ ))
}
-
- String::from("")
+ let contents = fs::read_to_string("Cargo.toml").expect("Unable to read `Cargo.toml`");
+ parse_manifest(&contents).expect("Unable to find package version in `Cargo.toml`")
}
fn get_test_file_contents(lint_name: &str, header_commands: Option<&str>) -> String {
diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs
index d368ef1f46a2..59db51fbfac5 100644
--- a/clippy_dev/src/update_lints.rs
+++ b/clippy_dev/src/update_lints.rs
@@ -1,9 +1,9 @@
+use core::fmt::Write;
use itertools::Itertools;
-use regex::Regex;
+use rustc_lexer::{tokenize, unescape, LiteralKind, TokenKind};
use std::collections::HashMap;
use std::ffi::OsStr;
use std::fs;
-use std::lazy::SyncLazy;
use std::path::Path;
use walkdir::WalkDir;
@@ -13,35 +13,7 @@ const GENERATED_FILE_COMMENT: &str = "// This file was generated by `cargo dev u
// Use that command to update this file and do not edit by hand.\n\
// Manual edits will be overwritten.\n\n";
-static DEC_CLIPPY_LINT_RE: SyncLazy = SyncLazy::new(|| {
- Regex::new(
- r#"(?x)
- declare_clippy_lint!\s*[\{(]
- (?:\s+///.*)*
- (?:\s*\#\[clippy::version\s*=\s*"[^"]*"\])?
- \s+pub\s+(?P[A-Z_][A-Z_0-9]*)\s*,\s*
- (?P[a-z_]+)\s*,\s*
- "(?P(?:[^"\\]+|\\(?s).(?-s))*)"\s*[})]
-"#,
- )
- .unwrap()
-});
-
-static DEC_DEPRECATED_LINT_RE: SyncLazy = SyncLazy::new(|| {
- Regex::new(
- r#"(?x)
- declare_deprecated_lint!\s*[{(]\s*
- (?:\s+///.*)*
- (?:\s*\#\[clippy::version\s*=\s*"[^"]*"\])?
- \s+pub\s+(?P[A-Z_][A-Z_0-9]*)\s*,\s*
- "(?P(?:[^"\\]+|\\(?s).(?-s))*)"\s*[})]
-"#,
- )
- .unwrap()
-});
-static NL_ESCAPE_RE: SyncLazy = SyncLazy::new(|| Regex::new(r#"\\\n\s*"#).unwrap());
-
-static DOCS_LINK: &str = "https://rust-lang.github.io/rust-clippy/master/index.html";
+const DOCS_LINK: &str = "https://rust-lang.github.io/rust-clippy/master/index.html";
#[derive(Clone, Copy, PartialEq)]
pub enum UpdateMode {
@@ -60,60 +32,52 @@ pub enum UpdateMode {
/// Panics if a file path could not read from or then written to
#[allow(clippy::too_many_lines)]
pub fn run(update_mode: UpdateMode) {
- let lint_list: Vec = gather_all().collect();
+ let (lints, deprecated_lints) = gather_all();
- let internal_lints = Lint::internal_lints(&lint_list);
- let deprecated_lints = Lint::deprecated_lints(&lint_list);
- let usable_lints = Lint::usable_lints(&lint_list);
+ let internal_lints = Lint::internal_lints(&lints);
+ let usable_lints = Lint::usable_lints(&lints);
let mut sorted_usable_lints = usable_lints.clone();
sorted_usable_lints.sort_by_key(|lint| lint.name.clone());
- let usable_lint_count = round_to_fifty(usable_lints.len());
-
- let mut file_change = false;
-
- file_change |= replace_region_in_file(
+ replace_region_in_file(
+ update_mode,
Path::new("README.md"),
- &format!(
- r#"\[There are over \d+ lints included in this crate!\]\({}\)"#,
- DOCS_LINK
- ),
- "",
- true,
- update_mode == UpdateMode::Change,
- || {
- vec",
- usable_lint_count, DOCS_LINK
- )]
+ "[There are over ",
+ " lints included in this crate!]",
+ |res| {
+ write!(res, "{}", round_to_fifty(usable_lints.len())).unwrap();
},
- )
- .changed;
+ );
- file_change |= replace_region_in_file(
+ replace_region_in_file(
+ update_mode,
Path::new("CHANGELOG.md"),
- "",
+ "\n",
"",
- false,
- update_mode == UpdateMode::Change,
- || gen_changelog_lint_list(usable_lints.iter().chain(deprecated_lints.iter())),
- )
- .changed;
+ |res| {
+ for lint in usable_lints
+ .iter()
+ .map(|l| &l.name)
+ .chain(deprecated_lints.iter().map(|l| &l.name))
+ .sorted()
+ {
+ writeln!(res, "[`{}`]: {}#{}", lint, DOCS_LINK, lint).unwrap();
+ }
+ },
+ );
// This has to be in lib.rs, otherwise rustfmt doesn't work
- file_change |= replace_region_in_file(
+ replace_region_in_file(
+ update_mode,
Path::new("clippy_lints/src/lib.rs"),
- "begin lints modules",
- "end lints modules",
- false,
- update_mode == UpdateMode::Change,
- || gen_modules_list(usable_lints.iter()),
- )
- .changed;
-
- if file_change && update_mode == UpdateMode::Check {
- exit_with_failure();
- }
+ "// begin lints modules, do not remove this comment, it’s used in `update_lints`\n",
+ "// end lints modules, do not remove this comment, it’s used in `update_lints`",
+ |res| {
+ for lint_mod in usable_lints.iter().map(|l| &l.module).unique().sorted() {
+ writeln!(res, "mod {};", lint_mod).unwrap();
+ }
+ },
+ );
process_file(
"clippy_lints/src/lib.register_lints.rs",
@@ -123,7 +87,7 @@ pub fn run(update_mode: UpdateMode) {
process_file(
"clippy_lints/src/lib.deprecated.rs",
update_mode,
- &gen_deprecated(deprecated_lints.iter()),
+ &gen_deprecated(&deprecated_lints),
);
let all_group_lints = usable_lints.iter().filter(|l| {
@@ -146,15 +110,12 @@ pub fn run(update_mode: UpdateMode) {
}
pub fn print_lints() {
- let lint_list: Vec = gather_all().collect();
+ let (lint_list, _) = gather_all();
let usable_lints = Lint::usable_lints(&lint_list);
let usable_lint_count = usable_lints.len();
let grouped_by_lint_group = Lint::by_lint_group(usable_lints.into_iter());
for (lint_group, mut lints) in grouped_by_lint_group {
- if lint_group == "Deprecated" {
- continue;
- }
println!("\n## {}", lint_group);
lints.sort_by_key(|l| l.name.clone());
@@ -198,19 +159,17 @@ struct Lint {
name: String,
group: String,
desc: String,
- deprecation: Option,
module: String,
}
impl Lint {
#[must_use]
- fn new(name: &str, group: &str, desc: &str, deprecation: Option<&str>, module: &str) -> Self {
+ fn new(name: &str, group: &str, desc: &str, module: &str) -> Self {
Self {
name: name.to_lowercase(),
- group: group.to_string(),
- desc: NL_ESCAPE_RE.replace(&desc.replace("\\\"", "\""), "").to_string(),
- deprecation: deprecation.map(ToString::to_string),
- module: module.to_string(),
+ group: group.into(),
+ desc: remove_line_splices(desc),
+ module: module.into(),
}
}
@@ -219,7 +178,7 @@ impl Lint {
fn usable_lints(lints: &[Self]) -> Vec {
lints
.iter()
- .filter(|l| l.deprecation.is_none() && !l.group.starts_with("internal"))
+ .filter(|l| !l.group.starts_with("internal"))
.cloned()
.collect()
}
@@ -230,12 +189,6 @@ impl Lint {
lints.iter().filter(|l| l.group == "internal").cloned().collect()
}
- /// Returns all deprecated lints
- #[must_use]
- fn deprecated_lints(lints: &[Self]) -> Vec {
- lints.iter().filter(|l| l.deprecation.is_some()).cloned().collect()
- }
-
/// Returns the lints in a `HashMap`, grouped by the different lint groups
#[must_use]
fn by_lint_group(lints: impl Iterator- ) -> HashMap
> {
@@ -243,6 +196,20 @@ impl Lint {
}
}
+#[derive(Clone, PartialEq, Debug)]
+struct DeprecatedLint {
+ name: String,
+ reason: String,
+}
+impl DeprecatedLint {
+ fn new(name: &str, reason: &str) -> Self {
+ Self {
+ name: name.to_lowercase(),
+ reason: remove_line_splices(reason),
+ }
+ }
+}
+
/// Generates the code for registering a group
fn gen_lint_group_list<'a>(group_name: &str, lints: impl Iterator- ) -> String {
let mut details: Vec<_> = lints.map(|l| (&l.module, l.name.to_uppercase())).collect();
@@ -262,32 +229,12 @@ fn gen_lint_group_list<'a>(group_name: &str, lints: impl Iterator
- (lints: impl Iterator
- ) -> Vec
{
- lints
- .map(|l| &l.module)
- .unique()
- .map(|module| format!("mod {};", module))
- .sorted()
- .collect::>()
-}
-
-/// Generates the list of lint links at the bottom of the CHANGELOG
-#[must_use]
-fn gen_changelog_lint_list<'a>(lints: impl Iterator- ) -> Vec
{
- lints
- .sorted_by_key(|l| &l.name)
- .map(|l| format!("[`{}`]: {}#{}", l.name, DOCS_LINK, l.name))
- .collect()
-}
-
/// Generates the `register_removed` code
#[must_use]
-fn gen_deprecated<'a>(lints: impl Iterator- ) -> String {
+fn gen_deprecated(lints: &[DeprecatedLint]) -> String {
let mut output = GENERATED_FILE_COMMENT.to_string();
output.push_str("{\n");
- for Lint { name, deprecation, .. } in lints {
+ for lint in lints {
output.push_str(&format!(
concat!(
" store.register_removed(\n",
@@ -295,8 +242,7 @@ fn gen_deprecated<'a>(lints: impl Iterator
- ) -> String {
" \"{}\",\n",
" );\n"
),
- name,
- deprecation.as_ref().expect("`lints` are deprecated")
+ lint.name, lint.reason,
));
}
output.push_str("}\n");
@@ -330,61 +276,136 @@ fn gen_register_lint_list<'a>(
output
}
-/// Gathers all files in `src/clippy_lints` and gathers all lints inside
-fn gather_all() -> impl Iterator
- {
- lint_files().flat_map(|f| gather_from_file(&f))
-}
+/// Gathers all lints defined in `clippy_lints/src`
+fn gather_all() -> (Vec
, Vec) {
+ let mut lints = Vec::with_capacity(1000);
+ let mut deprecated_lints = Vec::with_capacity(50);
+ let root_path = clippy_project_root().join("clippy_lints/src");
-fn gather_from_file(dir_entry: &walkdir::DirEntry) -> impl Iterator- {
- let content = fs::read_to_string(dir_entry.path()).unwrap();
- let path = dir_entry.path();
- let filename = path.file_stem().unwrap();
- let path_buf = path.with_file_name(filename);
- let mut rel_path = path_buf
- .strip_prefix(clippy_project_root().join("clippy_lints/src"))
- .expect("only files in `clippy_lints/src` should be looked at");
- // If the lints are stored in mod.rs, we get the module name from
- // the containing directory:
- if filename == "mod" {
- rel_path = rel_path.parent().unwrap();
- }
-
- let module = rel_path
- .components()
- .map(|c| c.as_os_str().to_str().unwrap())
- .collect::
>()
- .join("::");
-
- parse_contents(&content, &module)
-}
-
-fn parse_contents(content: &str, module: &str) -> impl Iterator- {
- let lints = DEC_CLIPPY_LINT_RE
- .captures_iter(content)
- .map(|m| Lint::new(&m["name"], &m["cat"], &m["desc"], None, module));
- let deprecated = DEC_DEPRECATED_LINT_RE
- .captures_iter(content)
- .map(|m| Lint::new(&m["name"], "Deprecated", &m["desc"], Some(&m["desc"]), module));
- // Removing the `.collect::
>().into_iter()` causes some lifetime issues due to the map
- lints.chain(deprecated).collect::>().into_iter()
-}
-
-/// Collects all .rs files in the `clippy_lints/src` directory
-fn lint_files() -> impl Iterator- {
- // We use `WalkDir` instead of `fs::read_dir` here in order to recurse into subdirectories.
- // Otherwise we would not collect all the lints, for example in `clippy_lints/src/methods/`.
- let path = clippy_project_root().join("clippy_lints/src");
- WalkDir::new(path)
+ for (rel_path, file) in WalkDir::new(&root_path)
.into_iter()
- .filter_map(Result::ok)
+ .map(Result::unwrap)
.filter(|f| f.path().extension() == Some(OsStr::new("rs")))
+ .map(|f| (f.path().strip_prefix(&root_path).unwrap().to_path_buf(), f))
+ {
+ let path = file.path();
+ let contents =
+ fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from `{}`: {}", path.display(), e));
+ let module = rel_path
+ .components()
+ .map(|c| c.as_os_str().to_str().unwrap())
+ .collect::
>()
+ .join("::");
+
+ // If the lints are stored in mod.rs, we get the module name from
+ // the containing directory:
+ let module = if let Some(module) = module.strip_suffix("::mod.rs") {
+ module
+ } else {
+ module.strip_suffix(".rs").unwrap_or(&module)
+ };
+
+ if module == "deprecated_lints" {
+ parse_deprecated_contents(&contents, &mut deprecated_lints);
+ } else {
+ parse_contents(&contents, module, &mut lints);
+ }
+ }
+ (lints, deprecated_lints)
}
-/// Whether a file has had its text changed or not
-#[derive(PartialEq, Debug)]
-struct FileChange {
- changed: bool,
- new_lines: String,
+macro_rules! match_tokens {
+ ($iter:ident, $($token:ident $({$($fields:tt)*})? $(($capture:ident))?)*) => {
+ {
+ $($(let $capture =)? if let Some((TokenKind::$token $({$($fields)*})?, _x)) = $iter.next() {
+ _x
+ } else {
+ continue;
+ };)*
+ #[allow(clippy::unused_unit)]
+ { ($($($capture,)?)*) }
+ }
+ }
+}
+
+/// Parse a source file looking for `declare_clippy_lint` macro invocations.
+fn parse_contents(contents: &str, module: &str, lints: &mut Vec) {
+ let mut offset = 0usize;
+ let mut iter = tokenize(contents).map(|t| {
+ let range = offset..offset + t.len;
+ offset = range.end;
+ (t.kind, &contents[range])
+ });
+
+ while iter.any(|(kind, s)| kind == TokenKind::Ident && s == "declare_clippy_lint") {
+ let mut iter = iter
+ .by_ref()
+ .filter(|&(kind, _)| !matches!(kind, TokenKind::Whitespace | TokenKind::LineComment { .. }));
+ // matches `!{`
+ match_tokens!(iter, Bang OpenBrace);
+ match iter.next() {
+ // #[clippy::version = "version"] pub
+ Some((TokenKind::Pound, _)) => {
+ match_tokens!(iter, OpenBracket Ident Colon Colon Ident Eq Literal{..} CloseBracket Ident);
+ },
+ // pub
+ Some((TokenKind::Ident, _)) => (),
+ _ => continue,
+ }
+ let (name, group, desc) = match_tokens!(
+ iter,
+ // LINT_NAME
+ Ident(name) Comma
+ // group,
+ Ident(group) Comma
+ // "description" }
+ Literal{..}(desc) CloseBrace
+ );
+ lints.push(Lint::new(name, group, desc, module));
+ }
+}
+
+/// Parse a source file looking for `declare_deprecated_lint` macro invocations.
+fn parse_deprecated_contents(contents: &str, lints: &mut Vec) {
+ let mut offset = 0usize;
+ let mut iter = tokenize(contents).map(|t| {
+ let range = offset..offset + t.len;
+ offset = range.end;
+ (t.kind, &contents[range])
+ });
+ while iter.any(|(kind, s)| kind == TokenKind::Ident && s == "declare_deprecated_lint") {
+ let mut iter = iter
+ .by_ref()
+ .filter(|&(kind, _)| !matches!(kind, TokenKind::Whitespace | TokenKind::LineComment { .. }));
+ let (name, reason) = match_tokens!(
+ iter,
+ // !{
+ Bang OpenBrace
+ // #[clippy::version = "version"]
+ Pound OpenBracket Ident Colon Colon Ident Eq Literal{..} CloseBracket
+ // pub LINT_NAME,
+ Ident Ident(name) Comma
+ // "description"
+ Literal{kind: LiteralKind::Str{..},..}(reason)
+ // }
+ CloseBrace
+ );
+ lints.push(DeprecatedLint::new(name, reason));
+ }
+}
+
+/// Removes the line splices and surrounding quotes from a string literal
+fn remove_line_splices(s: &str) -> String {
+ let s = s
+ .strip_prefix('r')
+ .unwrap_or(s)
+ .trim_matches('#')
+ .strip_prefix('"')
+ .and_then(|s| s.strip_suffix('"'))
+ .unwrap_or_else(|| panic!("expected quoted string, found `{}`", s));
+ let mut res = String::with_capacity(s.len());
+ unescape::unescape_literal(s, unescape::Mode::Str, &mut |range, _| res.push_str(&s[range]));
+ res
}
/// Replaces a region in a file delimited by two lines matching regexes.
@@ -396,144 +417,49 @@ struct FileChange {
/// # Panics
///
/// Panics if the path could not read or then written
-fn replace_region_in_file(
+fn replace_region_in_file(
+ update_mode: UpdateMode,
path: &Path,
start: &str,
end: &str,
- replace_start: bool,
- write_back: bool,
- replacements: F,
-) -> FileChange
-where
- F: FnOnce() -> Vec,
-{
- let contents = fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from {}: {}", path.display(), e));
- let file_change = replace_region_in_text(&contents, start, end, replace_start, replacements);
+ write_replacement: impl FnMut(&mut String),
+) {
+ let contents = fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from `{}`: {}", path.display(), e));
+ let new_contents = match replace_region_in_text(&contents, start, end, write_replacement) {
+ Ok(x) => x,
+ Err(delim) => panic!("Couldn't find `{}` in file `{}`", delim, path.display()),
+ };
- if write_back {
- if let Err(e) = fs::write(path, file_change.new_lines.as_bytes()) {
- panic!("Cannot write to {}: {}", path.display(), e);
- }
- }
- file_change
-}
-
-/// Replaces a region in a text delimited by two lines matching regexes.
-///
-/// * `text` is the input text on which you want to perform the replacement
-/// * `start` is a `&str` that describes the delimiter line before the region you want to replace.
-/// As the `&str` will be converted to a `Regex`, this can contain regex syntax, too.
-/// * `end` is a `&str` that describes the delimiter line until where the replacement should happen.
-/// As the `&str` will be converted to a `Regex`, this can contain regex syntax, too.
-/// * If `replace_start` is true, the `start` delimiter line is replaced as well. The `end`
-/// delimiter line is never replaced.
-/// * `replacements` is a closure that has to return a `Vec` which contains the new text.
-///
-/// If you want to perform the replacement on files instead of already parsed text,
-/// use `replace_region_in_file`.
-///
-/// # Example
-///
-/// ```ignore
-/// let the_text = "replace_start\nsome text\nthat will be replaced\nreplace_end";
-/// let result =
-/// replace_region_in_text(the_text, "replace_start", "replace_end", false, || {
-/// vec!["a different".to_string(), "text".to_string()]
-/// })
-/// .new_lines;
-/// assert_eq!("replace_start\na different\ntext\nreplace_end", result);
-/// ```
-///
-/// # Panics
-///
-/// Panics if start or end is not valid regex
-fn replace_region_in_text(text: &str, start: &str, end: &str, replace_start: bool, replacements: F) -> FileChange
-where
- F: FnOnce() -> Vec,
-{
- let replace_it = replacements();
- let mut in_old_region = false;
- let mut found = false;
- let mut new_lines = vec![];
- let start = Regex::new(start).unwrap();
- let end = Regex::new(end).unwrap();
-
- for line in text.lines() {
- if in_old_region {
- if end.is_match(line) {
- in_old_region = false;
- new_lines.extend(replace_it.clone());
- new_lines.push(line.to_string());
+ match update_mode {
+ UpdateMode::Check if contents != new_contents => exit_with_failure(),
+ UpdateMode::Check => (),
+ UpdateMode::Change => {
+ if let Err(e) = fs::write(path, new_contents.as_bytes()) {
+ panic!("Cannot write to `{}`: {}", path.display(), e);
}
- } else if start.is_match(line) {
- if !replace_start {
- new_lines.push(line.to_string());
- }
- in_old_region = true;
- found = true;
- } else {
- new_lines.push(line.to_string());
- }
+ },
}
-
- if !found {
- // This happens if the provided regex in `clippy_dev/src/main.rs` does not match in the
- // given text or file. Most likely this is an error on the programmer's side and the Regex
- // is incorrect.
- eprintln!("error: regex \n{:?}\ndoesn't match. You may have to update it.", start);
- std::process::exit(1);
- }
-
- let mut new_lines = new_lines.join("\n");
- if text.ends_with('\n') {
- new_lines.push('\n');
- }
- let changed = new_lines != text;
- FileChange { changed, new_lines }
}
-#[test]
-fn test_parse_contents() {
- let result: Vec = parse_contents(
- r#"
-declare_clippy_lint! {
- #[clippy::version = "Hello Clippy!"]
- pub PTR_ARG,
- style,
- "really long \
- text"
-}
+/// Replaces a region in a text delimited by two strings. Returns the new text if both delimiters
+/// were found, or the missing delimiter if not.
+fn replace_region_in_text<'a>(
+ text: &str,
+ start: &'a str,
+ end: &'a str,
+ mut write_replacement: impl FnMut(&mut String),
+) -> Result {
+ let (text_start, rest) = text.split_once(start).ok_or(start)?;
+ let (_, text_end) = rest.split_once(end).ok_or(end)?;
-declare_clippy_lint!{
- #[clippy::version = "Test version"]
- pub DOC_MARKDOWN,
- pedantic,
- "single line"
-}
+ let mut res = String::with_capacity(text.len() + 4096);
+ res.push_str(text_start);
+ res.push_str(start);
+ write_replacement(&mut res);
+ res.push_str(end);
+ res.push_str(text_end);
-/// some doc comment
-declare_deprecated_lint! {
- #[clippy::version = "I'm a version"]
- pub SHOULD_ASSERT_EQ,
- "`assert!()` will be more flexible with RFC 2011"
-}
- "#,
- "module_name",
- )
- .collect();
-
- let expected = vec![
- Lint::new("ptr_arg", "style", "really long text", None, "module_name"),
- Lint::new("doc_markdown", "pedantic", "single line", None, "module_name"),
- Lint::new(
- "should_assert_eq",
- "Deprecated",
- "`assert!()` will be more flexible with RFC 2011",
- Some("`assert!()` will be more flexible with RFC 2011"),
- "module_name",
- ),
- ];
- assert_eq!(expected, result);
+ Ok(res)
}
#[cfg(test)]
@@ -541,55 +467,65 @@ mod tests {
use super::*;
#[test]
- fn test_replace_region() {
- let text = "\nabc\n123\n789\ndef\nghi";
- let expected = FileChange {
- changed: true,
- new_lines: "\nabc\nhello world\ndef\nghi".to_string(),
- };
- let result = replace_region_in_text(text, r#"^\s*abc$"#, r#"^\s*def"#, false, || {
- vec!["hello world".to_string()]
- });
+ fn test_parse_contents() {
+ static CONTENTS: &str = r#"
+ declare_clippy_lint! {
+ #[clippy::version = "Hello Clippy!"]
+ pub PTR_ARG,
+ style,
+ "really long \
+ text"
+ }
+
+ declare_clippy_lint!{
+ #[clippy::version = "Test version"]
+ pub DOC_MARKDOWN,
+ pedantic,
+ "single line"
+ }
+ "#;
+ let mut result = Vec::new();
+ parse_contents(CONTENTS, "module_name", &mut result);
+
+ let expected = vec![
+ Lint::new("ptr_arg", "style", "\"really long text\"", "module_name"),
+ Lint::new("doc_markdown", "pedantic", "\"single line\"", "module_name"),
+ ];
assert_eq!(expected, result);
}
#[test]
- fn test_replace_region_with_start() {
- let text = "\nabc\n123\n789\ndef\nghi";
- let expected = FileChange {
- changed: true,
- new_lines: "\nhello world\ndef\nghi".to_string(),
- };
- let result = replace_region_in_text(text, r#"^\s*abc$"#, r#"^\s*def"#, true, || {
- vec!["hello world".to_string()]
- });
- assert_eq!(expected, result);
- }
+ fn test_parse_deprecated_contents() {
+ static DEPRECATED_CONTENTS: &str = r#"
+ /// some doc comment
+ declare_deprecated_lint! {
+ #[clippy::version = "I'm a version"]
+ pub SHOULD_ASSERT_EQ,
+ "`assert!()` will be more flexible with RFC 2011"
+ }
+ "#;
- #[test]
- fn test_replace_region_no_changes() {
- let text = "123\n456\n789";
- let expected = FileChange {
- changed: false,
- new_lines: "123\n456\n789".to_string(),
- };
- let result = replace_region_in_text(text, r#"^\s*123$"#, r#"^\s*456"#, false, Vec::new);
+ let mut result = Vec::new();
+ parse_deprecated_contents(DEPRECATED_CONTENTS, &mut result);
+
+ let expected = vec![DeprecatedLint::new(
+ "should_assert_eq",
+ "\"`assert!()` will be more flexible with RFC 2011\"",
+ )];
assert_eq!(expected, result);
}
#[test]
fn test_usable_lints() {
let lints = vec![
- Lint::new("should_assert_eq", "Deprecated", "abc", Some("Reason"), "module_name"),
- Lint::new("should_assert_eq2", "Not Deprecated", "abc", None, "module_name"),
- Lint::new("should_assert_eq2", "internal", "abc", None, "module_name"),
- Lint::new("should_assert_eq2", "internal_style", "abc", None, "module_name"),
+ Lint::new("should_assert_eq2", "Not Deprecated", "\"abc\"", "module_name"),
+ Lint::new("should_assert_eq2", "internal", "\"abc\"", "module_name"),
+ Lint::new("should_assert_eq2", "internal_style", "\"abc\"", "module_name"),
];
let expected = vec![Lint::new(
"should_assert_eq2",
"Not Deprecated",
- "abc",
- None,
+ "\"abc\"",
"module_name",
)];
assert_eq!(expected, Lint::usable_lints(&lints));
@@ -598,55 +534,30 @@ mod tests {
#[test]
fn test_by_lint_group() {
let lints = vec![
- Lint::new("should_assert_eq", "group1", "abc", None, "module_name"),
- Lint::new("should_assert_eq2", "group2", "abc", None, "module_name"),
- Lint::new("incorrect_match", "group1", "abc", None, "module_name"),
+ Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name"),
+ Lint::new("should_assert_eq2", "group2", "\"abc\"", "module_name"),
+ Lint::new("incorrect_match", "group1", "\"abc\"", "module_name"),
];
let mut expected: HashMap> = HashMap::new();
expected.insert(
"group1".to_string(),
vec![
- Lint::new("should_assert_eq", "group1", "abc", None, "module_name"),
- Lint::new("incorrect_match", "group1", "abc", None, "module_name"),
+ Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name"),
+ Lint::new("incorrect_match", "group1", "\"abc\"", "module_name"),
],
);
expected.insert(
"group2".to_string(),
- vec![Lint::new("should_assert_eq2", "group2", "abc", None, "module_name")],
+ vec![Lint::new("should_assert_eq2", "group2", "\"abc\"", "module_name")],
);
assert_eq!(expected, Lint::by_lint_group(lints.into_iter()));
}
- #[test]
- fn test_gen_changelog_lint_list() {
- let lints = vec![
- Lint::new("should_assert_eq", "group1", "abc", None, "module_name"),
- Lint::new("should_assert_eq2", "group2", "abc", None, "module_name"),
- ];
- let expected = vec![
- format!("[`should_assert_eq`]: {}#should_assert_eq", DOCS_LINK),
- format!("[`should_assert_eq2`]: {}#should_assert_eq2", DOCS_LINK),
- ];
- assert_eq!(expected, gen_changelog_lint_list(lints.iter()));
- }
-
#[test]
fn test_gen_deprecated() {
let lints = vec![
- Lint::new(
- "should_assert_eq",
- "group1",
- "abc",
- Some("has been superseded by should_assert_eq2"),
- "module_name",
- ),
- Lint::new(
- "another_deprecated",
- "group2",
- "abc",
- Some("will be removed"),
- "module_name",
- ),
+ DeprecatedLint::new("should_assert_eq", "\"has been superseded by should_assert_eq2\""),
+ DeprecatedLint::new("another_deprecated", "\"will be removed\""),
];
let expected = GENERATED_FILE_COMMENT.to_string()
@@ -665,32 +576,15 @@ mod tests {
.join("\n")
+ "\n";
- assert_eq!(expected, gen_deprecated(lints.iter()));
- }
-
- #[test]
- #[should_panic]
- fn test_gen_deprecated_fail() {
- let lints = vec![Lint::new("should_assert_eq2", "group2", "abc", None, "module_name")];
- let _deprecated_lints = gen_deprecated(lints.iter());
- }
-
- #[test]
- fn test_gen_modules_list() {
- let lints = vec![
- Lint::new("should_assert_eq", "group1", "abc", None, "module_name"),
- Lint::new("incorrect_stuff", "group3", "abc", None, "another_module"),
- ];
- let expected = vec!["mod another_module;".to_string(), "mod module_name;".to_string()];
- assert_eq!(expected, gen_modules_list(lints.iter()));
+ assert_eq!(expected, gen_deprecated(&lints));
}
#[test]
fn test_gen_lint_group_list() {
let lints = vec![
- Lint::new("abc", "group1", "abc", None, "module_name"),
- Lint::new("should_assert_eq", "group1", "abc", None, "module_name"),
- Lint::new("internal", "internal_style", "abc", None, "module_name"),
+ Lint::new("abc", "group1", "\"abc\"", "module_name"),
+ Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name"),
+ Lint::new("internal", "internal_style", "\"abc\"", "module_name"),
];
let expected = GENERATED_FILE_COMMENT.to_string()
+ &[
diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml
index 66e61660d313..aebf9a87cabd 100644
--- a/clippy_lints/Cargo.toml
+++ b/clippy_lints/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "clippy_lints"
-version = "0.1.61"
+version = "0.1.62"
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
repository = "https://github.com/rust-lang/rust-clippy"
readme = "README.md"
diff --git a/clippy_lints/src/casts/cast_abs_to_unsigned.rs b/clippy_lints/src/casts/cast_abs_to_unsigned.rs
new file mode 100644
index 000000000000..e9b0f1f672de
--- /dev/null
+++ b/clippy_lints/src/casts/cast_abs_to_unsigned.rs
@@ -0,0 +1,42 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::sugg::Sugg;
+use clippy_utils::{meets_msrv, msrvs};
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir::{Expr, ExprKind};
+use rustc_lint::LateContext;
+use rustc_middle::ty::Ty;
+use rustc_semver::RustcVersion;
+
+use super::CAST_ABS_TO_UNSIGNED;
+
+pub(super) fn check(
+ cx: &LateContext<'_>,
+ expr: &Expr<'_>,
+ cast_expr: &Expr<'_>,
+ cast_from: Ty<'_>,
+ cast_to: Ty<'_>,
+ msrv: &Option,
+) {
+ if_chain! {
+ if meets_msrv(msrv.as_ref(), &msrvs::UNSIGNED_ABS);
+ if cast_from.is_integral();
+ if cast_to.is_integral();
+ if cast_from.is_signed();
+ if !cast_to.is_signed();
+ if let ExprKind::MethodCall(method_path, args, _) = cast_expr.kind;
+ if let method_name = method_path.ident.name.as_str();
+ if method_name == "abs";
+ then {
+ span_lint_and_sugg(
+ cx,
+ CAST_ABS_TO_UNSIGNED,
+ expr.span,
+ &format!("casting the result of `{}::{}()` to {}", cast_from, method_name, cast_to),
+ "replace with",
+ format!("{}.unsigned_abs()", Sugg::hir(cx, &args[0], "..")),
+ Applicability::MachineApplicable,
+ );
+ }
+ }
+}
diff --git a/clippy_lints/src/casts/cast_ptr_alignment.rs b/clippy_lints/src/casts/cast_ptr_alignment.rs
index a4ef1344ab95..d476a1a7646c 100644
--- a/clippy_lints/src/casts/cast_ptr_alignment.rs
+++ b/clippy_lints/src/casts/cast_ptr_alignment.rs
@@ -1,7 +1,6 @@
use clippy_utils::diagnostics::span_lint;
-use clippy_utils::is_hir_ty_cfg_dependant;
use clippy_utils::ty::is_c_void;
-use if_chain::if_chain;
+use clippy_utils::{get_parent_expr, is_hir_ty_cfg_dependant, match_any_def_paths, paths};
use rustc_hir::{Expr, ExprKind, GenericArg};
use rustc_lint::LateContext;
use rustc_middle::ty::layout::LayoutOf;
@@ -20,45 +19,78 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
);
lint_cast_ptr_alignment(cx, expr, cast_from, cast_to);
} else if let ExprKind::MethodCall(method_path, [self_arg, ..], _) = &expr.kind {
- if_chain! {
- if method_path.ident.name == sym!(cast);
- if let Some(generic_args) = method_path.args;
- if let [GenericArg::Type(cast_to)] = generic_args.args;
+ if method_path.ident.name == sym!(cast)
+ && let Some(generic_args) = method_path.args
+ && let [GenericArg::Type(cast_to)] = generic_args.args
// There probably is no obvious reason to do this, just to be consistent with `as` cases.
- if !is_hir_ty_cfg_dependant(cx, cast_to);
- then {
- let (cast_from, cast_to) =
- (cx.typeck_results().expr_ty(self_arg), cx.typeck_results().expr_ty(expr));
- lint_cast_ptr_alignment(cx, expr, cast_from, cast_to);
- }
+ && !is_hir_ty_cfg_dependant(cx, cast_to)
+ {
+ let (cast_from, cast_to) =
+ (cx.typeck_results().expr_ty(self_arg), cx.typeck_results().expr_ty(expr));
+ lint_cast_ptr_alignment(cx, expr, cast_from, cast_to);
}
}
}
fn lint_cast_ptr_alignment<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, cast_from: Ty<'tcx>, cast_to: Ty<'tcx>) {
- if_chain! {
- if let ty::RawPtr(from_ptr_ty) = &cast_from.kind();
- if let ty::RawPtr(to_ptr_ty) = &cast_to.kind();
- if let Ok(from_layout) = cx.layout_of(from_ptr_ty.ty);
- if let Ok(to_layout) = cx.layout_of(to_ptr_ty.ty);
- if from_layout.align.abi < to_layout.align.abi;
+ if let ty::RawPtr(from_ptr_ty) = &cast_from.kind()
+ && let ty::RawPtr(to_ptr_ty) = &cast_to.kind()
+ && let Ok(from_layout) = cx.layout_of(from_ptr_ty.ty)
+ && let Ok(to_layout) = cx.layout_of(to_ptr_ty.ty)
+ && from_layout.align.abi < to_layout.align.abi
// with c_void, we inherently need to trust the user
- if !is_c_void(cx, from_ptr_ty.ty);
+ && !is_c_void(cx, from_ptr_ty.ty)
// when casting from a ZST, we don't know enough to properly lint
- if !from_layout.is_zst();
- then {
- span_lint(
- cx,
- CAST_PTR_ALIGNMENT,
- expr.span,
- &format!(
- "casting from `{}` to a more-strictly-aligned pointer (`{}`) ({} < {} bytes)",
- cast_from,
- cast_to,
- from_layout.align.abi.bytes(),
- to_layout.align.abi.bytes(),
- ),
- );
- }
+ && !from_layout.is_zst()
+ && !is_used_as_unaligned(cx, expr)
+ {
+ span_lint(
+ cx,
+ CAST_PTR_ALIGNMENT,
+ expr.span,
+ &format!(
+ "casting from `{}` to a more-strictly-aligned pointer (`{}`) ({} < {} bytes)",
+ cast_from,
+ cast_to,
+ from_layout.align.abi.bytes(),
+ to_layout.align.abi.bytes(),
+ ),
+ );
+ }
+}
+
+fn is_used_as_unaligned(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
+ let Some(parent) = get_parent_expr(cx, e) else {
+ return false;
+ };
+ match parent.kind {
+ ExprKind::MethodCall(name, [self_arg, ..], _) if self_arg.hir_id == e.hir_id => {
+ if matches!(name.ident.as_str(), "read_unaligned" | "write_unaligned")
+ && let Some(def_id) = cx.typeck_results().type_dependent_def_id(parent.hir_id)
+ && let Some(def_id) = cx.tcx.impl_of_method(def_id)
+ && cx.tcx.type_of(def_id).is_unsafe_ptr()
+ {
+ true
+ } else {
+ false
+ }
+ },
+ ExprKind::Call(func, [arg, ..]) if arg.hir_id == e.hir_id => {
+ static PATHS: &[&[&str]] = &[
+ paths::PTR_READ_UNALIGNED.as_slice(),
+ paths::PTR_WRITE_UNALIGNED.as_slice(),
+ paths::PTR_UNALIGNED_VOLATILE_LOAD.as_slice(),
+ paths::PTR_UNALIGNED_VOLATILE_STORE.as_slice(),
+ ];
+ if let ExprKind::Path(path) = &func.kind
+ && let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id()
+ && match_any_def_paths(cx, def_id, PATHS).is_some()
+ {
+ true
+ } else {
+ false
+ }
+ },
+ _ => false,
}
}
diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs
index be59145afa00..55c1f085657b 100644
--- a/clippy_lints/src/casts/mod.rs
+++ b/clippy_lints/src/casts/mod.rs
@@ -1,3 +1,4 @@
+mod cast_abs_to_unsigned;
mod cast_enum_constructor;
mod cast_lossless;
mod cast_possible_truncation;
@@ -473,6 +474,28 @@ declare_clippy_lint! {
"casts from an enum tuple constructor to an integer"
}
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for uses of the `abs()` method that cast the result to unsigned.
+ ///
+ /// ### Why is this bad?
+ /// The `unsigned_abs()` method avoids panic when called on the MIN value.
+ ///
+ /// ### Example
+ /// ```rust
+ /// let x: i32 = -42;
+ /// let y: u32 = x.abs() as u32;
+ /// ```
+ /// Use instead:
+ /// let x: i32 = -42;
+ /// let y: u32 = x.unsigned_abs();
+ /// ```
+ #[clippy::version = "1.61.0"]
+ pub CAST_ABS_TO_UNSIGNED,
+ suspicious,
+ "casting the result of `abs()` to an unsigned integer can panic"
+}
+
pub struct Casts {
msrv: Option,
}
@@ -500,7 +523,8 @@ impl_lint_pass!(Casts => [
CHAR_LIT_AS_U8,
PTR_AS_PTR,
CAST_ENUM_TRUNCATION,
- CAST_ENUM_CONSTRUCTOR
+ CAST_ENUM_CONSTRUCTOR,
+ CAST_ABS_TO_UNSIGNED
]);
impl<'tcx> LateLintPass<'tcx> for Casts {
@@ -536,6 +560,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
cast_possible_wrap::check(cx, expr, cast_from, cast_to);
cast_precision_loss::check(cx, expr, cast_from, cast_to);
cast_sign_loss::check(cx, expr, cast_expr, cast_from, cast_to);
+ cast_abs_to_unsigned::check(cx, expr, cast_expr, cast_from, cast_to, &self.msrv);
}
cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to, &self.msrv);
cast_enum_constructor::check(cx, expr, cast_expr, cast_from);
diff --git a/clippy_lints/src/casts/unnecessary_cast.rs b/clippy_lints/src/casts/unnecessary_cast.rs
index 470c8c7ea26a..af56ec11ef8a 100644
--- a/clippy_lints/src/casts/unnecessary_cast.rs
+++ b/clippy_lints/src/casts/unnecessary_cast.rs
@@ -4,7 +4,8 @@ use clippy_utils::source::snippet_opt;
use if_chain::if_chain;
use rustc_ast::{LitFloatType, LitIntType, LitKind};
use rustc_errors::Applicability;
-use rustc_hir::{Expr, ExprKind, Lit, UnOp};
+use rustc_hir::def::Res;
+use rustc_hir::{Expr, ExprKind, Lit, QPath, TyKind, UnOp};
use rustc_lint::{LateContext, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::{self, FloatTy, InferTy, Ty};
@@ -18,6 +19,17 @@ pub(super) fn check(
cast_from: Ty<'_>,
cast_to: Ty<'_>,
) -> bool {
+ // skip non-primitive type cast
+ if_chain! {
+ if let ExprKind::Cast(_, cast_to) = expr.kind;
+ if let TyKind::Path(QPath::Resolved(_, path)) = &cast_to.kind;
+ if let Res::PrimTy(_) = path.res;
+ then {}
+ else {
+ return false
+ }
+ }
+
if let Some(lit) = get_numeric_literal(cast_expr) {
let literal_str = snippet_opt(cx, cast_expr.span).unwrap_or_default();
diff --git a/clippy_lints/src/crate_in_macro_def.rs b/clippy_lints/src/crate_in_macro_def.rs
new file mode 100644
index 000000000000..fc141b4a6e3a
--- /dev/null
+++ b/clippy_lints/src/crate_in_macro_def.rs
@@ -0,0 +1,125 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use rustc_ast::ast::{AttrKind, Attribute, Item, ItemKind};
+use rustc_ast::token::{Token, TokenKind};
+use rustc_ast::tokenstream::{TokenStream, TokenTree};
+use rustc_errors::Applicability;
+use rustc_lint::{EarlyContext, EarlyLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::{symbol::sym, Span};
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for use of `crate` as opposed to `$crate` in a macro definition.
+ ///
+ /// ### Why is this bad?
+ /// `crate` refers to the macro call's crate, whereas `$crate` refers to the macro definition's
+ /// crate. Rarely is the former intended. See:
+ /// https://doc.rust-lang.org/reference/macros-by-example.html#hygiene
+ ///
+ /// ### Example
+ /// ```rust
+ /// #[macro_export]
+ /// macro_rules! print_message {
+ /// () => {
+ /// println!("{}", crate::MESSAGE);
+ /// };
+ /// }
+ /// pub const MESSAGE: &str = "Hello!";
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// #[macro_export]
+ /// macro_rules! print_message {
+ /// () => {
+ /// println!("{}", $crate::MESSAGE);
+ /// };
+ /// }
+ /// pub const MESSAGE: &str = "Hello!";
+ /// ```
+ ///
+ /// Note that if the use of `crate` is intentional, an `allow` attribute can be applied to the
+ /// macro definition, e.g.:
+ /// ```rust,ignore
+ /// #[allow(clippy::crate_in_macro_def)]
+ /// macro_rules! ok { ... crate::foo ... }
+ /// ```
+ #[clippy::version = "1.61.0"]
+ pub CRATE_IN_MACRO_DEF,
+ suspicious,
+ "using `crate` in a macro definition"
+}
+declare_lint_pass!(CrateInMacroDef => [CRATE_IN_MACRO_DEF]);
+
+impl EarlyLintPass for CrateInMacroDef {
+ fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
+ if_chain! {
+ if item.attrs.iter().any(is_macro_export);
+ if let ItemKind::MacroDef(macro_def) = &item.kind;
+ let tts = macro_def.body.inner_tokens();
+ if let Some(span) = contains_unhygienic_crate_reference(&tts);
+ then {
+ span_lint_and_sugg(
+ cx,
+ CRATE_IN_MACRO_DEF,
+ span,
+ "`crate` references the macro call's crate",
+ "to reference the macro definition's crate, use",
+ String::from("$crate"),
+ Applicability::MachineApplicable,
+ );
+ }
+ }
+ }
+}
+
+fn is_macro_export(attr: &Attribute) -> bool {
+ if_chain! {
+ if let AttrKind::Normal(attr_item, _) = &attr.kind;
+ if let [segment] = attr_item.path.segments.as_slice();
+ then {
+ segment.ident.name == sym::macro_export
+ } else {
+ false
+ }
+ }
+}
+
+fn contains_unhygienic_crate_reference(tts: &TokenStream) -> Option {
+ let mut prev_is_dollar = false;
+ let mut cursor = tts.trees();
+ while let Some(curr) = cursor.next() {
+ if_chain! {
+ if !prev_is_dollar;
+ if let Some(span) = is_crate_keyword(&curr);
+ if let Some(next) = cursor.look_ahead(0);
+ if is_token(next, &TokenKind::ModSep);
+ then {
+ return Some(span);
+ }
+ }
+ if let TokenTree::Delimited(_, _, tts) = &curr {
+ let span = contains_unhygienic_crate_reference(tts);
+ if span.is_some() {
+ return span;
+ }
+ }
+ prev_is_dollar = is_token(&curr, &TokenKind::Dollar);
+ }
+ None
+}
+
+fn is_crate_keyword(tt: &TokenTree) -> Option {
+ if_chain! {
+ if let TokenTree::Token(Token { kind: TokenKind::Ident(symbol, _), span }) = tt;
+ if symbol.as_str() == "crate";
+ then { Some(*span) } else { None }
+ }
+}
+
+fn is_token(tt: &TokenTree, kind: &TokenKind) -> bool {
+ if let TokenTree::Token(Token { kind: other, .. }) = tt {
+ kind == other
+ } else {
+ false
+ }
+}
diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs
index 92cf82bcd6a3..28d0c75fde6b 100644
--- a/clippy_lints/src/doc.rs
+++ b/clippy_lints/src/doc.rs
@@ -621,8 +621,8 @@ fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) {
let filename = FileName::anon_source_code(&code);
let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
- let fallback_bundle = rustc_errors::fallback_fluent_bundle(false)
- .expect("failed to load fallback fluent bundle");
+ let fallback_bundle =
+ rustc_errors::fallback_fluent_bundle(false).expect("failed to load fallback fluent bundle");
let emitter = EmitterWriter::new(
Box::new(io::sink()),
None,
diff --git a/clippy_lints/src/drop_forget_ref.rs b/clippy_lints/src/drop_forget_ref.rs
index 5c4b35fd4b9d..88c54828da83 100644
--- a/clippy_lints/src/drop_forget_ref.rs
+++ b/clippy_lints/src/drop_forget_ref.rs
@@ -1,9 +1,8 @@
-use clippy_utils::diagnostics::span_lint_and_note;
-use clippy_utils::ty::is_copy;
-use if_chain::if_chain;
-use rustc_hir::{Expr, ExprKind};
+use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note};
+use clippy_utils::is_must_use_func_call;
+use clippy_utils::ty::{is_copy, is_must_use_ty, is_type_lang_item};
+use rustc_hir::{Expr, ExprKind, LangItem};
use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym;
@@ -103,6 +102,75 @@ declare_clippy_lint! {
"calls to `std::mem::forget` with a value that implements Copy"
}
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for calls to `std::mem::drop` with a value that does not implement `Drop`.
+ ///
+ /// ### Why is this bad?
+ /// Calling `std::mem::drop` is no different than dropping such a type. A different value may
+ /// have been intended.
+ ///
+ /// ### Example
+ /// ```rust
+ /// struct Foo;
+ /// let x = Foo;
+ /// std::mem::drop(x);
+ /// ```
+ #[clippy::version = "1.61.0"]
+ pub DROP_NON_DROP,
+ suspicious,
+ "call to `std::mem::drop` with a value which does not implement `Drop`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for calls to `std::mem::forget` with a value that does not implement `Drop`.
+ ///
+ /// ### Why is this bad?
+ /// Calling `std::mem::forget` is no different than dropping such a type. A different value may
+ /// have been intended.
+ ///
+ /// ### Example
+ /// ```rust
+ /// struct Foo;
+ /// let x = Foo;
+ /// std::mem::forget(x);
+ /// ```
+ #[clippy::version = "1.61.0"]
+ pub FORGET_NON_DROP,
+ suspicious,
+ "call to `std::mem::forget` with a value which does not implement `Drop`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Prevents the safe `std::mem::drop` function from being called on `std::mem::ManuallyDrop`.
+ ///
+ /// ### Why is this bad?
+ /// The safe `drop` function does not drop the inner value of a `ManuallyDrop`.
+ ///
+ /// ### Known problems
+ /// Does not catch cases if the user binds `std::mem::drop`
+ /// to a different name and calls it that way.
+ ///
+ /// ### Example
+ /// ```rust
+ /// struct S;
+ /// drop(std::mem::ManuallyDrop::new(S));
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// struct S;
+ /// unsafe {
+ /// std::mem::ManuallyDrop::drop(&mut std::mem::ManuallyDrop::new(S));
+ /// }
+ /// ```
+ #[clippy::version = "1.49.0"]
+ pub UNDROPPED_MANUALLY_DROPS,
+ correctness,
+ "use of safe `std::mem::drop` function to drop a std::mem::ManuallyDrop, which will not drop the inner value"
+}
+
const DROP_REF_SUMMARY: &str = "calls to `std::mem::drop` with a reference instead of an owned value. \
Dropping a reference does nothing";
const FORGET_REF_SUMMARY: &str = "calls to `std::mem::forget` with a reference instead of an owned value. \
@@ -111,60 +179,65 @@ const DROP_COPY_SUMMARY: &str = "calls to `std::mem::drop` with a value that imp
Dropping a copy leaves the original intact";
const FORGET_COPY_SUMMARY: &str = "calls to `std::mem::forget` with a value that implements `Copy`. \
Forgetting a copy leaves the original intact";
+const DROP_NON_DROP_SUMMARY: &str = "call to `std::mem::drop` with a value that does not implement `Drop`. \
+ Dropping such a type only extends it's contained lifetimes";
+const FORGET_NON_DROP_SUMMARY: &str = "call to `std::mem::forget` with a value that does not implement `Drop`. \
+ Forgetting such a type is the same as dropping it";
-declare_lint_pass!(DropForgetRef => [DROP_REF, FORGET_REF, DROP_COPY, FORGET_COPY]);
+declare_lint_pass!(DropForgetRef => [
+ DROP_REF,
+ FORGET_REF,
+ DROP_COPY,
+ FORGET_COPY,
+ DROP_NON_DROP,
+ FORGET_NON_DROP,
+ UNDROPPED_MANUALLY_DROPS
+]);
impl<'tcx> LateLintPass<'tcx> for DropForgetRef {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
- if_chain! {
- if let ExprKind::Call(path, args) = expr.kind;
- if let ExprKind::Path(ref qpath) = path.kind;
- if args.len() == 1;
- if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id();
- then {
- let lint;
- let msg;
- let arg = &args[0];
- let arg_ty = cx.typeck_results().expr_ty(arg);
-
- if let ty::Ref(..) = arg_ty.kind() {
- match cx.tcx.get_diagnostic_name(def_id) {
- Some(sym::mem_drop) => {
- lint = DROP_REF;
- msg = DROP_REF_SUMMARY.to_string();
- },
- Some(sym::mem_forget) => {
- lint = FORGET_REF;
- msg = FORGET_REF_SUMMARY.to_string();
- },
- _ => return,
- }
- span_lint_and_note(cx,
- lint,
- expr.span,
- &msg,
- Some(arg.span),
- &format!("argument has type `{}`", arg_ty));
- } else if is_copy(cx, arg_ty) {
- match cx.tcx.get_diagnostic_name(def_id) {
- Some(sym::mem_drop) => {
- lint = DROP_COPY;
- msg = DROP_COPY_SUMMARY.to_string();
- },
- Some(sym::mem_forget) => {
- lint = FORGET_COPY;
- msg = FORGET_COPY_SUMMARY.to_string();
- },
- _ => return,
- }
- span_lint_and_note(cx,
- lint,
- expr.span,
- &msg,
- Some(arg.span),
- &format!("argument has type {}", arg_ty));
+ if let ExprKind::Call(path, [arg]) = expr.kind
+ && let ExprKind::Path(ref qpath) = path.kind
+ && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
+ && let Some(fn_name) = cx.tcx.get_diagnostic_name(def_id)
+ {
+ let arg_ty = cx.typeck_results().expr_ty(arg);
+ let (lint, msg) = match fn_name {
+ sym::mem_drop if arg_ty.is_ref() => (DROP_REF, DROP_REF_SUMMARY),
+ sym::mem_forget if arg_ty.is_ref() => (FORGET_REF, FORGET_REF_SUMMARY),
+ sym::mem_drop if is_copy(cx, arg_ty) => (DROP_COPY, DROP_COPY_SUMMARY),
+ sym::mem_forget if is_copy(cx, arg_ty) => (FORGET_COPY, FORGET_COPY_SUMMARY),
+ sym::mem_drop if is_type_lang_item(cx, arg_ty, LangItem::ManuallyDrop) => {
+ span_lint_and_help(
+ cx,
+ UNDROPPED_MANUALLY_DROPS,
+ expr.span,
+ "the inner value of this ManuallyDrop will not be dropped",
+ None,
+ "to drop a `ManuallyDrop`, use std::mem::ManuallyDrop::drop",
+ );
+ return;
}
- }
+ sym::mem_drop
+ if !(arg_ty.needs_drop(cx.tcx, cx.param_env)
+ || is_must_use_func_call(cx, arg)
+ || is_must_use_ty(cx, arg_ty)) =>
+ {
+ (DROP_NON_DROP, DROP_NON_DROP_SUMMARY)
+ },
+ sym::mem_forget if !arg_ty.needs_drop(cx.tcx, cx.param_env) => {
+ (FORGET_NON_DROP, FORGET_NON_DROP_SUMMARY)
+ },
+ _ => return,
+ };
+ span_lint_and_note(
+ cx,
+ lint,
+ expr.span,
+ msg,
+ Some(arg.span),
+ &format!("argument has type `{}`", arg_ty),
+ );
}
}
}
diff --git a/clippy_lints/src/empty_structs_with_brackets.rs b/clippy_lints/src/empty_structs_with_brackets.rs
new file mode 100644
index 000000000000..fdeac8d82557
--- /dev/null
+++ b/clippy_lints/src/empty_structs_with_brackets.rs
@@ -0,0 +1,99 @@
+use clippy_utils::{diagnostics::span_lint_and_then, source::snippet_opt};
+use rustc_ast::ast::{Item, ItemKind, VariantData};
+use rustc_errors::Applicability;
+use rustc_lexer::TokenKind;
+use rustc_lint::{EarlyContext, EarlyLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::Span;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Finds structs without fields (a so-called "empty struct") that are declared with brackets.
+ ///
+ /// ### Why is this bad?
+ /// Empty brackets after a struct declaration can be omitted.
+ ///
+ /// ### Example
+ /// ```rust
+ /// struct Cookie {}
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// struct Cookie;
+ /// ```
+ #[clippy::version = "1.62.0"]
+ pub EMPTY_STRUCTS_WITH_BRACKETS,
+ restriction,
+ "finds struct declarations with empty brackets"
+}
+declare_lint_pass!(EmptyStructsWithBrackets => [EMPTY_STRUCTS_WITH_BRACKETS]);
+
+impl EarlyLintPass for EmptyStructsWithBrackets {
+ fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
+ let span_after_ident = item.span.with_lo(item.ident.span.hi());
+
+ if let ItemKind::Struct(var_data, _) = &item.kind
+ && has_brackets(var_data)
+ && has_no_fields(cx, var_data, span_after_ident) {
+ span_lint_and_then(
+ cx,
+ EMPTY_STRUCTS_WITH_BRACKETS,
+ span_after_ident,
+ "found empty brackets on struct declaration",
+ |diagnostic| {
+ diagnostic.span_suggestion_hidden(
+ span_after_ident,
+ "remove the brackets",
+ ";".to_string(),
+ Applicability::MachineApplicable);
+ },
+ );
+ }
+ }
+}
+
+fn has_no_ident_token(braces_span_str: &str) -> bool {
+ !rustc_lexer::tokenize(braces_span_str).any(|t| t.kind == TokenKind::Ident)
+}
+
+fn has_brackets(var_data: &VariantData) -> bool {
+ !matches!(var_data, VariantData::Unit(_))
+}
+
+fn has_no_fields(cx: &EarlyContext<'_>, var_data: &VariantData, braces_span: Span) -> bool {
+ if !var_data.fields().is_empty() {
+ return false;
+ }
+
+ // there might still be field declarations hidden from the AST
+ // (conditionaly compiled code using #[cfg(..)])
+
+ let Some(braces_span_str) = snippet_opt(cx, braces_span) else {
+ return false;
+ };
+
+ has_no_ident_token(braces_span_str.as_ref())
+}
+
+#[cfg(test)]
+mod unit_test {
+ use super::*;
+
+ #[test]
+ fn test_has_no_ident_token() {
+ let input = "{ field: u8 }";
+ assert!(!has_no_ident_token(input));
+
+ let input = "(u8, String);";
+ assert!(!has_no_ident_token(input));
+
+ let input = " {
+ // test = 5
+ }
+ ";
+ assert!(has_no_ident_token(input));
+
+ let input = " ();";
+ assert!(has_no_ident_token(input));
+ }
+}
diff --git a/clippy_lints/src/identity_op.rs b/clippy_lints/src/identity_op.rs
index f824f20ca40a..4d6bef89bea7 100644
--- a/clippy_lints/src/identity_op.rs
+++ b/clippy_lints/src/identity_op.rs
@@ -5,7 +5,7 @@ use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Span;
-use clippy_utils::consts::{constant_simple, Constant};
+use clippy_utils::consts::{constant_full_int, constant_simple, Constant, FullInt};
use clippy_utils::diagnostics::span_lint;
use clippy_utils::{clip, unsext};
@@ -54,6 +54,7 @@ impl<'tcx> LateLintPass<'tcx> for IdentityOp {
check(cx, left, -1, e.span, right.span);
check(cx, right, -1, e.span, left.span);
},
+ BinOpKind::Rem => check_remainder(cx, left, right, e.span, left.span),
_ => (),
}
}
@@ -70,6 +71,18 @@ fn is_allowed(cx: &LateContext<'_>, cmp: BinOp, left: &Expr<'_>, right: &Expr<'_
&& constant_simple(cx, cx.typeck_results(), left) == Some(Constant::Int(1)))
}
+fn check_remainder(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>, span: Span, arg: Span) {
+ let lhs_const = constant_full_int(cx, cx.typeck_results(), left);
+ let rhs_const = constant_full_int(cx, cx.typeck_results(), right);
+ if match (lhs_const, rhs_const) {
+ (Some(FullInt::S(lv)), Some(FullInt::S(rv))) => lv.abs() < rv.abs(),
+ (Some(FullInt::U(lv)), Some(FullInt::U(rv))) => lv < rv,
+ _ => return,
+ } {
+ span_ineffective_operation(cx, span, arg);
+ }
+}
+
fn check(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span) {
if let Some(Constant::Int(v)) = constant_simple(cx, cx.typeck_results(), e).map(Constant::peel_refs) {
let check = match *cx.typeck_results().expr_ty(e).peel_refs().kind() {
@@ -83,15 +96,19 @@ fn check(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span) {
1 => v == 1,
_ => unreachable!(),
} {
- span_lint(
- cx,
- IDENTITY_OP,
- span,
- &format!(
- "the operation is ineffective. Consider reducing it to `{}`",
- snippet(cx, arg, "..")
- ),
- );
+ span_ineffective_operation(cx, span, arg);
}
}
}
+
+fn span_ineffective_operation(cx: &LateContext<'_>, span: Span, arg: Span) {
+ span_lint(
+ cx,
+ IDENTITY_OP,
+ span,
+ &format!(
+ "the operation is ineffective. Consider reducing it to `{}`",
+ snippet(cx, arg, "..")
+ ),
+ );
+}
diff --git a/clippy_lints/src/indexing_slicing.rs b/clippy_lints/src/indexing_slicing.rs
index 9ead4bb27a58..4ba7477add82 100644
--- a/clippy_lints/src/indexing_slicing.rs
+++ b/clippy_lints/src/indexing_slicing.rs
@@ -96,6 +96,10 @@ declare_lint_pass!(IndexingSlicing => [INDEXING_SLICING, OUT_OF_BOUNDS_INDEXING]
impl<'tcx> LateLintPass<'tcx> for IndexingSlicing {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+ if cx.tcx.hir().is_inside_const_context(expr.hir_id) {
+ return;
+ }
+
if let ExprKind::Index(array, index) = &expr.kind {
let ty = cx.typeck_results().expr_ty(array).peel_refs();
if let Some(range) = higher::Range::hir(index) {
@@ -151,6 +155,10 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing {
} else {
// Catchall non-range index, i.e., [n] or [n << m]
if let ty::Array(..) = ty.kind() {
+ // Index is a const block.
+ if let ExprKind::ConstBlock(..) = index.kind {
+ return;
+ }
// Index is a constant uint.
if let Some(..) = constant(cx, cx.typeck_results(), index) {
// Let rustc's `const_err` lint handle constant `usize` indexing on arrays.
diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs
index 132a46626762..14ca93b5f3c1 100644
--- a/clippy_lints/src/lib.register_all.rs
+++ b/clippy_lints/src/lib.register_all.rs
@@ -23,6 +23,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON),
LintId::of(booleans::LOGIC_BUG),
LintId::of(booleans::NONMINIMAL_BOOL),
+ LintId::of(casts::CAST_ABS_TO_UNSIGNED),
LintId::of(casts::CAST_ENUM_CONSTRUCTOR),
LintId::of(casts::CAST_ENUM_TRUNCATION),
LintId::of(casts::CAST_REF_TO_MUT),
@@ -37,6 +38,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
LintId::of(comparison_chain::COMPARISON_CHAIN),
LintId::of(copies::IFS_SAME_COND),
LintId::of(copies::IF_SAME_THEN_ELSE),
+ LintId::of(crate_in_macro_def::CRATE_IN_MACRO_DEF),
LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT),
LintId::of(dereference::NEEDLESS_BORROW),
LintId::of(derivable_impls::DERIVABLE_IMPLS),
@@ -49,9 +51,12 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
LintId::of(double_comparison::DOUBLE_COMPARISONS),
LintId::of(double_parens::DOUBLE_PARENS),
LintId::of(drop_forget_ref::DROP_COPY),
+ LintId::of(drop_forget_ref::DROP_NON_DROP),
LintId::of(drop_forget_ref::DROP_REF),
LintId::of(drop_forget_ref::FORGET_COPY),
+ LintId::of(drop_forget_ref::FORGET_NON_DROP),
LintId::of(drop_forget_ref::FORGET_REF),
+ LintId::of(drop_forget_ref::UNDROPPED_MANUALLY_DROPS),
LintId::of(duration_subsec::DURATION_SUBSEC),
LintId::of(entry::MAP_ENTRY),
LintId::of(enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT),
@@ -152,6 +157,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
LintId::of(methods::CHARS_NEXT_CMP),
LintId::of(methods::CLONE_DOUBLE_REF),
LintId::of(methods::CLONE_ON_COPY),
+ LintId::of(methods::ERR_EXPECT),
LintId::of(methods::EXPECT_FUN_CALL),
LintId::of(methods::EXTEND_WITH_DRAIN),
LintId::of(methods::FILTER_MAP_IDENTITY),
@@ -175,6 +181,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
LintId::of(methods::MAP_COLLECT_RESULT_UNIT),
LintId::of(methods::MAP_FLATTEN),
LintId::of(methods::MAP_IDENTITY),
+ LintId::of(methods::NEEDLESS_OPTION_AS_DEREF),
LintId::of(methods::NEEDLESS_SPLITN),
LintId::of(methods::NEW_RET_NO_SELF),
LintId::of(methods::OK_EXPECT),
@@ -224,7 +231,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
LintId::of(needless_bool::NEEDLESS_BOOL),
LintId::of(needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE),
LintId::of(needless_late_init::NEEDLESS_LATE_INIT),
- LintId::of(needless_option_as_deref::NEEDLESS_OPTION_AS_DEREF),
LintId::of(needless_question_mark::NEEDLESS_QUESTION_MARK),
LintId::of(needless_update::NEEDLESS_UPDATE),
LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD),
@@ -296,7 +302,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
LintId::of(types::REDUNDANT_ALLOCATION),
LintId::of(types::TYPE_COMPLEXITY),
LintId::of(types::VEC_BOX),
- LintId::of(undropped_manually_drops::UNDROPPED_MANUALLY_DROPS),
LintId::of(unicode::INVISIBLE_CHARACTERS),
LintId::of(uninit_vec::UNINIT_VEC),
LintId::of(unit_hash::UNIT_HASH),
diff --git a/clippy_lints/src/lib.register_complexity.rs b/clippy_lints/src/lib.register_complexity.rs
index a2ce69065f94..10369a855ae6 100644
--- a/clippy_lints/src/lib.register_complexity.rs
+++ b/clippy_lints/src/lib.register_complexity.rs
@@ -44,6 +44,7 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec!
LintId::of(methods::MANUAL_SPLIT_ONCE),
LintId::of(methods::MAP_FLATTEN),
LintId::of(methods::MAP_IDENTITY),
+ LintId::of(methods::NEEDLESS_OPTION_AS_DEREF),
LintId::of(methods::NEEDLESS_SPLITN),
LintId::of(methods::OPTION_AS_REF_DEREF),
LintId::of(methods::OPTION_FILTER_MAP),
@@ -60,7 +61,6 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec!
LintId::of(needless_bool::BOOL_COMPARISON),
LintId::of(needless_bool::NEEDLESS_BOOL),
LintId::of(needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE),
- LintId::of(needless_option_as_deref::NEEDLESS_OPTION_AS_DEREF),
LintId::of(needless_question_mark::NEEDLESS_QUESTION_MARK),
LintId::of(needless_update::NEEDLESS_UPDATE),
LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD),
diff --git a/clippy_lints/src/lib.register_correctness.rs b/clippy_lints/src/lib.register_correctness.rs
index df63f84463db..6bf2c4bbaedc 100644
--- a/clippy_lints/src/lib.register_correctness.rs
+++ b/clippy_lints/src/lib.register_correctness.rs
@@ -22,6 +22,7 @@ store.register_group(true, "clippy::correctness", Some("clippy_correctness"), ve
LintId::of(drop_forget_ref::DROP_REF),
LintId::of(drop_forget_ref::FORGET_COPY),
LintId::of(drop_forget_ref::FORGET_REF),
+ LintId::of(drop_forget_ref::UNDROPPED_MANUALLY_DROPS),
LintId::of(enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT),
LintId::of(eq_op::EQ_OP),
LintId::of(erasing_op::ERASING_OP),
@@ -62,7 +63,6 @@ store.register_group(true, "clippy::correctness", Some("clippy_correctness"), ve
LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE),
LintId::of(transmute::WRONG_TRANSMUTE),
LintId::of(transmuting_null::TRANSMUTING_NULL),
- LintId::of(undropped_manually_drops::UNDROPPED_MANUALLY_DROPS),
LintId::of(unicode::INVISIBLE_CHARACTERS),
LintId::of(uninit_vec::UNINIT_VEC),
LintId::of(unit_hash::UNIT_HASH),
diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs
index 21f1ef562b5a..532590aaa5a3 100644
--- a/clippy_lints/src/lib.register_lints.rs
+++ b/clippy_lints/src/lib.register_lints.rs
@@ -70,6 +70,7 @@ store.register_lints(&[
cargo::REDUNDANT_FEATURE_NAMES,
cargo::WILDCARD_DEPENDENCIES,
case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
+ casts::CAST_ABS_TO_UNSIGNED,
casts::CAST_ENUM_CONSTRUCTOR,
casts::CAST_ENUM_TRUNCATION,
casts::CAST_LOSSLESS,
@@ -97,6 +98,7 @@ store.register_lints(&[
copies::IF_SAME_THEN_ELSE,
copies::SAME_FUNCTIONS_IN_IF_CONDITION,
copy_iterator::COPY_ITERATOR,
+ crate_in_macro_def::CRATE_IN_MACRO_DEF,
create_dir::CREATE_DIR,
dbg_macro::DBG_MACRO,
default::DEFAULT_TRAIT_ACCESS,
@@ -122,12 +124,16 @@ store.register_lints(&[
double_comparison::DOUBLE_COMPARISONS,
double_parens::DOUBLE_PARENS,
drop_forget_ref::DROP_COPY,
+ drop_forget_ref::DROP_NON_DROP,
drop_forget_ref::DROP_REF,
drop_forget_ref::FORGET_COPY,
+ drop_forget_ref::FORGET_NON_DROP,
drop_forget_ref::FORGET_REF,
+ drop_forget_ref::UNDROPPED_MANUALLY_DROPS,
duration_subsec::DURATION_SUBSEC,
else_if_without_else::ELSE_IF_WITHOUT_ELSE,
empty_enum::EMPTY_ENUM,
+ empty_structs_with_brackets::EMPTY_STRUCTS_WITH_BRACKETS,
entry::MAP_ENTRY,
enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT,
enum_variants::ENUM_VARIANT_NAMES,
@@ -280,6 +286,7 @@ store.register_lints(&[
methods::CLONE_DOUBLE_REF,
methods::CLONE_ON_COPY,
methods::CLONE_ON_REF_PTR,
+ methods::ERR_EXPECT,
methods::EXPECT_FUN_CALL,
methods::EXPECT_USED,
methods::EXTEND_WITH_DRAIN,
@@ -313,6 +320,7 @@ store.register_lints(&[
methods::MAP_FLATTEN,
methods::MAP_IDENTITY,
methods::MAP_UNWRAP_OR,
+ methods::NEEDLESS_OPTION_AS_DEREF,
methods::NEEDLESS_SPLITN,
methods::NEW_RET_NO_SELF,
methods::OK_EXPECT,
@@ -384,7 +392,6 @@ store.register_lints(&[
needless_continue::NEEDLESS_CONTINUE,
needless_for_each::NEEDLESS_FOR_EACH,
needless_late_init::NEEDLESS_LATE_INIT,
- needless_option_as_deref::NEEDLESS_OPTION_AS_DEREF,
needless_pass_by_value::NEEDLESS_PASS_BY_VALUE,
needless_question_mark::NEEDLESS_QUESTION_MARK,
needless_update::NEEDLESS_UPDATE,
@@ -505,7 +512,6 @@ store.register_lints(&[
types::TYPE_COMPLEXITY,
types::VEC_BOX,
undocumented_unsafe_blocks::UNDOCUMENTED_UNSAFE_BLOCKS,
- undropped_manually_drops::UNDROPPED_MANUALLY_DROPS,
unicode::INVISIBLE_CHARACTERS,
unicode::NON_ASCII_LITERAL,
unicode::UNICODE_NOT_NFC,
diff --git a/clippy_lints/src/lib.register_restriction.rs b/clippy_lints/src/lib.register_restriction.rs
index 6ab139b2fb67..4802dd877e99 100644
--- a/clippy_lints/src/lib.register_restriction.rs
+++ b/clippy_lints/src/lib.register_restriction.rs
@@ -16,6 +16,7 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve
LintId::of(default_union_representation::DEFAULT_UNION_REPRESENTATION),
LintId::of(disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS),
LintId::of(else_if_without_else::ELSE_IF_WITHOUT_ELSE),
+ LintId::of(empty_structs_with_brackets::EMPTY_STRUCTS_WITH_BRACKETS),
LintId::of(exhaustive_items::EXHAUSTIVE_ENUMS),
LintId::of(exhaustive_items::EXHAUSTIVE_STRUCTS),
LintId::of(exit::EXIT),
diff --git a/clippy_lints/src/lib.register_style.rs b/clippy_lints/src/lib.register_style.rs
index dcf399cf9562..3114afac8863 100644
--- a/clippy_lints/src/lib.register_style.rs
+++ b/clippy_lints/src/lib.register_style.rs
@@ -59,6 +59,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
LintId::of(methods::BYTES_NTH),
LintId::of(methods::CHARS_LAST_CMP),
LintId::of(methods::CHARS_NEXT_CMP),
+ LintId::of(methods::ERR_EXPECT),
LintId::of(methods::INTO_ITER_ON_REF),
LintId::of(methods::ITER_CLONED_COLLECT),
LintId::of(methods::ITER_NEXT_SLICE),
diff --git a/clippy_lints/src/lib.register_suspicious.rs b/clippy_lints/src/lib.register_suspicious.rs
index fa3a88e1368c..82f45b5fd58b 100644
--- a/clippy_lints/src/lib.register_suspicious.rs
+++ b/clippy_lints/src/lib.register_suspicious.rs
@@ -7,8 +7,12 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec!
LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS),
LintId::of(await_holding_invalid::AWAIT_HOLDING_LOCK),
LintId::of(await_holding_invalid::AWAIT_HOLDING_REFCELL_REF),
+ LintId::of(casts::CAST_ABS_TO_UNSIGNED),
LintId::of(casts::CAST_ENUM_CONSTRUCTOR),
LintId::of(casts::CAST_ENUM_TRUNCATION),
+ LintId::of(crate_in_macro_def::CRATE_IN_MACRO_DEF),
+ LintId::of(drop_forget_ref::DROP_NON_DROP),
+ LintId::of(drop_forget_ref::FORGET_NON_DROP),
LintId::of(eval_order_dependence::EVAL_ORDER_DEPENDENCE),
LintId::of(float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS),
LintId::of(format_impl::PRINT_IN_FORMAT_IMPL),
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index f2a079991444..c9b836f95808 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -1,5 +1,6 @@
// error-pattern:cargo-clippy
+#![feature(array_windows)]
#![feature(binary_heap_into_iter_sorted)]
#![feature(box_patterns)]
#![feature(control_flow_enum)]
@@ -190,6 +191,7 @@ mod collapsible_match;
mod comparison_chain;
mod copies;
mod copy_iterator;
+mod crate_in_macro_def;
mod create_dir;
mod dbg_macro;
mod default;
@@ -208,6 +210,7 @@ mod drop_forget_ref;
mod duration_subsec;
mod else_if_without_else;
mod empty_enum;
+mod empty_structs_with_brackets;
mod entry;
mod enum_clike;
mod enum_variants;
@@ -305,7 +308,6 @@ mod needless_borrowed_ref;
mod needless_continue;
mod needless_for_each;
mod needless_late_init;
-mod needless_option_as_deref;
mod needless_pass_by_value;
mod needless_question_mark;
mod needless_update;
@@ -375,7 +377,6 @@ mod transmuting_null;
mod try_err;
mod types;
mod undocumented_unsafe_blocks;
-mod undropped_manually_drops;
mod unicode;
mod uninit_vec;
mod unit_hash;
@@ -533,7 +534,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|| Box::new(ptr::Ptr));
store.register_late_pass(|| Box::new(ptr_eq::PtrEq));
store.register_late_pass(|| Box::new(needless_bool::NeedlessBool));
- store.register_late_pass(|| Box::new(needless_option_as_deref::OptionNeedlessDeref));
store.register_late_pass(|| Box::new(needless_bool::BoolComparison));
store.register_late_pass(|| Box::new(needless_for_each::NeedlessForEach));
store.register_late_pass(|| Box::new(misc::MiscLints));
@@ -812,7 +812,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(move || Box::new(disallowed_methods::DisallowedMethods::new(disallowed_methods.clone())));
store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86AttSyntax));
store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86IntelSyntax));
- store.register_late_pass(|| Box::new(undropped_manually_drops::UndroppedManuallyDrops));
store.register_late_pass(|| Box::new(strings::StrToString));
store.register_late_pass(|| Box::new(strings::StringToString));
store.register_late_pass(|| Box::new(zero_sized_map_values::ZeroSizedMapValues));
@@ -847,7 +846,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
enable_raw_pointer_heuristic_for_send,
))
});
- store.register_late_pass(move || Box::new(undocumented_unsafe_blocks::UndocumentedUnsafeBlocks::default()));
+ store.register_late_pass(move || Box::new(undocumented_unsafe_blocks::UndocumentedUnsafeBlocks));
store.register_late_pass(|| Box::new(match_str_case_mismatch::MatchStrCaseMismatch));
store.register_late_pass(move || Box::new(format_args::FormatArgs));
store.register_late_pass(|| Box::new(trailing_empty_array::TrailingEmptyArray));
@@ -867,6 +866,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
ignore_publish: cargo_ignore_publish,
})
});
+ store.register_early_pass(|| Box::new(crate_in_macro_def::CrateInMacroDef));
+ store.register_early_pass(|| Box::new(empty_structs_with_brackets::EmptyStructsWithBrackets));
// add lints here, do not remove this comment, it's used in `new_lint`
}
diff --git a/clippy_lints/src/loops/single_element_loop.rs b/clippy_lints/src/loops/single_element_loop.rs
index 36ecd83f7d64..a0bd7ad0ac64 100644
--- a/clippy_lints/src/loops/single_element_loop.rs
+++ b/clippy_lints/src/loops/single_element_loop.rs
@@ -2,9 +2,12 @@ use super::SINGLE_ELEMENT_LOOP;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::{indent_of, snippet_with_applicability};
use if_chain::if_chain;
+use rustc_ast::util::parser::PREC_PREFIX;
+use rustc_ast::Mutability;
use rustc_errors::Applicability;
-use rustc_hir::{BorrowKind, Expr, ExprKind, Pat};
+use rustc_hir::{is_range_literal, BorrowKind, Expr, ExprKind, Pat};
use rustc_lint::LateContext;
+use rustc_span::edition::Edition;
pub(super) fn check<'tcx>(
cx: &LateContext<'tcx>,
@@ -13,31 +16,84 @@ pub(super) fn check<'tcx>(
body: &'tcx Expr<'_>,
expr: &'tcx Expr<'_>,
) {
- let arg_expr = match arg.kind {
- ExprKind::AddrOf(BorrowKind::Ref, _, ref_arg) => ref_arg,
- ExprKind::MethodCall(method, [arg], _) if method.ident.name == rustc_span::sym::iter => arg,
+ let (arg_expression, prefix) = match arg.kind {
+ ExprKind::AddrOf(
+ BorrowKind::Ref,
+ Mutability::Not,
+ Expr {
+ kind: ExprKind::Array([arg]),
+ ..
+ },
+ ) => (arg, "&"),
+ ExprKind::AddrOf(
+ BorrowKind::Ref,
+ Mutability::Mut,
+ Expr {
+ kind: ExprKind::Array([arg]),
+ ..
+ },
+ ) => (arg, "&mut "),
+ ExprKind::MethodCall(
+ method,
+ [
+ Expr {
+ kind: ExprKind::Array([arg]),
+ ..
+ },
+ ],
+ _,
+ ) if method.ident.name == rustc_span::sym::iter => (arg, "&"),
+ ExprKind::MethodCall(
+ method,
+ [
+ Expr {
+ kind: ExprKind::Array([arg]),
+ ..
+ },
+ ],
+ _,
+ ) if method.ident.name.as_str() == "iter_mut" => (arg, "&mut "),
+ ExprKind::MethodCall(
+ method,
+ [
+ Expr {
+ kind: ExprKind::Array([arg]),
+ ..
+ },
+ ],
+ _,
+ ) if method.ident.name == rustc_span::sym::into_iter => (arg, ""),
+ // Only check for arrays edition 2021 or later, as this case will trigger a compiler error otherwise.
+ ExprKind::Array([arg]) if cx.tcx.sess.edition() >= Edition::Edition2021 => (arg, ""),
_ => return,
};
if_chain! {
- if let ExprKind::Array([arg_expression]) = arg_expr.kind;
if let ExprKind::Block(block, _) = body.kind;
if !block.stmts.is_empty();
then {
let mut applicability = Applicability::MachineApplicable;
let pat_snip = snippet_with_applicability(cx, pat.span, "..", &mut applicability);
- let arg_snip = snippet_with_applicability(cx, arg_expression.span, "..", &mut applicability);
+ let mut arg_snip = snippet_with_applicability(cx, arg_expression.span, "..", &mut applicability);
let mut block_str = snippet_with_applicability(cx, block.span, "..", &mut applicability).into_owned();
block_str.remove(0);
block_str.pop();
let indent = " ".repeat(indent_of(cx, block.stmts[0].span).unwrap_or(0));
+ // Reference iterator from `&(mut) []` or `[].iter(_mut)()`.
+ if !prefix.is_empty() && (
+ // Precedence of internal expression is less than or equal to precedence of `&expr`.
+ arg_expression.precedence().order() <= PREC_PREFIX || is_range_literal(arg_expression)
+ ) {
+ arg_snip = format!("({arg_snip})").into();
+ }
+
span_lint_and_sugg(
cx,
SINGLE_ELEMENT_LOOP,
expr.span,
"for loop over a single element",
"try",
- format!("{{\n{}let {} = &{};{}}}", indent, pat_snip, arg_snip, block_str),
+ format!("{{\n{indent}let {pat_snip} = {prefix}{arg_snip};{block_str}}}"),
applicability,
)
}
diff --git a/clippy_lints/src/map_unit_fn.rs b/clippy_lints/src/map_unit_fn.rs
index 0f6ac4784324..f552d5c1afab 100644
--- a/clippy_lints/src/map_unit_fn.rs
+++ b/clippy_lints/src/map_unit_fn.rs
@@ -1,5 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::source::snippet;
+use clippy_utils::source::{snippet, snippet_with_applicability, snippet_with_context};
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::{iter_input_pats, method_chain_args};
use if_chain::if_chain;
@@ -217,36 +217,33 @@ fn lint_map_unit_fn(cx: &LateContext<'_>, stmt: &hir::Stmt<'_>, expr: &hir::Expr
let fn_arg = &map_args[1];
if is_unit_function(cx, fn_arg) {
+ let mut applicability = Applicability::MachineApplicable;
let msg = suggestion_msg("function", map_type);
let suggestion = format!(
"if let {0}({binding}) = {1} {{ {2}({binding}) }}",
variant,
- snippet(cx, var_arg.span, "_"),
- snippet(cx, fn_arg.span, "_"),
+ snippet_with_applicability(cx, var_arg.span, "_", &mut applicability),
+ snippet_with_applicability(cx, fn_arg.span, "_", &mut applicability),
binding = let_binding_name(cx, var_arg)
);
span_lint_and_then(cx, lint, expr.span, &msg, |diag| {
- diag.span_suggestion(stmt.span, "try this", suggestion, Applicability::MachineApplicable);
+ diag.span_suggestion(stmt.span, "try this", suggestion, applicability);
});
} else if let Some((binding, closure_expr)) = unit_closure(cx, fn_arg) {
let msg = suggestion_msg("closure", map_type);
span_lint_and_then(cx, lint, expr.span, &msg, |diag| {
if let Some(reduced_expr_span) = reduce_unit_expression(cx, closure_expr) {
+ let mut applicability = Applicability::MachineApplicable;
let suggestion = format!(
"if let {0}({1}) = {2} {{ {3} }}",
variant,
- snippet(cx, binding.pat.span, "_"),
- snippet(cx, var_arg.span, "_"),
- snippet(cx, reduced_expr_span, "_")
- );
- diag.span_suggestion(
- stmt.span,
- "try this",
- suggestion,
- Applicability::MachineApplicable, // snippet
+ snippet_with_applicability(cx, binding.pat.span, "_", &mut applicability),
+ snippet_with_applicability(cx, var_arg.span, "_", &mut applicability),
+ snippet_with_context(cx, reduced_expr_span, var_arg.span.ctxt(), "_", &mut applicability).0,
);
+ diag.span_suggestion(stmt.span, "try this", suggestion, applicability);
} else {
let suggestion = format!(
"if let {0}({1}) = {2} {{ ... }}",
diff --git a/clippy_lints/src/matches/mod.rs b/clippy_lints/src/matches/mod.rs
index ff85623acf49..e93b494653fc 100644
--- a/clippy_lints/src/matches/mod.rs
+++ b/clippy_lints/src/matches/mod.rs
@@ -667,7 +667,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
overlapping_arms::check(cx, ex, arms);
match_wild_enum::check(cx, ex, arms);
match_as_ref::check(cx, ex, arms, expr);
- needless_match::check_match(cx, ex, arms);
+ needless_match::check_match(cx, ex, arms, expr);
if self.infallible_destructuring_match_linted {
self.infallible_destructuring_match_linted = false;
diff --git a/clippy_lints/src/matches/needless_match.rs b/clippy_lints/src/matches/needless_match.rs
index 76131d307d77..2105a03e03a3 100644
--- a/clippy_lints/src/matches/needless_match.rs
+++ b/clippy_lints/src/matches/needless_match.rs
@@ -1,37 +1,25 @@
use super::NEEDLESS_MATCH;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{eq_expr_value, get_parent_expr, higher, is_else_clause, is_lang_ctor, peel_blocks_with_stmt};
+use clippy_utils::ty::{is_type_diagnostic_item, same_type_and_consts};
+use clippy_utils::{
+ eq_expr_value, get_parent_expr_for_hir, get_parent_node, higher, is_else_clause, is_lang_ctor, over,
+ peel_blocks_with_stmt,
+};
use rustc_errors::Applicability;
use rustc_hir::LangItem::OptionNone;
-use rustc_hir::{Arm, BindingAnnotation, Expr, ExprKind, Pat, PatKind, Path, PathSegment, QPath, UnOp};
+use rustc_hir::{Arm, BindingAnnotation, Expr, ExprKind, FnRetTy, Node, Pat, PatKind, Path, QPath};
use rustc_lint::LateContext;
use rustc_span::sym;
+use rustc_typeck::hir_ty_to_ty;
-pub(crate) fn check_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) {
- // This is for avoiding collision with `match_single_binding`.
- if arms.len() < 2 {
- return;
- }
-
- for arm in arms {
- if let PatKind::Wild = arm.pat.kind {
- let ret_expr = strip_return(arm.body);
- if !eq_expr_value(cx, ex, ret_expr) {
- return;
- }
- } else if !pat_same_as_expr(arm.pat, arm.body) {
- return;
- }
- }
-
- if let Some(match_expr) = get_parent_expr(cx, ex) {
+pub(crate) fn check_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
+ if arms.len() > 1 && expr_ty_matches_p_ty(cx, ex, expr) && check_all_arms(cx, ex, arms) {
let mut applicability = Applicability::MachineApplicable;
span_lint_and_sugg(
cx,
NEEDLESS_MATCH,
- match_expr.span,
+ expr.span,
"this match expression is unnecessary",
"replace it with",
snippet_with_applicability(cx, ex.span, "..", &mut applicability).to_string(),
@@ -60,11 +48,8 @@ pub(crate) fn check_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>])
/// }
/// ```
pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>) {
- if_chain! {
- if let Some(ref if_let) = higher::IfLet::hir(cx, ex);
- if !is_else_clause(cx.tcx, ex);
- if check_if_let(cx, if_let);
- then {
+ if let Some(ref if_let) = higher::IfLet::hir(cx, ex) {
+ if !is_else_clause(cx.tcx, ex) && expr_ty_matches_p_ty(cx, if_let.let_expr, ex) && check_if_let(cx, if_let) {
let mut applicability = Applicability::MachineApplicable;
span_lint_and_sugg(
cx,
@@ -79,6 +64,19 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>) {
}
}
+fn check_all_arms(cx: &LateContext<'_>, match_expr: &Expr<'_>, arms: &[Arm<'_>]) -> bool {
+ for arm in arms {
+ let arm_expr = peel_blocks_with_stmt(arm.body);
+ if let PatKind::Wild = arm.pat.kind {
+ return eq_expr_value(cx, match_expr, strip_return(arm_expr));
+ } else if !pat_same_as_expr(arm.pat, arm_expr) {
+ return false;
+ }
+ }
+
+ true
+}
+
fn check_if_let(cx: &LateContext<'_>, if_let: &higher::IfLet<'_>) -> bool {
if let Some(if_else) = if_let.if_else {
if !pat_same_as_expr(if_let.let_pat, peel_blocks_with_stmt(if_let.if_then)) {
@@ -92,18 +90,21 @@ fn check_if_let(cx: &LateContext<'_>, if_let: &higher::IfLet<'_>) -> bool {
if matches!(if_else.kind, ExprKind::Block(..)) {
let else_expr = peel_blocks_with_stmt(if_else);
+ if matches!(else_expr.kind, ExprKind::Block(..)) {
+ return false;
+ }
let ret = strip_return(else_expr);
let let_expr_ty = cx.typeck_results().expr_ty(if_let.let_expr);
if is_type_diagnostic_item(cx, let_expr_ty, sym::Option) {
if let ExprKind::Path(ref qpath) = ret.kind {
return is_lang_ctor(cx, qpath, OptionNone) || eq_expr_value(cx, if_let.let_expr, ret);
}
- } else {
- return eq_expr_value(cx, if_let.let_expr, ret);
+ return true;
}
- return true;
+ return eq_expr_value(cx, if_let.let_expr, ret);
}
}
+
false
}
@@ -116,48 +117,70 @@ fn strip_return<'hir>(expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> {
}
}
+/// Manually check for coercion casting by checking if the type of the match operand or let expr
+/// differs with the assigned local variable or the funtion return type.
+fn expr_ty_matches_p_ty(cx: &LateContext<'_>, expr: &Expr<'_>, p_expr: &Expr<'_>) -> bool {
+ if let Some(p_node) = get_parent_node(cx.tcx, p_expr.hir_id) {
+ match p_node {
+ // Compare match_expr ty with local in `let local = match match_expr {..}`
+ Node::Local(local) => {
+ let results = cx.typeck_results();
+ return same_type_and_consts(results.node_type(local.hir_id), results.expr_ty(expr));
+ },
+ // compare match_expr ty with RetTy in `fn foo() -> RetTy`
+ Node::Item(..) => {
+ if let Some(fn_decl) = p_node.fn_decl() {
+ if let FnRetTy::Return(ret_ty) = fn_decl.output {
+ return same_type_and_consts(hir_ty_to_ty(cx.tcx, ret_ty), cx.typeck_results().expr_ty(expr));
+ }
+ }
+ },
+ // check the parent expr for this whole block `{ match match_expr {..} }`
+ Node::Block(block) => {
+ if let Some(block_parent_expr) = get_parent_expr_for_hir(cx, block.hir_id) {
+ return expr_ty_matches_p_ty(cx, expr, block_parent_expr);
+ }
+ },
+ // recursively call on `if xxx {..}` etc.
+ Node::Expr(p_expr) => {
+ return expr_ty_matches_p_ty(cx, expr, p_expr);
+ },
+ _ => {},
+ }
+ }
+ false
+}
+
fn pat_same_as_expr(pat: &Pat<'_>, expr: &Expr<'_>) -> bool {
let expr = strip_return(expr);
match (&pat.kind, &expr.kind) {
// Example: `Some(val) => Some(val)`
- (
- PatKind::TupleStruct(QPath::Resolved(_, path), [first_pat, ..], _),
- ExprKind::Call(call_expr, [first_param, ..]),
- ) => {
+ (PatKind::TupleStruct(QPath::Resolved(_, path), tuple_params, _), ExprKind::Call(call_expr, call_params)) => {
if let ExprKind::Path(QPath::Resolved(_, call_path)) = call_expr.kind {
- if has_identical_segments(path.segments, call_path.segments)
- && has_same_non_ref_symbol(first_pat, first_param)
- {
- return true;
- }
+ return over(path.segments, call_path.segments, |pat_seg, call_seg| {
+ pat_seg.ident.name == call_seg.ident.name
+ }) && same_non_ref_symbols(tuple_params, call_params);
}
},
- // Example: `val => val`, or `ref val => *val`
- (PatKind::Binding(annot, _, pat_ident, _), _) => {
- let new_expr = if let (
- BindingAnnotation::Ref | BindingAnnotation::RefMut,
- ExprKind::Unary(UnOp::Deref, operand_expr),
- ) = (annot, &expr.kind)
- {
- operand_expr
- } else {
- expr
- };
-
- if let ExprKind::Path(QPath::Resolved(
+ // Example: `val => val`
+ (
+ PatKind::Binding(annot, _, pat_ident, _),
+ ExprKind::Path(QPath::Resolved(
_,
Path {
segments: [first_seg, ..],
..
},
- )) = new_expr.kind
- {
- return pat_ident.name == first_seg.ident.name;
- }
+ )),
+ ) => {
+ return !matches!(annot, BindingAnnotation::Ref | BindingAnnotation::RefMut)
+ && pat_ident.name == first_seg.ident.name;
},
// Example: `Custom::TypeA => Custom::TypeB`, or `None => None`
(PatKind::Path(QPath::Resolved(_, p_path)), ExprKind::Path(QPath::Resolved(_, e_path))) => {
- return has_identical_segments(p_path.segments, e_path.segments);
+ return over(p_path.segments, e_path.segments, |p_seg, e_seg| {
+ p_seg.ident.name == e_seg.ident.name
+ });
},
// Example: `5 => 5`
(PatKind::Lit(pat_lit_expr), ExprKind::Lit(expr_spanned)) => {
@@ -171,27 +194,16 @@ fn pat_same_as_expr(pat: &Pat<'_>, expr: &Expr<'_>) -> bool {
false
}
-fn has_identical_segments(left_segs: &[PathSegment<'_>], right_segs: &[PathSegment<'_>]) -> bool {
- if left_segs.len() != right_segs.len() {
+fn same_non_ref_symbols(pats: &[Pat<'_>], exprs: &[Expr<'_>]) -> bool {
+ if pats.len() != exprs.len() {
return false;
}
- for i in 0..left_segs.len() {
- if left_segs[i].ident.name != right_segs[i].ident.name {
+
+ for i in 0..pats.len() {
+ if !pat_same_as_expr(&pats[i], &exprs[i]) {
return false;
}
}
+
true
}
-
-fn has_same_non_ref_symbol(pat: &Pat<'_>, expr: &Expr<'_>) -> bool {
- if_chain! {
- if let PatKind::Binding(annot, _, pat_ident, _) = pat.kind;
- if !matches!(annot, BindingAnnotation::Ref | BindingAnnotation::RefMut);
- if let ExprKind::Path(QPath::Resolved(_, Path {segments: [first_seg, ..], .. })) = expr.kind;
- then {
- return pat_ident.name == first_seg.ident.name;
- }
- }
-
- false
-}
diff --git a/clippy_lints/src/methods/bytes_nth.rs b/clippy_lints/src/methods/bytes_nth.rs
index 76eaedea8a0d..44857d61fef8 100644
--- a/clippy_lints/src/methods/bytes_nth.rs
+++ b/clippy_lints/src/methods/bytes_nth.rs
@@ -22,7 +22,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, recv: &'tcx E
cx,
BYTES_NTH,
expr.span,
- &format!("called `.byte().nth()` on a `{}`", caller_type),
+ &format!("called `.bytes().nth()` on a `{}`", caller_type),
"try",
format!(
"{}.as_bytes().get({})",
diff --git a/clippy_lints/src/methods/err_expect.rs b/clippy_lints/src/methods/err_expect.rs
new file mode 100644
index 000000000000..be9d4ad94fb8
--- /dev/null
+++ b/clippy_lints/src/methods/err_expect.rs
@@ -0,0 +1,60 @@
+use super::ERR_EXPECT;
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::ty::implements_trait;
+use clippy_utils::{meets_msrv, msrvs, ty::is_type_diagnostic_item};
+use rustc_errors::Applicability;
+use rustc_lint::LateContext;
+use rustc_middle::ty;
+use rustc_middle::ty::Ty;
+use rustc_semver::RustcVersion;
+use rustc_span::{sym, Span};
+
+pub(super) fn check(
+ cx: &LateContext<'_>,
+ _expr: &rustc_hir::Expr<'_>,
+ recv: &rustc_hir::Expr<'_>,
+ msrv: Option<&RustcVersion>,
+ expect_span: Span,
+ err_span: Span,
+) {
+ if_chain! {
+ if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result);
+ // Test the version to make sure the lint can be showed (expect_err has been
+ // introduced in rust 1.17.0 : https://github.com/rust-lang/rust/pull/38982)
+ if meets_msrv(msrv, &msrvs::EXPECT_ERR);
+
+ // Grabs the `Result` type
+ let result_type = cx.typeck_results().expr_ty(recv);
+ // Tests if the T type in a `Result` is not None
+ if let Some(data_type) = get_data_type(cx, result_type);
+ // Tests if the T type in a `Result` implements debug
+ if has_debug_impl(data_type, cx);
+
+ then {
+ span_lint_and_sugg(
+ cx,
+ ERR_EXPECT,
+ err_span.to(expect_span),
+ "called `.err().expect()` on a `Result` value",
+ "try",
+ "expect_err".to_string(),
+ Applicability::MachineApplicable
+ );
+ }
+ };
+}
+
+/// Given a `Result` type, return its data (`T`).
+fn get_data_type<'a>(cx: &LateContext<'_>, ty: Ty<'a>) -> Option> {
+ match ty.kind() {
+ ty::Adt(_, substs) if is_type_diagnostic_item(cx, ty, sym::Result) => substs.types().next(),
+ _ => None,
+ }
+}
+
+/// Given a type, very if the Debug trait has been impl'd
+fn has_debug_impl<'tcx>(ty: Ty<'tcx>, cx: &LateContext<'tcx>) -> bool {
+ cx.tcx
+ .get_diagnostic_item(sym::Debug)
+ .map_or(false, |debug| implements_trait(cx, ty, debug, &[]))
+}
diff --git a/clippy_lints/src/methods/implicit_clone.rs b/clippy_lints/src/methods/implicit_clone.rs
index c98cdfbca434..9651a52be4e7 100644
--- a/clippy_lints/src/methods/implicit_clone.rs
+++ b/clippy_lints/src/methods/implicit_clone.rs
@@ -48,13 +48,11 @@ pub fn is_clone_like(cx: &LateContext<'_>, method_name: &str, method_def_id: hir
"to_os_string" => is_diag_item_method(cx, method_def_id, sym::OsStr),
"to_owned" => is_diag_trait_item(cx, method_def_id, sym::ToOwned),
"to_path_buf" => is_diag_item_method(cx, method_def_id, sym::Path),
- "to_vec" => {
- cx.tcx.impl_of_method(method_def_id)
- .filter(|&impl_did| {
- cx.tcx.type_of(impl_did).is_slice() && cx.tcx.impl_trait_ref(impl_did).is_none()
- })
- .is_some()
- },
+ "to_vec" => cx
+ .tcx
+ .impl_of_method(method_def_id)
+ .filter(|&impl_did| cx.tcx.type_of(impl_did).is_slice() && cx.tcx.impl_trait_ref(impl_did).is_none())
+ .is_some(),
_ => false,
}
}
diff --git a/clippy_lints/src/methods/iter_overeager_cloned.rs b/clippy_lints/src/methods/iter_overeager_cloned.rs
index b93f1399eaee..54c9ca435a44 100644
--- a/clippy_lints/src/methods/iter_overeager_cloned.rs
+++ b/clippy_lints/src/methods/iter_overeager_cloned.rs
@@ -1,11 +1,12 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet;
-use clippy_utils::ty::{get_iterator_item_ty, is_copy};
+use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy};
use itertools::Itertools;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
use rustc_middle::ty;
+use rustc_span::sym;
use std::ops::Not;
use super::ITER_OVEREAGER_CLONED;
@@ -20,9 +21,16 @@ pub(super) fn check<'tcx>(
map_arg: &[hir::Expr<'_>],
) {
// Check if it's iterator and get type associated with `Item`.
- let inner_ty = match get_iterator_item_ty(cx, cx.typeck_results().expr_ty_adjusted(recv)) {
- Some(ty) => ty,
- _ => return,
+ let inner_ty = if_chain! {
+ if let Some(iterator_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator);
+ let recv_ty = cx.typeck_results().expr_ty(recv);
+ if implements_trait(cx, recv_ty, iterator_trait_id, &[]);
+ if let Some(inner_ty) = get_iterator_item_ty(cx, cx.typeck_results().expr_ty_adjusted(recv));
+ then {
+ inner_ty
+ } else {
+ return;
+ }
};
match inner_ty.kind() {
diff --git a/clippy_lints/src/methods/map_identity.rs b/clippy_lints/src/methods/map_identity.rs
index f112b500d3d2..862a9578e6ff 100644
--- a/clippy_lints/src/methods/map_identity.rs
+++ b/clippy_lints/src/methods/map_identity.rs
@@ -13,6 +13,7 @@ pub(super) fn check(
expr: &hir::Expr<'_>,
caller: &hir::Expr<'_>,
map_arg: &hir::Expr<'_>,
+ name: &str,
_map_span: Span,
) {
let caller_ty = cx.typeck_results().expr_ty(caller);
@@ -29,7 +30,7 @@ pub(super) fn check(
MAP_IDENTITY,
sugg_span,
"unnecessary map of the identity function",
- "remove the call to `map`",
+ &format!("remove the call to `{}`", name),
String::new(),
Applicability::MachineApplicable,
)
diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs
index 9d4e1fa39940..70d021a1668e 100644
--- a/clippy_lints/src/methods/mod.rs
+++ b/clippy_lints/src/methods/mod.rs
@@ -9,6 +9,7 @@ mod chars_next_cmp_with_unwrap;
mod clone_on_copy;
mod clone_on_ref_ptr;
mod cloned_instead_of_copied;
+mod err_expect;
mod expect_fun_call;
mod expect_used;
mod extend_with_drain;
@@ -40,6 +41,7 @@ mod map_collect_result_unit;
mod map_flatten;
mod map_identity;
mod map_unwrap_or;
+mod needless_option_as_deref;
mod ok_expect;
mod option_as_ref_deref;
mod option_map_or_none;
@@ -362,6 +364,29 @@ declare_clippy_lint! {
"using `ok().expect()`, which gives worse error messages than calling `expect` directly on the Result"
}
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for `.err().expect()` calls on the `Result` type.
+ ///
+ /// ### Why is this bad?
+ /// `.expect_err()` can be called directly to avoid the extra type conversion from `err()`.
+ ///
+ /// ### Example
+ /// ```should_panic
+ /// let x: Result = Ok(10);
+ /// x.err().expect("Testing err().expect()");
+ /// ```
+ /// Use instead:
+ /// ```should_panic
+ /// let x: Result = Ok(10);
+ /// x.expect_err("Testing expect_err");
+ /// ```
+ #[clippy::version = "1.61.0"]
+ pub ERR_EXPECT,
+ style,
+ r#"using `.err().expect("")` when `.expect_err("")` can be used"#
+}
+
declare_clippy_lint! {
/// ### What it does
/// Checks for usages of `_.unwrap_or_else(Default::default)` on `Option` and
@@ -2055,7 +2080,7 @@ declare_clippy_lint! {
/// Checks for use of `.collect::>().join("")` on iterators.
///
/// ### Why is this bad?
- /// `.collect::()` is more concise and usually more performant
+ /// `.collect::()` is more concise and might be more performant
///
/// ### Example
/// ```rust
@@ -2070,15 +2095,42 @@ declare_clippy_lint! {
/// println!("{}", output);
/// ```
/// ### Known problems
- /// While `.collect::()` is more performant in most cases, there are cases where
+ /// While `.collect::()` is sometimes more performant, there are cases where
/// using `.collect::()` over `.collect::>().join("")`
/// will prevent loop unrolling and will result in a negative performance impact.
+ ///
+ /// Additionlly, differences have been observed between aarch64 and x86_64 assembly output,
+ /// with aarch64 tending to producing faster assembly in more cases when using `.collect::()`
#[clippy::version = "1.61.0"]
pub UNNECESSARY_JOIN,
pedantic,
"using `.collect::>().join(\"\")` on an iterator"
}
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for no-op uses of `Option::{as_deref, as_deref_mut}`,
+ /// for example, `Option<&T>::as_deref()` returns the same type.
+ ///
+ /// ### Why is this bad?
+ /// Redundant code and improving readability.
+ ///
+ /// ### Example
+ /// ```rust
+ /// let a = Some(&1);
+ /// let b = a.as_deref(); // goes from Option<&i32> to Option<&i32>
+ /// ```
+ /// Could be written as:
+ /// ```rust
+ /// let a = Some(&1);
+ /// let b = a;
+ /// ```
+ #[clippy::version = "1.57.0"]
+ pub NEEDLESS_OPTION_AS_DEREF,
+ complexity,
+ "no-op use of `deref` or `deref_mut` method to `Option`."
+}
+
pub struct Methods {
avoid_breaking_exported_api: bool,
msrv: Option,
@@ -2165,6 +2217,8 @@ impl_lint_pass!(Methods => [
NEEDLESS_SPLITN,
UNNECESSARY_TO_OWNED,
UNNECESSARY_JOIN,
+ ERR_EXPECT,
+ NEEDLESS_OPTION_AS_DEREF,
]);
/// Extracts a method call name, args, and `Span` of the method name.
@@ -2397,6 +2451,9 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
unnecessary_lazy_eval::check(cx, expr, recv, arg, "and");
}
},
+ ("as_deref" | "as_deref_mut", []) => {
+ needless_option_as_deref::check(cx, expr, recv, name);
+ },
("as_mut", []) => useless_asref::check(cx, expr, "as_mut", recv),
("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv),
("assume_init", []) => uninit_assumed_init::check(cx, expr, recv),
@@ -2428,6 +2485,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
},
("expect", [_]) => match method_call(recv) {
Some(("ok", [recv], _)) => ok_expect::check(cx, expr, recv),
+ Some(("err", [recv], err_span)) => err_expect::check(cx, expr, recv, msrv, span, err_span),
_ => expect_used::check(cx, expr, recv),
},
("extend", [arg]) => {
@@ -2472,7 +2530,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
}
}
},
- ("map", [m_arg]) => {
+ (name @ ("map" | "map_err"), [m_arg]) => {
if let Some((name, [recv2, args @ ..], span2)) = method_call(recv) {
match (name, args) {
("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, msrv),
@@ -2484,7 +2542,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
_ => {},
}
}
- map_identity::check(cx, expr, recv, m_arg, span);
+ map_identity::check(cx, expr, recv, m_arg, name, span);
},
("map_or", [def, map]) => option_map_or_none::check(cx, expr, recv, def, map),
(name @ "next", args @ []) => {
diff --git a/clippy_lints/src/methods/needless_option_as_deref.rs b/clippy_lints/src/methods/needless_option_as_deref.rs
new file mode 100644
index 000000000000..7030baf19ff5
--- /dev/null
+++ b/clippy_lints/src/methods/needless_option_as_deref.rs
@@ -0,0 +1,37 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::path_res;
+use clippy_utils::source::snippet_opt;
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::usage::local_used_after_expr;
+use rustc_errors::Applicability;
+use rustc_hir::def::Res;
+use rustc_hir::Expr;
+use rustc_lint::LateContext;
+use rustc_span::sym;
+
+use super::NEEDLESS_OPTION_AS_DEREF;
+
+pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, name: &str) {
+ let typeck = cx.typeck_results();
+ let outer_ty = typeck.expr_ty(expr);
+
+ if is_type_diagnostic_item(cx, outer_ty, sym::Option) && outer_ty == typeck.expr_ty(recv) {
+ if name == "as_deref_mut" && recv.is_syntactic_place_expr() {
+ let Res::Local(binding_id) = path_res(cx, recv) else { return };
+
+ if local_used_after_expr(cx, binding_id, recv) {
+ return;
+ }
+ }
+
+ span_lint_and_sugg(
+ cx,
+ NEEDLESS_OPTION_AS_DEREF,
+ expr.span,
+ "derefed type is same as origin",
+ "try this",
+ snippet_opt(cx, recv.span).unwrap(),
+ Applicability::MachineApplicable,
+ );
+ }
+}
diff --git a/clippy_lints/src/module_style.rs b/clippy_lints/src/module_style.rs
index b8dfe9968806..0a393657267b 100644
--- a/clippy_lints/src/module_style.rs
+++ b/clippy_lints/src/module_style.rs
@@ -1,17 +1,14 @@
-use std::{
- ffi::OsString,
- path::{Component, Path},
-};
-
use rustc_ast::ast;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_lint::{EarlyContext, EarlyLintPass, Level, LintContext};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::{FileName, RealFileName, SourceFile, Span, SyntaxContext};
+use std::ffi::OsStr;
+use std::path::{Component, Path};
declare_clippy_lint! {
/// ### What it does
- /// Checks that module layout uses only self named module files, bans mod.rs files.
+ /// Checks that module layout uses only self named module files, bans `mod.rs` files.
///
/// ### Why is this bad?
/// Having multiple module layout styles in a project can be confusing.
@@ -40,7 +37,7 @@ declare_clippy_lint! {
declare_clippy_lint! {
/// ### What it does
- /// Checks that module layout uses only mod.rs files.
+ /// Checks that module layout uses only `mod.rs` files.
///
/// ### Why is this bad?
/// Having multiple module layout styles in a project can be confusing.
@@ -82,11 +79,7 @@ impl EarlyLintPass for ModStyle {
let files = cx.sess().source_map().files();
- let trim_to_src = if let RealFileName::LocalPath(p) = &cx.sess().opts.working_dir {
- p.to_string_lossy()
- } else {
- return;
- };
+ let RealFileName::LocalPath(trim_to_src) = &cx.sess().opts.working_dir else { return };
// `folder_segments` is all unique folder path segments `path/to/foo.rs` gives
// `[path, to]` but not foo
@@ -97,26 +90,27 @@ impl EarlyLintPass for ModStyle {
// `{ foo => path/to/foo.rs, .. }
let mut file_map = FxHashMap::default();
for file in files.iter() {
- match &file.name {
- FileName::Real(RealFileName::LocalPath(lp))
- if lp.to_string_lossy().starts_with(trim_to_src.as_ref()) =>
- {
- let p = lp.to_string_lossy();
- let path = Path::new(p.trim_start_matches(trim_to_src.as_ref()));
- if let Some(stem) = path.file_stem() {
- file_map.insert(stem.to_os_string(), (file, path.to_owned()));
- }
- process_paths_for_mod_files(path, &mut folder_segments, &mut mod_folders);
- check_self_named_mod_exists(cx, path, file);
- },
- _ => {},
+ if let FileName::Real(RealFileName::LocalPath(lp)) = &file.name {
+ let path = if lp.is_relative() {
+ lp
+ } else if let Ok(relative) = lp.strip_prefix(trim_to_src) {
+ relative
+ } else {
+ continue;
+ };
+
+ if let Some(stem) = path.file_stem() {
+ file_map.insert(stem, (file, path));
+ }
+ process_paths_for_mod_files(path, &mut folder_segments, &mut mod_folders);
+ check_self_named_mod_exists(cx, path, file);
}
}
for folder in &folder_segments {
if !mod_folders.contains(folder) {
if let Some((file, path)) = file_map.get(folder) {
- let mut correct = path.clone();
+ let mut correct = path.to_path_buf();
correct.pop();
correct.push(folder);
correct.push("mod.rs");
@@ -138,25 +132,17 @@ impl EarlyLintPass for ModStyle {
/// For each `path` we add each folder component to `folder_segments` and if the file name
/// is `mod.rs` we add it's parent folder to `mod_folders`.
-fn process_paths_for_mod_files(
- path: &Path,
- folder_segments: &mut FxHashSet,
- mod_folders: &mut FxHashSet,
+fn process_paths_for_mod_files<'a>(
+ path: &'a Path,
+ folder_segments: &mut FxHashSet<&'a OsStr>,
+ mod_folders: &mut FxHashSet<&'a OsStr>,
) {
let mut comp = path.components().rev().peekable();
let _ = comp.next();
if path.ends_with("mod.rs") {
- mod_folders.insert(comp.peek().map(|c| c.as_os_str().to_owned()).unwrap_or_default());
+ mod_folders.insert(comp.peek().map(|c| c.as_os_str()).unwrap_or_default());
}
- let folders = comp
- .filter_map(|c| {
- if let Component::Normal(s) = c {
- Some(s.to_os_string())
- } else {
- None
- }
- })
- .collect::>();
+ let folders = comp.filter_map(|c| if let Component::Normal(s) = c { Some(s) } else { None });
folder_segments.extend(folders);
}
diff --git a/clippy_lints/src/needless_option_as_deref.rs b/clippy_lints/src/needless_option_as_deref.rs
deleted file mode 100644
index 9d3d7d1f24cb..000000000000
--- a/clippy_lints/src/needless_option_as_deref.rs
+++ /dev/null
@@ -1,65 +0,0 @@
-use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet_opt;
-use clippy_utils::ty::is_type_diagnostic_item;
-use rustc_errors::Applicability;
-use rustc_hir::{Expr, ExprKind};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::symbol::sym;
-
-declare_clippy_lint! {
- /// ### What it does
- /// Checks for no-op uses of Option::{as_deref,as_deref_mut},
- /// for example, `Option<&T>::as_deref()` returns the same type.
- ///
- /// ### Why is this bad?
- /// Redundant code and improving readability.
- ///
- /// ### Example
- /// ```rust
- /// let a = Some(&1);
- /// let b = a.as_deref(); // goes from Option<&i32> to Option<&i32>
- /// ```
- /// Could be written as:
- /// ```rust
- /// let a = Some(&1);
- /// let b = a;
- /// ```
- #[clippy::version = "1.57.0"]
- pub NEEDLESS_OPTION_AS_DEREF,
- complexity,
- "no-op use of `deref` or `deref_mut` method to `Option`."
-}
-
-declare_lint_pass!(OptionNeedlessDeref=> [
- NEEDLESS_OPTION_AS_DEREF,
-]);
-
-impl<'tcx> LateLintPass<'tcx> for OptionNeedlessDeref {
- fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
- if expr.span.from_expansion() {
- return;
- }
- let typeck = cx.typeck_results();
- let outer_ty = typeck.expr_ty(expr);
-
- if_chain! {
- if is_type_diagnostic_item(cx,outer_ty,sym::Option);
- if let ExprKind::MethodCall(path, [sub_expr], _) = expr.kind;
- let symbol = path.ident.as_str();
- if symbol == "as_deref" || symbol == "as_deref_mut";
- if outer_ty == typeck.expr_ty(sub_expr);
- then{
- span_lint_and_sugg(
- cx,
- NEEDLESS_OPTION_AS_DEREF,
- expr.span,
- "derefed type is same as origin",
- "try this",
- snippet_opt(cx,sub_expr.span).unwrap(),
- Applicability::MachineApplicable
- );
- }
- }
- }
-}
diff --git a/clippy_lints/src/panic_unimplemented.rs b/clippy_lints/src/panic_unimplemented.rs
index 6ef6b9a20aa4..2f3007658ea6 100644
--- a/clippy_lints/src/panic_unimplemented.rs
+++ b/clippy_lints/src/panic_unimplemented.rs
@@ -78,6 +78,10 @@ impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return };
if is_panic(cx, macro_call.def_id) {
+ if cx.tcx.hir().is_inside_const_context(expr.hir_id) {
+ return;
+ }
+
span_lint(
cx,
PANIC,
diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs
index 5f453dc16555..48a2666a2e0c 100644
--- a/clippy_lints/src/ptr.rs
+++ b/clippy_lints/src/ptr.rs
@@ -601,9 +601,7 @@ fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, args:
},
// If the types match check for methods which exist on both types. e.g. `Vec::len` and
// `slice::len`
- ty::Adt(def, _)
- if def.did() == args.ty_did =>
- {
+ ty::Adt(def, _) if def.did() == args.ty_did => {
set_skip_flag();
},
_ => (),
diff --git a/clippy_lints/src/transmute/mod.rs b/clippy_lints/src/transmute/mod.rs
index 02569bd3a476..342f23f030cd 100644
--- a/clippy_lints/src/transmute/mod.rs
+++ b/clippy_lints/src/transmute/mod.rs
@@ -410,9 +410,10 @@ impl<'tcx> LateLintPass<'tcx> for Transmute {
if let Some(def_id) = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id();
if cx.tcx.is_diagnostic_item(sym::transmute, def_id);
then {
- // Avoid suggesting from/to bits and dereferencing raw pointers in const contexts.
- // See https://github.com/rust-lang/rust/issues/73736 for progress on making them `const fn`.
- // And see https://github.com/rust-lang/rust/issues/51911 for dereferencing raw pointers.
+ // Avoid suggesting non-const operations in const contexts:
+ // - from/to bits (https://github.com/rust-lang/rust/issues/73736)
+ // - dereferencing raw pointers (https://github.com/rust-lang/rust/issues/51911)
+ // - char conversions (https://github.com/rust-lang/rust/issues/89259)
let const_context = in_constant(cx, e.hir_id);
let from_ty = cx.typeck_results().expr_ty_adjusted(arg);
@@ -427,7 +428,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute {
let linted = wrong_transmute::check(cx, e, from_ty, to_ty)
| crosspointer_transmute::check(cx, e, from_ty, to_ty)
| transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, qpath)
- | transmute_int_to_char::check(cx, e, from_ty, to_ty, arg)
+ | transmute_int_to_char::check(cx, e, from_ty, to_ty, arg, const_context)
| transmute_ref_to_ref::check(cx, e, from_ty, to_ty, arg, const_context)
| transmute_ptr_to_ptr::check(cx, e, from_ty, to_ty, arg)
| transmute_int_to_bool::check(cx, e, from_ty, to_ty, arg)
diff --git a/clippy_lints/src/transmute/transmute_int_to_char.rs b/clippy_lints/src/transmute/transmute_int_to_char.rs
index 3eb07b68992a..9e1823c373bf 100644
--- a/clippy_lints/src/transmute/transmute_int_to_char.rs
+++ b/clippy_lints/src/transmute/transmute_int_to_char.rs
@@ -15,9 +15,10 @@ pub(super) fn check<'tcx>(
from_ty: Ty<'tcx>,
to_ty: Ty<'tcx>,
arg: &'tcx Expr<'_>,
+ const_context: bool,
) -> bool {
match (&from_ty.kind(), &to_ty.kind()) {
- (ty::Int(ty::IntTy::I32) | ty::Uint(ty::UintTy::U32), &ty::Char) => {
+ (ty::Int(ty::IntTy::I32) | ty::Uint(ty::UintTy::U32), &ty::Char) if !const_context => {
span_lint_and_then(
cx,
TRANSMUTE_INT_TO_CHAR,
diff --git a/clippy_lints/src/transmute/transmute_ref_to_ref.rs b/clippy_lints/src/transmute/transmute_ref_to_ref.rs
index 7570bc2a7a8f..786e7bfc56f6 100644
--- a/clippy_lints/src/transmute/transmute_ref_to_ref.rs
+++ b/clippy_lints/src/transmute/transmute_ref_to_ref.rs
@@ -32,18 +32,20 @@ pub(super) fn check<'tcx>(
""
};
+ let snippet = snippet(cx, arg.span, "..");
+
span_lint_and_sugg(
cx,
TRANSMUTE_BYTES_TO_STR,
e.span,
&format!("transmute from a `{}` to a `{}`", from_ty, to_ty),
"consider using",
- format!(
- "std::str::from_utf8{}({}).unwrap()",
- postfix,
- snippet(cx, arg.span, ".."),
- ),
- Applicability::Unspecified,
+ if const_context {
+ format!("std::str::from_utf8_unchecked{postfix}({snippet})")
+ } else {
+ format!("std::str::from_utf8{postfix}({snippet}).unwrap()")
+ },
+ Applicability::MaybeIncorrect,
);
triggered = true;
} else {
diff --git a/clippy_lints/src/undocumented_unsafe_blocks.rs b/clippy_lints/src/undocumented_unsafe_blocks.rs
index e42c6c63ede0..c8912a18f185 100644
--- a/clippy_lints/src/undocumented_unsafe_blocks.rs
+++ b/clippy_lints/src/undocumented_unsafe_blocks.rs
@@ -1,16 +1,13 @@
-use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::is_lint_allowed;
-use clippy_utils::source::{indent_of, reindent_multiline, snippet};
-use rustc_errors::Applicability;
-use rustc_hir::intravisit::{walk_expr, Visitor};
-use rustc_hir::{Block, BlockCheckMode, Expr, ExprKind, HirId, Local, UnsafeSource};
-use rustc_lexer::TokenKind;
-use rustc_lint::{LateContext, LateLintPass};
+use clippy_utils::source::walk_span_to_context;
+use rustc_data_structures::sync::Lrc;
+use rustc_hir::{Block, BlockCheckMode, UnsafeSource};
+use rustc_lexer::{tokenize, TokenKind};
+use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
-use rustc_middle::ty::TyCtxt;
-use rustc_session::{declare_tool_lint, impl_lint_pass};
-use rustc_span::{BytePos, Span};
-use std::borrow::Cow;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::{BytePos, Pos, SyntaxContext};
declare_clippy_lint! {
/// ### What it does
@@ -18,6 +15,24 @@ declare_clippy_lint! {
/// explaining why the unsafe operations performed inside
/// the block are safe.
///
+ /// Note the comment must appear on the line(s) preceding the unsafe block
+ /// with nothing appearing in between. The following is ok:
+ /// ```ignore
+ /// foo(
+ /// // SAFETY:
+ /// // This is a valid safety comment
+ /// unsafe { *x }
+ /// )
+ /// ```
+ /// But neither of these are:
+ /// ```ignore
+ /// // SAFETY:
+ /// // This is not a valid safety comment
+ /// foo(
+ /// /* SAFETY: Neither is this */ unsafe { *x },
+ /// );
+ /// ```
+ ///
/// ### Why is this bad?
/// Undocumented unsafe blocks can make it difficult to
/// read and maintain code, as well as uncover unsoundness
@@ -44,179 +59,139 @@ declare_clippy_lint! {
"creating an unsafe block without explaining why it is safe"
}
-impl_lint_pass!(UndocumentedUnsafeBlocks => [UNDOCUMENTED_UNSAFE_BLOCKS]);
-
-#[derive(Default)]
-pub struct UndocumentedUnsafeBlocks {
- pub local_level: u32,
- pub local_span: Option,
- // The local was already checked for an overall safety comment
- // There is no need to continue checking the blocks in the local
- pub local_checked: bool,
- // Since we can only check the blocks from expanded macros
- // We have to omit the suggestion due to the actual definition
- // Not being available to us
- pub macro_expansion: bool,
-}
+declare_lint_pass!(UndocumentedUnsafeBlocks => [UNDOCUMENTED_UNSAFE_BLOCKS]);
impl LateLintPass<'_> for UndocumentedUnsafeBlocks {
fn check_block(&mut self, cx: &LateContext<'_>, block: &'_ Block<'_>) {
- if_chain! {
- if !self.local_checked;
- if !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, block.hir_id);
- if !in_external_macro(cx.tcx.sess, block.span);
- if let BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) = block.rules;
- if let Some(enclosing_scope_hir_id) = cx.tcx.hir().get_enclosing_scope(block.hir_id);
- if self.block_has_safety_comment(cx.tcx, enclosing_scope_hir_id, block.span) == Some(false);
- then {
- let mut span = block.span;
+ if block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided)
+ && !in_external_macro(cx.tcx.sess, block.span)
+ && !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, block.hir_id)
+ && !is_unsafe_from_proc_macro(cx, block)
+ && !block_has_safety_comment(cx, block)
+ {
+ let source_map = cx.tcx.sess.source_map();
+ let span = if source_map.is_multiline(block.span) {
+ source_map.span_until_char(block.span, '\n')
+ } else {
+ block.span
+ };
- if let Some(local_span) = self.local_span {
- span = local_span;
-
- let result = self.block_has_safety_comment(cx.tcx, enclosing_scope_hir_id, span);
-
- if result.unwrap_or(true) {
- self.local_checked = true;
- return;
- }
- }
-
- self.lint(cx, span);
- }
- }
- }
-
- fn check_local(&mut self, cx: &LateContext<'_>, local: &'_ Local<'_>) {
- if_chain! {
- if !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, local.hir_id);
- if !in_external_macro(cx.tcx.sess, local.span);
- if let Some(init) = local.init;
- then {
- self.visit_expr(init);
-
- if self.local_level > 0 {
- self.local_span = Some(local.span);
- }
- }
- }
- }
-
- fn check_block_post(&mut self, _: &LateContext<'_>, _: &'_ Block<'_>) {
- self.local_level = self.local_level.saturating_sub(1);
-
- if self.local_level == 0 {
- self.local_checked = false;
- self.local_span = None;
- }
- }
-}
-
-impl<'v> Visitor<'v> for UndocumentedUnsafeBlocks {
- fn visit_expr(&mut self, ex: &'v Expr<'v>) {
- match ex.kind {
- ExprKind::Block(_, _) => self.local_level = self.local_level.saturating_add(1),
- _ => walk_expr(self, ex),
- }
- }
-}
-
-impl UndocumentedUnsafeBlocks {
- fn block_has_safety_comment(&mut self, tcx: TyCtxt<'_>, enclosing_hir_id: HirId, block_span: Span) -> Option {
- let map = tcx.hir();
- let source_map = tcx.sess.source_map();
-
- let enclosing_scope_span = map.opt_span(enclosing_hir_id)?;
-
- let between_span = if block_span.from_expansion() {
- self.macro_expansion = true;
- enclosing_scope_span.with_hi(block_span.hi()).source_callsite()
- } else {
- self.macro_expansion = false;
- enclosing_scope_span.to(block_span).source_callsite()
- };
-
- let file_name = source_map.span_to_filename(between_span);
- let source_file = source_map.get_source_file(&file_name)?;
-
- let lex_start = (between_span.lo().0 - source_file.start_pos.0 + 1) as usize;
- let lex_end = (between_span.hi().0 - source_file.start_pos.0) as usize;
- let src_str = source_file.src.as_ref()?[lex_start..lex_end].to_string();
-
- let source_start_pos = source_file.start_pos.0 as usize + lex_start;
-
- let mut pos = 0;
- let mut comment = false;
-
- for token in rustc_lexer::tokenize(&src_str) {
- match token.kind {
- TokenKind::LineComment { doc_style: None }
- | TokenKind::BlockComment {
- doc_style: None,
- terminated: true,
- } => {
- let comment_str = src_str[pos + 2..pos + token.len].to_ascii_uppercase();
-
- if comment_str.contains("SAFETY:") {
- comment = true;
- }
- },
- // We need to add all whitespace to `pos` before checking the comment's line number
- TokenKind::Whitespace => {},
- _ => {
- if comment {
- // Get the line number of the "comment" (really wherever the trailing whitespace ended)
- let comment_line_num = source_file
- .lookup_file_pos(BytePos((source_start_pos + pos).try_into().unwrap()))
- .0;
- // Find the block/local's line number
- let block_line_num = tcx.sess.source_map().lookup_char_pos(block_span.lo()).line;
-
- // Check the comment is immediately followed by the block/local
- if block_line_num == comment_line_num + 1 || block_line_num == comment_line_num {
- return Some(true);
- }
-
- comment = false;
- }
- },
- }
-
- pos += token.len;
- }
-
- Some(false)
- }
-
- fn lint(&self, cx: &LateContext<'_>, mut span: Span) {
- let source_map = cx.tcx.sess.source_map();
-
- if source_map.is_multiline(span) {
- span = source_map.span_until_char(span, '\n');
- }
-
- if self.macro_expansion {
span_lint_and_help(
- cx,
- UNDOCUMENTED_UNSAFE_BLOCKS,
- span,
- "unsafe block in macro expansion missing a safety comment",
- None,
- "consider adding a safety comment in the macro definition",
- );
- } else {
- let block_indent = indent_of(cx, span);
- let suggestion = format!("// SAFETY: ...\n{}", snippet(cx, span, ".."));
-
- span_lint_and_sugg(
cx,
UNDOCUMENTED_UNSAFE_BLOCKS,
span,
"unsafe block missing a safety comment",
- "consider adding a safety comment",
- reindent_multiline(Cow::Borrowed(&suggestion), true, block_indent).to_string(),
- Applicability::HasPlaceholders,
+ None,
+ "consider adding a safety comment on the preceding line",
);
}
}
}
+
+fn is_unsafe_from_proc_macro(cx: &LateContext<'_>, block: &Block<'_>) -> bool {
+ let source_map = cx.sess().source_map();
+ let file_pos = source_map.lookup_byte_offset(block.span.lo());
+ file_pos
+ .sf
+ .src
+ .as_deref()
+ .and_then(|src| src.get(file_pos.pos.to_usize()..))
+ .map_or(true, |src| !src.starts_with("unsafe"))
+}
+
+/// Checks if the lines immediately preceding the block contain a safety comment.
+fn block_has_safety_comment(cx: &LateContext<'_>, block: &Block<'_>) -> bool {
+ // This intentionally ignores text before the start of a function so something like:
+ // ```
+ // // SAFETY: reason
+ // fn foo() { unsafe { .. } }
+ // ```
+ // won't work. This is to avoid dealing with where such a comment should be place relative to
+ // attributes and doc comments.
+
+ let source_map = cx.sess().source_map();
+ let ctxt = block.span.ctxt();
+ if ctxt != SyntaxContext::root() {
+ // From a macro expansion. Get the text from the start of the macro declaration to start of the unsafe block.
+ // macro_rules! foo { () => { stuff }; (x) => { unsafe { stuff } }; }
+ // ^--------------------------------------------^
+ if let Ok(unsafe_line) = source_map.lookup_line(block.span.lo())
+ && let Ok(macro_line) = source_map.lookup_line(ctxt.outer_expn_data().def_site.lo())
+ && Lrc::ptr_eq(&unsafe_line.sf, ¯o_line.sf)
+ && let Some(src) = unsafe_line.sf.src.as_deref()
+ {
+ macro_line.line < unsafe_line.line && text_has_safety_comment(
+ src,
+ &unsafe_line.sf.lines[macro_line.line + 1..=unsafe_line.line],
+ unsafe_line.sf.start_pos.to_usize(),
+ )
+ } else {
+ // Problem getting source text. Pretend a comment was found.
+ true
+ }
+ } else if let Ok(unsafe_line) = source_map.lookup_line(block.span.lo())
+ && let Some(body) = cx.enclosing_body
+ && let Some(body_span) = walk_span_to_context(cx.tcx.hir().body(body).value.span, SyntaxContext::root())
+ && let Ok(body_line) = source_map.lookup_line(body_span.lo())
+ && Lrc::ptr_eq(&unsafe_line.sf, &body_line.sf)
+ && let Some(src) = unsafe_line.sf.src.as_deref()
+ {
+ // Get the text from the start of function body to the unsafe block.
+ // fn foo() { some_stuff; unsafe { stuff }; other_stuff; }
+ // ^-------------^
+ body_line.line < unsafe_line.line && text_has_safety_comment(
+ src,
+ &unsafe_line.sf.lines[body_line.line + 1..=unsafe_line.line],
+ unsafe_line.sf.start_pos.to_usize(),
+ )
+ } else {
+ // Problem getting source text. Pretend a comment was found.
+ true
+ }
+}
+
+/// Checks if the given text has a safety comment for the immediately proceeding line.
+fn text_has_safety_comment(src: &str, line_starts: &[BytePos], offset: usize) -> bool {
+ let mut lines = line_starts
+ .array_windows::<2>()
+ .rev()
+ .map_while(|[start, end]| {
+ src.get(start.to_usize() - offset..end.to_usize() - offset)
+ .map(|text| (start.to_usize(), text.trim_start()))
+ })
+ .filter(|(_, text)| !text.is_empty());
+
+ let Some((line_start, line)) = lines.next() else {
+ return false;
+ };
+ // Check for a sequence of line comments.
+ if line.starts_with("//") {
+ let mut line = line;
+ loop {
+ if line.to_ascii_uppercase().contains("SAFETY:") {
+ return true;
+ }
+ match lines.next() {
+ Some((_, x)) if x.starts_with("//") => line = x,
+ _ => return false,
+ }
+ }
+ }
+ // No line comments; look for the start of a block comment.
+ // This will only find them if they are at the start of a line.
+ let (mut line_start, mut line) = (line_start, line);
+ loop {
+ if line.starts_with("/*") {
+ let src = src[line_start..line_starts.last().unwrap().to_usize()].trim_start();
+ let mut tokens = tokenize(src);
+ return src[..tokens.next().unwrap().len]
+ .to_ascii_uppercase()
+ .contains("SAFETY:")
+ && tokens.all(|t| t.kind == TokenKind::Whitespace);
+ }
+ match lines.next() {
+ Some(x) => (line_start, line) = x,
+ None => return false,
+ }
+ }
+}
diff --git a/clippy_lints/src/undropped_manually_drops.rs b/clippy_lints/src/undropped_manually_drops.rs
deleted file mode 100644
index db652766705c..000000000000
--- a/clippy_lints/src/undropped_manually_drops.rs
+++ /dev/null
@@ -1,59 +0,0 @@
-use clippy_utils::diagnostics::span_lint_and_help;
-use clippy_utils::path_res;
-use clippy_utils::ty::is_type_lang_item;
-use rustc_hir::{lang_items, Expr, ExprKind};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::sym;
-
-declare_clippy_lint! {
- /// ### What it does
- /// Prevents the safe `std::mem::drop` function from being called on `std::mem::ManuallyDrop`.
- ///
- /// ### Why is this bad?
- /// The safe `drop` function does not drop the inner value of a `ManuallyDrop`.
- ///
- /// ### Known problems
- /// Does not catch cases if the user binds `std::mem::drop`
- /// to a different name and calls it that way.
- ///
- /// ### Example
- /// ```rust
- /// struct S;
- /// drop(std::mem::ManuallyDrop::new(S));
- /// ```
- /// Use instead:
- /// ```rust
- /// struct S;
- /// unsafe {
- /// std::mem::ManuallyDrop::drop(&mut std::mem::ManuallyDrop::new(S));
- /// }
- /// ```
- #[clippy::version = "1.49.0"]
- pub UNDROPPED_MANUALLY_DROPS,
- correctness,
- "use of safe `std::mem::drop` function to drop a std::mem::ManuallyDrop, which will not drop the inner value"
-}
-
-declare_lint_pass!(UndroppedManuallyDrops => [UNDROPPED_MANUALLY_DROPS]);
-
-impl<'tcx> LateLintPass<'tcx> for UndroppedManuallyDrops {
- fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
- if_chain! {
- if let ExprKind::Call(fun, [arg_0, ..]) = expr.kind;
- if path_res(cx, fun).opt_def_id() == cx.tcx.get_diagnostic_item(sym::mem_drop);
- let ty = cx.typeck_results().expr_ty(arg_0);
- if is_type_lang_item(cx, ty, lang_items::LangItem::ManuallyDrop);
- then {
- span_lint_and_help(
- cx,
- UNDROPPED_MANUALLY_DROPS,
- expr.span,
- "the inner value of this ManuallyDrop will not be dropped",
- None,
- "to drop a `ManuallyDrop`, use std::mem::ManuallyDrop::drop",
- );
- }
- }
- }
-}
diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs
index 09d671e11184..f8e1021af0ea 100644
--- a/clippy_lints/src/use_self.rs
+++ b/clippy_lints/src/use_self.rs
@@ -34,7 +34,7 @@ declare_clippy_lint! {
///
/// ### Example
/// ```rust
- /// struct Foo {}
+ /// struct Foo;
/// impl Foo {
/// fn new() -> Foo {
/// Foo {}
@@ -43,7 +43,7 @@ declare_clippy_lint! {
/// ```
/// could be
/// ```rust
- /// struct Foo {}
+ /// struct Foo;
/// impl Foo {
/// fn new() -> Self {
/// Self {}
diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs
index 680b2eb1da72..271c3a3dd181 100644
--- a/clippy_lints/src/utils/conf.rs
+++ b/clippy_lints/src/utils/conf.rs
@@ -156,7 +156,7 @@ define_Conf! {
///
/// Suppress lints whenever the suggested change would cause breakage for other crates.
(avoid_breaking_exported_api: bool = true),
- /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS.
+ /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS, ERR_EXPECT, CAST_ABS_TO_UNSIGNED.
///
/// The minimum rust version that the project supports
(msrv: Option = None),
diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs
index b3b241392fed..25d74b8c4993 100644
--- a/clippy_lints/src/utils/internal_lints.rs
+++ b/clippy_lints/src/utils/internal_lints.rs
@@ -25,7 +25,7 @@ use rustc_hir::{
use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
use rustc_middle::hir::nested_filter;
use rustc_middle::mir::interpret::ConstValue;
-use rustc_middle::ty::{self, subst::GenericArgKind};
+use rustc_middle::ty::{self, fast_reject::SimplifiedTypeGen, subst::GenericArgKind, FloatTy};
use rustc_semver::RustcVersion;
use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
use rustc_span::source_map::Spanned;
@@ -889,7 +889,7 @@ fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option {
+ Res::Def(DefKind::Const | DefKind::Static(..), def_id) => {
if let Some(Node::Item(item)) = cx.tcx.hir().get_if_local(def_id) {
if let ItemKind::Const(.., body_id) | ItemKind::Static(.., body_id) = item.kind {
let body = cx.tcx.hir().body(body_id);
@@ -934,7 +934,16 @@ pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool {
// implementations of native types. Check lang items.
let path_syms: Vec<_> = path.iter().map(|p| Symbol::intern(p)).collect();
let lang_items = cx.tcx.lang_items();
- for item_def_id in lang_items.items().iter().flatten() {
+ // This list isn't complete, but good enough for our current list of paths.
+ let incoherent_impls = [
+ SimplifiedTypeGen::FloatSimplifiedType(FloatTy::F32),
+ SimplifiedTypeGen::FloatSimplifiedType(FloatTy::F64),
+ SimplifiedTypeGen::SliceSimplifiedType,
+ SimplifiedTypeGen::StrSimplifiedType,
+ ]
+ .iter()
+ .flat_map(|&ty| cx.tcx.incoherent_impls(ty));
+ for item_def_id in lang_items.items().iter().flatten().chain(incoherent_impls) {
let lang_item_path = cx.get_def_path(*item_def_id);
if path_syms.starts_with(&lang_item_path) {
if let [item] = &path_syms[lang_item_path.len()..] {
diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs
index b3fad6ce7b65..ca03b8010dd8 100644
--- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs
+++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs
@@ -756,7 +756,7 @@ impl<'a, 'hir> intravisit::Visitor<'hir> for LintResolver<'a, 'hir> {
let (expr_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(expr));
if match_type(self.cx, expr_ty, &paths::LINT);
then {
- if let hir::def::Res::Def(DefKind::Static, _) = path.res {
+ if let hir::def::Res::Def(DefKind::Static(..), _) = path.res {
let lint_name = last_path_segment(qpath).ident.name;
self.lints.push(sym_to_string(lint_name).to_ascii_lowercase());
} else if let Some(local) = get_parent_local(self.cx, expr) {
diff --git a/clippy_utils/Cargo.toml b/clippy_utils/Cargo.toml
index d3ed8da4499f..0b1fd95c3453 100644
--- a/clippy_utils/Cargo.toml
+++ b/clippy_utils/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "clippy_utils"
-version = "0.1.61"
+version = "0.1.62"
edition = "2021"
publish = false
diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs
index 62e144398012..a275bac4ce63 100644
--- a/clippy_utils/src/lib.rs
+++ b/clippy_utils/src/lib.rs
@@ -77,19 +77,22 @@ use rustc_hir::intravisit::{walk_expr, FnKind, Visitor};
use rustc_hir::itemlikevisit::ItemLikeVisitor;
use rustc_hir::LangItem::{OptionNone, ResultErr, ResultOk};
use rustc_hir::{
- def, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Constness, Destination, Expr,
- ExprKind, FnDecl, ForeignItem, HirId, Impl, ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local,
- MatchSource, Mutability, Node, Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind,
- TraitItem, TraitItemKind, TraitRef, TyKind, UnOp,
+ def, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Constness, Destination, Expr, ExprKind, FnDecl,
+ ForeignItem, HirId, Impl, ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local, MatchSource,
+ Mutability, Node, Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind,
+ TraitRef, TyKind, UnOp,
};
use rustc_lint::{LateContext, Level, Lint, LintContext};
use rustc_middle::hir::place::PlaceBase;
use rustc_middle::ty as rustc_ty;
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
use rustc_middle::ty::binding::BindingMode;
-use rustc_middle::ty::{IntTy, UintTy, FloatTy};
-use rustc_middle::ty::fast_reject::SimplifiedTypeGen::*;
+use rustc_middle::ty::fast_reject::SimplifiedTypeGen::{
+ ArraySimplifiedType, BoolSimplifiedType, CharSimplifiedType, FloatSimplifiedType, IntSimplifiedType,
+ PtrSimplifiedType, SliceSimplifiedType, StrSimplifiedType, UintSimplifiedType,
+};
use rustc_middle::ty::{layout::IntegerExt, BorrowKind, DefIdTree, Ty, TyCtxt, TypeAndMut, TypeFoldable, UpvarCapture};
+use rustc_middle::ty::{FloatTy, IntTy, UintTy};
use rustc_semver::RustcVersion;
use rustc_session::Session;
use rustc_span::hygiene::{ExpnKind, MacroKind};
@@ -522,7 +525,7 @@ pub fn def_path_res(cx: &LateContext<'_>, path: &[&str]) -> Res {
let tcx = cx.tcx;
let starts = find_primitive(tcx, base)
.chain(find_crate(tcx, base))
- .flat_map(|id| item_child_by_name(tcx, id, first));
+ .filter_map(|id| item_child_by_name(tcx, id, first));
for first in starts {
let last = path
diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs
index fce93153d96e..0424e0672026 100644
--- a/clippy_utils/src/msrvs.rs
+++ b/clippy_utils/src/msrvs.rs
@@ -14,7 +14,7 @@ macro_rules! msrv_aliases {
msrv_aliases! {
1,53,0 { OR_PATTERNS, MANUAL_BITS }
1,52,0 { STR_SPLIT_ONCE }
- 1,51,0 { BORROW_AS_PTR }
+ 1,51,0 { BORROW_AS_PTR, UNSIGNED_ABS }
1,50,0 { BOOL_THEN }
1,47,0 { TAU }
1,46,0 { CONST_IF_MATCH }
@@ -30,6 +30,6 @@ msrv_aliases! {
1,34,0 { TRY_FROM }
1,30,0 { ITERATOR_FIND_MAP, TOOL_ATTRIBUTES }
1,28,0 { FROM_BOOL }
- 1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST }
+ 1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST, EXPECT_ERR }
1,16,0 { STR_REPEAT }
}
diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs
index 6f56f8d51365..79e6e92dc0aa 100644
--- a/clippy_utils/src/paths.rs
+++ b/clippy_utils/src/paths.rs
@@ -105,6 +105,8 @@ pub const PTR_READ_UNALIGNED: [&str; 3] = ["core", "ptr", "read_unaligned"];
pub const PTR_READ_VOLATILE: [&str; 3] = ["core", "ptr", "read_volatile"];
pub const PTR_REPLACE: [&str; 3] = ["core", "ptr", "replace"];
pub const PTR_SWAP: [&str; 3] = ["core", "ptr", "swap"];
+pub const PTR_UNALIGNED_VOLATILE_LOAD: [&str; 3] = ["core", "intrinsics", "unaligned_volatile_load"];
+pub const PTR_UNALIGNED_VOLATILE_STORE: [&str; 3] = ["core", "intrinsics", "unaligned_volatile_store"];
pub const PTR_WRITE: [&str; 3] = ["core", "ptr", "write"];
pub const PTR_WRITE_BYTES: [&str; 3] = ["core", "intrinsics", "write_bytes"];
pub const PTR_WRITE_UNALIGNED: [&str; 3] = ["core", "ptr", "write_unaligned"];
diff --git a/doc/release.md b/doc/release.md
index afe3033c288c..c4f8f9893842 100644
--- a/doc/release.md
+++ b/doc/release.md
@@ -121,4 +121,25 @@ happened a stable backport, make sure to re-merge those changes just as with the
For this see the document on [how to update the changelog].
+If you don't have time to do a complete changelog update right away, just update
+the following parts:
+
+- Remove the `(beta)` from the new stable version:
+
+ ```markdown
+ ## Rust 1.XX (beta) -> ## Rust 1.XX
+ ```
+
+- Update the release date line of the new stable version:
+
+ ```markdown
+ Current beta, release 20YY-MM-DD -> Current stable, released 20YY-MM-DD
+ ```
+
+- Update the release date line of the previous stable version:
+
+ ```markdown
+ Current stable, released 20YY-MM-DD -> Released 20YY-MM-DD
+ ```
+
[how to update the changelog]: https://github.com/rust-lang/rust-clippy/blob/master/doc/changelog_update.md
diff --git a/rust-toolchain b/rust-toolchain
index 5befb856a023..bb29c71e9f45 100644
--- a/rust-toolchain
+++ b/rust-toolchain
@@ -1,3 +1,3 @@
[toolchain]
-channel = "nightly-2022-03-24"
+channel = "nightly-2022-04-07"
components = ["cargo", "llvm-tools-preview", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
diff --git a/src/driver.rs b/src/driver.rs
index bc1b0d745755..00dc916b217c 100644
--- a/src/driver.rs
+++ b/src/driver.rs
@@ -165,8 +165,7 @@ fn report_clippy_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) {
// Separate the output with an empty line
eprintln!();
- let fallback_bundle = rustc_errors::fallback_fluent_bundle(false)
- .expect("failed to load fallback fluent bundle");
+ let fallback_bundle = rustc_errors::fallback_fluent_bundle(false).expect("failed to load fallback fluent bundle");
let emitter = Box::new(rustc_errors::emitter::EmitterWriter::stderr(
rustc_errors::ColorConfig::Auto,
None,
diff --git a/tests/fmt.rs b/tests/check-fmt.rs
similarity index 100%
rename from tests/fmt.rs
rename to tests/check-fmt.rs
diff --git a/tests/ui-cargo/module_style/fail_mod/src/main.stderr b/tests/ui-cargo/module_style/fail_mod/src/main.stderr
index af4c298b3108..e2010e998131 100644
--- a/tests/ui-cargo/module_style/fail_mod/src/main.stderr
+++ b/tests/ui-cargo/module_style/fail_mod/src/main.stderr
@@ -1,19 +1,19 @@
-error: `mod.rs` files are required, found `/bad/inner.rs`
+error: `mod.rs` files are required, found `bad/inner.rs`
--> $DIR/bad/inner.rs:1:1
|
LL | pub mod stuff;
| ^
|
= note: `-D clippy::self-named-module-files` implied by `-D warnings`
- = help: move `/bad/inner.rs` to `/bad/inner/mod.rs`
+ = help: move `bad/inner.rs` to `bad/inner/mod.rs`
-error: `mod.rs` files are required, found `/bad/inner/stuff.rs`
+error: `mod.rs` files are required, found `bad/inner/stuff.rs`
--> $DIR/bad/inner/stuff.rs:1:1
|
LL | pub mod most;
| ^
|
- = help: move `/bad/inner/stuff.rs` to `/bad/inner/stuff/mod.rs`
+ = help: move `bad/inner/stuff.rs` to `bad/inner/stuff/mod.rs`
error: aborting due to 2 previous errors
diff --git a/tests/ui-cargo/module_style/fail_no_mod/src/main.stderr b/tests/ui-cargo/module_style/fail_no_mod/src/main.stderr
index 11e15db7fb96..f91940209383 100644
--- a/tests/ui-cargo/module_style/fail_no_mod/src/main.stderr
+++ b/tests/ui-cargo/module_style/fail_no_mod/src/main.stderr
@@ -1,11 +1,11 @@
-error: `mod.rs` files are not allowed, found `/bad/mod.rs`
+error: `mod.rs` files are not allowed, found `bad/mod.rs`
--> $DIR/bad/mod.rs:1:1
|
LL | pub struct Thing;
| ^
|
= note: `-D clippy::mod-module-files` implied by `-D warnings`
- = help: move `/bad/mod.rs` to `/bad.rs`
+ = help: move `bad/mod.rs` to `bad.rs`
error: aborting due to previous error
diff --git a/tests/ui-internal/check_clippy_version_attribute.stderr b/tests/ui-internal/check_clippy_version_attribute.stderr
index 9302e02ccb9c..67e1a07b7f5f 100644
--- a/tests/ui-internal/check_clippy_version_attribute.stderr
+++ b/tests/ui-internal/check_clippy_version_attribute.stderr
@@ -46,11 +46,6 @@ LL | | report_in_external_macro: true
LL | | }
| |_^
|
-note: the lint level is defined here
- --> $DIR/check_clippy_version_attribute.rs:1:9
- |
-LL | #![deny(clippy::internal)]
- | ^^^^^^^^^^^^^^^^
= note: `#[deny(clippy::missing_clippy_version_attribute)]` implied by `#[deny(clippy::internal)]`
= help: please use a `clippy::version` attribute, see `doc/adding_lints.md`
= note: this error originates in the macro `$crate::declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info)
diff --git a/tests/ui-toml/struct_excessive_bools/test.rs b/tests/ui-toml/struct_excessive_bools/test.rs
index 242984680e16..32dd80246fab 100644
--- a/tests/ui-toml/struct_excessive_bools/test.rs
+++ b/tests/ui-toml/struct_excessive_bools/test.rs
@@ -4,6 +4,6 @@ struct S {
a: bool,
}
-struct Foo {}
+struct Foo;
fn main() {}
diff --git a/tests/ui/auxiliary/proc_macro_unsafe.rs b/tests/ui/auxiliary/proc_macro_unsafe.rs
new file mode 100644
index 000000000000..3c40f77469b8
--- /dev/null
+++ b/tests/ui/auxiliary/proc_macro_unsafe.rs
@@ -0,0 +1,18 @@
+// compile-flags: --emit=link
+// no-prefer-dynamic
+
+#![crate_type = "proc-macro"]
+
+extern crate proc_macro;
+
+use proc_macro::{Delimiter, Group, Ident, TokenStream, TokenTree};
+
+#[proc_macro]
+pub fn unsafe_block(input: TokenStream) -> TokenStream {
+ let span = input.into_iter().next().unwrap().span();
+ TokenStream::from_iter([TokenTree::Ident(Ident::new("unsafe", span)), {
+ let mut group = Group::new(Delimiter::Brace, TokenStream::new());
+ group.set_span(span);
+ TokenTree::Group(group)
+ }])
+}
diff --git a/tests/ui/bytes_nth.stderr b/tests/ui/bytes_nth.stderr
index 8a7afa934502..9851d4791d8d 100644
--- a/tests/ui/bytes_nth.stderr
+++ b/tests/ui/bytes_nth.stderr
@@ -1,4 +1,4 @@
-error: called `.byte().nth()` on a `String`
+error: called `.bytes().nth()` on a `String`
--> $DIR/bytes_nth.rs:8:13
|
LL | let _ = s.bytes().nth(3);
@@ -6,13 +6,13 @@ LL | let _ = s.bytes().nth(3);
|
= note: `-D clippy::bytes-nth` implied by `-D warnings`
-error: called `.byte().nth()` on a `String`
+error: called `.bytes().nth()` on a `String`
--> $DIR/bytes_nth.rs:9:14
|
LL | let _ = &s.bytes().nth(3);
| ^^^^^^^^^^^^^^^^ help: try: `s.as_bytes().get(3)`
-error: called `.byte().nth()` on a `str`
+error: called `.bytes().nth()` on a `str`
--> $DIR/bytes_nth.rs:10:13
|
LL | let _ = s[..].bytes().nth(3);
diff --git a/tests/ui/case_sensitive_file_extension_comparisons.rs b/tests/ui/case_sensitive_file_extension_comparisons.rs
index 68719c2bc6d0..0d65071af15e 100644
--- a/tests/ui/case_sensitive_file_extension_comparisons.rs
+++ b/tests/ui/case_sensitive_file_extension_comparisons.rs
@@ -2,7 +2,7 @@
use std::string::String;
-struct TestStruct {}
+struct TestStruct;
impl TestStruct {
fn ends_with(self, arg: &str) {}
diff --git a/tests/ui/cast.rs b/tests/ui/cast.rs
index 2e31ad3172ee..cf85a5ca931d 100644
--- a/tests/ui/cast.rs
+++ b/tests/ui/cast.rs
@@ -7,7 +7,7 @@
clippy::cast_sign_loss,
clippy::cast_possible_wrap
)]
-#[allow(clippy::no_effect, clippy::unnecessary_operation)]
+#[allow(clippy::cast_abs_to_unsigned, clippy::no_effect, clippy::unnecessary_operation)]
fn main() {
// Test clippy::cast_precision_loss
let x0 = 1i32;
diff --git a/tests/ui/cast_abs_to_unsigned.fixed b/tests/ui/cast_abs_to_unsigned.fixed
new file mode 100644
index 000000000000..4ec2465be06d
--- /dev/null
+++ b/tests/ui/cast_abs_to_unsigned.fixed
@@ -0,0 +1,8 @@
+// run-rustfix
+#![warn(clippy::cast_abs_to_unsigned)]
+
+fn main() {
+ let x: i32 = -42;
+ let y: u32 = x.unsigned_abs();
+ println!("The absolute value of {} is {}", x, y);
+}
diff --git a/tests/ui/cast_abs_to_unsigned.rs b/tests/ui/cast_abs_to_unsigned.rs
new file mode 100644
index 000000000000..59b9c8c36788
--- /dev/null
+++ b/tests/ui/cast_abs_to_unsigned.rs
@@ -0,0 +1,8 @@
+// run-rustfix
+#![warn(clippy::cast_abs_to_unsigned)]
+
+fn main() {
+ let x: i32 = -42;
+ let y: u32 = x.abs() as u32;
+ println!("The absolute value of {} is {}", x, y);
+}
diff --git a/tests/ui/cast_abs_to_unsigned.stderr b/tests/ui/cast_abs_to_unsigned.stderr
new file mode 100644
index 000000000000..eb12857357a4
--- /dev/null
+++ b/tests/ui/cast_abs_to_unsigned.stderr
@@ -0,0 +1,10 @@
+error: casting the result of `i32::abs()` to u32
+ --> $DIR/cast_abs_to_unsigned.rs:6:18
+ |
+LL | let y: u32 = x.abs() as u32;
+ | ^^^^^^^^^^^^^^ help: replace with: `x.unsigned_abs()`
+ |
+ = note: `-D clippy::cast-abs-to-unsigned` implied by `-D warnings`
+
+error: aborting due to previous error
+
diff --git a/tests/ui/cast_alignment.rs b/tests/ui/cast_alignment.rs
index 659591fffbec..e4e7290a30e9 100644
--- a/tests/ui/cast_alignment.rs
+++ b/tests/ui/cast_alignment.rs
@@ -1,6 +1,7 @@
//! Test casts for alignment issues
#![feature(rustc_private)]
+#![feature(core_intrinsics)]
extern crate libc;
#[warn(clippy::cast_ptr_alignment)]
@@ -34,4 +35,17 @@ fn main() {
(&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;
+
+ // Issue #2881
+ let mut data = [0u8, 0u8];
+ unsafe {
+ let ptr = &data as *const [u8; 2] as *const u8;
+ let _ = (ptr as *const u16).read_unaligned();
+ let _ = core::ptr::read_unaligned(ptr as *const u16);
+ let _ = core::intrinsics::unaligned_volatile_load(ptr as *const u16);
+ let ptr = &mut data as *mut [u8; 2] as *mut u8;
+ let _ = (ptr as *mut u16).write_unaligned(0);
+ let _ = core::ptr::write_unaligned(ptr as *mut u16, 0);
+ let _ = core::intrinsics::unaligned_volatile_store(ptr as *mut u16, 0);
+ }
}
diff --git a/tests/ui/cast_alignment.stderr b/tests/ui/cast_alignment.stderr
index aedd36844555..5df2b5b1094b 100644
--- a/tests/ui/cast_alignment.stderr
+++ b/tests/ui/cast_alignment.stderr
@@ -1,5 +1,5 @@
error: casting from `*const u8` to a more-strictly-aligned pointer (`*const u16`) (1 < 2 bytes)
- --> $DIR/cast_alignment.rs:18:5
+ --> $DIR/cast_alignment.rs:19:5
|
LL | (&1u8 as *const u8) as *const u16;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -7,19 +7,19 @@ 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:19:5
+ --> $DIR/cast_alignment.rs:20:5
|
LL | (&mut 1u8 as *mut u8) as *mut u16;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: casting from `*const u8` to a more-strictly-aligned pointer (`*const u16`) (1 < 2 bytes)
- --> $DIR/cast_alignment.rs:22:5
+ --> $DIR/cast_alignment.rs:23:5
|
LL | (&1u8 as *const u8).cast::();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: casting from `*mut u8` to a more-strictly-aligned pointer (`*mut u16`) (1 < 2 bytes)
- --> $DIR/cast_alignment.rs:23:5
+ --> $DIR/cast_alignment.rs:24:5
|
LL | (&mut 1u8 as *mut u8).cast::();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/crashes/ice-2774.rs b/tests/ui/crashes/ice-2774.rs
index d44b0fae8200..88cfa1f923c0 100644
--- a/tests/ui/crashes/ice-2774.rs
+++ b/tests/ui/crashes/ice-2774.rs
@@ -8,7 +8,7 @@ pub struct Bar {
}
#[derive(Eq, PartialEq, Debug, Hash)]
-pub struct Foo {}
+pub struct Foo;
#[allow(clippy::implicit_hasher)]
// This should not cause a "cannot relate bound region" ICE.
diff --git a/tests/ui/crashes/ice-6179.rs b/tests/ui/crashes/ice-6179.rs
index 8d9a1af8ff11..4fe92d356c44 100644
--- a/tests/ui/crashes/ice-6179.rs
+++ b/tests/ui/crashes/ice-6179.rs
@@ -4,7 +4,7 @@
#![warn(clippy::use_self)]
#![allow(dead_code)]
-struct Foo {}
+struct Foo;
impl Foo {
fn new() -> Self {
diff --git a/tests/ui/crashes/ice-6792.rs b/tests/ui/crashes/ice-6792.rs
index 0e2ab1a39b82..9cbafc716b50 100644
--- a/tests/ui/crashes/ice-6792.rs
+++ b/tests/ui/crashes/ice-6792.rs
@@ -7,7 +7,7 @@ trait Trait {
fn broken() -> Self::Ty;
}
-struct Foo {}
+struct Foo;
impl Trait for Foo {
type Ty = Foo;
diff --git a/tests/ui/crashes/ice-7868.stderr b/tests/ui/crashes/ice-7868.stderr
index 111350a6280d..1a33e647588f 100644
--- a/tests/ui/crashes/ice-7868.stderr
+++ b/tests/ui/crashes/ice-7868.stderr
@@ -5,11 +5,7 @@ LL | unsafe { 0 };
| ^^^^^^^^^^^^
|
= note: `-D clippy::undocumented-unsafe-blocks` implied by `-D warnings`
-help: consider adding a safety comment
- |
-LL ~ // SAFETY: ...
-LL ~ unsafe { 0 };
- |
+ = help: consider adding a safety comment on the preceding line
error: aborting due to previous error
diff --git a/tests/ui/crashes/needless_lifetimes_impl_trait.rs b/tests/ui/crashes/needless_lifetimes_impl_trait.rs
index 676564b2445d..376ff97ba603 100644
--- a/tests/ui/crashes/needless_lifetimes_impl_trait.rs
+++ b/tests/ui/crashes/needless_lifetimes_impl_trait.rs
@@ -3,7 +3,7 @@
trait Foo {}
-struct Bar {}
+struct Bar;
struct Baz<'a> {
bar: &'a Bar,
diff --git a/tests/ui/crashes/regressions.rs b/tests/ui/crashes/regressions.rs
index a41bcb33b446..6f9d98bbfe7f 100644
--- a/tests/ui/crashes/regressions.rs
+++ b/tests/ui/crashes/regressions.rs
@@ -6,6 +6,6 @@ pub fn foo(bar: *const u8) {
// Regression test for https://github.com/rust-lang/rust-clippy/issues/4917
/// {
+ println!("{}", $crate::hygienic::MESSAGE);
+ };
+ }
+
+ pub const MESSAGE: &str = "Hello!";
+}
+
+mod unhygienic {
+ #[macro_export]
+ macro_rules! print_message_unhygienic {
+ () => {
+ println!("{}", $crate::unhygienic::MESSAGE);
+ };
+ }
+
+ pub const MESSAGE: &str = "Hello!";
+}
+
+mod unhygienic_intentionally {
+ // For cases where the use of `crate` is intentional, applying `allow` to the macro definition
+ // should suppress the lint.
+ #[allow(clippy::crate_in_macro_def)]
+ #[macro_export]
+ macro_rules! print_message_unhygienic_intentionally {
+ () => {
+ println!("{}", crate::CALLER_PROVIDED_MESSAGE);
+ };
+ }
+}
+
+#[macro_use]
+mod not_exported {
+ macro_rules! print_message_not_exported {
+ () => {
+ println!("{}", crate::not_exported::MESSAGE);
+ };
+ }
+
+ pub const MESSAGE: &str = "Hello!";
+}
+
+fn main() {
+ print_message_hygienic!();
+ print_message_unhygienic!();
+ print_message_unhygienic_intentionally!();
+ print_message_not_exported!();
+}
+
+pub const CALLER_PROVIDED_MESSAGE: &str = "Hello!";
diff --git a/tests/ui/crate_in_macro_def.rs b/tests/ui/crate_in_macro_def.rs
new file mode 100644
index 000000000000..ac456108e4ab
--- /dev/null
+++ b/tests/ui/crate_in_macro_def.rs
@@ -0,0 +1,56 @@
+// run-rustfix
+#![warn(clippy::crate_in_macro_def)]
+
+mod hygienic {
+ #[macro_export]
+ macro_rules! print_message_hygienic {
+ () => {
+ println!("{}", $crate::hygienic::MESSAGE);
+ };
+ }
+
+ pub const MESSAGE: &str = "Hello!";
+}
+
+mod unhygienic {
+ #[macro_export]
+ macro_rules! print_message_unhygienic {
+ () => {
+ println!("{}", crate::unhygienic::MESSAGE);
+ };
+ }
+
+ pub const MESSAGE: &str = "Hello!";
+}
+
+mod unhygienic_intentionally {
+ // For cases where the use of `crate` is intentional, applying `allow` to the macro definition
+ // should suppress the lint.
+ #[allow(clippy::crate_in_macro_def)]
+ #[macro_export]
+ macro_rules! print_message_unhygienic_intentionally {
+ () => {
+ println!("{}", crate::CALLER_PROVIDED_MESSAGE);
+ };
+ }
+}
+
+#[macro_use]
+mod not_exported {
+ macro_rules! print_message_not_exported {
+ () => {
+ println!("{}", crate::not_exported::MESSAGE);
+ };
+ }
+
+ pub const MESSAGE: &str = "Hello!";
+}
+
+fn main() {
+ print_message_hygienic!();
+ print_message_unhygienic!();
+ print_message_unhygienic_intentionally!();
+ print_message_not_exported!();
+}
+
+pub const CALLER_PROVIDED_MESSAGE: &str = "Hello!";
diff --git a/tests/ui/crate_in_macro_def.stderr b/tests/ui/crate_in_macro_def.stderr
new file mode 100644
index 000000000000..9ac5937dcc06
--- /dev/null
+++ b/tests/ui/crate_in_macro_def.stderr
@@ -0,0 +1,10 @@
+error: `crate` references the macro call's crate
+ --> $DIR/crate_in_macro_def.rs:19:28
+ |
+LL | println!("{}", crate::unhygienic::MESSAGE);
+ | ^^^^^ help: to reference the macro definition's crate, use: `$crate`
+ |
+ = note: `-D clippy::crate-in-macro-def` implied by `-D warnings`
+
+error: aborting due to previous error
+
diff --git a/tests/ui/default_numeric_fallback_f64.fixed b/tests/ui/default_numeric_fallback_f64.fixed
index 1b0e7544e79c..e0b4a2f69423 100644
--- a/tests/ui/default_numeric_fallback_f64.fixed
+++ b/tests/ui/default_numeric_fallback_f64.fixed
@@ -134,7 +134,7 @@ mod enum_ctor {
}
mod method_calls {
- struct StructForMethodCallTest {}
+ struct StructForMethodCallTest;
impl StructForMethodCallTest {
fn concrete_arg(&self, f: f64) {}
diff --git a/tests/ui/default_numeric_fallback_f64.rs b/tests/ui/default_numeric_fallback_f64.rs
index e9687777bbd0..50bbb6eec6c7 100644
--- a/tests/ui/default_numeric_fallback_f64.rs
+++ b/tests/ui/default_numeric_fallback_f64.rs
@@ -134,7 +134,7 @@ mod enum_ctor {
}
mod method_calls {
- struct StructForMethodCallTest {}
+ struct StructForMethodCallTest;
impl StructForMethodCallTest {
fn concrete_arg(&self, f: f64) {}
diff --git a/tests/ui/default_numeric_fallback_i32.fixed b/tests/ui/default_numeric_fallback_i32.fixed
index 55c082fcb19f..bded9e2c0e80 100644
--- a/tests/ui/default_numeric_fallback_i32.fixed
+++ b/tests/ui/default_numeric_fallback_i32.fixed
@@ -133,7 +133,7 @@ mod enum_ctor {
}
mod method_calls {
- struct StructForMethodCallTest {}
+ struct StructForMethodCallTest;
impl StructForMethodCallTest {
fn concrete_arg(&self, x: i32) {}
diff --git a/tests/ui/default_numeric_fallback_i32.rs b/tests/ui/default_numeric_fallback_i32.rs
index e0a4828ce9ff..3fceefa551c7 100644
--- a/tests/ui/default_numeric_fallback_i32.rs
+++ b/tests/ui/default_numeric_fallback_i32.rs
@@ -133,7 +133,7 @@ mod enum_ctor {
}
mod method_calls {
- struct StructForMethodCallTest {}
+ struct StructForMethodCallTest;
impl StructForMethodCallTest {
fn concrete_arg(&self, x: i32) {}
diff --git a/tests/ui/drop_forget_copy.rs b/tests/ui/drop_forget_copy.rs
index 9ddd6d64701a..7c7a9ecff67f 100644
--- a/tests/ui/drop_forget_copy.rs
+++ b/tests/ui/drop_forget_copy.rs
@@ -5,7 +5,7 @@ use std::mem::{drop, forget};
use std::vec::Vec;
#[derive(Copy, Clone)]
-struct SomeStruct {}
+struct SomeStruct;
struct AnotherStruct {
x: u8,
diff --git a/tests/ui/drop_forget_copy.stderr b/tests/ui/drop_forget_copy.stderr
index 01de0be7caea..88228afae89c 100644
--- a/tests/ui/drop_forget_copy.stderr
+++ b/tests/ui/drop_forget_copy.stderr
@@ -5,7 +5,7 @@ LL | drop(s1);
| ^^^^^^^^
|
= note: `-D clippy::drop-copy` implied by `-D warnings`
-note: argument has type SomeStruct
+note: argument has type `SomeStruct`
--> $DIR/drop_forget_copy.rs:33:10
|
LL | drop(s1);
@@ -17,7 +17,7 @@ error: calls to `std::mem::drop` with a value that implements `Copy`. Dropping a
LL | drop(s2);
| ^^^^^^^^
|
-note: argument has type SomeStruct
+note: argument has type `SomeStruct`
--> $DIR/drop_forget_copy.rs:34:10
|
LL | drop(s2);
@@ -29,7 +29,7 @@ error: calls to `std::mem::drop` with a value that implements `Copy`. Dropping a
LL | drop(s4);
| ^^^^^^^^
|
-note: argument has type SomeStruct
+note: argument has type `SomeStruct`
--> $DIR/drop_forget_copy.rs:36:10
|
LL | drop(s4);
@@ -42,7 +42,7 @@ LL | forget(s1);
| ^^^^^^^^^^
|
= note: `-D clippy::forget-copy` implied by `-D warnings`
-note: argument has type SomeStruct
+note: argument has type `SomeStruct`
--> $DIR/drop_forget_copy.rs:39:12
|
LL | forget(s1);
@@ -54,7 +54,7 @@ error: calls to `std::mem::forget` with a value that implements `Copy`. Forgetti
LL | forget(s2);
| ^^^^^^^^^^
|
-note: argument has type SomeStruct
+note: argument has type `SomeStruct`
--> $DIR/drop_forget_copy.rs:40:12
|
LL | forget(s2);
@@ -66,7 +66,7 @@ error: calls to `std::mem::forget` with a value that implements `Copy`. Forgetti
LL | forget(s4);
| ^^^^^^^^^^
|
-note: argument has type SomeStruct
+note: argument has type `SomeStruct`
--> $DIR/drop_forget_copy.rs:42:12
|
LL | forget(s4);
diff --git a/tests/ui/drop_non_drop.rs b/tests/ui/drop_non_drop.rs
new file mode 100644
index 000000000000..5a0ebde82c5d
--- /dev/null
+++ b/tests/ui/drop_non_drop.rs
@@ -0,0 +1,40 @@
+#![warn(clippy::drop_non_drop)]
+
+use core::mem::drop;
+
+fn make_result(t: T) -> Result {
+ Ok(t)
+}
+
+#[must_use]
+fn must_use(t: T) -> T {
+ t
+}
+
+fn drop_generic(t: T) {
+ // Don't lint
+ drop(t)
+}
+
+fn main() {
+ struct Foo;
+ // Lint
+ drop(Foo);
+ // Don't lint
+ drop(make_result(Foo));
+ // Don't lint
+ drop(must_use(Foo));
+
+ struct Bar;
+ impl Drop for Bar {
+ fn drop(&mut self) {}
+ }
+ // Don't lint
+ drop(Bar);
+
+ struct Baz(T);
+ // Lint
+ drop(Baz(Foo));
+ // Don't lint
+ drop(Baz(Bar));
+}
diff --git a/tests/ui/drop_non_drop.stderr b/tests/ui/drop_non_drop.stderr
new file mode 100644
index 000000000000..f73068901c50
--- /dev/null
+++ b/tests/ui/drop_non_drop.stderr
@@ -0,0 +1,27 @@
+error: call to `std::mem::drop` with a value that does not implement `Drop`. Dropping such a type only extends it's contained lifetimes
+ --> $DIR/drop_non_drop.rs:22:5
+ |
+LL | drop(Foo);
+ | ^^^^^^^^^
+ |
+ = note: `-D clippy::drop-non-drop` implied by `-D warnings`
+note: argument has type `main::Foo`
+ --> $DIR/drop_non_drop.rs:22:10
+ |
+LL | drop(Foo);
+ | ^^^
+
+error: call to `std::mem::drop` with a value that does not implement `Drop`. Dropping such a type only extends it's contained lifetimes
+ --> $DIR/drop_non_drop.rs:37:5
+ |
+LL | drop(Baz(Foo));
+ | ^^^^^^^^^^^^^^
+ |
+note: argument has type `main::Baz`
+ --> $DIR/drop_non_drop.rs:37:10
+ |
+LL | drop(Baz(Foo));
+ | ^^^^^^^^
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/drop_ref.rs b/tests/ui/drop_ref.rs
index e1a15c609fd2..7de0b0bbdf9a 100644
--- a/tests/ui/drop_ref.rs
+++ b/tests/ui/drop_ref.rs
@@ -1,7 +1,7 @@
#![warn(clippy::drop_ref)]
#![allow(clippy::toplevel_ref_arg)]
#![allow(clippy::map_err_ignore)]
-#![allow(clippy::unnecessary_wraps)]
+#![allow(clippy::unnecessary_wraps, clippy::drop_non_drop)]
use std::mem::drop;
diff --git a/tests/ui/empty_structs_with_brackets.fixed b/tests/ui/empty_structs_with_brackets.fixed
new file mode 100644
index 000000000000..80f07603b8d4
--- /dev/null
+++ b/tests/ui/empty_structs_with_brackets.fixed
@@ -0,0 +1,25 @@
+// run-rustfix
+#![warn(clippy::empty_structs_with_brackets)]
+#![allow(dead_code)]
+
+pub struct MyEmptyStruct; // should trigger lint
+struct MyEmptyTupleStruct; // should trigger lint
+
+// should not trigger lint
+struct MyCfgStruct {
+ #[cfg(feature = "thisisneverenabled")]
+ field: u8,
+}
+
+// should not trigger lint
+struct MyCfgTupleStruct(#[cfg(feature = "thisisneverenabled")] u8);
+
+// should not trigger lint
+struct MyStruct {
+ field: u8,
+}
+struct MyTupleStruct(usize, String); // should not trigger lint
+struct MySingleTupleStruct(usize); // should not trigger lint
+struct MyUnitLikeStruct; // should not trigger lint
+
+fn main() {}
diff --git a/tests/ui/empty_structs_with_brackets.rs b/tests/ui/empty_structs_with_brackets.rs
new file mode 100644
index 000000000000..1d1ed4c76902
--- /dev/null
+++ b/tests/ui/empty_structs_with_brackets.rs
@@ -0,0 +1,25 @@
+// run-rustfix
+#![warn(clippy::empty_structs_with_brackets)]
+#![allow(dead_code)]
+
+pub struct MyEmptyStruct {} // should trigger lint
+struct MyEmptyTupleStruct(); // should trigger lint
+
+// should not trigger lint
+struct MyCfgStruct {
+ #[cfg(feature = "thisisneverenabled")]
+ field: u8,
+}
+
+// should not trigger lint
+struct MyCfgTupleStruct(#[cfg(feature = "thisisneverenabled")] u8);
+
+// should not trigger lint
+struct MyStruct {
+ field: u8,
+}
+struct MyTupleStruct(usize, String); // should not trigger lint
+struct MySingleTupleStruct(usize); // should not trigger lint
+struct MyUnitLikeStruct; // should not trigger lint
+
+fn main() {}
diff --git a/tests/ui/empty_structs_with_brackets.stderr b/tests/ui/empty_structs_with_brackets.stderr
new file mode 100644
index 000000000000..0308cb5571af
--- /dev/null
+++ b/tests/ui/empty_structs_with_brackets.stderr
@@ -0,0 +1,19 @@
+error: found empty brackets on struct declaration
+ --> $DIR/empty_structs_with_brackets.rs:5:25
+ |
+LL | pub struct MyEmptyStruct {} // should trigger lint
+ | ^^^
+ |
+ = note: `-D clippy::empty-structs-with-brackets` implied by `-D warnings`
+ = help: remove the brackets
+
+error: found empty brackets on struct declaration
+ --> $DIR/empty_structs_with_brackets.rs:6:26
+ |
+LL | struct MyEmptyTupleStruct(); // should trigger lint
+ | ^^^
+ |
+ = help: remove the brackets
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/err_expect.fixed b/tests/ui/err_expect.fixed
new file mode 100644
index 000000000000..7e18d70bae40
--- /dev/null
+++ b/tests/ui/err_expect.fixed
@@ -0,0 +1,14 @@
+// run-rustfix
+
+struct MyTypeNonDebug;
+
+#[derive(Debug)]
+struct MyTypeDebug;
+
+fn main() {
+ let test_debug: Result = Ok(MyTypeDebug);
+ test_debug.expect_err("Testing debug type");
+
+ let test_non_debug: Result = Ok(MyTypeNonDebug);
+ test_non_debug.err().expect("Testing non debug type");
+}
diff --git a/tests/ui/err_expect.rs b/tests/ui/err_expect.rs
new file mode 100644
index 000000000000..bf8c3c9fb8c9
--- /dev/null
+++ b/tests/ui/err_expect.rs
@@ -0,0 +1,14 @@
+// run-rustfix
+
+struct MyTypeNonDebug;
+
+#[derive(Debug)]
+struct MyTypeDebug;
+
+fn main() {
+ let test_debug: Result = Ok(MyTypeDebug);
+ test_debug.err().expect("Testing debug type");
+
+ let test_non_debug: Result = Ok(MyTypeNonDebug);
+ test_non_debug.err().expect("Testing non debug type");
+}
diff --git a/tests/ui/err_expect.stderr b/tests/ui/err_expect.stderr
new file mode 100644
index 000000000000..ffd97e00a5c0
--- /dev/null
+++ b/tests/ui/err_expect.stderr
@@ -0,0 +1,10 @@
+error: called `.err().expect()` on a `Result` value
+ --> $DIR/err_expect.rs:10:16
+ |
+LL | test_debug.err().expect("Testing debug type");
+ | ^^^^^^^^^^^^ help: try: `expect_err`
+ |
+ = note: `-D clippy::err-expect` implied by `-D warnings`
+
+error: aborting due to previous error
+
diff --git a/tests/ui/fn_params_excessive_bools.rs b/tests/ui/fn_params_excessive_bools.rs
index 1442ee08e754..f805bcc9ba8a 100644
--- a/tests/ui/fn_params_excessive_bools.rs
+++ b/tests/ui/fn_params_excessive_bools.rs
@@ -20,7 +20,7 @@ fn h(_: bool, _: bool, _: bool) {}
fn e(_: S, _: S, _: Box, _: Vec) {}
fn t(_: S, _: S, _: Box, _: Vec, _: bool, _: bool, _: bool, _: bool) {}
-struct S {}
+struct S;
trait Trait {
fn f(_: bool, _: bool, _: bool, _: bool);
fn g(_: bool, _: bool, _: bool, _: Vec);
diff --git a/tests/ui/forget_non_drop.rs b/tests/ui/forget_non_drop.rs
new file mode 100644
index 000000000000..7580cf95ebfa
--- /dev/null
+++ b/tests/ui/forget_non_drop.rs
@@ -0,0 +1,27 @@
+#![warn(clippy::forget_non_drop)]
+
+use core::mem::forget;
+
+fn forget_generic(t: T) {
+ // Don't lint
+ forget(t)
+}
+
+fn main() {
+ struct Foo;
+ // Lint
+ forget(Foo);
+
+ struct Bar;
+ impl Drop for Bar {
+ fn drop(&mut self) {}
+ }
+ // Don't lint
+ forget(Bar);
+
+ struct Baz(T);
+ // Lint
+ forget(Baz(Foo));
+ // Don't lint
+ forget(Baz(Bar));
+}
diff --git a/tests/ui/forget_non_drop.stderr b/tests/ui/forget_non_drop.stderr
new file mode 100644
index 000000000000..03fb00960a44
--- /dev/null
+++ b/tests/ui/forget_non_drop.stderr
@@ -0,0 +1,27 @@
+error: call to `std::mem::forget` with a value that does not implement `Drop`. Forgetting such a type is the same as dropping it
+ --> $DIR/forget_non_drop.rs:13:5
+ |
+LL | forget(Foo);
+ | ^^^^^^^^^^^
+ |
+ = note: `-D clippy::forget-non-drop` implied by `-D warnings`
+note: argument has type `main::Foo`
+ --> $DIR/forget_non_drop.rs:13:12
+ |
+LL | forget(Foo);
+ | ^^^
+
+error: call to `std::mem::forget` with a value that does not implement `Drop`. Forgetting such a type is the same as dropping it
+ --> $DIR/forget_non_drop.rs:24:5
+ |
+LL | forget(Baz(Foo));
+ | ^^^^^^^^^^^^^^^^
+ |
+note: argument has type `main::Baz`
+ --> $DIR/forget_non_drop.rs:24:12
+ |
+LL | forget(Baz(Foo));
+ | ^^^^^^^^
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/forget_ref.rs b/tests/ui/forget_ref.rs
index c49e6756a6c5..6c8c4c9c0ede 100644
--- a/tests/ui/forget_ref.rs
+++ b/tests/ui/forget_ref.rs
@@ -1,6 +1,6 @@
#![warn(clippy::forget_ref)]
#![allow(clippy::toplevel_ref_arg)]
-#![allow(clippy::unnecessary_wraps)]
+#![allow(clippy::unnecessary_wraps, clippy::forget_non_drop)]
use std::mem::forget;
diff --git a/tests/ui/identity_op.rs b/tests/ui/identity_op.rs
index 12bbda71f434..edc3fe1aec13 100644
--- a/tests/ui/identity_op.rs
+++ b/tests/ui/identity_op.rs
@@ -66,4 +66,13 @@ fn main() {
let b = a << 0; // no error: non-integer
1 * Meter; // no error: non-integer
+
+ 2 % 3;
+ -2 % 3;
+ 2 % -3 + x;
+ -2 % -3 + x;
+ x + 1 % 3;
+ (x + 1) % 3; // no error
+ 4 % 3; // no error
+ 4 % -3; // no error
}
diff --git a/tests/ui/identity_op.stderr b/tests/ui/identity_op.stderr
index 0103cf5457e8..706f01a3dd6c 100644
--- a/tests/ui/identity_op.stderr
+++ b/tests/ui/identity_op.stderr
@@ -78,5 +78,35 @@ error: the operation is ineffective. Consider reducing it to `x`
LL | x >> &0;
| ^^^^^^^
-error: aborting due to 13 previous errors
+error: the operation is ineffective. Consider reducing it to `2`
+ --> $DIR/identity_op.rs:70:5
+ |
+LL | 2 % 3;
+ | ^^^^^
+
+error: the operation is ineffective. Consider reducing it to `-2`
+ --> $DIR/identity_op.rs:71:5
+ |
+LL | -2 % 3;
+ | ^^^^^^
+
+error: the operation is ineffective. Consider reducing it to `2`
+ --> $DIR/identity_op.rs:72:5
+ |
+LL | 2 % -3 + x;
+ | ^^^^^^
+
+error: the operation is ineffective. Consider reducing it to `-2`
+ --> $DIR/identity_op.rs:73:5
+ |
+LL | -2 % -3 + x;
+ | ^^^^^^^
+
+error: the operation is ineffective. Consider reducing it to `1`
+ --> $DIR/identity_op.rs:74:9
+ |
+LL | x + 1 % 3;
+ | ^^^^^
+
+error: aborting due to 18 previous errors
diff --git a/tests/ui/implicit_clone.rs b/tests/ui/implicit_clone.rs
index 639fecb8927b..2549c9f32f90 100644
--- a/tests/ui/implicit_clone.rs
+++ b/tests/ui/implicit_clone.rs
@@ -30,7 +30,7 @@ where
}
#[derive(Copy, Clone)]
-struct Kitten {}
+struct Kitten;
impl Kitten {
// badly named method
fn to_vec(self) -> Kitten {
@@ -44,7 +44,7 @@ impl Borrow for Kitten {
}
}
-struct BorrowedKitten {}
+struct BorrowedKitten;
impl ToOwned for BorrowedKitten {
type Owned = Kitten;
fn to_owned(&self) -> Kitten {
diff --git a/tests/ui/indexing_slicing_index.rs b/tests/ui/indexing_slicing_index.rs
index ca8ca53c80c3..45a430edcb58 100644
--- a/tests/ui/indexing_slicing_index.rs
+++ b/tests/ui/indexing_slicing_index.rs
@@ -1,18 +1,34 @@
+#![feature(inline_const)]
#![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)]
+#![allow(const_err, clippy::no_effect, clippy::unnecessary_operation)]
+
+const ARR: [i32; 2] = [1, 2];
+const REF: &i32 = &ARR[idx()]; // Ok, should not produce stderr.
+const REF_ERR: &i32 = &ARR[idx4()]; // Ok, let rustc handle const contexts.
+
+const fn idx() -> usize {
+ 1
+}
+const fn idx4() -> usize {
+ 4
+}
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[4]; // Ok, let rustc's `unconditional_panic` lint handle `usize` indexing on arrays.
+ x[1 << 3]; // Ok, let rustc's `unconditional_panic` lint handle `usize` indexing on arrays.
x[0]; // Ok, should not produce stderr.
x[3]; // Ok, should not produce stderr.
+ x[const { idx() }]; // Ok, should not produce stderr.
+ x[const { idx4() }]; // Ok, let rustc's `unconditional_panic` lint handle `usize` indexing on arrays.
+ const { &ARR[idx()] }; // Ok, should not produce stderr.
+ const { &ARR[idx4()] }; // Ok, let rustc handle const contexts.
let y = &x;
y[0]; // Ok, referencing shouldn't affect this lint. See the issue 6021
@@ -25,7 +41,7 @@ fn main() {
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[N]; // Ok, let rustc's `unconditional_panic` lint handle `usize` indexing on arrays.
x[M]; // Ok, should not produce stderr.
v[N];
v[M];
diff --git a/tests/ui/indexing_slicing_index.stderr b/tests/ui/indexing_slicing_index.stderr
index 76ecec334840..83a36f407d5d 100644
--- a/tests/ui/indexing_slicing_index.stderr
+++ b/tests/ui/indexing_slicing_index.stderr
@@ -1,5 +1,17 @@
+error[E0080]: evaluation of `main::{constant#3}::<&i32>` failed
+ --> $DIR/indexing_slicing_index.rs:31:14
+ |
+LL | const { &ARR[idx4()] }; // Ok, let rustc handle const contexts.
+ | ^^^^^^^^^^^ index out of bounds: the length is 2 but the index is 4
+
+error[E0080]: erroneous constant used
+ --> $DIR/indexing_slicing_index.rs:31:5
+ |
+LL | const { &ARR[idx4()] }; // Ok, let rustc handle const contexts.
+ | ^^^^^^^^^^^^^^^^^^^^^^ referenced constant has errors
+
error: indexing may panic
- --> $DIR/indexing_slicing_index.rs:10:5
+ --> $DIR/indexing_slicing_index.rs:22:5
|
LL | x[index];
| ^^^^^^^^
@@ -8,7 +20,7 @@ LL | x[index];
= help: consider using `.get(n)` or `.get_mut(n)` instead
error: indexing may panic
- --> $DIR/indexing_slicing_index.rs:22:5
+ --> $DIR/indexing_slicing_index.rs:38:5
|
LL | v[0];
| ^^^^
@@ -16,7 +28,7 @@ LL | v[0];
= help: consider using `.get(n)` or `.get_mut(n)` instead
error: indexing may panic
- --> $DIR/indexing_slicing_index.rs:23:5
+ --> $DIR/indexing_slicing_index.rs:39:5
|
LL | v[10];
| ^^^^^
@@ -24,7 +36,7 @@ LL | v[10];
= help: consider using `.get(n)` or `.get_mut(n)` instead
error: indexing may panic
- --> $DIR/indexing_slicing_index.rs:24:5
+ --> $DIR/indexing_slicing_index.rs:40:5
|
LL | v[1 << 3];
| ^^^^^^^^^
@@ -32,7 +44,7 @@ LL | v[1 << 3];
= help: consider using `.get(n)` or `.get_mut(n)` instead
error: indexing may panic
- --> $DIR/indexing_slicing_index.rs:30:5
+ --> $DIR/indexing_slicing_index.rs:46:5
|
LL | v[N];
| ^^^^
@@ -40,12 +52,13 @@ LL | v[N];
= help: consider using `.get(n)` or `.get_mut(n)` instead
error: indexing may panic
- --> $DIR/indexing_slicing_index.rs:31:5
+ --> $DIR/indexing_slicing_index.rs:47:5
|
LL | v[M];
| ^^^^
|
= help: consider using `.get(n)` or `.get_mut(n)` instead
-error: aborting due to 6 previous errors
+error: aborting due to 8 previous errors
+For more information about this error, try `rustc --explain E0080`.
diff --git a/tests/ui/iter_nth_zero.fixed b/tests/ui/iter_nth_zero.fixed
index b54147c94d19..f23671c26e4c 100644
--- a/tests/ui/iter_nth_zero.fixed
+++ b/tests/ui/iter_nth_zero.fixed
@@ -3,7 +3,7 @@
#![warn(clippy::iter_nth_zero)]
use std::collections::HashSet;
-struct Foo {}
+struct Foo;
impl Foo {
fn nth(&self, index: usize) -> usize {
diff --git a/tests/ui/iter_nth_zero.rs b/tests/ui/iter_nth_zero.rs
index b92c7d18adb4..7c968d498457 100644
--- a/tests/ui/iter_nth_zero.rs
+++ b/tests/ui/iter_nth_zero.rs
@@ -3,7 +3,7 @@
#![warn(clippy::iter_nth_zero)]
use std::collections::HashSet;
-struct Foo {}
+struct Foo;
impl Foo {
fn nth(&self, index: usize) -> usize {
diff --git a/tests/ui/iter_overeager_cloned.fixed b/tests/ui/iter_overeager_cloned.fixed
index a9041671101b..56761ebbcb80 100644
--- a/tests/ui/iter_overeager_cloned.fixed
+++ b/tests/ui/iter_overeager_cloned.fixed
@@ -1,5 +1,6 @@
// run-rustfix
#![warn(clippy::iter_overeager_cloned, clippy::redundant_clone, clippy::filter_next)]
+#![allow(dead_code)]
fn main() {
let vec = vec!["1".to_string(), "2".to_string(), "3".to_string()];
@@ -43,3 +44,8 @@ fn main() {
// Should probably stay as it is.
let _ = [0, 1, 2, 3, 4].iter().cloned().take(10);
}
+
+// #8527
+fn cloned_flatten(x: Option<&Option>) -> Option {
+ x.cloned().flatten()
+}
diff --git a/tests/ui/iter_overeager_cloned.rs b/tests/ui/iter_overeager_cloned.rs
index dd04e33a4b3a..98321d889b58 100644
--- a/tests/ui/iter_overeager_cloned.rs
+++ b/tests/ui/iter_overeager_cloned.rs
@@ -1,5 +1,6 @@
// run-rustfix
#![warn(clippy::iter_overeager_cloned, clippy::redundant_clone, clippy::filter_next)]
+#![allow(dead_code)]
fn main() {
let vec = vec!["1".to_string(), "2".to_string(), "3".to_string()];
@@ -45,3 +46,8 @@ fn main() {
// Should probably stay as it is.
let _ = [0, 1, 2, 3, 4].iter().cloned().take(10);
}
+
+// #8527
+fn cloned_flatten(x: Option<&Option>) -> Option {
+ x.cloned().flatten()
+}
diff --git a/tests/ui/iter_overeager_cloned.stderr b/tests/ui/iter_overeager_cloned.stderr
index e36b0e36fbdf..0582700fd16a 100644
--- a/tests/ui/iter_overeager_cloned.stderr
+++ b/tests/ui/iter_overeager_cloned.stderr
@@ -1,5 +1,5 @@
error: called `cloned().last()` on an `Iterator`. It may be more efficient to call `last().cloned()` instead
- --> $DIR/iter_overeager_cloned.rs:7:29
+ --> $DIR/iter_overeager_cloned.rs:8:29
|
LL | let _: Option = vec.iter().cloned().last();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec.iter().last().cloned()`
@@ -7,13 +7,13 @@ LL | let _: Option = vec.iter().cloned().last();
= note: `-D clippy::iter-overeager-cloned` implied by `-D warnings`
error: called `cloned().next()` on an `Iterator`. It may be more efficient to call `next().cloned()` instead
- --> $DIR/iter_overeager_cloned.rs:9:29
+ --> $DIR/iter_overeager_cloned.rs:10:29
|
LL | let _: Option = vec.iter().chain(vec.iter()).cloned().next();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec.iter().chain(vec.iter()).next().cloned()`
error: called `cloned().count()` on an `Iterator`. It may be more efficient to call `count()` instead
- --> $DIR/iter_overeager_cloned.rs:11:20
+ --> $DIR/iter_overeager_cloned.rs:12:20
|
LL | let _: usize = vec.iter().filter(|x| x == &"2").cloned().count();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec.iter().filter(|x| x == &"2").count()`
@@ -21,25 +21,25 @@ LL | let _: usize = vec.iter().filter(|x| x == &"2").cloned().count();
= note: `-D clippy::redundant-clone` implied by `-D warnings`
error: called `cloned().take(...)` on an `Iterator`. It may be more efficient to call `take(...).cloned()` instead
- --> $DIR/iter_overeager_cloned.rs:13:21
+ --> $DIR/iter_overeager_cloned.rs:14:21
|
LL | let _: Vec<_> = vec.iter().cloned().take(2).collect();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec.iter().take(2).cloned()`
error: called `cloned().skip(...)` on an `Iterator`. It may be more efficient to call `skip(...).cloned()` instead
- --> $DIR/iter_overeager_cloned.rs:15:21
+ --> $DIR/iter_overeager_cloned.rs:16:21
|
LL | let _: Vec<_> = vec.iter().cloned().skip(2).collect();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec.iter().skip(2).cloned()`
error: called `cloned().nth(...)` on an `Iterator`. It may be more efficient to call `nth(...).cloned()` instead
- --> $DIR/iter_overeager_cloned.rs:17:13
+ --> $DIR/iter_overeager_cloned.rs:18:13
|
LL | let _ = vec.iter().filter(|x| x == &"2").cloned().nth(2);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec.iter().filter(|x| x == &"2").nth(2).cloned()`
error: called `cloned().flatten()` on an `Iterator`. It may be more efficient to call `flatten().cloned()` instead
- --> $DIR/iter_overeager_cloned.rs:19:13
+ --> $DIR/iter_overeager_cloned.rs:20:13
|
LL | let _ = [Some(Some("str".to_string())), Some(Some("str".to_string()))]
| _____________^
diff --git a/tests/ui/large_types_passed_by_value.rs b/tests/ui/large_types_passed_by_value.rs
index e4a2e9df4d7b..7601b5c66fa3 100644
--- a/tests/ui/large_types_passed_by_value.rs
+++ b/tests/ui/large_types_passed_by_value.rs
@@ -37,7 +37,7 @@ pub trait PubLargeTypeDevourer {
fn devoure_array_in_public(&self, array: [u8; 6666]);
}
-struct S {}
+struct S;
impl LargeTypeDevourer for S {
fn devoure_array(&self, array: [u8; 6666]) {
todo!();
diff --git a/tests/ui/let_and_return.rs b/tests/ui/let_and_return.rs
index e3561863c1e1..bb162adc9adb 100644
--- a/tests/ui/let_and_return.rs
+++ b/tests/ui/let_and_return.rs
@@ -88,7 +88,7 @@ mod no_lint_if_stmt_borrows {
ret
}
- struct Bar {}
+ struct Bar;
impl Bar {
fn new() -> Self {
diff --git a/tests/ui/let_underscore_must_use.rs b/tests/ui/let_underscore_must_use.rs
index a842e872a37b..1edb77c748bf 100644
--- a/tests/ui/let_underscore_must_use.rs
+++ b/tests/ui/let_underscore_must_use.rs
@@ -26,7 +26,7 @@ fn h() -> u32 {
0
}
-struct S {}
+struct S;
impl S {
#[must_use]
diff --git a/tests/ui/manual_async_fn.fixed b/tests/ui/manual_async_fn.fixed
index 136cc96be70c..b7e46a4a8ccc 100644
--- a/tests/ui/manual_async_fn.fixed
+++ b/tests/ui/manual_async_fn.fixed
@@ -38,7 +38,7 @@ async fn already_async() -> impl Future {
async { 42 }
}
-struct S {}
+struct S;
impl S {
async fn inh_fut() -> i32 {
// NOTE: this code is here just to check that the indentation is correct in the suggested fix
diff --git a/tests/ui/manual_async_fn.rs b/tests/ui/manual_async_fn.rs
index ddc453ffdb75..b05429da6622 100644
--- a/tests/ui/manual_async_fn.rs
+++ b/tests/ui/manual_async_fn.rs
@@ -52,7 +52,7 @@ async fn already_async() -> impl Future {
async { 42 }
}
-struct S {}
+struct S;
impl S {
fn inh_fut() -> impl Future {
async {
diff --git a/tests/ui/manual_unwrap_or.fixed b/tests/ui/manual_unwrap_or.fixed
index 05d6c56f2aca..7d68978216c9 100644
--- a/tests/ui/manual_unwrap_or.fixed
+++ b/tests/ui/manual_unwrap_or.fixed
@@ -78,7 +78,7 @@ fn result_unwrap_or() {
(Ok(1) as Result).unwrap_or(42);
// method call case, suggestion must not surround Result expr `s.method()` with parentheses
- struct S {}
+ struct S;
impl S {
fn method(self) -> Option {
Some(42)
diff --git a/tests/ui/manual_unwrap_or.rs b/tests/ui/manual_unwrap_or.rs
index 09f62c69b71d..b937fe6f977e 100644
--- a/tests/ui/manual_unwrap_or.rs
+++ b/tests/ui/manual_unwrap_or.rs
@@ -102,7 +102,7 @@ fn result_unwrap_or() {
};
// method call case, suggestion must not surround Result expr `s.method()` with parentheses
- struct S {}
+ struct S;
impl S {
fn method(self) -> Option {
Some(42)
diff --git a/tests/ui/map_identity.fixed b/tests/ui/map_identity.fixed
index 4a1452b25f34..2256e51f2d09 100644
--- a/tests/ui/map_identity.fixed
+++ b/tests/ui/map_identity.fixed
@@ -16,6 +16,8 @@ fn main() {
let _: Result = Err(2.3).map(|x: i8| {
return x + 3;
});
+ let _: Result = Ok(1);
+ let _: Result = Ok(1).map_err(|a: u32| a * 42);
}
fn not_identity(x: &u16) -> u16 {
diff --git a/tests/ui/map_identity.rs b/tests/ui/map_identity.rs
index 65c7e6e1ea55..ccfdc9ea76d5 100644
--- a/tests/ui/map_identity.rs
+++ b/tests/ui/map_identity.rs
@@ -18,6 +18,8 @@ fn main() {
let _: Result = Err(2.3).map(|x: i8| {
return x + 3;
});
+ let _: Result = Ok(1).map_err(|a| a);
+ let _: Result = Ok(1).map_err(|a: u32| a * 42);
}
fn not_identity(x: &u16) -> u16 {
diff --git a/tests/ui/map_identity.stderr b/tests/ui/map_identity.stderr
index e4a0320cbda5..b6a77281f6de 100644
--- a/tests/ui/map_identity.stderr
+++ b/tests/ui/map_identity.stderr
@@ -33,5 +33,11 @@ LL | | return x;
LL | | });
| |______^ help: remove the call to `map`
-error: aborting due to 5 previous errors
+error: unnecessary map of the identity function
+ --> $DIR/map_identity.rs:21:36
+ |
+LL | let _: Result = Ok(1).map_err(|a| a);
+ | ^^^^^^^^^^^^^^^ help: remove the call to `map_err`
+
+error: aborting due to 6 previous errors
diff --git a/tests/ui/map_unit_fn.rs b/tests/ui/map_unit_fn.rs
index 9a74da4e3b8b..e7f07b50f3ab 100644
--- a/tests/ui/map_unit_fn.rs
+++ b/tests/ui/map_unit_fn.rs
@@ -1,5 +1,5 @@
#![allow(unused)]
-struct Mappable {}
+struct Mappable;
impl Mappable {
pub fn map(&self) {}
diff --git a/tests/ui/min_rust_version_attr.rs b/tests/ui/min_rust_version_attr.rs
index c5f221220ece..f83c3e0e281c 100644
--- a/tests/ui/min_rust_version_attr.rs
+++ b/tests/ui/min_rust_version_attr.rs
@@ -99,7 +99,7 @@ pub fn manual_range_contains() {
}
pub fn use_self() {
- struct Foo {}
+ struct Foo;
impl Foo {
fn new() -> Foo {
@@ -145,6 +145,16 @@ fn int_from_bool() -> u8 {
true as u8
}
+fn err_expect() {
+ let x: Result = Ok(10);
+ x.err().expect("Testing expect_err");
+}
+
+fn cast_abs_to_unsigned() {
+ let x: i32 = 10;
+ assert_eq!(10u32, x.abs() as u32);
+}
+
fn main() {
filter_map_next();
checked_conversion();
@@ -162,6 +172,8 @@ fn main() {
missing_const_for_fn();
unnest_or_patterns();
int_from_bool();
+ err_expect();
+ cast_abs_to_unsigned();
}
mod just_under_msrv {
diff --git a/tests/ui/min_rust_version_attr.stderr b/tests/ui/min_rust_version_attr.stderr
index 6b3fdb0844b4..de225eb740d0 100644
--- a/tests/ui/min_rust_version_attr.stderr
+++ b/tests/ui/min_rust_version_attr.stderr
@@ -1,27 +1,10 @@
-error: stripping a prefix manually
- --> $DIR/min_rust_version_attr.rs:186:24
- |
-LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!");
- | ^^^^^^^^^^^^^^^^^^^^
- |
- = note: `-D clippy::manual-strip` implied by `-D warnings`
-note: the prefix was tested here
- --> $DIR/min_rust_version_attr.rs:185:9
- |
-LL | if s.starts_with("hello, ") {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-help: try using the `strip_prefix` method
- |
-LL ~ if let Some() = s.strip_prefix("hello, ") {
-LL ~ assert_eq!(.to_uppercase(), "WORLD!");
- |
-
error: stripping a prefix manually
--> $DIR/min_rust_version_attr.rs:198:24
|
LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!");
| ^^^^^^^^^^^^^^^^^^^^
|
+ = note: `-D clippy::manual-strip` implied by `-D warnings`
note: the prefix was tested here
--> $DIR/min_rust_version_attr.rs:197:9
|
@@ -33,5 +16,22 @@ LL ~ if let Some() = s.strip_prefix("hello, ") {
LL ~ assert_eq!(.to_uppercase(), "WORLD!");
|
+error: stripping a prefix manually
+ --> $DIR/min_rust_version_attr.rs:210:24
+ |
+LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!");
+ | ^^^^^^^^^^^^^^^^^^^^
+ |
+note: the prefix was tested here
+ --> $DIR/min_rust_version_attr.rs:209:9
+ |
+LL | if s.starts_with("hello, ") {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: try using the `strip_prefix` method
+ |
+LL ~ if let Some() = s.strip_prefix("hello, ") {
+LL ~ assert_eq!(.to_uppercase(), "WORLD!");
+ |
+
error: aborting due to 2 previous errors
diff --git a/tests/ui/missing_inline.rs b/tests/ui/missing_inline.rs
index b73b24b8e0a3..07f8e3888c99 100644
--- a/tests/ui/missing_inline.rs
+++ b/tests/ui/missing_inline.rs
@@ -7,8 +7,8 @@
type Typedef = String;
pub type PubTypedef = String;
-struct Foo {} // ok
-pub struct PubFoo {} // ok
+struct Foo; // ok
+pub struct PubFoo; // ok
enum FooE {} // ok
pub enum PubFooE {} // ok
@@ -63,4 +63,4 @@ impl PubFoo {
// do not lint this since users cannot control the external code
#[derive(Debug)]
-pub struct S {}
+pub struct S;
diff --git a/tests/ui/module_name_repetitions.rs b/tests/ui/module_name_repetitions.rs
index f5908cb5701f..ebaa77cc283e 100644
--- a/tests/ui/module_name_repetitions.rs
+++ b/tests/ui/module_name_repetitions.rs
@@ -7,7 +7,7 @@ mod foo {
pub fn foo() {}
pub fn foo_bar() {}
pub fn bar_foo() {}
- pub struct FooCake {}
+ pub struct FooCake;
pub enum CakeFoo {}
pub struct Foo7Bar;
diff --git a/tests/ui/module_name_repetitions.stderr b/tests/ui/module_name_repetitions.stderr
index bdd217a969c0..3f343a3e4301 100644
--- a/tests/ui/module_name_repetitions.stderr
+++ b/tests/ui/module_name_repetitions.stderr
@@ -15,8 +15,8 @@ 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 {}
- | ^^^^^^^^^^^^^^^^^^^^^
+LL | pub struct FooCake;
+ | ^^^^^^^^^^^^^^^^^^^
error: item name ends with its containing module's name
--> $DIR/module_name_repetitions.rs:11:5
diff --git a/tests/ui/modulo_arithmetic_integral_const.rs b/tests/ui/modulo_arithmetic_integral_const.rs
index 047a29fa1e32..3ebe46bc5be7 100644
--- a/tests/ui/modulo_arithmetic_integral_const.rs
+++ b/tests/ui/modulo_arithmetic_integral_const.rs
@@ -1,5 +1,10 @@
#![warn(clippy::modulo_arithmetic)]
-#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::modulo_one)]
+#![allow(
+ clippy::no_effect,
+ clippy::unnecessary_operation,
+ clippy::modulo_one,
+ clippy::identity_op
+)]
fn main() {
// Lint when both sides are const and of the opposite sign
diff --git a/tests/ui/modulo_arithmetic_integral_const.stderr b/tests/ui/modulo_arithmetic_integral_const.stderr
index 64335f35f0f8..11b5f77461ba 100644
--- a/tests/ui/modulo_arithmetic_integral_const.stderr
+++ b/tests/ui/modulo_arithmetic_integral_const.stderr
@@ -1,5 +1,5 @@
error: you are using modulo operator on constants with different signs: `-1 % 2`
- --> $DIR/modulo_arithmetic_integral_const.rs:6:5
+ --> $DIR/modulo_arithmetic_integral_const.rs:11:5
|
LL | -1 % 2;
| ^^^^^^
@@ -9,7 +9,7 @@ LL | -1 % 2;
= 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:7:5
+ --> $DIR/modulo_arithmetic_integral_const.rs:12:5
|
LL | 1 % -2;
| ^^^^^^
@@ -18,7 +18,7 @@ LL | 1 % -2;
= 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:8:5
+ --> $DIR/modulo_arithmetic_integral_const.rs:13:5
|
LL | (1 - 2) % (1 + 2);
| ^^^^^^^^^^^^^^^^^
@@ -27,7 +27,7 @@ LL | (1 - 2) % (1 + 2);
= 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:9:5
+ --> $DIR/modulo_arithmetic_integral_const.rs:14:5
|
LL | (1 + 2) % (1 - 2);
| ^^^^^^^^^^^^^^^^^
@@ -36,7 +36,7 @@ LL | (1 + 2) % (1 - 2);
= 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:10:5
+ --> $DIR/modulo_arithmetic_integral_const.rs:15:5
|
LL | 35 * (7 - 4 * 2) % (-500 * -600);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -45,7 +45,7 @@ LL | 35 * (7 - 4 * 2) % (-500 * -600);
= 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:12:5
+ --> $DIR/modulo_arithmetic_integral_const.rs:17:5
|
LL | -1i8 % 2i8;
| ^^^^^^^^^^
@@ -54,7 +54,7 @@ LL | -1i8 % 2i8;
= 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:13:5
+ --> $DIR/modulo_arithmetic_integral_const.rs:18:5
|
LL | 1i8 % -2i8;
| ^^^^^^^^^^
@@ -63,7 +63,7 @@ LL | 1i8 % -2i8;
= 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
+ --> $DIR/modulo_arithmetic_integral_const.rs:19:5
|
LL | -1i16 % 2i16;
| ^^^^^^^^^^^^
@@ -72,7 +72,7 @@ LL | -1i16 % 2i16;
= 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:15:5
+ --> $DIR/modulo_arithmetic_integral_const.rs:20:5
|
LL | 1i16 % -2i16;
| ^^^^^^^^^^^^
@@ -81,7 +81,7 @@ LL | 1i16 % -2i16;
= 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:16:5
+ --> $DIR/modulo_arithmetic_integral_const.rs:21:5
|
LL | -1i32 % 2i32;
| ^^^^^^^^^^^^
@@ -90,7 +90,7 @@ LL | -1i32 % 2i32;
= 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:17:5
+ --> $DIR/modulo_arithmetic_integral_const.rs:22:5
|
LL | 1i32 % -2i32;
| ^^^^^^^^^^^^
@@ -99,7 +99,7 @@ LL | 1i32 % -2i32;
= 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:18:5
+ --> $DIR/modulo_arithmetic_integral_const.rs:23:5
|
LL | -1i64 % 2i64;
| ^^^^^^^^^^^^
@@ -108,7 +108,7 @@ LL | -1i64 % 2i64;
= 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
+ --> $DIR/modulo_arithmetic_integral_const.rs:24:5
|
LL | 1i64 % -2i64;
| ^^^^^^^^^^^^
@@ -117,7 +117,7 @@ LL | 1i64 % -2i64;
= 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
+ --> $DIR/modulo_arithmetic_integral_const.rs:25:5
|
LL | -1i128 % 2i128;
| ^^^^^^^^^^^^^^
@@ -126,7 +126,7 @@ LL | -1i128 % 2i128;
= 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
+ --> $DIR/modulo_arithmetic_integral_const.rs:26:5
|
LL | 1i128 % -2i128;
| ^^^^^^^^^^^^^^
@@ -135,7 +135,7 @@ LL | 1i128 % -2i128;
= 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
+ --> $DIR/modulo_arithmetic_integral_const.rs:27:5
|
LL | -1isize % 2isize;
| ^^^^^^^^^^^^^^^^
@@ -144,7 +144,7 @@ LL | -1isize % 2isize;
= 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
+ --> $DIR/modulo_arithmetic_integral_const.rs:28:5
|
LL | 1isize % -2isize;
| ^^^^^^^^^^^^^^^^
diff --git a/tests/ui/needless_arbitrary_self_type_unfixable.rs b/tests/ui/needless_arbitrary_self_type_unfixable.rs
index ad0d694a2174..02b43cce2bd4 100644
--- a/tests/ui/needless_arbitrary_self_type_unfixable.rs
+++ b/tests/ui/needless_arbitrary_self_type_unfixable.rs
@@ -14,7 +14,7 @@ mod issue_6089 {
fn test(self: &Self);
}
- struct S1 {}
+ struct S1;
impl T1 for S1 {
fn test(self: &Self) {}
@@ -32,7 +32,7 @@ mod issue_6089 {
fn call_with_mut_self(&mut self);
}
- struct S2 {}
+ struct S2;
// The method's signature will be expanded to:
// fn call_with_mut_self<'life0>(self: &'life0 mut Self) {}
diff --git a/tests/ui/needless_lifetimes.rs b/tests/ui/needless_lifetimes.rs
index f3eafe8e9279..1456204ca869 100644
--- a/tests/ui/needless_lifetimes.rs
+++ b/tests/ui/needless_lifetimes.rs
@@ -268,7 +268,7 @@ mod issue4291 {
mod issue2944 {
trait Foo {}
- struct Bar {}
+ struct Bar;
struct Baz<'a> {
bar: &'a Bar,
}
diff --git a/tests/ui/needless_match.fixed b/tests/ui/needless_match.fixed
index ece18ad737fd..9ccccaa1725a 100644
--- a/tests/ui/needless_match.fixed
+++ b/tests/ui/needless_match.fixed
@@ -4,38 +4,35 @@
#![allow(dead_code)]
#[derive(Clone, Copy)]
-enum Choice {
+enum Simple {
A,
B,
C,
D,
}
-#[allow(unused_mut)]
fn useless_match() {
- let mut i = 10;
+ let i = 10;
let _: i32 = i;
- let _: i32 = i;
- let mut _i_mut = i;
-
let s = "test";
let _: &str = s;
}
-fn custom_type_match(se: Choice) {
- let _: Choice = se;
+fn custom_type_match() {
+ let se = Simple::A;
+ let _: Simple = se;
// Don't trigger
- let _: Choice = match se {
- Choice::A => Choice::A,
- Choice::B => Choice::B,
- _ => Choice::C,
+ let _: Simple = match se {
+ Simple::A => Simple::A,
+ Simple::B => Simple::B,
+ _ => Simple::C,
};
// Mingled, don't trigger
- let _: Choice = match se {
- Choice::A => Choice::B,
- Choice::B => Choice::C,
- Choice::C => Choice::D,
- Choice::D => Choice::A,
+ let _: Simple = match se {
+ Simple::A => Simple::B,
+ Simple::B => Simple::C,
+ Simple::C => Simple::D,
+ Simple::D => Simple::A,
};
}
@@ -55,29 +52,146 @@ fn func_ret_err(err: T) -> Result {
fn result_match() {
let _: Result = Ok(1);
let _: Result = func_ret_err(0_i32);
+ // as ref, don't trigger
+ let res = &func_ret_err(0_i32);
+ let _: Result<&i32, &i32> = match *res {
+ Ok(ref x) => Ok(x),
+ Err(ref x) => Err(x),
+ };
}
-fn if_let_option() -> Option {
- Some(1)
-}
+fn if_let_option() {
+ let _ = Some(1);
-fn if_let_result(x: Result<(), i32>) {
- let _: Result<(), i32> = x;
- let _: Result<(), i32> = x;
- // Input type mismatch, don't trigger
- let _: Result<(), i32> = if let Err(e) = Ok(1) { Err(e) } else { x };
-}
+ fn do_something() {}
-fn if_let_custom_enum(x: Choice) {
- let _: Choice = x;
// Don't trigger
- let _: Choice = if let Choice::A = x {
- Choice::A
+ let _ = if let Some(a) = Some(1) {
+ Some(a)
+ } else {
+ do_something();
+ None
+ };
+
+ // Don't trigger
+ let _ = if let Some(a) = Some(1) {
+ do_something();
+ Some(a)
+ } else {
+ None
+ };
+}
+
+fn if_let_result() {
+ let x: Result = Ok(1);
+ let _: Result = x;
+ let _: Result = x;
+ // Input type mismatch, don't trigger
+ let _: Result = if let Err(e) = Ok(1) { Err(e) } else { x };
+}
+
+fn if_let_custom_enum(x: Simple) {
+ let _: Simple = x;
+
+ // Don't trigger
+ let _: Simple = if let Simple::A = x {
+ Simple::A
} else if true {
- Choice::B
+ Simple::B
} else {
x
};
}
+mod issue8542 {
+ #[derive(Clone, Copy)]
+ enum E {
+ VariantA(u8, u8),
+ VariantB(u8, bool),
+ }
+
+ enum Complex {
+ A(u8),
+ B(u8, bool),
+ C(u8, i32, f64),
+ D(E, bool),
+ }
+
+ fn match_test() {
+ let ce = Complex::B(8, false);
+ let aa = 0_u8;
+ let bb = false;
+
+ let _: Complex = ce;
+
+ // Don't trigger
+ let _: Complex = match ce {
+ Complex::A(_) => Complex::A(aa),
+ Complex::B(_, b) => Complex::B(aa, b),
+ Complex::C(_, b, _) => Complex::C(aa, b, 64_f64),
+ Complex::D(e, b) => Complex::D(e, b),
+ };
+
+ // Don't trigger
+ let _: Complex = match ce {
+ Complex::A(a) => Complex::A(a),
+ Complex::B(a, _) => Complex::B(a, bb),
+ Complex::C(a, _, _) => Complex::C(a, 32_i32, 64_f64),
+ _ => ce,
+ };
+ }
+}
+
+/// Lint triggered when type coercions happen.
+/// Do NOT trigger on any of these.
+mod issue8551 {
+ trait Trait {}
+ struct Struct;
+ impl Trait for Struct {}
+
+ fn optmap(s: Option<&Struct>) -> Option<&dyn Trait> {
+ match s {
+ Some(s) => Some(s),
+ None => None,
+ }
+ }
+
+ fn lint_tests() {
+ let option: Option<&Struct> = None;
+ let _: Option<&dyn Trait> = match option {
+ Some(s) => Some(s),
+ None => None,
+ };
+
+ let _: Option<&dyn Trait> = if true {
+ match option {
+ Some(s) => Some(s),
+ None => None,
+ }
+ } else {
+ None
+ };
+
+ let result: Result<&Struct, i32> = Err(0);
+ let _: Result<&dyn Trait, i32> = match result {
+ Ok(s) => Ok(s),
+ Err(e) => Err(e),
+ };
+
+ let _: Option<&dyn Trait> = if let Some(s) = option { Some(s) } else { None };
+ }
+}
+
+trait Tr {
+ fn as_mut(&mut self) -> Result<&mut i32, &mut i32>;
+}
+impl Tr for Result {
+ fn as_mut(&mut self) -> Result<&mut i32, &mut i32> {
+ match self {
+ Ok(x) => Ok(x),
+ Err(e) => Err(e),
+ }
+ }
+}
+
fn main() {}
diff --git a/tests/ui/needless_match.rs b/tests/ui/needless_match.rs
index 36649de35a60..d210ecff7f16 100644
--- a/tests/ui/needless_match.rs
+++ b/tests/ui/needless_match.rs
@@ -4,33 +4,21 @@
#![allow(dead_code)]
#[derive(Clone, Copy)]
-enum Choice {
+enum Simple {
A,
B,
C,
D,
}
-#[allow(unused_mut)]
fn useless_match() {
- let mut i = 10;
+ let i = 10;
let _: i32 = match i {
0 => 0,
1 => 1,
2 => 2,
_ => i,
};
- let _: i32 = match i {
- 0 => 0,
- 1 => 1,
- ref i => *i,
- };
- let mut _i_mut = match i {
- 0 => 0,
- 1 => 1,
- ref mut i => *i,
- };
-
let s = "test";
let _: &str = match s {
"a" => "a",
@@ -39,25 +27,26 @@ fn useless_match() {
};
}
-fn custom_type_match(se: Choice) {
- let _: Choice = match se {
- Choice::A => Choice::A,
- Choice::B => Choice::B,
- Choice::C => Choice::C,
- Choice::D => Choice::D,
+fn custom_type_match() {
+ let se = Simple::A;
+ let _: Simple = match se {
+ Simple::A => Simple::A,
+ Simple::B => Simple::B,
+ Simple::C => Simple::C,
+ Simple::D => Simple::D,
};
// Don't trigger
- let _: Choice = match se {
- Choice::A => Choice::A,
- Choice::B => Choice::B,
- _ => Choice::C,
+ let _: Simple = match se {
+ Simple::A => Simple::A,
+ Simple::B => Simple::B,
+ _ => Simple::C,
};
// Mingled, don't trigger
- let _: Choice = match se {
- Choice::A => Choice::B,
- Choice::B => Choice::C,
- Choice::C => Choice::D,
- Choice::D => Choice::A,
+ let _: Simple = match se {
+ Simple::A => Simple::B,
+ Simple::B => Simple::C,
+ Simple::C => Simple::D,
+ Simple::D => Simple::A,
};
}
@@ -86,37 +75,160 @@ fn result_match() {
Err(err) => Err(err),
Ok(a) => Ok(a),
};
-}
-
-fn if_let_option() -> Option {
- if let Some(a) = Some(1) { Some(a) } else { None }
-}
-
-fn if_let_result(x: Result<(), i32>) {
- let _: Result<(), i32> = if let Err(e) = x { Err(e) } else { x };
- let _: Result<(), i32> = if let Ok(val) = x { Ok(val) } else { x };
- // Input type mismatch, don't trigger
- let _: Result<(), i32> = if let Err(e) = Ok(1) { Err(e) } else { x };
-}
-
-fn if_let_custom_enum(x: Choice) {
- let _: Choice = if let Choice::A = x {
- Choice::A
- } else if let Choice::B = x {
- Choice::B
- } else if let Choice::C = x {
- Choice::C
- } else {
- x
+ // as ref, don't trigger
+ let res = &func_ret_err(0_i32);
+ let _: Result<&i32, &i32> = match *res {
+ Ok(ref x) => Ok(x),
+ Err(ref x) => Err(x),
};
+}
+
+fn if_let_option() {
+ let _ = if let Some(a) = Some(1) { Some(a) } else { None };
+
+ fn do_something() {}
+
// Don't trigger
- let _: Choice = if let Choice::A = x {
- Choice::A
- } else if true {
- Choice::B
+ let _ = if let Some(a) = Some(1) {
+ Some(a)
+ } else {
+ do_something();
+ None
+ };
+
+ // Don't trigger
+ let _ = if let Some(a) = Some(1) {
+ do_something();
+ Some(a)
+ } else {
+ None
+ };
+}
+
+fn if_let_result() {
+ let x: Result = Ok(1);
+ let _: Result = if let Err(e) = x { Err(e) } else { x };
+ let _: Result = if let Ok(val) = x { Ok(val) } else { x };
+ // Input type mismatch, don't trigger
+ let _: Result = if let Err(e) = Ok(1) { Err(e) } else { x };
+}
+
+fn if_let_custom_enum(x: Simple) {
+ let _: Simple = if let Simple::A = x {
+ Simple::A
+ } else if let Simple::B = x {
+ Simple::B
+ } else if let Simple::C = x {
+ Simple::C
} else {
x
};
+
+ // Don't trigger
+ let _: Simple = if let Simple::A = x {
+ Simple::A
+ } else if true {
+ Simple::B
+ } else {
+ x
+ };
+}
+
+mod issue8542 {
+ #[derive(Clone, Copy)]
+ enum E {
+ VariantA(u8, u8),
+ VariantB(u8, bool),
+ }
+
+ enum Complex {
+ A(u8),
+ B(u8, bool),
+ C(u8, i32, f64),
+ D(E, bool),
+ }
+
+ fn match_test() {
+ let ce = Complex::B(8, false);
+ let aa = 0_u8;
+ let bb = false;
+
+ let _: Complex = match ce {
+ Complex::A(a) => Complex::A(a),
+ Complex::B(a, b) => Complex::B(a, b),
+ Complex::C(a, b, c) => Complex::C(a, b, c),
+ Complex::D(E::VariantA(ea, eb), b) => Complex::D(E::VariantA(ea, eb), b),
+ Complex::D(E::VariantB(ea, eb), b) => Complex::D(E::VariantB(ea, eb), b),
+ };
+
+ // Don't trigger
+ let _: Complex = match ce {
+ Complex::A(_) => Complex::A(aa),
+ Complex::B(_, b) => Complex::B(aa, b),
+ Complex::C(_, b, _) => Complex::C(aa, b, 64_f64),
+ Complex::D(e, b) => Complex::D(e, b),
+ };
+
+ // Don't trigger
+ let _: Complex = match ce {
+ Complex::A(a) => Complex::A(a),
+ Complex::B(a, _) => Complex::B(a, bb),
+ Complex::C(a, _, _) => Complex::C(a, 32_i32, 64_f64),
+ _ => ce,
+ };
+ }
+}
+
+/// Lint triggered when type coercions happen.
+/// Do NOT trigger on any of these.
+mod issue8551 {
+ trait Trait {}
+ struct Struct;
+ impl Trait for Struct {}
+
+ fn optmap(s: Option<&Struct>) -> Option<&dyn Trait> {
+ match s {
+ Some(s) => Some(s),
+ None => None,
+ }
+ }
+
+ fn lint_tests() {
+ let option: Option<&Struct> = None;
+ let _: Option<&dyn Trait> = match option {
+ Some(s) => Some(s),
+ None => None,
+ };
+
+ let _: Option<&dyn Trait> = if true {
+ match option {
+ Some(s) => Some(s),
+ None => None,
+ }
+ } else {
+ None
+ };
+
+ let result: Result<&Struct, i32> = Err(0);
+ let _: Result<&dyn Trait, i32> = match result {
+ Ok(s) => Ok(s),
+ Err(e) => Err(e),
+ };
+
+ let _: Option<&dyn Trait> = if let Some(s) = option { Some(s) } else { None };
+ }
+}
+
+trait Tr {
+ fn as_mut(&mut self) -> Result<&mut i32, &mut i32>;
+}
+impl Tr for Result {
+ fn as_mut(&mut self) -> Result<&mut i32, &mut i32> {
+ match self {
+ Ok(x) => Ok(x),
+ Err(e) => Err(e),
+ }
+ }
}
fn main() {}
diff --git a/tests/ui/needless_match.stderr b/tests/ui/needless_match.stderr
index ad1525406ade..34c5226f0605 100644
--- a/tests/ui/needless_match.stderr
+++ b/tests/ui/needless_match.stderr
@@ -1,5 +1,5 @@
error: this match expression is unnecessary
- --> $DIR/needless_match.rs:17:18
+ --> $DIR/needless_match.rs:16:18
|
LL | let _: i32 = match i {
| __________________^
@@ -13,29 +13,7 @@ LL | | };
= note: `-D clippy::needless-match` implied by `-D warnings`
error: this match expression is unnecessary
- --> $DIR/needless_match.rs:23:18
- |
-LL | let _: i32 = match i {
- | __________________^
-LL | | 0 => 0,
-LL | | 1 => 1,
-LL | | ref i => *i,
-LL | | };
- | |_____^ help: replace it with: `i`
-
-error: this match expression is unnecessary
- --> $DIR/needless_match.rs:28:22
- |
-LL | let mut _i_mut = match i {
- | ______________________^
-LL | | 0 => 0,
-LL | | 1 => 1,
-LL | | ref mut i => *i,
-LL | | };
- | |_____^ help: replace it with: `i`
-
-error: this match expression is unnecessary
- --> $DIR/needless_match.rs:35:19
+ --> $DIR/needless_match.rs:23:19
|
LL | let _: &str = match s {
| ___________________^
@@ -46,19 +24,19 @@ LL | | };
| |_____^ help: replace it with: `s`
error: this match expression is unnecessary
- --> $DIR/needless_match.rs:43:21
+ --> $DIR/needless_match.rs:32:21
|
-LL | let _: Choice = match se {
+LL | let _: Simple = match se {
| _____________________^
-LL | | Choice::A => Choice::A,
-LL | | Choice::B => Choice::B,
-LL | | Choice::C => Choice::C,
-LL | | Choice::D => Choice::D,
+LL | | Simple::A => Simple::A,
+LL | | Simple::B => Simple::B,
+LL | | Simple::C => Simple::C,
+LL | | Simple::D => Simple::D,
LL | | };
| |_____^ help: replace it with: `se`
error: this match expression is unnecessary
- --> $DIR/needless_match.rs:65:26
+ --> $DIR/needless_match.rs:54:26
|
LL | let _: Option = match x {
| __________________________^
@@ -68,7 +46,7 @@ LL | | };
| |_____^ help: replace it with: `x`
error: this match expression is unnecessary
- --> $DIR/needless_match.rs:81:31
+ --> $DIR/needless_match.rs:70:31
|
LL | let _: Result = match Ok(1) {
| _______________________________^
@@ -78,7 +56,7 @@ LL | | };
| |_____^ help: replace it with: `Ok(1)`
error: this match expression is unnecessary
- --> $DIR/needless_match.rs:85:31
+ --> $DIR/needless_match.rs:74:31
|
LL | let _: Result = match func_ret_err(0_i32) {
| _______________________________^
@@ -88,35 +66,48 @@ LL | | };
| |_____^ help: replace it with: `func_ret_err(0_i32)`
error: this if-let expression is unnecessary
- --> $DIR/needless_match.rs:92:5
+ --> $DIR/needless_match.rs:87:13
|
-LL | if let Some(a) = Some(1) { Some(a) } else { None }
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `Some(1)`
+LL | let _ = if let Some(a) = Some(1) { Some(a) } else { None };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `Some(1)`
error: this if-let expression is unnecessary
- --> $DIR/needless_match.rs:96:30
+ --> $DIR/needless_match.rs:110:31
|
-LL | let _: Result<(), i32> = if let Err(e) = x { Err(e) } else { x };
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x`
+LL | let _: Result = if let Err(e) = x { Err(e) } else { x };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x`
error: this if-let expression is unnecessary
- --> $DIR/needless_match.rs:97:30
+ --> $DIR/needless_match.rs:111:31
|
-LL | let _: Result<(), i32> = if let Ok(val) = x { Ok(val) } else { x };
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x`
+LL | let _: Result = if let Ok(val) = x { Ok(val) } else { x };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x`
error: this if-let expression is unnecessary
- --> $DIR/needless_match.rs:103:21
+ --> $DIR/needless_match.rs:117:21
|
-LL | let _: Choice = if let Choice::A = x {
+LL | let _: Simple = if let Simple::A = x {
| _____________________^
-LL | | Choice::A
-LL | | } else if let Choice::B = x {
-LL | | Choice::B
+LL | | Simple::A
+LL | | } else if let Simple::B = x {
+LL | | Simple::B
... |
LL | | x
LL | | };
| |_____^ help: replace it with: `x`
-error: aborting due to 12 previous errors
+error: this match expression is unnecessary
+ --> $DIR/needless_match.rs:156:26
+ |
+LL | let _: Complex = match ce {
+ | __________________________^
+LL | | Complex::A(a) => Complex::A(a),
+LL | | Complex::B(a, b) => Complex::B(a, b),
+LL | | Complex::C(a, b, c) => Complex::C(a, b, c),
+LL | | Complex::D(E::VariantA(ea, eb), b) => Complex::D(E::VariantA(ea, eb), b),
+LL | | Complex::D(E::VariantB(ea, eb), b) => Complex::D(E::VariantB(ea, eb), b),
+LL | | };
+ | |_________^ help: replace it with: `ce`
+
+error: aborting due to 11 previous errors
diff --git a/tests/ui/needless_option_as_deref.fixed b/tests/ui/needless_option_as_deref.fixed
index d721452ae888..c09b07db3dca 100644
--- a/tests/ui/needless_option_as_deref.fixed
+++ b/tests/ui/needless_option_as_deref.fixed
@@ -1,13 +1,41 @@
// run-rustfix
-#[warn(clippy::needless_option_as_deref)]
+#![allow(unused)]
+#![warn(clippy::needless_option_as_deref)]
fn main() {
// should lint
let _: Option<&usize> = Some(&1);
let _: Option<&mut usize> = Some(&mut 1);
+ let mut y = 0;
+ let mut x = Some(&mut y);
+ let _ = x;
+
// should not lint
let _ = Some(Box::new(1)).as_deref();
let _ = Some(Box::new(1)).as_deref_mut();
+
+ // #7846
+ let mut i = 0;
+ let mut opt_vec = vec![Some(&mut i)];
+ opt_vec[0].as_deref_mut().unwrap();
+
+ let mut i = 0;
+ let x = &mut Some(&mut i);
+ (*x).as_deref_mut();
+
+ // #8047
+ let mut y = 0;
+ let mut x = Some(&mut y);
+ x.as_deref_mut();
+ dbg!(x);
+}
+
+struct S<'a> {
+ opt: Option<&'a mut usize>,
+}
+
+fn from_field<'a>(s: &'a mut S<'a>) -> Option<&'a mut usize> {
+ s.opt.as_deref_mut()
}
diff --git a/tests/ui/needless_option_as_deref.rs b/tests/ui/needless_option_as_deref.rs
index bb15512adf6a..c3ba27ecccf2 100644
--- a/tests/ui/needless_option_as_deref.rs
+++ b/tests/ui/needless_option_as_deref.rs
@@ -1,13 +1,41 @@
// run-rustfix
-#[warn(clippy::needless_option_as_deref)]
+#![allow(unused)]
+#![warn(clippy::needless_option_as_deref)]
fn main() {
// should lint
let _: Option<&usize> = Some(&1).as_deref();
let _: Option<&mut usize> = Some(&mut 1).as_deref_mut();
+ let mut y = 0;
+ let mut x = Some(&mut y);
+ let _ = x.as_deref_mut();
+
// should not lint
let _ = Some(Box::new(1)).as_deref();
let _ = Some(Box::new(1)).as_deref_mut();
+
+ // #7846
+ let mut i = 0;
+ let mut opt_vec = vec![Some(&mut i)];
+ opt_vec[0].as_deref_mut().unwrap();
+
+ let mut i = 0;
+ let x = &mut Some(&mut i);
+ (*x).as_deref_mut();
+
+ // #8047
+ let mut y = 0;
+ let mut x = Some(&mut y);
+ x.as_deref_mut();
+ dbg!(x);
+}
+
+struct S<'a> {
+ opt: Option<&'a mut usize>,
+}
+
+fn from_field<'a>(s: &'a mut S<'a>) -> Option<&'a mut usize> {
+ s.opt.as_deref_mut()
}
diff --git a/tests/ui/needless_option_as_deref.stderr b/tests/ui/needless_option_as_deref.stderr
index 5dd507b4a71e..bc07db5b38ed 100644
--- a/tests/ui/needless_option_as_deref.stderr
+++ b/tests/ui/needless_option_as_deref.stderr
@@ -1,5 +1,5 @@
error: derefed type is same as origin
- --> $DIR/needless_option_as_deref.rs:7:29
+ --> $DIR/needless_option_as_deref.rs:8:29
|
LL | let _: Option<&usize> = Some(&1).as_deref();
| ^^^^^^^^^^^^^^^^^^^ help: try this: `Some(&1)`
@@ -7,10 +7,16 @@ LL | let _: Option<&usize> = Some(&1).as_deref();
= note: `-D clippy::needless-option-as-deref` implied by `-D warnings`
error: derefed type is same as origin
- --> $DIR/needless_option_as_deref.rs:8:33
+ --> $DIR/needless_option_as_deref.rs:9:33
|
LL | let _: Option<&mut usize> = Some(&mut 1).as_deref_mut();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `Some(&mut 1)`
-error: aborting due to 2 previous errors
+error: derefed type is same as origin
+ --> $DIR/needless_option_as_deref.rs:13:13
+ |
+LL | let _ = x.as_deref_mut();
+ | ^^^^^^^^^^^^^^^^ help: try this: `x`
+
+error: aborting due to 3 previous errors
diff --git a/tests/ui/no_effect.rs b/tests/ui/no_effect.rs
index 5427c88faf34..7ece66a1ccb6 100644
--- a/tests/ui/no_effect.rs
+++ b/tests/ui/no_effect.rs
@@ -78,7 +78,7 @@ impl FnOnce<(&str,)> for GreetStruct2 {
}
}
-struct GreetStruct3 {}
+struct GreetStruct3;
impl FnOnce<(&str,)> for GreetStruct3 {
type Output = ();
diff --git a/tests/ui/option_map_unit_fn_fixable.fixed b/tests/ui/option_map_unit_fn_fixable.fixed
index 7d29445e66c8..1290bd8efebd 100644
--- a/tests/ui/option_map_unit_fn_fixable.fixed
+++ b/tests/ui/option_map_unit_fn_fixable.fixed
@@ -80,6 +80,9 @@ fn option_map_unit_fn() {
if let Some(ref value) = x.field { do_nothing(value + captured) }
- if let Some(a) = option() { do_nothing(a) }}
+ if let Some(a) = option() { do_nothing(a) }
+
+ if let Some(value) = option() { println!("{:?}", value) }
+}
fn main() {}
diff --git a/tests/ui/option_map_unit_fn_fixable.rs b/tests/ui/option_map_unit_fn_fixable.rs
index b6f834f686f9..f3e5b62c65b7 100644
--- a/tests/ui/option_map_unit_fn_fixable.rs
+++ b/tests/ui/option_map_unit_fn_fixable.rs
@@ -80,6 +80,9 @@ fn option_map_unit_fn() {
x.field.map(|ref value| { do_nothing(value + captured) });
- option().map(do_nothing);}
+ option().map(do_nothing);
+
+ option().map(|value| println!("{:?}", value));
+}
fn main() {}
diff --git a/tests/ui/option_map_unit_fn_fixable.stderr b/tests/ui/option_map_unit_fn_fixable.stderr
index 8abdbcafb6e9..ab2a294a060f 100644
--- a/tests/ui/option_map_unit_fn_fixable.stderr
+++ b/tests/ui/option_map_unit_fn_fixable.stderr
@@ -139,10 +139,18 @@ LL | x.field.map(|ref value| { 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:83:5
|
-LL | option().map(do_nothing);}
+LL | option().map(do_nothing);
| ^^^^^^^^^^^^^^^^^^^^^^^^-
| |
| help: try this: `if let Some(a) = option() { do_nothing(a) }`
-error: aborting due to 18 previous errors
+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:85:5
+ |
+LL | option().map(|value| println!("{:?}", value));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
+ | |
+ | help: try this: `if let Some(value) = option() { println!("{:?}", value) }`
+
+error: aborting due to 19 previous errors
diff --git a/tests/ui/or_then_unwrap.fixed b/tests/ui/or_then_unwrap.fixed
index 27d4b795a5ee..6e0d5a87f680 100644
--- a/tests/ui/or_then_unwrap.fixed
+++ b/tests/ui/or_then_unwrap.fixed
@@ -3,7 +3,7 @@
#![warn(clippy::or_then_unwrap)]
#![allow(clippy::map_identity)]
-struct SomeStruct {}
+struct SomeStruct;
impl SomeStruct {
fn or(self, _: Option) -> Self {
self
@@ -11,7 +11,7 @@ impl SomeStruct {
fn unwrap(&self) {}
}
-struct SomeOtherStruct {}
+struct SomeOtherStruct;
impl SomeOtherStruct {
fn or(self) -> Self {
self
diff --git a/tests/ui/or_then_unwrap.rs b/tests/ui/or_then_unwrap.rs
index 0dab5ae2f1c0..e406a71d46d0 100644
--- a/tests/ui/or_then_unwrap.rs
+++ b/tests/ui/or_then_unwrap.rs
@@ -3,7 +3,7 @@
#![warn(clippy::or_then_unwrap)]
#![allow(clippy::map_identity)]
-struct SomeStruct {}
+struct SomeStruct;
impl SomeStruct {
fn or(self, _: Option) -> Self {
self
@@ -11,7 +11,7 @@ impl SomeStruct {
fn unwrap(&self) {}
}
-struct SomeOtherStruct {}
+struct SomeOtherStruct;
impl SomeOtherStruct {
fn or(self) -> Self {
self
diff --git a/tests/ui/panicking_macros.rs b/tests/ui/panicking_macros.rs
index 733806464624..12a0c776ae2f 100644
--- a/tests/ui/panicking_macros.rs
+++ b/tests/ui/panicking_macros.rs
@@ -1,8 +1,23 @@
-#![warn(clippy::unimplemented, clippy::unreachable, clippy::todo, clippy::panic)]
#![allow(clippy::assertions_on_constants, clippy::eq_op)]
+#![feature(inline_const)]
+#![warn(clippy::unimplemented, clippy::unreachable, clippy::todo, clippy::panic)]
extern crate core;
+const _: () = {
+ if 1 == 0 {
+ panic!("A balanced diet means a cupcake in each hand");
+ }
+};
+
+fn inline_const() {
+ let _ = const {
+ if 1 == 0 {
+ panic!("When nothing goes right, go left")
+ }
+ };
+}
+
fn panic() {
let a = 2;
panic!();
diff --git a/tests/ui/panicking_macros.stderr b/tests/ui/panicking_macros.stderr
index bfd1c7a38014..4ceb6d1440f6 100644
--- a/tests/ui/panicking_macros.stderr
+++ b/tests/ui/panicking_macros.stderr
@@ -1,5 +1,5 @@
error: `panic` should not be present in production code
- --> $DIR/panicking_macros.rs:8:5
+ --> $DIR/panicking_macros.rs:23:5
|
LL | panic!();
| ^^^^^^^^
@@ -7,19 +7,19 @@ LL | panic!();
= note: `-D clippy::panic` implied by `-D warnings`
error: `panic` should not be present in production code
- --> $DIR/panicking_macros.rs:9:5
+ --> $DIR/panicking_macros.rs:24:5
|
LL | panic!("message");
| ^^^^^^^^^^^^^^^^^
error: `panic` should not be present in production code
- --> $DIR/panicking_macros.rs:10:5
+ --> $DIR/panicking_macros.rs:25:5
|
LL | panic!("{} {}", "panic with", "multiple arguments");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: `todo` should not be present in production code
- --> $DIR/panicking_macros.rs:16:5
+ --> $DIR/panicking_macros.rs:31:5
|
LL | todo!();
| ^^^^^^^
@@ -27,19 +27,19 @@ LL | todo!();
= note: `-D clippy::todo` implied by `-D warnings`
error: `todo` should not be present in production code
- --> $DIR/panicking_macros.rs:17:5
+ --> $DIR/panicking_macros.rs:32:5
|
LL | todo!("message");
| ^^^^^^^^^^^^^^^^
error: `todo` should not be present in production code
- --> $DIR/panicking_macros.rs:18:5
+ --> $DIR/panicking_macros.rs:33:5
|
LL | todo!("{} {}", "panic with", "multiple arguments");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: `unimplemented` should not be present in production code
- --> $DIR/panicking_macros.rs:24:5
+ --> $DIR/panicking_macros.rs:39:5
|
LL | unimplemented!();
| ^^^^^^^^^^^^^^^^
@@ -47,19 +47,19 @@ LL | unimplemented!();
= note: `-D clippy::unimplemented` implied by `-D warnings`
error: `unimplemented` should not be present in production code
- --> $DIR/panicking_macros.rs:25:5
+ --> $DIR/panicking_macros.rs:40:5
|
LL | unimplemented!("message");
| ^^^^^^^^^^^^^^^^^^^^^^^^^
error: `unimplemented` should not be present in production code
- --> $DIR/panicking_macros.rs:26:5
+ --> $DIR/panicking_macros.rs:41:5
|
LL | unimplemented!("{} {}", "panic with", "multiple arguments");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: usage of the `unreachable!` macro
- --> $DIR/panicking_macros.rs:32:5
+ --> $DIR/panicking_macros.rs:47:5
|
LL | unreachable!();
| ^^^^^^^^^^^^^^
@@ -67,37 +67,37 @@ LL | unreachable!();
= note: `-D clippy::unreachable` implied by `-D warnings`
error: usage of the `unreachable!` macro
- --> $DIR/panicking_macros.rs:33:5
+ --> $DIR/panicking_macros.rs:48:5
|
LL | unreachable!("message");
| ^^^^^^^^^^^^^^^^^^^^^^^
error: usage of the `unreachable!` macro
- --> $DIR/panicking_macros.rs:34:5
+ --> $DIR/panicking_macros.rs:49:5
|
LL | unreachable!("{} {}", "panic with", "multiple arguments");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: `panic` should not be present in production code
- --> $DIR/panicking_macros.rs:40:5
+ --> $DIR/panicking_macros.rs:55:5
|
LL | panic!();
| ^^^^^^^^
error: `todo` should not be present in production code
- --> $DIR/panicking_macros.rs:41:5
+ --> $DIR/panicking_macros.rs:56:5
|
LL | todo!();
| ^^^^^^^
error: `unimplemented` should not be present in production code
- --> $DIR/panicking_macros.rs:42:5
+ --> $DIR/panicking_macros.rs:57:5
|
LL | unimplemented!();
| ^^^^^^^^^^^^^^^^
error: usage of the `unreachable!` macro
- --> $DIR/panicking_macros.rs:43:5
+ --> $DIR/panicking_macros.rs:58:5
|
LL | unreachable!();
| ^^^^^^^^^^^^^^
diff --git a/tests/ui/ptr_arg.rs b/tests/ui/ptr_arg.rs
index 03dd938a2339..814bbc7af713 100644
--- a/tests/ui/ptr_arg.rs
+++ b/tests/ui/ptr_arg.rs
@@ -112,7 +112,7 @@ mod issue_5644 {
) {
}
- struct S {}
+ struct S;
impl S {
fn allowed(
#[allow(clippy::ptr_arg)] _v: &Vec,
diff --git a/tests/ui/recursive_format_impl.rs b/tests/ui/recursive_format_impl.rs
index 9241bf7ed740..f72fc77ab997 100644
--- a/tests/ui/recursive_format_impl.rs
+++ b/tests/ui/recursive_format_impl.rs
@@ -66,7 +66,7 @@ impl std::fmt::Display for D {
// Check for use of self as Display, in Display impl
// Triggers on direct use of self
-struct G {}
+struct G;
impl std::fmt::Display for G {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
@@ -75,7 +75,7 @@ impl std::fmt::Display for G {
}
// Triggers on reference to self
-struct H {}
+struct H;
impl std::fmt::Display for H {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
@@ -90,7 +90,7 @@ impl std::fmt::Debug for H {
}
// Triggers on multiple reference to self
-struct H2 {}
+struct H2;
impl std::fmt::Display for H2 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
@@ -99,7 +99,7 @@ impl std::fmt::Display for H2 {
}
// Doesn't trigger on correct deref
-struct I {}
+struct I;
impl std::ops::Deref for I {
type Target = str;
@@ -122,7 +122,7 @@ impl std::fmt::Debug for I {
}
// Doesn't trigger on multiple correct deref
-struct I2 {}
+struct I2;
impl std::ops::Deref for I2 {
type Target = str;
@@ -139,7 +139,7 @@ impl std::fmt::Display for I2 {
}
// Doesn't trigger on multiple correct deref
-struct I3 {}
+struct I3;
impl std::ops::Deref for I3 {
type Target = str;
@@ -156,7 +156,7 @@ impl std::fmt::Display for I3 {
}
// Does trigger when deref resolves to self
-struct J {}
+struct J;
impl std::ops::Deref for J {
type Target = str;
@@ -178,7 +178,7 @@ impl std::fmt::Debug for J {
}
}
-struct J2 {}
+struct J2;
impl std::ops::Deref for J2 {
type Target = str;
@@ -194,7 +194,7 @@ impl std::fmt::Display for J2 {
}
}
-struct J3 {}
+struct J3;
impl std::ops::Deref for J3 {
type Target = str;
@@ -210,7 +210,7 @@ impl std::fmt::Display for J3 {
}
}
-struct J4 {}
+struct J4;
impl std::ops::Deref for J4 {
type Target = str;
@@ -227,7 +227,7 @@ impl std::fmt::Display for J4 {
}
// Doesn't trigger on Debug from Display
-struct K {}
+struct K;
impl std::fmt::Debug for K {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
@@ -242,7 +242,7 @@ impl std::fmt::Display for K {
}
// Doesn't trigger on Display from Debug
-struct K2 {}
+struct K2;
impl std::fmt::Debug for K2 {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
diff --git a/tests/ui/redundant_allocation.rs b/tests/ui/redundant_allocation.rs
index 52fbc91e3255..80f94e5f3cbb 100644
--- a/tests/ui/redundant_allocation.rs
+++ b/tests/ui/redundant_allocation.rs
@@ -3,7 +3,7 @@
#![allow(clippy::blacklisted_name, unused_variables, dead_code)]
#![allow(unused_imports)]
-pub struct MyStruct {}
+pub struct MyStruct;
pub struct SubT {
foo: T,
diff --git a/tests/ui/redundant_allocation_fixable.fixed b/tests/ui/redundant_allocation_fixable.fixed
index ef089b25f47f..e7ed84731c02 100644
--- a/tests/ui/redundant_allocation_fixable.fixed
+++ b/tests/ui/redundant_allocation_fixable.fixed
@@ -4,7 +4,7 @@
#![allow(clippy::blacklisted_name, unused_variables, dead_code)]
#![allow(unused_imports)]
-pub struct MyStruct {}
+pub struct MyStruct;
pub struct SubT {
foo: T,
diff --git a/tests/ui/redundant_allocation_fixable.rs b/tests/ui/redundant_allocation_fixable.rs
index fefa87721d72..de763f98b5c8 100644
--- a/tests/ui/redundant_allocation_fixable.rs
+++ b/tests/ui/redundant_allocation_fixable.rs
@@ -4,7 +4,7 @@
#![allow(clippy::blacklisted_name, unused_variables, dead_code)]
#![allow(unused_imports)]
-pub struct MyStruct {}
+pub struct MyStruct;
pub struct SubT {
foo: T,
diff --git a/tests/ui/redundant_clone.fixed b/tests/ui/redundant_clone.fixed
index 16b40dcd9028..1525f6a93dfd 100644
--- a/tests/ui/redundant_clone.fixed
+++ b/tests/ui/redundant_clone.fixed
@@ -1,7 +1,7 @@
// run-rustfix
// rustfix-only-machine-applicable
-#![allow(clippy::implicit_clone)]
+#![allow(clippy::implicit_clone, clippy::drop_non_drop)]
use std::ffi::OsString;
use std::path::Path;
diff --git a/tests/ui/redundant_clone.rs b/tests/ui/redundant_clone.rs
index bd3d7365229f..2f82aefd9283 100644
--- a/tests/ui/redundant_clone.rs
+++ b/tests/ui/redundant_clone.rs
@@ -1,7 +1,7 @@
// run-rustfix
// rustfix-only-machine-applicable
-#![allow(clippy::implicit_clone)]
+#![allow(clippy::implicit_clone, clippy::drop_non_drop)]
use std::ffi::OsString;
use std::path::Path;
diff --git a/tests/ui/redundant_static_lifetimes.fixed b/tests/ui/redundant_static_lifetimes.fixed
index 921249606ad2..acc8f1e25b6e 100644
--- a/tests/ui/redundant_static_lifetimes.fixed
+++ b/tests/ui/redundant_static_lifetimes.fixed
@@ -3,7 +3,7 @@
#![allow(unused)]
#[derive(Debug)]
-struct Foo {}
+struct Foo;
const VAR_ONE: &str = "Test constant #1"; // ERROR Consider removing 'static.
diff --git a/tests/ui/redundant_static_lifetimes.rs b/tests/ui/redundant_static_lifetimes.rs
index 4d4b249d076f..f2f0f78659c9 100644
--- a/tests/ui/redundant_static_lifetimes.rs
+++ b/tests/ui/redundant_static_lifetimes.rs
@@ -3,7 +3,7 @@
#![allow(unused)]
#[derive(Debug)]
-struct Foo {}
+struct Foo;
const VAR_ONE: &'static str = "Test constant #1"; // ERROR Consider removing 'static.
diff --git a/tests/ui/result_map_unit_fn_fixable.fixed b/tests/ui/result_map_unit_fn_fixable.fixed
index 631042c616bc..14c331f67e73 100644
--- a/tests/ui/result_map_unit_fn_fixable.fixed
+++ b/tests/ui/result_map_unit_fn_fixable.fixed
@@ -75,6 +75,8 @@ fn result_map_unit_fn() {
if let Ok(ref value) = x.field { do_nothing(value + captured) }
+
+ if let Ok(value) = x.field { println!("{:?}", value) }
}
fn main() {}
diff --git a/tests/ui/result_map_unit_fn_fixable.rs b/tests/ui/result_map_unit_fn_fixable.rs
index 679eb2326268..8b0fca9ece1a 100644
--- a/tests/ui/result_map_unit_fn_fixable.rs
+++ b/tests/ui/result_map_unit_fn_fixable.rs
@@ -75,6 +75,8 @@ fn result_map_unit_fn() {
x.field.map(|ref value| { do_nothing(value + captured) });
+
+ x.field.map(|value| println!("{:?}", value));
}
fn main() {}
diff --git a/tests/ui/result_map_unit_fn_fixable.stderr b/tests/ui/result_map_unit_fn_fixable.stderr
index 4f3a8c6b7923..782febd52644 100644
--- a/tests/ui/result_map_unit_fn_fixable.stderr
+++ b/tests/ui/result_map_unit_fn_fixable.stderr
@@ -136,5 +136,13 @@ 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
+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:79:5
+ |
+LL | x.field.map(|value| println!("{:?}", value));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
+ | |
+ | help: try this: `if let Ok(value) = x.field { println!("{:?}", value) }`
+
+error: aborting due to 18 previous errors
diff --git a/tests/ui/same_item_push.rs b/tests/ui/same_item_push.rs
index 9d420ec672a2..99964f0de075 100644
--- a/tests/ui/same_item_push.rs
+++ b/tests/ui/same_item_push.rs
@@ -120,7 +120,7 @@ fn main() {
}
// Fix #5979
#[derive(Clone)]
- struct S {}
+ struct S;
trait T {}
impl T for S {}
diff --git a/tests/ui/single_element_loop.fixed b/tests/ui/single_element_loop.fixed
index c307afffcb86..63d31ff83f9b 100644
--- a/tests/ui/single_element_loop.fixed
+++ b/tests/ui/single_element_loop.fixed
@@ -6,11 +6,31 @@ fn main() {
let item1 = 2;
{
let item = &item1;
- println!("{}", item);
+ dbg!(item);
}
{
let item = &item1;
- println!("{:?}", item);
+ dbg!(item);
+ }
+
+ {
+ let item = &(0..5);
+ dbg!(item);
+ }
+
+ {
+ let item = &mut (0..5);
+ dbg!(item);
+ }
+
+ {
+ let item = 0..5;
+ dbg!(item);
+ }
+
+ {
+ let item = 0..5;
+ dbg!(item);
}
}
diff --git a/tests/ui/single_element_loop.rs b/tests/ui/single_element_loop.rs
index 2c0c03b72119..2cda5a329d25 100644
--- a/tests/ui/single_element_loop.rs
+++ b/tests/ui/single_element_loop.rs
@@ -5,10 +5,26 @@
fn main() {
let item1 = 2;
for item in &[item1] {
- println!("{}", item);
+ dbg!(item);
}
for item in [item1].iter() {
- println!("{:?}", item);
+ dbg!(item);
+ }
+
+ for item in &[0..5] {
+ dbg!(item);
+ }
+
+ for item in [0..5].iter_mut() {
+ dbg!(item);
+ }
+
+ for item in [0..5] {
+ dbg!(item);
+ }
+
+ for item in [0..5].into_iter() {
+ dbg!(item);
}
}
diff --git a/tests/ui/single_element_loop.stderr b/tests/ui/single_element_loop.stderr
index f52ca8c5a9b0..0aeb8da1a2e2 100644
--- a/tests/ui/single_element_loop.stderr
+++ b/tests/ui/single_element_loop.stderr
@@ -2,7 +2,7 @@ error: for loop over a single element
--> $DIR/single_element_loop.rs:7:5
|
LL | / for item in &[item1] {
-LL | | println!("{}", item);
+LL | | dbg!(item);
LL | | }
| |_____^
|
@@ -11,7 +11,7 @@ help: try
|
LL ~ {
LL + let item = &item1;
-LL + println!("{}", item);
+LL + dbg!(item);
LL + }
|
@@ -19,7 +19,7 @@ error: for loop over a single element
--> $DIR/single_element_loop.rs:11:5
|
LL | / for item in [item1].iter() {
-LL | | println!("{:?}", item);
+LL | | dbg!(item);
LL | | }
| |_____^
|
@@ -27,9 +27,73 @@ help: try
|
LL ~ {
LL + let item = &item1;
-LL + println!("{:?}", item);
+LL + dbg!(item);
LL + }
|
-error: aborting due to 2 previous errors
+error: for loop over a single element
+ --> $DIR/single_element_loop.rs:15:5
+ |
+LL | / for item in &[0..5] {
+LL | | dbg!(item);
+LL | | }
+ | |_____^
+ |
+help: try
+ |
+LL ~ {
+LL + let item = &(0..5);
+LL + dbg!(item);
+LL + }
+ |
+
+error: for loop over a single element
+ --> $DIR/single_element_loop.rs:19:5
+ |
+LL | / for item in [0..5].iter_mut() {
+LL | | dbg!(item);
+LL | | }
+ | |_____^
+ |
+help: try
+ |
+LL ~ {
+LL + let item = &mut (0..5);
+LL + dbg!(item);
+LL + }
+ |
+
+error: for loop over a single element
+ --> $DIR/single_element_loop.rs:23:5
+ |
+LL | / for item in [0..5] {
+LL | | dbg!(item);
+LL | | }
+ | |_____^
+ |
+help: try
+ |
+LL ~ {
+LL + let item = 0..5;
+LL + dbg!(item);
+LL + }
+ |
+
+error: for loop over a single element
+ --> $DIR/single_element_loop.rs:27:5
+ |
+LL | / for item in [0..5].into_iter() {
+LL | | dbg!(item);
+LL | | }
+ | |_____^
+ |
+help: try
+ |
+LL ~ {
+LL + let item = 0..5;
+LL + dbg!(item);
+LL + }
+ |
+
+error: aborting due to 6 previous errors
diff --git a/tests/ui/trait_duplication_in_bounds.rs b/tests/ui/trait_duplication_in_bounds.rs
index 21de19a26014..f5ca91143af2 100644
--- a/tests/ui/trait_duplication_in_bounds.rs
+++ b/tests/ui/trait_duplication_in_bounds.rs
@@ -62,7 +62,7 @@ trait BadTrait: Default + Clone {
}
#[derive(Default, Clone)]
-struct Life {}
+struct Life;
impl T for Life {
// this should not warn
@@ -85,7 +85,7 @@ trait Iter: Iterator {
}
}
-struct Foo {}
+struct Foo;
trait FooIter: Iterator- {
fn bar()
diff --git a/tests/ui/transmute.rs b/tests/ui/transmute.rs
index 9b681a79aae7..5b688ce4d644 100644
--- a/tests/ui/transmute.rs
+++ b/tests/ui/transmute.rs
@@ -73,6 +73,10 @@ fn crosspointer() {
fn int_to_char() {
let _: char = unsafe { std::mem::transmute(0_u32) };
let _: char = unsafe { std::mem::transmute(0_i32) };
+
+ // These shouldn't warn
+ const _: char = unsafe { std::mem::transmute(0_u32) };
+ const _: char = unsafe { std::mem::transmute(0_i32) };
}
#[warn(clippy::transmute_int_to_bool)]
@@ -130,9 +134,12 @@ mod num_to_bytes {
}
}
-fn bytes_to_str(b: &[u8], mb: &mut [u8]) {
- let _: &str = unsafe { std::mem::transmute(b) };
+fn bytes_to_str(mb: &mut [u8]) {
+ const B: &[u8] = b"";
+
+ let _: &str = unsafe { std::mem::transmute(B) };
let _: &mut str = unsafe { std::mem::transmute(mb) };
+ const _: &str = unsafe { std::mem::transmute(B) };
}
fn main() {}
diff --git a/tests/ui/transmute.stderr b/tests/ui/transmute.stderr
index 86537153e322..72a386e8fa61 100644
--- a/tests/ui/transmute.stderr
+++ b/tests/ui/transmute.stderr
@@ -107,7 +107,7 @@ 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
+ --> $DIR/transmute.rs:84:28
|
LL | let _: bool = unsafe { std::mem::transmute(0_u8) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `0_u8 != 0`
@@ -115,7 +115,7 @@ LL | let _: bool = unsafe { std::mem::transmute(0_u8) };
= note: `-D clippy::transmute-int-to-bool` implied by `-D warnings`
error: transmute from a `u32` to a `f32`
- --> $DIR/transmute.rs:86:31
+ --> $DIR/transmute.rs:90:31
|
LL | let _: f32 = unsafe { std::mem::transmute(0_u32) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_u32)`
@@ -123,25 +123,25 @@ LL | let _: f32 = unsafe { std::mem::transmute(0_u32) };
= note: `-D clippy::transmute-int-to-float` implied by `-D warnings`
error: transmute from a `i32` to a `f32`
- --> $DIR/transmute.rs:87:31
+ --> $DIR/transmute.rs:91:31
|
LL | let _: f32 = unsafe { std::mem::transmute(0_i32) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_i32 as u32)`
error: transmute from a `u64` to a `f64`
- --> $DIR/transmute.rs:88:31
+ --> $DIR/transmute.rs:92:31
|
LL | let _: f64 = unsafe { std::mem::transmute(0_u64) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(0_u64)`
error: transmute from a `i64` to a `f64`
- --> $DIR/transmute.rs:89:31
+ --> $DIR/transmute.rs:93:31
|
LL | let _: f64 = unsafe { std::mem::transmute(0_i64) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(0_i64 as u64)`
error: transmute from a `u8` to a `[u8; 1]`
- --> $DIR/transmute.rs:109:30
+ --> $DIR/transmute.rs:113:30
|
LL | let _: [u8; 1] = std::mem::transmute(0u8);
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u8.to_ne_bytes()`
@@ -149,96 +149,102 @@ LL | let _: [u8; 1] = std::mem::transmute(0u8);
= note: `-D clippy::transmute-num-to-bytes` implied by `-D warnings`
error: transmute from a `u32` to a `[u8; 4]`
- --> $DIR/transmute.rs:110:30
+ --> $DIR/transmute.rs:114:30
|
LL | let _: [u8; 4] = std::mem::transmute(0u32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u32.to_ne_bytes()`
error: transmute from a `u128` to a `[u8; 16]`
- --> $DIR/transmute.rs:111:31
+ --> $DIR/transmute.rs:115:31
|
LL | let _: [u8; 16] = std::mem::transmute(0u128);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u128.to_ne_bytes()`
error: transmute from a `i8` to a `[u8; 1]`
- --> $DIR/transmute.rs:112:30
+ --> $DIR/transmute.rs:116:30
|
LL | let _: [u8; 1] = std::mem::transmute(0i8);
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i8.to_ne_bytes()`
error: transmute from a `i32` to a `[u8; 4]`
- --> $DIR/transmute.rs:113:30
+ --> $DIR/transmute.rs:117:30
|
LL | let _: [u8; 4] = std::mem::transmute(0i32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i32.to_ne_bytes()`
error: transmute from a `i128` to a `[u8; 16]`
- --> $DIR/transmute.rs:114:31
+ --> $DIR/transmute.rs:118:31
|
LL | let _: [u8; 16] = std::mem::transmute(0i128);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i128.to_ne_bytes()`
error: transmute from a `f32` to a `[u8; 4]`
- --> $DIR/transmute.rs:115:30
+ --> $DIR/transmute.rs:119:30
|
LL | let _: [u8; 4] = std::mem::transmute(0.0f32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f32.to_ne_bytes()`
error: transmute from a `f64` to a `[u8; 8]`
- --> $DIR/transmute.rs:116:30
+ --> $DIR/transmute.rs:120:30
|
LL | let _: [u8; 8] = std::mem::transmute(0.0f64);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f64.to_ne_bytes()`
error: transmute from a `u8` to a `[u8; 1]`
- --> $DIR/transmute.rs:121:30
+ --> $DIR/transmute.rs:125:30
|
LL | let _: [u8; 1] = std::mem::transmute(0u8);
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u8.to_ne_bytes()`
error: transmute from a `u32` to a `[u8; 4]`
- --> $DIR/transmute.rs:122:30
+ --> $DIR/transmute.rs:126:30
|
LL | let _: [u8; 4] = std::mem::transmute(0u32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u32.to_ne_bytes()`
error: transmute from a `u128` to a `[u8; 16]`
- --> $DIR/transmute.rs:123:31
+ --> $DIR/transmute.rs:127:31
|
LL | let _: [u8; 16] = std::mem::transmute(0u128);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u128.to_ne_bytes()`
error: transmute from a `i8` to a `[u8; 1]`
- --> $DIR/transmute.rs:124:30
+ --> $DIR/transmute.rs:128:30
|
LL | let _: [u8; 1] = std::mem::transmute(0i8);
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i8.to_ne_bytes()`
error: transmute from a `i32` to a `[u8; 4]`
- --> $DIR/transmute.rs:125:30
+ --> $DIR/transmute.rs:129:30
|
LL | let _: [u8; 4] = std::mem::transmute(0i32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i32.to_ne_bytes()`
error: transmute from a `i128` to a `[u8; 16]`
- --> $DIR/transmute.rs:126:31
+ --> $DIR/transmute.rs:130:31
|
LL | let _: [u8; 16] = std::mem::transmute(0i128);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i128.to_ne_bytes()`
error: transmute from a `&[u8]` to a `&str`
- --> $DIR/transmute.rs:134:28
+ --> $DIR/transmute.rs:140:28
|
-LL | let _: &str = unsafe { std::mem::transmute(b) };
- | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8(b).unwrap()`
+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:135:32
+ --> $DIR/transmute.rs:141:32
|
LL | let _: &mut str = unsafe { std::mem::transmute(mb) };
| ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_mut(mb).unwrap()`
-error: aborting due to 38 previous errors
+error: transmute from a `&[u8]` to a `&str`
+ --> $DIR/transmute.rs:142:30
+ |
+LL | const _: &str = unsafe { std::mem::transmute(B) };
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_unchecked(B)`
+
+error: aborting due to 39 previous errors
diff --git a/tests/ui/undocumented_unsafe_blocks.rs b/tests/ui/undocumented_unsafe_blocks.rs
index 380303d8152a..afa337c45f41 100644
--- a/tests/ui/undocumented_unsafe_blocks.rs
+++ b/tests/ui/undocumented_unsafe_blocks.rs
@@ -1,5 +1,9 @@
+// aux-build:proc_macro_unsafe.rs
+
#![warn(clippy::undocumented_unsafe_blocks)]
+extern crate proc_macro_unsafe;
+
// Valid comments
fn nested_local() {
@@ -89,11 +93,6 @@ fn block_comment_newlines() {
unsafe {}
}
-#[rustfmt::skip]
-fn inline_block_comment() {
- /* Safety: */unsafe {}
-}
-
fn block_comment_with_extras() {
/* This is a description
* SAFETY:
@@ -209,8 +208,54 @@ fn local_nest() {
let _ = [(42, unsafe {}, unsafe {}), (52, unsafe {}, unsafe {})];
}
+fn in_fn_call(x: *const u32) {
+ fn f(x: u32) {}
+
+ // Safety: reason
+ f(unsafe { *x });
+}
+
+fn multi_in_fn_call(x: *const u32) {
+ fn f(x: u32, y: u32) {}
+
+ // Safety: reason
+ f(unsafe { *x }, unsafe { *x });
+}
+
+fn in_multiline_fn_call(x: *const u32) {
+ fn f(x: u32, y: u32) {}
+
+ f(
+ // Safety: reason
+ unsafe { *x },
+ 0,
+ );
+}
+
+fn in_macro_call(x: *const u32) {
+ // Safety: reason
+ println!("{}", unsafe { *x });
+}
+
+fn in_multiline_macro_call(x: *const u32) {
+ println!(
+ "{}",
+ // Safety: reason
+ unsafe { *x },
+ );
+}
+
+fn from_proc_macro() {
+ proc_macro_unsafe::unsafe_block!(token);
+}
+
// Invalid comments
+#[rustfmt::skip]
+fn inline_block_comment() {
+ /* Safety: */ unsafe {}
+}
+
fn no_comment() {
unsafe {}
}
diff --git a/tests/ui/undocumented_unsafe_blocks.stderr b/tests/ui/undocumented_unsafe_blocks.stderr
index f69d0da54e0d..856a07fd3168 100644
--- a/tests/ui/undocumented_unsafe_blocks.stderr
+++ b/tests/ui/undocumented_unsafe_blocks.stderr
@@ -1,114 +1,110 @@
error: unsafe block missing a safety comment
- --> $DIR/undocumented_unsafe_blocks.rs:215:5
+ --> $DIR/undocumented_unsafe_blocks.rs:256:19
+ |
+LL | /* Safety: */ unsafe {}
+ | ^^^^^^^^^
+ |
+ = note: `-D clippy::undocumented-unsafe-blocks` implied by `-D warnings`
+ = help: consider adding a safety comment on the preceding line
+
+error: unsafe block missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:260:5
|
LL | unsafe {}
| ^^^^^^^^^
|
- = note: `-D clippy::undocumented-unsafe-blocks` implied by `-D warnings`
-help: consider adding a safety comment
- |
-LL ~ // SAFETY: ...
-LL + unsafe {}
- |
+ = help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
- --> $DIR/undocumented_unsafe_blocks.rs:219:5
+ --> $DIR/undocumented_unsafe_blocks.rs:264:14
|
LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
-help: consider adding a safety comment
- |
-LL ~ // SAFETY: ...
-LL + let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
+ | ^^^^^^^^^^^^^
|
+ = help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
- --> $DIR/undocumented_unsafe_blocks.rs:223:5
+ --> $DIR/undocumented_unsafe_blocks.rs:264:29
+ |
+LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
+ | ^^^^^^^^^^^^^
+ |
+ = help: consider adding a safety comment on the preceding line
+
+error: unsafe block missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:264:48
+ |
+LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
+ | ^^^^^^^^^^^^^
+ |
+ = help: consider adding a safety comment on the preceding line
+
+error: unsafe block missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:268:18
|
LL | let _ = (42, unsafe {}, "test", unsafe {});
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
-help: consider adding a safety comment
- |
-LL ~ // SAFETY: ...
-LL + let _ = (42, unsafe {}, "test", unsafe {});
+ | ^^^^^^^^^
|
+ = help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
- --> $DIR/undocumented_unsafe_blocks.rs:227:5
+ --> $DIR/undocumented_unsafe_blocks.rs:268:37
+ |
+LL | let _ = (42, unsafe {}, "test", unsafe {});
+ | ^^^^^^^^^
+ |
+ = help: consider adding a safety comment on the preceding line
+
+error: unsafe block missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:272:14
|
LL | let _ = *unsafe { &42 };
- | ^^^^^^^^^^^^^^^^^^^^^^^^
- |
-help: consider adding a safety comment
- |
-LL ~ // SAFETY: ...
-LL + let _ = *unsafe { &42 };
+ | ^^^^^^^^^^^^^^
|
+ = help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
- --> $DIR/undocumented_unsafe_blocks.rs:232:5
+ --> $DIR/undocumented_unsafe_blocks.rs:277:19
|
LL | let _ = match unsafe {} {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^
- |
-help: consider adding a safety comment
- |
-LL ~ // SAFETY: ...
-LL + let _ = match unsafe {} {
+ | ^^^^^^^^^
|
+ = help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
- --> $DIR/undocumented_unsafe_blocks.rs:238:5
+ --> $DIR/undocumented_unsafe_blocks.rs:283:14
|
LL | let _ = &unsafe {};
- | ^^^^^^^^^^^^^^^^^^^
- |
-help: consider adding a safety comment
- |
-LL ~ // SAFETY: ...
-LL + let _ = &unsafe {};
+ | ^^^^^^^^^
|
+ = help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
- --> $DIR/undocumented_unsafe_blocks.rs:242:5
+ --> $DIR/undocumented_unsafe_blocks.rs:287:14
|
LL | let _ = [unsafe {}; 5];
- | ^^^^^^^^^^^^^^^^^^^^^^^
- |
-help: consider adding a safety comment
- |
-LL ~ // SAFETY: ...
-LL + let _ = [unsafe {}; 5];
+ | ^^^^^^^^^
|
+ = help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
- --> $DIR/undocumented_unsafe_blocks.rs:246:5
+ --> $DIR/undocumented_unsafe_blocks.rs:291:13
|
LL | let _ = unsafe {};
- | ^^^^^^^^^^^^^^^^^^
- |
-help: consider adding a safety comment
- |
-LL ~ // SAFETY: ...
-LL + let _ = unsafe {};
+ | ^^^^^^^^^
|
+ = help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
- --> $DIR/undocumented_unsafe_blocks.rs:256:8
+ --> $DIR/undocumented_unsafe_blocks.rs:301:8
|
LL | t!(unsafe {});
| ^^^^^^^^^
|
-help: consider adding a safety comment
- |
-LL ~ t!(// SAFETY: ...
-LL ~ unsafe {});
- |
+ = help: consider adding a safety comment on the preceding line
-error: unsafe block in macro expansion missing a safety comment
- --> $DIR/undocumented_unsafe_blocks.rs:262:13
+error: unsafe block missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:307:13
|
LL | unsafe {}
| ^^^^^^^^^
@@ -116,56 +112,40 @@ LL | unsafe {}
LL | t!();
| ---- in this macro invocation
|
- = help: consider adding a safety comment in the macro definition
+ = help: consider adding a safety comment on the preceding line
= note: this error originates in the macro `t` (in Nightly builds, run with -Z macro-backtrace for more info)
error: unsafe block missing a safety comment
- --> $DIR/undocumented_unsafe_blocks.rs:270:5
+ --> $DIR/undocumented_unsafe_blocks.rs:315:5
|
LL | unsafe {} // SAFETY:
| ^^^^^^^^^
|
-help: consider adding a safety comment
- |
-LL ~ // SAFETY: ...
-LL ~ unsafe {} // SAFETY:
- |
+ = help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
- --> $DIR/undocumented_unsafe_blocks.rs:274:5
+ --> $DIR/undocumented_unsafe_blocks.rs:319:5
|
LL | unsafe {
| ^^^^^^^^
|
-help: consider adding a safety comment
- |
-LL ~ // SAFETY: ...
-LL + unsafe {
- |
+ = help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
- --> $DIR/undocumented_unsafe_blocks.rs:284:5
+ --> $DIR/undocumented_unsafe_blocks.rs:329:5
|
LL | unsafe {};
| ^^^^^^^^^
|
-help: consider adding a safety comment
- |
-LL ~ // SAFETY: ...
-LL ~ unsafe {};
- |
+ = help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
- --> $DIR/undocumented_unsafe_blocks.rs:288:20
+ --> $DIR/undocumented_unsafe_blocks.rs:333:20
|
LL | println!("{}", unsafe { String::from_utf8_unchecked(vec![]) });
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
-help: consider adding a safety comment
- |
-LL ~ println!("{}", // SAFETY: ...
-LL ~ unsafe { String::from_utf8_unchecked(vec![]) });
- |
+ = help: consider adding a safety comment on the preceding line
-error: aborting due to 14 previous errors
+error: aborting due to 18 previous errors
diff --git a/tests/ui/unnecessary_cast.rs b/tests/ui/unnecessary_cast.rs
index b77c19f2ba59..62c3e9636866 100644
--- a/tests/ui/unnecessary_cast.rs
+++ b/tests/ui/unnecessary_cast.rs
@@ -30,4 +30,10 @@ fn main() {
// do not lint cast to cfg-dependant type
1 as std::os::raw::c_char;
+
+ // do not lint cast to alias type
+ 1 as I32Alias;
+ &1 as &I32Alias;
}
+
+type I32Alias = i32;
diff --git a/tests/ui/unnecessary_cast_fixable.fixed b/tests/ui/unnecessary_cast_fixable.fixed
index 3332f49c80c9..36800c5340db 100644
--- a/tests/ui/unnecessary_cast_fixable.fixed
+++ b/tests/ui/unnecessary_cast_fixable.fixed
@@ -42,4 +42,9 @@ fn main() {
let _ = -1_i32;
let _ = -1.0_f32;
+
+ let _ = 1 as I32Alias;
+ let _ = &1 as &I32Alias;
}
+
+type I32Alias = i32;
diff --git a/tests/ui/unnecessary_cast_fixable.rs b/tests/ui/unnecessary_cast_fixable.rs
index ec01e9387792..d4b6bb952ab3 100644
--- a/tests/ui/unnecessary_cast_fixable.rs
+++ b/tests/ui/unnecessary_cast_fixable.rs
@@ -42,4 +42,9 @@ fn main() {
let _ = -1 as i32;
let _ = -1.0 as f32;
+
+ let _ = 1 as I32Alias;
+ let _ = &1 as &I32Alias;
}
+
+type I32Alias = i32;
diff --git a/tests/ui/unsafe_derive_deserialize.rs b/tests/ui/unsafe_derive_deserialize.rs
index 690d705573d3..bafca91917aa 100644
--- a/tests/ui/unsafe_derive_deserialize.rs
+++ b/tests/ui/unsafe_derive_deserialize.rs
@@ -6,7 +6,7 @@ extern crate serde;
use serde::Deserialize;
#[derive(Deserialize)]
-pub struct A {}
+pub struct A;
impl A {
pub unsafe fn new(_a: i32, _b: i32) -> Self {
Self {}
@@ -14,13 +14,13 @@ impl A {
}
#[derive(Deserialize)]
-pub struct B {}
+pub struct B;
impl B {
pub unsafe fn unsafe_method(&self) {}
}
#[derive(Deserialize)]
-pub struct C {}
+pub struct C;
impl C {
pub fn unsafe_block(&self) {
unsafe {}
@@ -28,7 +28,7 @@ impl C {
}
#[derive(Deserialize)]
-pub struct D {}
+pub struct D;
impl D {
pub fn inner_unsafe_fn(&self) {
unsafe fn inner() {}
@@ -36,7 +36,7 @@ impl D {
}
// Does not derive `Deserialize`, should be ignored
-pub struct E {}
+pub struct E;
impl E {
pub unsafe fn new(_a: i32, _b: i32) -> Self {
Self {}
@@ -55,12 +55,12 @@ impl E {
// Does not have methods using `unsafe`, should be ignored
#[derive(Deserialize)]
-pub struct F {}
+pub struct F;
// Check that we honor the `allow` attribute on the ADT
#[allow(clippy::unsafe_derive_deserialize)]
#[derive(Deserialize)]
-pub struct G {}
+pub struct G;
impl G {
pub fn unsafe_block(&self) {
unsafe {}
diff --git a/tests/ui/unsafe_removed_from_name.rs b/tests/ui/unsafe_removed_from_name.rs
index a1f616733bd9..cde4e96d668c 100644
--- a/tests/ui/unsafe_removed_from_name.rs
+++ b/tests/ui/unsafe_removed_from_name.rs
@@ -14,8 +14,8 @@ use std::cell::UnsafeCell as Dangerunsafe;
use std::cell::UnsafeCell as Bombsawayunsafe;
mod mod_with_some_unsafe_things {
- pub struct Safe {}
- pub struct Unsafe {}
+ pub struct Safe;
+ pub struct Unsafe;
}
use mod_with_some_unsafe_things::Unsafe as LieAboutModSafety;
diff --git a/tests/ui/unused_self.rs b/tests/ui/unused_self.rs
index 7a4bbdda1ab2..08bf58fec7c3 100644
--- a/tests/ui/unused_self.rs
+++ b/tests/ui/unused_self.rs
@@ -5,7 +5,7 @@ mod unused_self {
use std::pin::Pin;
use std::sync::{Arc, Mutex};
- struct A {}
+ struct A;
impl A {
fn unused_self_move(self) {}
@@ -27,7 +27,7 @@ mod unused_self {
}
mod unused_self_allow {
- struct A {}
+ struct A;
impl A {
// shouldn't trigger
@@ -35,7 +35,7 @@ mod unused_self_allow {
fn unused_self_move(self) {}
}
- struct B {}
+ struct B;
// shouldn't trigger
#[allow(clippy::unused_self)]
@@ -43,7 +43,7 @@ mod unused_self_allow {
fn unused_self_move(self) {}
}
- struct C {}
+ struct C;
#[allow(clippy::unused_self)]
impl C {
@@ -120,7 +120,7 @@ mod used_self {
mod not_applicable {
use std::fmt;
- struct A {}
+ struct A;
impl fmt::Debug for A {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
diff --git a/tests/ui/use_self.fixed b/tests/ui/use_self.fixed
index 9d216f56ae60..3e62ffe74fed 100644
--- a/tests/ui/use_self.fixed
+++ b/tests/ui/use_self.fixed
@@ -16,7 +16,7 @@ extern crate proc_macro_derive;
fn main() {}
mod use_self {
- struct Foo {}
+ struct Foo;
impl Foo {
fn new() -> Self {
@@ -35,7 +35,7 @@ mod use_self {
}
mod better {
- struct Foo {}
+ struct Foo;
impl Foo {
fn new() -> Self {
@@ -123,7 +123,7 @@ mod macros {
};
}
- struct Foo {}
+ struct Foo;
impl Foo {
use_self_expand!(); // Should not lint in local macros
@@ -134,7 +134,7 @@ mod macros {
}
mod nesting {
- struct Foo {}
+ struct Foo;
impl Foo {
fn foo() {
#[allow(unused_imports)]
@@ -209,7 +209,7 @@ mod issue3410 {
#[allow(clippy::no_effect, path_statements)]
mod rustfix {
mod nested {
- pub struct A {}
+ pub struct A;
}
impl nested::A {
@@ -227,7 +227,7 @@ mod rustfix {
}
mod issue3567 {
- struct TestStruct {}
+ struct TestStruct;
impl TestStruct {
fn from_something() -> Self {
Self {}
@@ -248,7 +248,7 @@ mod issue3567 {
mod paths_created_by_lowering {
use std::ops::Range;
- struct S {}
+ struct S;
impl S {
const A: usize = 0;
@@ -382,7 +382,7 @@ mod issue4305 {
}
mod lint_at_item_level {
- struct Foo {}
+ struct Foo;
#[allow(clippy::use_self)]
impl Foo {
@@ -400,7 +400,7 @@ mod lint_at_item_level {
}
mod lint_at_impl_item_level {
- struct Foo {}
+ struct Foo;
impl Foo {
#[allow(clippy::use_self)]
@@ -433,8 +433,8 @@ mod issue4734 {
mod nested_paths {
use std::convert::Into;
mod submod {
- pub struct B {}
- pub struct C {}
+ pub struct B;
+ pub struct C;
impl Into
for B {
fn into(self) -> C {
diff --git a/tests/ui/use_self.rs b/tests/ui/use_self.rs
index 5f604fe25e41..da2faddee12a 100644
--- a/tests/ui/use_self.rs
+++ b/tests/ui/use_self.rs
@@ -16,7 +16,7 @@ extern crate proc_macro_derive;
fn main() {}
mod use_self {
- struct Foo {}
+ struct Foo;
impl Foo {
fn new() -> Foo {
@@ -35,7 +35,7 @@ mod use_self {
}
mod better {
- struct Foo {}
+ struct Foo;
impl Foo {
fn new() -> Self {
@@ -123,7 +123,7 @@ mod macros {
};
}
- struct Foo {}
+ struct Foo;
impl Foo {
use_self_expand!(); // Should not lint in local macros
@@ -134,7 +134,7 @@ mod macros {
}
mod nesting {
- struct Foo {}
+ struct Foo;
impl Foo {
fn foo() {
#[allow(unused_imports)]
@@ -209,7 +209,7 @@ mod issue3410 {
#[allow(clippy::no_effect, path_statements)]
mod rustfix {
mod nested {
- pub struct A {}
+ pub struct A;
}
impl nested::A {
@@ -227,7 +227,7 @@ mod rustfix {
}
mod issue3567 {
- struct TestStruct {}
+ struct TestStruct;
impl TestStruct {
fn from_something() -> Self {
Self {}
@@ -248,7 +248,7 @@ mod issue3567 {
mod paths_created_by_lowering {
use std::ops::Range;
- struct S {}
+ struct S;
impl S {
const A: usize = 0;
@@ -382,7 +382,7 @@ mod issue4305 {
}
mod lint_at_item_level {
- struct Foo {}
+ struct Foo;
#[allow(clippy::use_self)]
impl Foo {
@@ -400,7 +400,7 @@ mod lint_at_item_level {
}
mod lint_at_impl_item_level {
- struct Foo {}
+ struct Foo;
impl Foo {
#[allow(clippy::use_self)]
@@ -433,8 +433,8 @@ mod issue4734 {
mod nested_paths {
use std::convert::Into;
mod submod {
- pub struct B {}
- pub struct C {}
+ pub struct B;
+ pub struct C;
impl Into for B {
fn into(self) -> C {
diff --git a/tests/ui/useless_attribute.fixed b/tests/ui/useless_attribute.fixed
index a5fcde768f18..ce58a80347b5 100644
--- a/tests/ui/useless_attribute.fixed
+++ b/tests/ui/useless_attribute.fixed
@@ -42,7 +42,7 @@ mod a {
mod b {
#[allow(dead_code)]
#[allow(unreachable_pub)]
- pub struct C {}
+ pub struct C;
}
#[allow(unreachable_pub)]
diff --git a/tests/ui/useless_attribute.rs b/tests/ui/useless_attribute.rs
index 0396d39e3d54..c82bb9ba07fd 100644
--- a/tests/ui/useless_attribute.rs
+++ b/tests/ui/useless_attribute.rs
@@ -42,7 +42,7 @@ mod a {
mod b {
#[allow(dead_code)]
#[allow(unreachable_pub)]
- pub struct C {}
+ pub struct C;
}
#[allow(unreachable_pub)]
diff --git a/tests/versioncheck.rs b/tests/versioncheck.rs
index 77102b8cac0c..38498ebdcf2c 100644
--- a/tests/versioncheck.rs
+++ b/tests/versioncheck.rs
@@ -3,34 +3,32 @@
#![allow(clippy::single_match_else)]
use rustc_tools_util::VersionInfo;
+use std::fs;
#[test]
fn check_that_clippy_lints_and_clippy_utils_have_the_same_version_as_clippy() {
+ fn read_version(path: &str) -> String {
+ let contents = fs::read_to_string(path).unwrap_or_else(|e| panic!("error reading `{}`: {:?}", path, e));
+ contents
+ .lines()
+ .filter_map(|l| l.split_once('='))
+ .find_map(|(k, v)| (k.trim() == "version").then(|| v.trim()))
+ .unwrap_or_else(|| panic!("error finding version in `{}`", path))
+ .to_string()
+ }
+
// do not run this test inside the upstream rustc repo:
// https://github.com/rust-lang/rust-clippy/issues/6683
if option_env!("RUSTC_TEST_SUITE").is_some() {
return;
}
- let clippy_meta = cargo_metadata::MetadataCommand::new()
- .no_deps()
- .exec()
- .expect("could not obtain cargo metadata");
+ let clippy_version = read_version("Cargo.toml");
+ let clippy_lints_version = read_version("clippy_lints/Cargo.toml");
+ let clippy_utils_version = read_version("clippy_utils/Cargo.toml");
- for krate in &["clippy_lints", "clippy_utils"] {
- let krate_meta = cargo_metadata::MetadataCommand::new()
- .current_dir(std::env::current_dir().unwrap().join(krate))
- .no_deps()
- .exec()
- .expect("could not obtain cargo metadata");
- assert_eq!(krate_meta.packages[0].version, clippy_meta.packages[0].version);
- for package in &clippy_meta.packages[0].dependencies {
- if package.name == *krate {
- assert!(package.req.matches(&krate_meta.packages[0].version));
- break;
- }
- }
- }
+ assert_eq!(clippy_version, clippy_lints_version);
+ assert_eq!(clippy_version, clippy_utils_version);
}
#[test]
From da26623d5420396304fd581450143f6edc5969e5 Mon Sep 17 00:00:00 2001
From: flip1995
Date: Fri, 8 Apr 2022 10:41:55 +0100
Subject: [PATCH 013/128] Update Cargo.lock
---
clippy_dev/Cargo.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clippy_dev/Cargo.toml b/clippy_dev/Cargo.toml
index c2ebba0683ca..81faa5fe5e14 100644
--- a/clippy_dev/Cargo.toml
+++ b/clippy_dev/Cargo.toml
@@ -9,7 +9,7 @@ indoc = "1.0"
itertools = "0.10.1"
opener = "0.5"
shell-escape = "0.1"
-tempfile = "3.3"
+tempfile = "3.2"
walkdir = "2.3"
[features]
From b79f13ea5cbe07d571fed747b8a77c9e033ce598 Mon Sep 17 00:00:00 2001
From: Oli Scherer
Date: Fri, 8 Apr 2022 15:57:44 +0000
Subject: [PATCH 014/128] Avoid looking at the internals of Interned directly
---
.../src/case_sensitive_file_extension_comparisons.rs | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/clippy_lints/src/case_sensitive_file_extension_comparisons.rs b/clippy_lints/src/case_sensitive_file_extension_comparisons.rs
index df780747a0c7..a8f9c189adec 100644
--- a/clippy_lints/src/case_sensitive_file_extension_comparisons.rs
+++ b/clippy_lints/src/case_sensitive_file_extension_comparisons.rs
@@ -1,7 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_help;
use if_chain::if_chain;
use rustc_ast::ast::LitKind;
-use rustc_data_structures::intern::Interned;
use rustc_hir::{Expr, ExprKind, PathSegment};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
@@ -56,8 +55,8 @@ fn check_case_sensitive_file_extension_comparison(ctx: &LateContext<'_>, expr: &
ty::Str => {
return Some(span);
},
- ty::Adt(ty::AdtDef(Interned(&ty::AdtDefData { did, .. }, _)), _) => {
- if ctx.tcx.is_diagnostic_item(sym::String, did) {
+ ty::Adt(def, _) => {
+ if ctx.tcx.is_diagnostic_item(sym::String, def.did()) {
return Some(span);
}
},
From 224916823a709cb16b77e8582c8433dc728ecc6c Mon Sep 17 00:00:00 2001
From: Miguel Guarniz
Date: Sun, 3 Apr 2022 15:50:33 -0400
Subject: [PATCH 015/128] Refactor HIR item-like traversal (part 1)
- Create hir_crate_items query which traverses tcx.hir_crate(()).owners to return a hir::ModuleItems
- use tcx.hir_crate_items in tcx.hir().items() to return an iterator of hir::ItemId
- add par_items(impl Fn(hir::ItemId)) to traverse all items in parallel
Signed-off-by: Miguel Guarniz
---
clippy_lints/src/same_name_method.rs | 3 ++-
tests/ui/same_name_method.stderr | 26 +++++++++++++-------------
2 files changed, 15 insertions(+), 14 deletions(-)
diff --git a/clippy_lints/src/same_name_method.rs b/clippy_lints/src/same_name_method.rs
index 22b458969551..c76061867145 100644
--- a/clippy_lints/src/same_name_method.rs
+++ b/clippy_lints/src/same_name_method.rs
@@ -50,7 +50,8 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod {
fn check_crate_post(&mut self, cx: &LateContext<'tcx>) {
let mut map = FxHashMap::::default();
- for item in cx.tcx.hir().items() {
+ for id in cx.tcx.hir().items() {
+ let item = cx.tcx.hir().item(id);
if let ItemKind::Impl(Impl {
items,
of_trait,
diff --git a/tests/ui/same_name_method.stderr b/tests/ui/same_name_method.stderr
index c32c3dd9880f..cf06eb32e0c7 100644
--- a/tests/ui/same_name_method.stderr
+++ b/tests/ui/same_name_method.stderr
@@ -11,6 +11,19 @@ note: existing `foo` defined here
LL | fn foo() {}
| ^^^^^^^^^^^
+error: method's name is the same as an existing method in a trait
+ --> $DIR/same_name_method.rs:34:13
+ |
+LL | fn clone() {}
+ | ^^^^^^^^^^^^^
+ |
+note: existing `clone` defined here
+ --> $DIR/same_name_method.rs:30:18
+ |
+LL | #[derive(Clone)]
+ | ^^^^^
+ = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info)
+
error: method's name is the same as an existing method in a trait
--> $DIR/same_name_method.rs:44:13
|
@@ -47,18 +60,5 @@ note: existing `foo` defined here
LL | impl T1 for S {}
| ^^^^^^^^^^^^^^^^
-error: method's name is the same as an existing method in a trait
- --> $DIR/same_name_method.rs:34:13
- |
-LL | fn clone() {}
- | ^^^^^^^^^^^^^
- |
-note: existing `clone` defined here
- --> $DIR/same_name_method.rs:30:18
- |
-LL | #[derive(Clone)]
- | ^^^^^
- = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info)
-
error: aborting due to 5 previous errors
From 3363a62068a5e8babb0dd9521207ead4bef90cac Mon Sep 17 00:00:00 2001
From: Miguel Guarniz
Date: Thu, 7 Apr 2022 16:47:40 -0400
Subject: [PATCH 016/128] remove CheckVisitor, CollectExternCrateVisitor and
ItemLikeVisitor impls
Signed-off-by: Miguel Guarniz
---
clippy_lints/src/same_name_method.rs | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/clippy_lints/src/same_name_method.rs b/clippy_lints/src/same_name_method.rs
index c76061867145..a01e2f2db3af 100644
--- a/clippy_lints/src/same_name_method.rs
+++ b/clippy_lints/src/same_name_method.rs
@@ -51,6 +51,10 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod {
let mut map = FxHashMap::::default();
for id in cx.tcx.hir().items() {
+ if !matches!(cx.tcx.hir().def_kind(id.def_id), DefKind::Impl) {
+ continue;
+ }
+
let item = cx.tcx.hir().item(id);
if let ItemKind::Impl(Impl {
items,
From 63f6a79bf87cd5960c069bb45c88646519d096f8 Mon Sep 17 00:00:00 2001
From: Jason Newcomb
Date: Fri, 8 Apr 2022 16:51:40 -0400
Subject: [PATCH 017/128] Don't lint various match lints when expanded by a
proc-macro
---
clippy_lints/src/matches/mod.rs | 5 +++-
clippy_utils/src/source.rs | 19 +++++++++++++
tests/ui/auxiliary/proc_macro_with_span.rs | 32 ++++++++++++++++++++++
tests/ui/single_match_else.rs | 18 ++++++++++--
tests/ui/single_match_else.stderr | 11 ++++----
5 files changed, 77 insertions(+), 8 deletions(-)
create mode 100644 tests/ui/auxiliary/proc_macro_with_span.rs
diff --git a/clippy_lints/src/matches/mod.rs b/clippy_lints/src/matches/mod.rs
index e93b494653fc..854cf06ed819 100644
--- a/clippy_lints/src/matches/mod.rs
+++ b/clippy_lints/src/matches/mod.rs
@@ -1,4 +1,4 @@
-use clippy_utils::source::{snippet_opt, walk_span_to_context};
+use clippy_utils::source::{snippet_opt, span_starts_with, walk_span_to_context};
use clippy_utils::{meets_msrv, msrvs};
use rustc_hir::{Arm, Expr, ExprKind, Local, MatchSource, Pat};
use rustc_lexer::{tokenize, TokenKind};
@@ -653,6 +653,9 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
}
if let ExprKind::Match(ex, arms, source) = expr.kind {
+ if !span_starts_with(cx, expr.span, "match") {
+ return;
+ }
if !contains_cfg_arm(cx, expr, ex, arms) {
if source == MatchSource::Normal {
if !(meets_msrv(self.msrv.as_ref(), &msrvs::MATCHES_MACRO)
diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs
index dbad607c58ea..beed73268037 100644
--- a/clippy_utils/src/source.rs
+++ b/clippy_utils/src/source.rs
@@ -7,9 +7,28 @@ use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LintContext};
use rustc_span::hygiene;
+use rustc_span::source_map::SourceMap;
use rustc_span::{BytePos, Pos, Span, SyntaxContext};
use std::borrow::Cow;
+/// Checks if the span starts with the given text. This will return false if the span crosses
+/// multiple files or if source is not available.
+///
+/// This is used to check for proc macros giving unhelpful spans to things.
+pub fn span_starts_with(cx: &T, span: Span, text: &str) -> bool {
+ fn helper(sm: &SourceMap, span: Span, text: &str) -> bool {
+ let pos = sm.lookup_byte_offset(span.lo());
+ let Some(ref src) = pos.sf.src else {
+ return false;
+ };
+ let end = span.hi() - pos.sf.start_pos;
+ src.get(pos.pos.0 as usize..end.0 as usize)
+ // Expression spans can include wrapping parenthesis. Remove them first.
+ .map_or(false, |s| s.trim_start_matches('(').starts_with(text))
+ }
+ helper(cx.sess().source_map(), span, text)
+}
+
/// Like `snippet_block`, but add braces if the expr is not an `ExprKind::Block`.
/// Also takes an `Option` which can be put inside the braces.
pub fn expr_block<'a, T: LintContext>(
diff --git a/tests/ui/auxiliary/proc_macro_with_span.rs b/tests/ui/auxiliary/proc_macro_with_span.rs
new file mode 100644
index 000000000000..8ea631f2bbd4
--- /dev/null
+++ b/tests/ui/auxiliary/proc_macro_with_span.rs
@@ -0,0 +1,32 @@
+// compile-flags: --emit=link
+// no-prefer-dynamic
+
+#![crate_type = "proc-macro"]
+
+extern crate proc_macro;
+
+use proc_macro::{token_stream::IntoIter, Group, Span, TokenStream, TokenTree};
+
+#[proc_macro]
+pub fn with_span(input: TokenStream) -> TokenStream {
+ let mut iter = input.into_iter();
+ let span = iter.next().unwrap().span();
+ let mut res = TokenStream::new();
+ write_with_span(span, iter, &mut res);
+ res
+}
+
+fn write_with_span(s: Span, input: IntoIter, out: &mut TokenStream) {
+ for mut tt in input {
+ if let TokenTree::Group(g) = tt {
+ let mut stream = TokenStream::new();
+ write_with_span(s, g.stream().into_iter(), &mut stream);
+ let mut group = Group::new(g.delimiter(), stream);
+ group.set_span(s);
+ out.extend([TokenTree::Group(group)]);
+ } else {
+ tt.set_span(s);
+ out.extend([tt]);
+ }
+ }
+}
diff --git a/tests/ui/single_match_else.rs b/tests/ui/single_match_else.rs
index b624a41a29b2..82387f3d80b7 100644
--- a/tests/ui/single_match_else.rs
+++ b/tests/ui/single_match_else.rs
@@ -1,7 +1,12 @@
+// aux-build: proc_macro_with_span.rs
+
#![warn(clippy::single_match_else)]
#![allow(clippy::needless_return)]
#![allow(clippy::no_effect)]
+extern crate proc_macro_with_span;
+use proc_macro_with_span::with_span;
+
enum ExprNode {
ExprAddrOf,
Butterflies,
@@ -11,13 +16,22 @@ enum ExprNode {
static NODE: ExprNode = ExprNode::Unicorns;
fn unwrap_addr() -> Option<&'static ExprNode> {
- match ExprNode::Butterflies {
+ let _ = match ExprNode::Butterflies {
ExprNode::ExprAddrOf => Some(&NODE),
_ => {
let x = 5;
None
},
- }
+ };
+
+ // Don't lint
+ with_span!(span match ExprNode::Butterflies {
+ ExprNode::ExprAddrOf => Some(&NODE),
+ _ => {
+ let x = 5;
+ None
+ },
+ })
}
macro_rules! unwrap_addr {
diff --git a/tests/ui/single_match_else.stderr b/tests/ui/single_match_else.stderr
index 21ea704b62ab..7756c6f204e6 100644
--- a/tests/ui/single_match_else.stderr
+++ b/tests/ui/single_match_else.stderr
@@ -1,22 +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:14:5
+ --> $DIR/single_match_else.rs:19:13
|
-LL | / match ExprNode::Butterflies {
+LL | let _ = match ExprNode::Butterflies {
+ | _____________^
LL | | ExprNode::ExprAddrOf => Some(&NODE),
LL | | _ => {
LL | | let x = 5;
LL | | None
LL | | },
-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 _ = if let ExprNode::ExprAddrOf = ExprNode::Butterflies { Some(&NODE) } else {
LL + let x = 5;
LL + None
-LL + }
+LL ~ };
|
error: aborting due to previous error
From 744b0ff9035298a8f62accf7f48701fa56d2df49 Mon Sep 17 00:00:00 2001
From: Jason Newcomb
Date: Fri, 8 Apr 2022 23:15:18 -0400
Subject: [PATCH 018/128] Don't lint `iter_with_drain` on references
---
clippy_lints/src/methods/iter_with_drain.rs | 95 ++++++++-------------
tests/ui/iter_with_drain.fixed | 9 ++
tests/ui/iter_with_drain.rs | 9 ++
3 files changed, 53 insertions(+), 60 deletions(-)
diff --git a/clippy_lints/src/methods/iter_with_drain.rs b/clippy_lints/src/methods/iter_with_drain.rs
index 958c3773087b..152072e09c77 100644
--- a/clippy_lints/src/methods/iter_with_drain.rs
+++ b/clippy_lints/src/methods/iter_with_drain.rs
@@ -1,72 +1,47 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::higher::Range;
use clippy_utils::is_integer_const;
-use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{
- higher::{self, Range},
- SpanlessEq,
-};
use rustc_ast::ast::RangeLimits;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, QPath};
use rustc_lint::LateContext;
-use rustc_span::symbol::{sym, Symbol};
+use rustc_span::symbol::sym;
use rustc_span::Span;
use super::ITER_WITH_DRAIN;
-const DRAIN_TYPES: &[Symbol] = &[sym::Vec, sym::VecDeque];
-
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span, arg: &Expr<'_>) {
- let ty = cx.typeck_results().expr_ty(recv).peel_refs();
- if let Some(drained_type) = DRAIN_TYPES.iter().find(|&&sym| is_type_diagnostic_item(cx, ty, sym)) {
- // Refuse to emit `into_iter` suggestion on draining struct fields due
- // to the strong possibility of processing unmovable field.
- if let ExprKind::Field(..) = recv.kind {
- return;
- }
-
- if let Some(range) = higher::Range::hir(arg) {
- let left_full = match range {
- Range { start: Some(start), .. } if is_integer_const(cx, start, 0) => true,
- Range { start: None, .. } => true,
- _ => false,
- };
- let full = left_full
- && match range {
- Range {
- end: Some(end),
- limits: RangeLimits::HalfOpen,
- ..
- } => {
- // `x.drain(..x.len())` call
- if_chain! {
- if let ExprKind::MethodCall(len_path, len_args, _) = end.kind;
- if len_path.ident.name == sym::len && len_args.len() == 1;
- if let ExprKind::Path(QPath::Resolved(_, drain_path)) = recv.kind;
- if let ExprKind::Path(QPath::Resolved(_, len_path)) = len_args[0].kind;
- if SpanlessEq::new(cx).eq_path(drain_path, len_path);
- then { true }
- else { false }
- }
- },
- Range {
- end: None,
- limits: RangeLimits::HalfOpen,
- ..
- } => true,
- _ => false,
- };
- if full {
- span_lint_and_sugg(
- cx,
- ITER_WITH_DRAIN,
- span.with_hi(expr.span.hi()),
- &format!("`drain(..)` used on a `{}`", drained_type),
- "try this",
- "into_iter()".to_string(),
- Applicability::MaybeIncorrect,
- );
- }
- }
- }
+ if !matches!(recv.kind, ExprKind::Field(..))
+ && let Some(adt) = cx.typeck_results().expr_ty(recv).ty_adt_def()
+ && let Some(ty_name) = cx.tcx.get_diagnostic_name(adt.did())
+ && matches!(ty_name, sym::Vec | sym::VecDeque)
+ && let Some(range) = Range::hir(arg)
+ && is_full_range(cx, recv, range)
+ {
+ span_lint_and_sugg(
+ cx,
+ ITER_WITH_DRAIN,
+ span.with_hi(expr.span.hi()),
+ &format!("`drain(..)` used on a `{}`", ty_name),
+ "try this",
+ "into_iter()".to_string(),
+ Applicability::MaybeIncorrect,
+ );
+ };
+}
+
+fn is_full_range(cx: &LateContext<'_>, container: &Expr<'_>, range: Range<'_>) -> bool {
+ range.start.map_or(true, |e| is_integer_const(cx, e, 0))
+ && range.end.map_or(true, |e| {
+ if range.limits == RangeLimits::HalfOpen
+ && let ExprKind::Path(QPath::Resolved(None, container_path)) = container.kind
+ && let ExprKind::MethodCall(name, [self_arg], _) = e.kind
+ && name.ident.name == sym::len
+ && let ExprKind::Path(QPath::Resolved(None, path)) = self_arg.kind
+ {
+ container_path.res == path.res
+ } else {
+ false
+ }
+ })
}
diff --git a/tests/ui/iter_with_drain.fixed b/tests/ui/iter_with_drain.fixed
index aea4dba9dd5a..0330d5549264 100644
--- a/tests/ui/iter_with_drain.fixed
+++ b/tests/ui/iter_with_drain.fixed
@@ -39,6 +39,15 @@ fn should_not_help() {
let _: Vec<_> = b.drain(0..a.len()).collect();
}
+fn _closed_range(mut x: Vec) {
+ let _: Vec = x.drain(0..=x.len()).collect();
+}
+
+fn _with_mut(x: &mut Vec, y: &mut VecDeque) {
+ let _: Vec = x.drain(..).collect();
+ let _: Vec = y.drain(..).collect();
+}
+
#[derive(Default)]
struct Bomb {
fire: Vec,
diff --git a/tests/ui/iter_with_drain.rs b/tests/ui/iter_with_drain.rs
index 271878cffb44..993936fb8de3 100644
--- a/tests/ui/iter_with_drain.rs
+++ b/tests/ui/iter_with_drain.rs
@@ -39,6 +39,15 @@ fn should_not_help() {
let _: Vec<_> = b.drain(0..a.len()).collect();
}
+fn _closed_range(mut x: Vec) {
+ let _: Vec = x.drain(0..=x.len()).collect();
+}
+
+fn _with_mut(x: &mut Vec, y: &mut VecDeque) {
+ let _: Vec = x.drain(..).collect();
+ let _: Vec = y.drain(..).collect();
+}
+
#[derive(Default)]
struct Bomb {
fire: Vec,
From a9511482d6d22188e8097261c17c69dece9a198e Mon Sep 17 00:00:00 2001
From: kyoto7250 <50972773+kyoto7250@users.noreply.github.com>
Date: Sat, 9 Apr 2022 20:34:43 +0900
Subject: [PATCH 019/128] fix comments in test for split_once
split_once was stabilized in 1.52
---
tests/ui/manual_split_once.fixed | 2 +-
tests/ui/manual_split_once.rs | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/tests/ui/manual_split_once.fixed b/tests/ui/manual_split_once.fixed
index d5113df569a0..24ac364ffd9f 100644
--- a/tests/ui/manual_split_once.fixed
+++ b/tests/ui/manual_split_once.fixed
@@ -46,7 +46,7 @@ fn main() {
fn _msrv_1_51() {
#![clippy::msrv = "1.51"]
- // `str::split_once` was stabilized in 1.16. Do not lint this
+ // `str::split_once` was stabilized in 1.52. Do not lint this
let _ = "key=value".splitn(2, '=').nth(1).unwrap();
}
diff --git a/tests/ui/manual_split_once.rs b/tests/ui/manual_split_once.rs
index 80e02952dbd0..85c0c7d69778 100644
--- a/tests/ui/manual_split_once.rs
+++ b/tests/ui/manual_split_once.rs
@@ -46,7 +46,7 @@ fn main() {
fn _msrv_1_51() {
#![clippy::msrv = "1.51"]
- // `str::split_once` was stabilized in 1.16. Do not lint this
+ // `str::split_once` was stabilized in 1.52. Do not lint this
let _ = "key=value".splitn(2, '=').nth(1).unwrap();
}
From ffde78b4564301888fcb4d5b970d8e9475bd33e9 Mon Sep 17 00:00:00 2001
From: Yoav Lavi
Date: Fri, 8 Apr 2022 15:02:49 +0200
Subject: [PATCH 020/128] Allow passing --remove to `cargo dev setup
`
add missing args
unque name not needed
more descriptive help
formatting fixes
missing quote
---
clippy_dev/src/main.rs | 51 ++++++++++++++++++++++++++++++++++++------
1 file changed, 44 insertions(+), 7 deletions(-)
diff --git a/clippy_dev/src/main.rs b/clippy_dev/src/main.rs
index b1fe35a0243f..833b7393ff8b 100644
--- a/clippy_dev/src/main.rs
+++ b/clippy_dev/src/main.rs
@@ -36,13 +36,31 @@ fn main() {
}
},
("setup", Some(sub_command)) => match sub_command.subcommand() {
- ("intellij", Some(matches)) => setup::intellij::setup_rustc_src(
- matches
- .value_of("rustc-repo-path")
- .expect("this field is mandatory and therefore always valid"),
- ),
- ("git-hook", Some(matches)) => setup::git_hook::install_hook(matches.is_present("force-override")),
- ("vscode-tasks", Some(matches)) => setup::vscode::install_tasks(matches.is_present("force-override")),
+ ("intellij", Some(matches)) => {
+ if matches.is_present("remove") {
+ setup::intellij::remove_rustc_src();
+ } else {
+ setup::intellij::setup_rustc_src(
+ matches
+ .value_of("rustc-repo-path")
+ .expect("this field is mandatory and therefore always valid"),
+ );
+ }
+ },
+ ("git-hook", Some(matches)) => {
+ if matches.is_present("remove") {
+ setup::git_hook::remove_hook();
+ } else {
+ setup::git_hook::install_hook(matches.is_present("force-override"));
+ }
+ },
+ ("vscode-tasks", Some(matches)) => {
+ if matches.is_present("remove") {
+ setup::vscode::remove_tasks();
+ } else {
+ setup::vscode::install_tasks(matches.is_present("force-override"));
+ }
+ },
_ => {},
},
("remove", Some(sub_command)) => match sub_command.subcommand() {
@@ -167,6 +185,12 @@ fn get_clap_config<'a>() -> ArgMatches<'a> {
.subcommand(
SubCommand::with_name("intellij")
.about("Alter dependencies so Intellij Rust can find rustc internals")
+ .arg(
+ Arg::with_name("remove")
+ .long("remove")
+ .help("Remove the dependencies added with 'cargo dev setup intellij'")
+ .required(false),
+ )
.arg(
Arg::with_name("rustc-repo-path")
.long("repo-path")
@@ -174,12 +198,19 @@ fn get_clap_config<'a>() -> ArgMatches<'a> {
.help("The path to a rustc repo that will be used for setting the dependencies")
.takes_value(true)
.value_name("path")
+ .conflicts_with("remove")
.required(true),
),
)
.subcommand(
SubCommand::with_name("git-hook")
.about("Add a pre-commit git hook that formats your code to make it look pretty")
+ .arg(
+ Arg::with_name("remove")
+ .long("remove")
+ .help("Remove the pre-commit hook added with 'cargo dev setup git-hook'")
+ .required(false),
+ )
.arg(
Arg::with_name("force-override")
.long("force-override")
@@ -191,6 +222,12 @@ fn get_clap_config<'a>() -> ArgMatches<'a> {
.subcommand(
SubCommand::with_name("vscode-tasks")
.about("Add several tasks to vscode for formatting, validation and testing")
+ .arg(
+ Arg::with_name("remove")
+ .long("remove")
+ .help("Remove the tasks added with 'cargo dev setup vscode-tasks'")
+ .required(false),
+ )
.arg(
Arg::with_name("force-override")
.long("force-override")
From 1450c989aeb5de1959759b6ed8b7b41c703f9969 Mon Sep 17 00:00:00 2001
From: Andy Kurnia
Date: Sun, 10 Apr 2022 01:07:01 +0800
Subject: [PATCH 021/128] fix typo
---
clippy_lints/src/drop_forget_ref.rs | 2 +-
tests/ui/drop_non_drop.stderr | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/clippy_lints/src/drop_forget_ref.rs b/clippy_lints/src/drop_forget_ref.rs
index 88c54828da83..25014bfa1a5b 100644
--- a/clippy_lints/src/drop_forget_ref.rs
+++ b/clippy_lints/src/drop_forget_ref.rs
@@ -180,7 +180,7 @@ const DROP_COPY_SUMMARY: &str = "calls to `std::mem::drop` with a value that imp
const FORGET_COPY_SUMMARY: &str = "calls to `std::mem::forget` with a value that implements `Copy`. \
Forgetting a copy leaves the original intact";
const DROP_NON_DROP_SUMMARY: &str = "call to `std::mem::drop` with a value that does not implement `Drop`. \
- Dropping such a type only extends it's contained lifetimes";
+ Dropping such a type only extends its contained lifetimes";
const FORGET_NON_DROP_SUMMARY: &str = "call to `std::mem::forget` with a value that does not implement `Drop`. \
Forgetting such a type is the same as dropping it";
diff --git a/tests/ui/drop_non_drop.stderr b/tests/ui/drop_non_drop.stderr
index f73068901c50..30121033de7e 100644
--- a/tests/ui/drop_non_drop.stderr
+++ b/tests/ui/drop_non_drop.stderr
@@ -1,4 +1,4 @@
-error: call to `std::mem::drop` with a value that does not implement `Drop`. Dropping such a type only extends it's contained lifetimes
+error: call to `std::mem::drop` with a value that does not implement `Drop`. Dropping such a type only extends its contained lifetimes
--> $DIR/drop_non_drop.rs:22:5
|
LL | drop(Foo);
@@ -11,7 +11,7 @@ note: argument has type `main::Foo`
LL | drop(Foo);
| ^^^
-error: call to `std::mem::drop` with a value that does not implement `Drop`. Dropping such a type only extends it's contained lifetimes
+error: call to `std::mem::drop` with a value that does not implement `Drop`. Dropping such a type only extends its contained lifetimes
--> $DIR/drop_non_drop.rs:37:5
|
LL | drop(Baz(Foo));
From 719a040397bedd1ca1444df066acdf41758b2c9a Mon Sep 17 00:00:00 2001
From: Jason Newcomb
Date: Sun, 10 Apr 2022 00:54:41 -0400
Subject: [PATCH 022/128] Compare inline constants by their bodies rather than
value in `SpanlessEq`
---
clippy_utils/src/hir_utils.rs | 37 +++++++++++--------
tests/ui/same_functions_in_if_condition.rs | 19 ++++++++++
.../ui/same_functions_in_if_condition.stderr | 24 ++++++------
3 files changed, 53 insertions(+), 27 deletions(-)
diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs
index 00594f4d42ad..20206ce82a72 100644
--- a/clippy_utils/src/hir_utils.rs
+++ b/clippy_utils/src/hir_utils.rs
@@ -1,4 +1,4 @@
-use crate::consts::{constant_context, constant_simple};
+use crate::consts::constant_simple;
use crate::source::snippet_opt;
use rustc_ast::ast::InlineAsmTemplatePiece;
use rustc_data_structures::fx::FxHasher;
@@ -16,15 +16,14 @@ use rustc_span::Symbol;
use std::hash::{Hash, Hasher};
/// Type used to check whether two ast are the same. This is different from the
-/// operator
-/// `==` on ast types as this operator would compare true equality with ID and
-/// span.
+/// operator `==` on ast types as this operator would compare true equality with
+/// ID and span.
///
/// Note that some expressions kinds are not considered but could be added.
pub struct SpanlessEq<'a, 'tcx> {
/// Context used to evaluate constant expressions.
cx: &'a LateContext<'tcx>,
- maybe_typeck_results: Option<&'tcx TypeckResults<'tcx>>,
+ maybe_typeck_results: Option<(&'tcx TypeckResults<'tcx>, &'tcx TypeckResults<'tcx>)>,
allow_side_effects: bool,
expr_fallback: Option, &Expr<'_>) -> bool + 'a>>,
}
@@ -33,7 +32,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
pub fn new(cx: &'a LateContext<'tcx>) -> Self {
Self {
cx,
- maybe_typeck_results: cx.maybe_typeck_results(),
+ maybe_typeck_results: cx.maybe_typeck_results().map(|x| (x, x)),
allow_side_effects: true,
expr_fallback: None,
}
@@ -102,9 +101,9 @@ impl HirEqInterExpr<'_, '_, '_> {
(&StmtKind::Local(l), &StmtKind::Local(r)) => {
// This additional check ensures that the type of the locals are equivalent even if the init
// expression or type have some inferred parts.
- if let Some(typeck) = self.inner.maybe_typeck_results {
- let l_ty = typeck.pat_ty(l.pat);
- let r_ty = typeck.pat_ty(r.pat);
+ if let Some((typeck_lhs, typeck_rhs)) = self.inner.maybe_typeck_results {
+ let l_ty = typeck_lhs.pat_ty(l.pat);
+ let r_ty = typeck_rhs.pat_ty(r.pat);
if l_ty != r_ty {
return false;
}
@@ -182,9 +181,17 @@ impl HirEqInterExpr<'_, '_, '_> {
}
pub fn eq_body(&mut self, left: BodyId, right: BodyId) -> bool {
- let cx = self.inner.cx;
- let eval_const = |body| constant_context(cx, cx.tcx.typeck_body(body)).expr(&cx.tcx.hir().body(body).value);
- eval_const(left) == eval_const(right)
+ // swap out TypeckResults when hashing a body
+ let old_maybe_typeck_results = self.inner.maybe_typeck_results.replace((
+ self.inner.cx.tcx.typeck_body(left),
+ self.inner.cx.tcx.typeck_body(right),
+ ));
+ let res = self.eq_expr(
+ &self.inner.cx.tcx.hir().body(left).value,
+ &self.inner.cx.tcx.hir().body(right).value,
+ );
+ self.inner.maybe_typeck_results = old_maybe_typeck_results;
+ res
}
#[allow(clippy::similar_names)]
@@ -193,10 +200,10 @@ impl HirEqInterExpr<'_, '_, '_> {
return false;
}
- if let Some(typeck_results) = self.inner.maybe_typeck_results {
+ if let Some((typeck_lhs, typeck_rhs)) = self.inner.maybe_typeck_results {
if let (Some(l), Some(r)) = (
- constant_simple(self.inner.cx, typeck_results, left),
- constant_simple(self.inner.cx, typeck_results, right),
+ constant_simple(self.inner.cx, typeck_lhs, left),
+ constant_simple(self.inner.cx, typeck_rhs, right),
) {
if l == r {
return true;
diff --git a/tests/ui/same_functions_in_if_condition.rs b/tests/ui/same_functions_in_if_condition.rs
index 7f28f0257904..3d2295912c9f 100644
--- a/tests/ui/same_functions_in_if_condition.rs
+++ b/tests/ui/same_functions_in_if_condition.rs
@@ -1,3 +1,5 @@
+#![feature(adt_const_params)]
+#![allow(incomplete_features)]
#![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
@@ -87,4 +89,21 @@ fn main() {
"linux"
};
println!("{}", os);
+
+ #[derive(PartialEq, Eq)]
+ enum E {
+ A,
+ B,
+ }
+ fn generic() -> bool {
+ match P {
+ E::A => true,
+ E::B => false,
+ }
+ }
+ if generic::<{ E::A }>() {
+ println!("A");
+ } else if generic::<{ E::B }>() {
+ println!("B");
+ }
}
diff --git a/tests/ui/same_functions_in_if_condition.stderr b/tests/ui/same_functions_in_if_condition.stderr
index 363a03846d23..71e82910ef75 100644
--- a/tests/ui/same_functions_in_if_condition.stderr
+++ b/tests/ui/same_functions_in_if_condition.stderr
@@ -1,72 +1,72 @@
error: this `if` has the same function call as a previous `if`
- --> $DIR/same_functions_in_if_condition.rs:29:15
+ --> $DIR/same_functions_in_if_condition.rs:31: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
+ --> $DIR/same_functions_in_if_condition.rs:30: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
+ --> $DIR/same_functions_in_if_condition.rs:36:15
|
LL | } else if fn_arg(a) {
| ^^^^^^^^^
|
note: same as this
- --> $DIR/same_functions_in_if_condition.rs:33:8
+ --> $DIR/same_functions_in_if_condition.rs:35: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
+ --> $DIR/same_functions_in_if_condition.rs:41:15
|
LL | } else if obj.method() {
| ^^^^^^^^^^^^
|
note: same as this
- --> $DIR/same_functions_in_if_condition.rs:38:8
+ --> $DIR/same_functions_in_if_condition.rs:40: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
+ --> $DIR/same_functions_in_if_condition.rs:46:15
|
LL | } else if obj.method_arg(a) {
| ^^^^^^^^^^^^^^^^^
|
note: same as this
- --> $DIR/same_functions_in_if_condition.rs:43:8
+ --> $DIR/same_functions_in_if_condition.rs:45: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
+ --> $DIR/same_functions_in_if_condition.rs:53:15
|
LL | } else if v.pop() == None {
| ^^^^^^^^^^^^^^^
|
note: same as this
- --> $DIR/same_functions_in_if_condition.rs:49:8
+ --> $DIR/same_functions_in_if_condition.rs:51: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
+ --> $DIR/same_functions_in_if_condition.rs:58:15
|
LL | } else if v.len() == 42 {
| ^^^^^^^^^^^^^
|
note: same as this
- --> $DIR/same_functions_in_if_condition.rs:54:8
+ --> $DIR/same_functions_in_if_condition.rs:56:8
|
LL | if v.len() == 42 {
| ^^^^^^^^^^^^^
From 5e335a52bc49991f6ffd40030b9948f3390f499d Mon Sep 17 00:00:00 2001
From: Alex Macleod
Date: Sun, 10 Apr 2022 14:02:58 +0100
Subject: [PATCH 023/128] Check for loops/closures in `local_used_after_expr`
---
clippy_lints/src/eta_reduction.rs | 5 ++--
clippy_utils/src/usage.rs | 33 +++++++++++++++++++++++--
tests/ui/eta.fixed | 4 +++
tests/ui/eta.rs | 4 +++
tests/ui/eta.stderr | 10 ++++++--
tests/ui/needless_option_as_deref.fixed | 14 +++++++++++
tests/ui/needless_option_as_deref.rs | 14 +++++++++++
7 files changed, 77 insertions(+), 7 deletions(-)
diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs
index 845863bd209c..c2e32f1d9a23 100644
--- a/clippy_lints/src/eta_reduction.rs
+++ b/clippy_lints/src/eta_reduction.rs
@@ -3,7 +3,7 @@ use clippy_utils::higher::VecArgs;
use clippy_utils::source::snippet_opt;
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::usage::local_used_after_expr;
-use clippy_utils::{get_enclosing_loop_or_closure, higher, path_to_local, path_to_local_id};
+use clippy_utils::{higher, path_to_local, path_to_local_id};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::def_id::DefId;
@@ -125,8 +125,7 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction {
if_chain! {
if let ty::Closure(_, substs) = callee_ty.peel_refs().kind();
if substs.as_closure().kind() == ClosureKind::FnMut;
- if get_enclosing_loop_or_closure(cx.tcx, expr).is_some()
- || path_to_local(callee).map_or(false, |l| local_used_after_expr(cx, l, callee));
+ if path_to_local(callee).map_or(false, |l| local_used_after_expr(cx, l, expr));
then {
// Mutable closure is used after current expr; we cannot consume it.
diff --git a/clippy_utils/src/usage.rs b/clippy_utils/src/usage.rs
index 405e306359bc..4236e3aae2fb 100644
--- a/clippy_utils/src/usage.rs
+++ b/clippy_utils/src/usage.rs
@@ -3,7 +3,7 @@ use crate::visitors::{expr_visitor, expr_visitor_no_bodies};
use rustc_hir as hir;
use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::HirIdSet;
-use rustc_hir::{Expr, ExprKind, HirId};
+use rustc_hir::{Expr, ExprKind, HirId, Node};
use rustc_infer::infer::TyCtxtInferExt;
use rustc_lint::LateContext;
use rustc_middle::hir::nested_filter;
@@ -169,6 +169,32 @@ pub fn contains_return_break_continue_macro(expression: &Expr<'_>) -> bool {
pub fn local_used_after_expr(cx: &LateContext<'_>, local_id: HirId, after: &Expr<'_>) -> bool {
let Some(block) = utils::get_enclosing_block(cx, local_id) else { return false };
+
+ // for _ in 1..3 {
+ // local
+ // }
+ //
+ // let closure = || local;
+ // closure();
+ // closure();
+ let in_loop_or_closure = cx
+ .tcx
+ .hir()
+ .parent_iter(after.hir_id)
+ .take_while(|&(id, _)| id != block.hir_id)
+ .any(|(_, node)| {
+ matches!(
+ node,
+ Node::Expr(Expr {
+ kind: ExprKind::Loop(..) | ExprKind::Closure(..),
+ ..
+ })
+ )
+ });
+ if in_loop_or_closure {
+ return true;
+ }
+
let mut used_after_expr = false;
let mut past_expr = false;
expr_visitor(cx, |expr| {
@@ -178,7 +204,10 @@ pub fn local_used_after_expr(cx: &LateContext<'_>, local_id: HirId, after: &Expr
if expr.hir_id == after.hir_id {
past_expr = true;
- } else if past_expr && utils::path_to_local_id(expr, local_id) {
+ return false;
+ }
+
+ if past_expr && utils::path_to_local_id(expr, local_id) {
used_after_expr = true;
}
!used_after_expr
diff --git a/tests/ui/eta.fixed b/tests/ui/eta.fixed
index 5aedbea381f2..873ed689a5be 100644
--- a/tests/ui/eta.fixed
+++ b/tests/ui/eta.fixed
@@ -211,6 +211,10 @@ fn mutable_closure_in_loop() {
let mut closure = |n| value += n;
for _ in 0..5 {
Some(1).map(&mut closure);
+
+ let mut value = 0;
+ let mut in_loop = |n| value += n;
+ Some(1).map(in_loop);
}
}
diff --git a/tests/ui/eta.rs b/tests/ui/eta.rs
index 5fdf7fb97716..4cb58eec94cd 100644
--- a/tests/ui/eta.rs
+++ b/tests/ui/eta.rs
@@ -211,6 +211,10 @@ fn mutable_closure_in_loop() {
let mut closure = |n| value += n;
for _ in 0..5 {
Some(1).map(|n| closure(n));
+
+ let mut value = 0;
+ let mut in_loop = |n| value += n;
+ Some(1).map(|n| in_loop(n));
}
}
diff --git a/tests/ui/eta.stderr b/tests/ui/eta.stderr
index cda84982c9b7..d1ae889d6f36 100644
--- a/tests/ui/eta.stderr
+++ b/tests/ui/eta.stderr
@@ -117,10 +117,16 @@ LL | Some(1).map(|n| closure(n));
| ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut closure`
error: redundant closure
- --> $DIR/eta.rs:232:21
+ --> $DIR/eta.rs:217:21
+ |
+LL | Some(1).map(|n| in_loop(n));
+ | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `in_loop`
+
+error: redundant closure
+ --> $DIR/eta.rs:236:21
|
LL | map_str_to_path(|s| s.as_ref());
| ^^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::convert::AsRef::as_ref`
-error: aborting due to 20 previous errors
+error: aborting due to 21 previous errors
diff --git a/tests/ui/needless_option_as_deref.fixed b/tests/ui/needless_option_as_deref.fixed
index c09b07db3dca..acd22c6bb433 100644
--- a/tests/ui/needless_option_as_deref.fixed
+++ b/tests/ui/needless_option_as_deref.fixed
@@ -16,6 +16,20 @@ fn main() {
let _ = Some(Box::new(1)).as_deref();
let _ = Some(Box::new(1)).as_deref_mut();
+ let mut y = 0;
+ let mut x = Some(&mut y);
+ for _ in 0..3 {
+ let _ = x.as_deref_mut();
+ }
+
+ let mut y = 0;
+ let mut x = Some(&mut y);
+ let mut closure = || {
+ let _ = x.as_deref_mut();
+ };
+ closure();
+ closure();
+
// #7846
let mut i = 0;
let mut opt_vec = vec![Some(&mut i)];
diff --git a/tests/ui/needless_option_as_deref.rs b/tests/ui/needless_option_as_deref.rs
index c3ba27ecccf2..61eda5052a21 100644
--- a/tests/ui/needless_option_as_deref.rs
+++ b/tests/ui/needless_option_as_deref.rs
@@ -16,6 +16,20 @@ fn main() {
let _ = Some(Box::new(1)).as_deref();
let _ = Some(Box::new(1)).as_deref_mut();
+ let mut y = 0;
+ let mut x = Some(&mut y);
+ for _ in 0..3 {
+ let _ = x.as_deref_mut();
+ }
+
+ let mut y = 0;
+ let mut x = Some(&mut y);
+ let mut closure = || {
+ let _ = x.as_deref_mut();
+ };
+ closure();
+ closure();
+
// #7846
let mut i = 0;
let mut opt_vec = vec![Some(&mut i)];
From 6fba89751bfacf59109d37331ec79a8f87ee0aaf Mon Sep 17 00:00:00 2001
From: Alex Macleod
Date: Mon, 4 Apr 2022 16:30:38 +0100
Subject: [PATCH 024/128] Remove overlap between `manual_split_once` and
`needless_splitn`
Also fixes some incorrect suggestions for rsplitn
---
clippy_lints/src/methods/mod.rs | 7 +-
clippy_lints/src/methods/str_splitn.rs | 246 ++++++++-----------------
tests/ui/crashes/ice-8250.stderr | 10 +-
tests/ui/manual_split_once.fixed | 21 +--
tests/ui/manual_split_once.rs | 21 +--
tests/ui/manual_split_once.stderr | 96 +++++-----
tests/ui/needless_splitn.fixed | 20 ++
tests/ui/needless_splitn.rs | 20 ++
tests/ui/needless_splitn.stderr | 44 ++++-
9 files changed, 221 insertions(+), 264 deletions(-)
diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs
index 021337280d13..7047aa4c69b5 100644
--- a/clippy_lints/src/methods/mod.rs
+++ b/clippy_lints/src/methods/mod.rs
@@ -2516,12 +2516,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
("splitn" | "rsplitn", [count_arg, pat_arg]) => {
if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) {
suspicious_splitn::check(cx, name, expr, recv, count);
- if count == 2 && meets_msrv(msrv, &msrvs::STR_SPLIT_ONCE) {
- str_splitn::check_manual_split_once(cx, name, expr, recv, pat_arg);
- }
- if count >= 2 {
- str_splitn::check_needless_splitn(cx, name, expr, recv, pat_arg, count);
- }
+ str_splitn::check(cx, name, expr, recv, pat_arg, count, msrv);
}
},
("splitn_mut" | "rsplitn_mut", [count_arg, _]) => {
diff --git a/clippy_lints/src/methods/str_splitn.rs b/clippy_lints/src/methods/str_splitn.rs
index 8125930b3046..6eaa9eb2ef0e 100644
--- a/clippy_lints/src/methods/str_splitn.rs
+++ b/clippy_lints/src/methods/str_splitn.rs
@@ -1,36 +1,77 @@
use clippy_utils::consts::{constant, Constant};
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_context;
-use clippy_utils::{is_diag_item_method, match_def_path, paths};
+use clippy_utils::{is_diag_item_method, match_def_path, meets_msrv, msrvs, paths};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, HirId, LangItem, Node, QPath};
use rustc_lint::LateContext;
-use rustc_middle::ty::{self, adjustment::Adjust};
+use rustc_middle::ty;
+use rustc_semver::RustcVersion;
use rustc_span::{symbol::sym, Span, SyntaxContext};
-use super::MANUAL_SPLIT_ONCE;
+use super::{MANUAL_SPLIT_ONCE, NEEDLESS_SPLITN};
-pub(super) fn check_manual_split_once(
+pub(super) fn check(
cx: &LateContext<'_>,
method_name: &str,
expr: &Expr<'_>,
self_arg: &Expr<'_>,
pat_arg: &Expr<'_>,
+ count: u128,
+ msrv: Option<&RustcVersion>,
) {
- if !cx.typeck_results().expr_ty_adjusted(self_arg).peel_refs().is_str() {
+ if count < 2 || !cx.typeck_results().expr_ty_adjusted(self_arg).peel_refs().is_str() {
return;
}
let ctxt = expr.span.ctxt();
- let (method_name, msg, reverse) = if method_name == "splitn" {
- ("split_once", "manual implementation of `split_once`", false)
- } else {
- ("rsplit_once", "manual implementation of `rsplit_once`", true)
+ let Some(usage) = parse_iter_usage(cx, ctxt, cx.tcx.hir().parent_iter(expr.hir_id)) else { return };
+
+ let needless = match usage.kind {
+ IterUsageKind::Nth(n) => count > n + 1,
+ IterUsageKind::NextTuple => count > 2,
};
- let usage = match parse_iter_usage(cx, ctxt, cx.tcx.hir().parent_iter(expr.hir_id), reverse) {
- Some(x) => x,
- None => return,
+
+ if needless {
+ let mut app = Applicability::MachineApplicable;
+ let (r, message) = if method_name == "splitn" {
+ ("", "unnecessary use of `splitn`")
+ } else {
+ ("r", "unnecessary use of `rsplitn`")
+ };
+
+ span_lint_and_sugg(
+ cx,
+ NEEDLESS_SPLITN,
+ expr.span,
+ message,
+ "try this",
+ format!(
+ "{}.{r}split({})",
+ snippet_with_context(cx, self_arg.span, ctxt, "..", &mut app).0,
+ snippet_with_context(cx, pat_arg.span, ctxt, "..", &mut app).0,
+ ),
+ app,
+ );
+ } else if count == 2 && meets_msrv(msrv, &msrvs::STR_SPLIT_ONCE) {
+ check_manual_split_once(cx, method_name, expr, self_arg, pat_arg, &usage);
+ }
+}
+
+fn check_manual_split_once(
+ cx: &LateContext<'_>,
+ method_name: &str,
+ expr: &Expr<'_>,
+ self_arg: &Expr<'_>,
+ pat_arg: &Expr<'_>,
+ usage: &IterUsage,
+) {
+ let ctxt = expr.span.ctxt();
+ let (msg, reverse) = if method_name == "splitn" {
+ ("manual implementation of `split_once`", false)
+ } else {
+ ("manual implementation of `rsplit_once`", true)
};
let mut app = Applicability::MachineApplicable;
@@ -39,77 +80,36 @@ pub(super) fn check_manual_split_once(
let sugg = match usage.kind {
IterUsageKind::NextTuple => {
- format!("{}.{}({})", self_snip, method_name, pat_snip)
- },
- IterUsageKind::RNextTuple => format!("{}.{}({}).map(|(x, y)| (y, x))", self_snip, method_name, pat_snip),
- IterUsageKind::Next | IterUsageKind::Second => {
- let self_deref = {
- let adjust = cx.typeck_results().expr_adjustments(self_arg);
- if adjust.len() < 2 {
- String::new()
- } else if cx.typeck_results().expr_ty(self_arg).is_box()
- || adjust
- .iter()
- .any(|a| matches!(a.kind, Adjust::Deref(Some(_))) || a.target.is_box())
- {
- format!("&{}", "*".repeat(adjust.len().saturating_sub(1)))
- } else {
- "*".repeat(adjust.len().saturating_sub(2))
- }
- };
- if matches!(usage.kind, IterUsageKind::Next) {
- match usage.unwrap_kind {
- Some(UnwrapKind::Unwrap) => {
- if reverse {
- format!("{}.{}({}).unwrap().0", self_snip, method_name, pat_snip)
- } else {
- format!(
- "{}.{}({}).map_or({}{}, |x| x.0)",
- self_snip, method_name, pat_snip, self_deref, &self_snip
- )
- }
- },
- Some(UnwrapKind::QuestionMark) => {
- format!(
- "{}.{}({}).map_or({}{}, |x| x.0)",
- self_snip, method_name, pat_snip, self_deref, &self_snip
- )
- },
- None => {
- format!(
- "Some({}.{}({}).map_or({}{}, |x| x.0))",
- &self_snip, method_name, pat_snip, self_deref, &self_snip
- )
- },
- }
+ if reverse {
+ format!("{self_snip}.rsplit_once({pat_snip}).map(|(x, y)| (y, x))")
} else {
- match usage.unwrap_kind {
- Some(UnwrapKind::Unwrap) => {
- if reverse {
- // In this case, no better suggestion is offered.
- return;
- }
- format!("{}.{}({}).unwrap().1", self_snip, method_name, pat_snip)
- },
- Some(UnwrapKind::QuestionMark) => {
- format!("{}.{}({})?.1", self_snip, method_name, pat_snip)
- },
- None => {
- format!("{}.{}({}).map(|x| x.1)", self_snip, method_name, pat_snip)
- },
- }
+ format!("{self_snip}.split_once({pat_snip})")
}
},
+ IterUsageKind::Nth(1) => {
+ let (r, field) = if reverse { ("r", 0) } else { ("", 1) };
+
+ match usage.unwrap_kind {
+ Some(UnwrapKind::Unwrap) => {
+ format!("{self_snip}.{r}split_once({pat_snip}).unwrap().{field}")
+ },
+ Some(UnwrapKind::QuestionMark) => {
+ format!("{self_snip}.{r}split_once({pat_snip})?.{field}")
+ },
+ None => {
+ format!("{self_snip}.{r}split_once({pat_snip}).map(|x| x.{field})")
+ },
+ }
+ },
+ IterUsageKind::Nth(_) => return,
};
span_lint_and_sugg(cx, MANUAL_SPLIT_ONCE, usage.span, msg, "try this", sugg, app);
}
enum IterUsageKind {
- Next,
- Second,
+ Nth(u128),
NextTuple,
- RNextTuple,
}
enum UnwrapKind {
@@ -128,7 +128,6 @@ fn parse_iter_usage<'tcx>(
cx: &LateContext<'tcx>,
ctxt: SyntaxContext,
mut iter: impl Iterator- )>,
- reverse: bool,
) -> Option
{
let (kind, span) = match iter.next() {
Some((_, Node::Expr(e))) if e.span.ctxt() == ctxt => {
@@ -141,13 +140,7 @@ fn parse_iter_usage<'tcx>(
let iter_id = cx.tcx.get_diagnostic_item(sym::Iterator)?;
match (name.ident.as_str(), args) {
- ("next", []) if cx.tcx.trait_of_item(did) == Some(iter_id) => {
- if reverse {
- (IterUsageKind::Second, e.span)
- } else {
- (IterUsageKind::Next, e.span)
- }
- },
+ ("next", []) if cx.tcx.trait_of_item(did) == Some(iter_id) => (IterUsageKind::Nth(0), e.span),
("next_tuple", []) => {
return if_chain! {
if match_def_path(cx, did, &paths::ITERTOOLS_NEXT_TUPLE);
@@ -157,7 +150,7 @@ fn parse_iter_usage<'tcx>(
if subs.len() == 2;
then {
Some(IterUsage {
- kind: if reverse { IterUsageKind::RNextTuple } else { IterUsageKind::NextTuple },
+ kind: IterUsageKind::NextTuple,
span: e.span,
unwrap_kind: None
})
@@ -185,11 +178,7 @@ fn parse_iter_usage<'tcx>(
}
}
};
- match if reverse { idx ^ 1 } else { idx } {
- 0 => (IterUsageKind::Next, span),
- 1 => (IterUsageKind::Second, span),
- _ => return None,
- }
+ (IterUsageKind::Nth(idx), span)
} else {
return None;
}
@@ -238,86 +227,3 @@ fn parse_iter_usage<'tcx>(
span,
})
}
-
-use super::NEEDLESS_SPLITN;
-
-pub(super) fn check_needless_splitn(
- cx: &LateContext<'_>,
- method_name: &str,
- expr: &Expr<'_>,
- self_arg: &Expr<'_>,
- pat_arg: &Expr<'_>,
- count: u128,
-) {
- if !cx.typeck_results().expr_ty_adjusted(self_arg).peel_refs().is_str() {
- return;
- }
- let ctxt = expr.span.ctxt();
- let mut app = Applicability::MachineApplicable;
- let (reverse, message) = if method_name == "splitn" {
- (false, "unnecessary use of `splitn`")
- } else {
- (true, "unnecessary use of `rsplitn`")
- };
- if_chain! {
- if count >= 2;
- if check_iter(cx, ctxt, cx.tcx.hir().parent_iter(expr.hir_id), count);
- then {
- span_lint_and_sugg(
- cx,
- NEEDLESS_SPLITN,
- expr.span,
- message,
- "try this",
- format!(
- "{}.{}({})",
- snippet_with_context(cx, self_arg.span, ctxt, "..", &mut app).0,
- if reverse {"rsplit"} else {"split"},
- snippet_with_context(cx, pat_arg.span, ctxt, "..", &mut app).0
- ),
- app,
- );
- }
- }
-}
-
-fn check_iter<'tcx>(
- cx: &LateContext<'tcx>,
- ctxt: SyntaxContext,
- mut iter: impl Iterator- )>,
- count: u128,
-) -> bool {
- match iter.next() {
- Some((_, Node::Expr(e))) if e.span.ctxt() == ctxt => {
- let (name, args) = if let ExprKind::MethodCall(name, [_, args @ ..], _) = e.kind {
- (name, args)
- } else {
- return false;
- };
- if_chain! {
- if let Some(did) = cx.typeck_results().type_dependent_def_id(e.hir_id);
- if let Some(iter_id) = cx.tcx.get_diagnostic_item(sym::Iterator);
- then {
- match (name.ident.as_str(), args) {
- ("next", []) if cx.tcx.trait_of_item(did) == Some(iter_id) => {
- return true;
- },
- ("next_tuple", []) if count > 2 => {
- return true;
- },
- ("nth", [idx_expr]) if cx.tcx.trait_of_item(did) == Some(iter_id) => {
- if let Some((Constant::Int(idx), _)) = constant(cx, cx.typeck_results(), idx_expr) {
- if count > idx + 1 {
- return true;
- }
- }
- },
- _ => return false,
- }
- }
- }
- },
- _ => return false,
- };
- false
-}
diff --git a/tests/ui/crashes/ice-8250.stderr b/tests/ui/crashes/ice-8250.stderr
index 04ea44566565..8ed8f3b3a064 100644
--- a/tests/ui/crashes/ice-8250.stderr
+++ b/tests/ui/crashes/ice-8250.stderr
@@ -1,11 +1,3 @@
-error: manual implementation of `split_once`
- --> $DIR/ice-8250.rs:2:13
- |
-LL | let _ = s[1..].splitn(2, '.').next()?;
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s[1..].split_once('.').map_or(s[1..], |x| x.0)`
- |
- = note: `-D clippy::manual-split-once` implied by `-D warnings`
-
error: unnecessary use of `splitn`
--> $DIR/ice-8250.rs:2:13
|
@@ -14,5 +6,5 @@ LL | let _ = s[1..].splitn(2, '.').next()?;
|
= note: `-D clippy::needless-splitn` implied by `-D warnings`
-error: aborting due to 2 previous errors
+error: aborting due to previous error
diff --git a/tests/ui/manual_split_once.fixed b/tests/ui/manual_split_once.fixed
index d5113df569a0..8621ddc74672 100644
--- a/tests/ui/manual_split_once.fixed
+++ b/tests/ui/manual_split_once.fixed
@@ -2,7 +2,7 @@
#![feature(custom_inner_attributes)]
#![warn(clippy::manual_split_once)]
-#![allow(clippy::iter_skip_next, clippy::iter_nth_zero, clippy::needless_splitn)]
+#![allow(clippy::iter_skip_next, clippy::iter_nth_zero)]
extern crate itertools;
@@ -10,27 +10,25 @@ extern crate itertools;
use itertools::Itertools;
fn main() {
- let _ = Some("key=value".split_once('=').map_or("key=value", |x| x.0));
let _ = "key=value".splitn(2, '=').nth(2);
- let _ = "key=value".split_once('=').map_or("key=value", |x| x.0);
- let _ = "key=value".split_once('=').map_or("key=value", |x| x.0);
let _ = "key=value".split_once('=').unwrap().1;
let _ = "key=value".split_once('=').unwrap().1;
let (_, _) = "key=value".split_once('=').unwrap();
let s = String::from("key=value");
- let _ = s.split_once('=').map_or(&*s, |x| x.0);
+ let _ = s.split_once('=').unwrap().1;
let s = Box::
::from("key=value");
- let _ = s.split_once('=').map_or(&*s, |x| x.0);
+ let _ = s.split_once('=').unwrap().1;
let s = &"key=value";
- let _ = s.split_once('=').map_or(*s, |x| x.0);
+ let _ = s.split_once('=').unwrap().1;
fn _f(s: &str) -> Option<&str> {
- let _ = s.split_once("key=value").map_or(s, |x| x.0);
- let _ = s.split_once("key=value")?.1;
- let _ = s.split_once("key=value")?.1;
+ let _ = s.split_once('=')?.1;
+ let _ = s.split_once('=')?.1;
+ let _ = s.rsplit_once('=')?.0;
+ let _ = s.rsplit_once('=')?.0;
None
}
@@ -38,10 +36,9 @@ fn main() {
let _ = [0, 1, 2].splitn(2, |&x| x == 1).nth(1).unwrap();
// `rsplitn` gives the results in the reverse order of `rsplit_once`
- let _ = "key=value".rsplitn(2, '=').next().unwrap();
let _ = "key=value".rsplit_once('=').unwrap().0;
- let _ = "key=value".rsplit_once('=').map(|x| x.1);
let (_, _) = "key=value".rsplit_once('=').map(|(x, y)| (y, x)).unwrap();
+ let _ = s.rsplit_once('=').map(|x| x.0);
}
fn _msrv_1_51() {
diff --git a/tests/ui/manual_split_once.rs b/tests/ui/manual_split_once.rs
index 80e02952dbd0..c8a1d7b91e5d 100644
--- a/tests/ui/manual_split_once.rs
+++ b/tests/ui/manual_split_once.rs
@@ -2,7 +2,7 @@
#![feature(custom_inner_attributes)]
#![warn(clippy::manual_split_once)]
-#![allow(clippy::iter_skip_next, clippy::iter_nth_zero, clippy::needless_splitn)]
+#![allow(clippy::iter_skip_next, clippy::iter_nth_zero)]
extern crate itertools;
@@ -10,27 +10,25 @@ extern crate itertools;
use itertools::Itertools;
fn main() {
- let _ = "key=value".splitn(2, '=').next();
let _ = "key=value".splitn(2, '=').nth(2);
- let _ = "key=value".splitn(2, '=').next().unwrap();
- let _ = "key=value".splitn(2, '=').nth(0).unwrap();
let _ = "key=value".splitn(2, '=').nth(1).unwrap();
let _ = "key=value".splitn(2, '=').skip(1).next().unwrap();
let (_, _) = "key=value".splitn(2, '=').next_tuple().unwrap();
let s = String::from("key=value");
- let _ = s.splitn(2, '=').next().unwrap();
+ let _ = s.splitn(2, '=').nth(1).unwrap();
let s = Box::::from("key=value");
- let _ = s.splitn(2, '=').nth(0).unwrap();
+ let _ = s.splitn(2, '=').nth(1).unwrap();
let s = &"key=value";
- let _ = s.splitn(2, '=').skip(0).next().unwrap();
+ let _ = s.splitn(2, '=').skip(1).next().unwrap();
fn _f(s: &str) -> Option<&str> {
- let _ = s.splitn(2, "key=value").next()?;
- let _ = s.splitn(2, "key=value").nth(1)?;
- let _ = s.splitn(2, "key=value").skip(1).next()?;
+ let _ = s.splitn(2, '=').nth(1)?;
+ let _ = s.splitn(2, '=').skip(1).next()?;
+ let _ = s.rsplitn(2, '=').nth(1)?;
+ let _ = s.rsplitn(2, '=').skip(1).next()?;
None
}
@@ -38,10 +36,9 @@ fn main() {
let _ = [0, 1, 2].splitn(2, |&x| x == 1).nth(1).unwrap();
// `rsplitn` gives the results in the reverse order of `rsplit_once`
- let _ = "key=value".rsplitn(2, '=').next().unwrap();
let _ = "key=value".rsplitn(2, '=').nth(1).unwrap();
- let _ = "key=value".rsplitn(2, '=').nth(0);
let (_, _) = "key=value".rsplitn(2, '=').next_tuple().unwrap();
+ let _ = s.rsplitn(2, '=').nth(1);
}
fn _msrv_1_51() {
diff --git a/tests/ui/manual_split_once.stderr b/tests/ui/manual_split_once.stderr
index af9c7a2d41bf..9635e6c5e4b4 100644
--- a/tests/ui/manual_split_once.stderr
+++ b/tests/ui/manual_split_once.stderr
@@ -1,100 +1,88 @@
error: manual implementation of `split_once`
- --> $DIR/manual_split_once.rs:13:13
+ --> $DIR/manual_split_once.rs:14:13
|
-LL | let _ = "key=value".splitn(2, '=').next();
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `Some("key=value".split_once('=').map_or("key=value", |x| x.0))`
+LL | let _ = "key=value".splitn(2, '=').nth(1).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1`
|
= note: `-D clippy::manual-split-once` implied by `-D warnings`
error: manual implementation of `split_once`
--> $DIR/manual_split_once.rs:15:13
|
-LL | let _ = "key=value".splitn(2, '=').next().unwrap();
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').map_or("key=value", |x| x.0)`
-
-error: manual implementation of `split_once`
- --> $DIR/manual_split_once.rs:16:13
- |
-LL | let _ = "key=value".splitn(2, '=').nth(0).unwrap();
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').map_or("key=value", |x| x.0)`
-
-error: manual implementation of `split_once`
- --> $DIR/manual_split_once.rs:17:13
- |
-LL | let _ = "key=value".splitn(2, '=').nth(1).unwrap();
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1`
-
-error: manual implementation of `split_once`
- --> $DIR/manual_split_once.rs:18:13
- |
LL | let _ = "key=value".splitn(2, '=').skip(1).next().unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1`
error: manual implementation of `split_once`
- --> $DIR/manual_split_once.rs:19:18
+ --> $DIR/manual_split_once.rs:16:18
|
LL | let (_, _) = "key=value".splitn(2, '=').next_tuple().unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=')`
+error: manual implementation of `split_once`
+ --> $DIR/manual_split_once.rs:19:13
+ |
+LL | let _ = s.splitn(2, '=').nth(1).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').unwrap().1`
+
error: manual implementation of `split_once`
--> $DIR/manual_split_once.rs:22:13
|
-LL | let _ = s.splitn(2, '=').next().unwrap();
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').map_or(&*s, |x| x.0)`
+LL | let _ = s.splitn(2, '=').nth(1).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').unwrap().1`
error: manual implementation of `split_once`
--> $DIR/manual_split_once.rs:25:13
|
-LL | let _ = s.splitn(2, '=').nth(0).unwrap();
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').map_or(&*s, |x| x.0)`
+LL | let _ = s.splitn(2, '=').skip(1).next().unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').unwrap().1`
error: manual implementation of `split_once`
- --> $DIR/manual_split_once.rs:28:13
+ --> $DIR/manual_split_once.rs:28:17
|
-LL | let _ = s.splitn(2, '=').skip(0).next().unwrap();
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').map_or(*s, |x| x.0)`
+LL | let _ = s.splitn(2, '=').nth(1)?;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=')?.1`
error: manual implementation of `split_once`
- --> $DIR/manual_split_once.rs:31:17
+ --> $DIR/manual_split_once.rs:29:17
|
-LL | let _ = s.splitn(2, "key=value").next()?;
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once("key=value").map_or(s, |x| x.0)`
-
-error: manual implementation of `split_once`
- --> $DIR/manual_split_once.rs:32:17
- |
-LL | let _ = s.splitn(2, "key=value").nth(1)?;
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once("key=value")?.1`
-
-error: manual implementation of `split_once`
- --> $DIR/manual_split_once.rs:33:17
- |
-LL | let _ = s.splitn(2, "key=value").skip(1).next()?;
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once("key=value")?.1`
+LL | let _ = s.splitn(2, '=').skip(1).next()?;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=')?.1`
error: manual implementation of `rsplit_once`
- --> $DIR/manual_split_once.rs:42:13
+ --> $DIR/manual_split_once.rs:30:17
+ |
+LL | let _ = s.rsplitn(2, '=').nth(1)?;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit_once('=')?.0`
+
+error: manual implementation of `rsplit_once`
+ --> $DIR/manual_split_once.rs:31:17
+ |
+LL | let _ = s.rsplitn(2, '=').skip(1).next()?;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit_once('=')?.0`
+
+error: manual implementation of `rsplit_once`
+ --> $DIR/manual_split_once.rs:39:13
|
LL | let _ = "key=value".rsplitn(2, '=').nth(1).unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').unwrap().0`
error: manual implementation of `rsplit_once`
- --> $DIR/manual_split_once.rs:43:13
- |
-LL | let _ = "key=value".rsplitn(2, '=').nth(0);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').map(|x| x.1)`
-
-error: manual implementation of `rsplit_once`
- --> $DIR/manual_split_once.rs:44:18
+ --> $DIR/manual_split_once.rs:40:18
|
LL | let (_, _) = "key=value".rsplitn(2, '=').next_tuple().unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').map(|(x, y)| (y, x))`
+error: manual implementation of `rsplit_once`
+ --> $DIR/manual_split_once.rs:41:13
+ |
+LL | let _ = s.rsplitn(2, '=').nth(1);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit_once('=').map(|x| x.0)`
+
error: manual implementation of `split_once`
- --> $DIR/manual_split_once.rs:55:13
+ --> $DIR/manual_split_once.rs:52:13
|
LL | let _ = "key=value".splitn(2, '=').nth(1).unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1`
-error: aborting due to 16 previous errors
+error: aborting due to 14 previous errors
diff --git a/tests/ui/needless_splitn.fixed b/tests/ui/needless_splitn.fixed
index f6a4b2f17d33..61f5fc4e679e 100644
--- a/tests/ui/needless_splitn.fixed
+++ b/tests/ui/needless_splitn.fixed
@@ -24,4 +24,24 @@ fn main() {
let _ = str.rsplitn(2, '=').nth(1);
let (_, _) = str.rsplitn(2, '=').next_tuple().unwrap();
let (_, _) = str.rsplit('=').next_tuple().unwrap();
+
+ let _ = str.split('=').next();
+ let _ = str.split('=').nth(3);
+ let _ = str.splitn(5, '=').nth(4);
+ let _ = str.splitn(5, '=').nth(5);
+}
+
+fn _question_mark(s: &str) -> Option<()> {
+ let _ = s.split('=').next()?;
+ let _ = s.split('=').nth(0)?;
+ let _ = s.rsplit('=').next()?;
+ let _ = s.rsplit('=').nth(0)?;
+
+ Some(())
+}
+
+fn _test_msrv() {
+ #![clippy::msrv = "1.51"]
+ // `manual_split_once` MSRV shouldn't apply to `needless_splitn`
+ let _ = "key=value".split('=').nth(0).unwrap();
}
diff --git a/tests/ui/needless_splitn.rs b/tests/ui/needless_splitn.rs
index 6ba32255bb2d..71d9a7077faa 100644
--- a/tests/ui/needless_splitn.rs
+++ b/tests/ui/needless_splitn.rs
@@ -24,4 +24,24 @@ fn main() {
let _ = str.rsplitn(2, '=').nth(1);
let (_, _) = str.rsplitn(2, '=').next_tuple().unwrap();
let (_, _) = str.rsplitn(3, '=').next_tuple().unwrap();
+
+ let _ = str.splitn(5, '=').next();
+ let _ = str.splitn(5, '=').nth(3);
+ let _ = str.splitn(5, '=').nth(4);
+ let _ = str.splitn(5, '=').nth(5);
+}
+
+fn _question_mark(s: &str) -> Option<()> {
+ let _ = s.splitn(2, '=').next()?;
+ let _ = s.splitn(2, '=').nth(0)?;
+ let _ = s.rsplitn(2, '=').next()?;
+ let _ = s.rsplitn(2, '=').nth(0)?;
+
+ Some(())
+}
+
+fn _test_msrv() {
+ #![clippy::msrv = "1.51"]
+ // `manual_split_once` MSRV shouldn't apply to `needless_splitn`
+ let _ = "key=value".splitn(2, '=').nth(0).unwrap();
}
diff --git a/tests/ui/needless_splitn.stderr b/tests/ui/needless_splitn.stderr
index 66de2256554e..f112b29e7f20 100644
--- a/tests/ui/needless_splitn.stderr
+++ b/tests/ui/needless_splitn.stderr
@@ -36,5 +36,47 @@ error: unnecessary use of `rsplitn`
LL | let (_, _) = str.rsplitn(3, '=').next_tuple().unwrap();
| ^^^^^^^^^^^^^^^^^^^ help: try this: `str.rsplit('=')`
-error: aborting due to 6 previous errors
+error: unnecessary use of `splitn`
+ --> $DIR/needless_splitn.rs:28:13
+ |
+LL | let _ = str.splitn(5, '=').next();
+ | ^^^^^^^^^^^^^^^^^^ help: try this: `str.split('=')`
+
+error: unnecessary use of `splitn`
+ --> $DIR/needless_splitn.rs:29:13
+ |
+LL | let _ = str.splitn(5, '=').nth(3);
+ | ^^^^^^^^^^^^^^^^^^ help: try this: `str.split('=')`
+
+error: unnecessary use of `splitn`
+ --> $DIR/needless_splitn.rs:35:13
+ |
+LL | let _ = s.splitn(2, '=').next()?;
+ | ^^^^^^^^^^^^^^^^ help: try this: `s.split('=')`
+
+error: unnecessary use of `splitn`
+ --> $DIR/needless_splitn.rs:36:13
+ |
+LL | let _ = s.splitn(2, '=').nth(0)?;
+ | ^^^^^^^^^^^^^^^^ help: try this: `s.split('=')`
+
+error: unnecessary use of `rsplitn`
+ --> $DIR/needless_splitn.rs:37:13
+ |
+LL | let _ = s.rsplitn(2, '=').next()?;
+ | ^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit('=')`
+
+error: unnecessary use of `rsplitn`
+ --> $DIR/needless_splitn.rs:38:13
+ |
+LL | let _ = s.rsplitn(2, '=').nth(0)?;
+ | ^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit('=')`
+
+error: unnecessary use of `splitn`
+ --> $DIR/needless_splitn.rs:46:13
+ |
+LL | let _ = "key=value".splitn(2, '=').nth(0).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split('=')`
+
+error: aborting due to 13 previous errors
From a4d1837f07f65e4c440646799a0730f1d9a5f798 Mon Sep 17 00:00:00 2001
From: Yoav Lavi
Date: Thu, 7 Apr 2022 20:21:47 +0200
Subject: [PATCH 025/128] unnecessary_string_new
---
CHANGELOG.md | 1 +
clippy_lints/src/lib.register_all.rs | 1 +
clippy_lints/src/lib.register_lints.rs | 1 +
clippy_lints/src/lib.register_style.rs | 1 +
clippy_lints/src/lib.rs | 2 +
.../src/unnecessary_owned_empty_string.rs | 81 +++++++++++++++++++
clippy_utils/src/paths.rs | 1 +
tests/ui/unnecessary_owned_empty_string.fixed | 22 +++++
tests/ui/unnecessary_owned_empty_string.rs | 22 +++++
.../ui/unnecessary_owned_empty_string.stderr | 16 ++++
10 files changed, 148 insertions(+)
create mode 100644 clippy_lints/src/unnecessary_owned_empty_string.rs
create mode 100644 tests/ui/unnecessary_owned_empty_string.fixed
create mode 100644 tests/ui/unnecessary_owned_empty_string.rs
create mode 100644 tests/ui/unnecessary_owned_empty_string.stderr
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b4097ea86a51..e48413dc30c5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3650,6 +3650,7 @@ Released 2018-09-13
[`unnecessary_lazy_evaluations`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations
[`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed
[`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation
+[`unnecessary_owned_empty_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_owned_empty_string
[`unnecessary_self_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_self_imports
[`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by
[`unnecessary_to_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_to_owned
diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs
index 14ca93b5f3c1..b601e37a4c87 100644
--- a/clippy_lints/src/lib.register_all.rs
+++ b/clippy_lints/src/lib.register_all.rs
@@ -310,6 +310,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
LintId::of(unit_types::UNIT_CMP),
LintId::of(unnamed_address::FN_ADDRESS_COMPARISONS),
LintId::of(unnamed_address::VTABLE_ADDRESS_COMPARISONS),
+ LintId::of(unnecessary_owned_empty_string::UNNECESSARY_OWNED_EMPTY_STRING),
LintId::of(unnecessary_sort_by::UNNECESSARY_SORT_BY),
LintId::of(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME),
LintId::of(unused_io_amount::UNUSED_IO_AMOUNT),
diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs
index 532590aaa5a3..0a23e60fd2c4 100644
--- a/clippy_lints/src/lib.register_lints.rs
+++ b/clippy_lints/src/lib.register_lints.rs
@@ -523,6 +523,7 @@ store.register_lints(&[
unit_types::UNIT_CMP,
unnamed_address::FN_ADDRESS_COMPARISONS,
unnamed_address::VTABLE_ADDRESS_COMPARISONS,
+ unnecessary_owned_empty_string::UNNECESSARY_OWNED_EMPTY_STRING,
unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS,
unnecessary_sort_by::UNNECESSARY_SORT_BY,
unnecessary_wraps::UNNECESSARY_WRAPS,
diff --git a/clippy_lints/src/lib.register_style.rs b/clippy_lints/src/lib.register_style.rs
index 3114afac8863..b014c11f0f4a 100644
--- a/clippy_lints/src/lib.register_style.rs
+++ b/clippy_lints/src/lib.register_style.rs
@@ -106,6 +106,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS),
LintId::of(tabs_in_doc_comments::TABS_IN_DOC_COMMENTS),
LintId::of(to_digit_is_some::TO_DIGIT_IS_SOME),
+ LintId::of(unnecessary_owned_empty_string::UNNECESSARY_OWNED_EMPTY_STRING),
LintId::of(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME),
LintId::of(unused_unit::UNUSED_UNIT),
LintId::of(upper_case_acronyms::UPPER_CASE_ACRONYMS),
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index c9b836f95808..576a66112675 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -383,6 +383,7 @@ mod unit_hash;
mod unit_return_expecting_ord;
mod unit_types;
mod unnamed_address;
+mod unnecessary_owned_empty_string;
mod unnecessary_self_imports;
mod unnecessary_sort_by;
mod unnecessary_wraps;
@@ -868,6 +869,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
});
store.register_early_pass(|| Box::new(crate_in_macro_def::CrateInMacroDef));
store.register_early_pass(|| Box::new(empty_structs_with_brackets::EmptyStructsWithBrackets));
+ store.register_late_pass(|| Box::new(unnecessary_owned_empty_string::UnnecessaryOwnedEmptyString));
// add lints here, do not remove this comment, it's used in `new_lint`
}
diff --git a/clippy_lints/src/unnecessary_owned_empty_string.rs b/clippy_lints/src/unnecessary_owned_empty_string.rs
new file mode 100644
index 000000000000..bb42c7816e7b
--- /dev/null
+++ b/clippy_lints/src/unnecessary_owned_empty_string.rs
@@ -0,0 +1,81 @@
+use clippy_utils::{diagnostics::span_lint_and_sugg, ty::is_type_diagnostic_item};
+use clippy_utils::{match_def_path, paths};
+use if_chain::if_chain;
+use rustc_ast::ast::LitKind;
+use rustc_errors::Applicability;
+use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::sym;
+
+declare_clippy_lint! {
+ /// ### What it does
+ ///
+ /// Detects cases of owned empty strings being passed as an argument to a function expecting `&str`
+ ///
+ /// ### Why is this bad?
+ ///
+ /// This results in longer and less readable code
+ ///
+ /// ### Example
+ /// ```rust
+ /// vec!["1", "2", "3"].join(&String::new());
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// vec!["1", "2", "3"].join("");
+ /// ```
+ #[clippy::version = "1.62.0"]
+ pub UNNECESSARY_OWNED_EMPTY_STRING,
+ style,
+ "detects cases of references to owned empty strings being passed as an argument to a function expecting `&str`"
+}
+declare_lint_pass!(UnnecessaryOwnedEmptyString => [UNNECESSARY_OWNED_EMPTY_STRING]);
+
+impl<'tcx> LateLintPass<'tcx> for UnnecessaryOwnedEmptyString {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
+ if_chain! {
+ if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner_expr) = expr.kind;
+ if let ExprKind::Call(fun, args) = inner_expr.kind;
+ if let ExprKind::Path(ref qpath) = fun.kind;
+ if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id();
+ if let ty::Ref(_, inner_str, _) = cx.typeck_results().expr_ty_adjusted(expr).kind();
+ if inner_str.is_str();
+ then {
+ if match_def_path(cx, fun_def_id, &paths::STRING_NEW) {
+ span_lint_and_sugg(
+ cx,
+ UNNECESSARY_OWNED_EMPTY_STRING,
+ expr.span,
+ "usage of `&String::new()` for a function expecting a `&str` argument",
+ "try",
+ "\"\"".to_owned(),
+ Applicability::MachineApplicable,
+ );
+ } else {
+ if_chain! {
+ if match_def_path(cx, fun_def_id, &paths::FROM_FROM);
+ if let [.., last_arg] = args;
+ if let ExprKind::Lit(spanned) = &last_arg.kind;
+ if let LitKind::Str(symbol, _) = spanned.node;
+ if symbol.is_empty();
+ let inner_expr_type = cx.typeck_results().expr_ty(inner_expr);
+ if is_type_diagnostic_item(cx, inner_expr_type, sym::String);
+ then {
+ span_lint_and_sugg(
+ cx,
+ UNNECESSARY_OWNED_EMPTY_STRING,
+ expr.span,
+ "usage of `&String::from(\"\")` for a function expecting a `&str` argument",
+ "try",
+ "\"\"".to_owned(),
+ Applicability::MachineApplicable,
+ );
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs
index 79e6e92dc0aa..deb548daf2d0 100644
--- a/clippy_utils/src/paths.rs
+++ b/clippy_utils/src/paths.rs
@@ -148,6 +148,7 @@ pub const CONVERT_IDENTITY: [&str; 3] = ["core", "convert", "identity"];
pub const STD_FS_CREATE_DIR: [&str; 3] = ["std", "fs", "create_dir"];
pub const STRING_AS_MUT_STR: [&str; 4] = ["alloc", "string", "String", "as_mut_str"];
pub const STRING_AS_STR: [&str; 4] = ["alloc", "string", "String", "as_str"];
+pub const STRING_NEW: [&str; 4] = ["alloc", "string", "String", "new"];
pub const STR_ENDS_WITH: [&str; 4] = ["core", "str", "", "ends_with"];
pub const STR_FROM_UTF8: [&str; 4] = ["core", "str", "converts", "from_utf8"];
pub const STR_LEN: [&str; 4] = ["core", "str", "", "len"];
diff --git a/tests/ui/unnecessary_owned_empty_string.fixed b/tests/ui/unnecessary_owned_empty_string.fixed
new file mode 100644
index 000000000000..cd69e956fd4d
--- /dev/null
+++ b/tests/ui/unnecessary_owned_empty_string.fixed
@@ -0,0 +1,22 @@
+// run-rustfix
+
+#![warn(clippy::unnecessary_owned_empty_string)]
+
+fn ref_str_argument(_value: &str) {}
+
+#[allow(clippy::ptr_arg)]
+fn ref_string_argument(_value: &String) {}
+
+fn main() {
+ // should be linted
+ ref_str_argument("");
+
+ // should be linted
+ ref_str_argument("");
+
+ // should not be linted
+ ref_str_argument("");
+
+ // should not be linted
+ ref_string_argument(&String::new());
+}
diff --git a/tests/ui/unnecessary_owned_empty_string.rs b/tests/ui/unnecessary_owned_empty_string.rs
new file mode 100644
index 000000000000..3fbba156240c
--- /dev/null
+++ b/tests/ui/unnecessary_owned_empty_string.rs
@@ -0,0 +1,22 @@
+// run-rustfix
+
+#![warn(clippy::unnecessary_owned_empty_string)]
+
+fn ref_str_argument(_value: &str) {}
+
+#[allow(clippy::ptr_arg)]
+fn ref_string_argument(_value: &String) {}
+
+fn main() {
+ // should be linted
+ ref_str_argument(&String::new());
+
+ // should be linted
+ ref_str_argument(&String::from(""));
+
+ // should not be linted
+ ref_str_argument("");
+
+ // should not be linted
+ ref_string_argument(&String::new());
+}
diff --git a/tests/ui/unnecessary_owned_empty_string.stderr b/tests/ui/unnecessary_owned_empty_string.stderr
new file mode 100644
index 000000000000..c32372290d1e
--- /dev/null
+++ b/tests/ui/unnecessary_owned_empty_string.stderr
@@ -0,0 +1,16 @@
+error: usage of `&String::new()` for a function expecting a `&str` argument
+ --> $DIR/unnecessary_owned_empty_string.rs:12:22
+ |
+LL | ref_str_argument(&String::new());
+ | ^^^^^^^^^^^^^^ help: try: `""`
+ |
+ = note: `-D clippy::unnecessary-owned-empty-string` implied by `-D warnings`
+
+error: usage of `&String::from("")` for a function expecting a `&str` argument
+ --> $DIR/unnecessary_owned_empty_string.rs:15:22
+ |
+LL | ref_str_argument(&String::from(""));
+ | ^^^^^^^^^^^^^^^^^ help: try: `""`
+
+error: aborting due to 2 previous errors
+
From 10201370a1509b806b9e2a83ded6b132466291b8 Mon Sep 17 00:00:00 2001
From: Yoav Lavi
Date: Mon, 11 Apr 2022 13:05:42 +0200
Subject: [PATCH 026/128] unnecessary_owned_empty_string ->
unnecessary_owned_empty_strings
---
CHANGELOG.md | 2 +-
clippy_lints/src/lib.register_all.rs | 2 +-
clippy_lints/src/lib.register_lints.rs | 2 +-
clippy_lints/src/lib.register_style.rs | 2 +-
clippy_lints/src/lib.rs | 4 ++--
...ty_string.rs => unnecessary_owned_empty_strings.rs} | 10 +++++-----
...ing.fixed => unnecessary_owned_empty_strings.fixed} | 2 +-
...ty_string.rs => unnecessary_owned_empty_strings.rs} | 2 +-
...g.stderr => unnecessary_owned_empty_strings.stderr} | 6 +++---
9 files changed, 16 insertions(+), 16 deletions(-)
rename clippy_lints/src/{unnecessary_owned_empty_string.rs => unnecessary_owned_empty_strings.rs} (90%)
rename tests/ui/{unnecessary_owned_empty_string.fixed => unnecessary_owned_empty_strings.fixed} (87%)
rename tests/ui/{unnecessary_owned_empty_string.rs => unnecessary_owned_empty_strings.rs} (88%)
rename tests/ui/{unnecessary_owned_empty_string.stderr => unnecessary_owned_empty_strings.stderr} (69%)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e48413dc30c5..44a36870108d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3650,7 +3650,7 @@ Released 2018-09-13
[`unnecessary_lazy_evaluations`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations
[`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed
[`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation
-[`unnecessary_owned_empty_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_owned_empty_string
+[`unnecessary_owned_empty_strings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_owned_empty_strings
[`unnecessary_self_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_self_imports
[`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by
[`unnecessary_to_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_to_owned
diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs
index b601e37a4c87..02ba7835639e 100644
--- a/clippy_lints/src/lib.register_all.rs
+++ b/clippy_lints/src/lib.register_all.rs
@@ -310,7 +310,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
LintId::of(unit_types::UNIT_CMP),
LintId::of(unnamed_address::FN_ADDRESS_COMPARISONS),
LintId::of(unnamed_address::VTABLE_ADDRESS_COMPARISONS),
- LintId::of(unnecessary_owned_empty_string::UNNECESSARY_OWNED_EMPTY_STRING),
+ LintId::of(unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS),
LintId::of(unnecessary_sort_by::UNNECESSARY_SORT_BY),
LintId::of(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME),
LintId::of(unused_io_amount::UNUSED_IO_AMOUNT),
diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs
index 0a23e60fd2c4..704e79885cfb 100644
--- a/clippy_lints/src/lib.register_lints.rs
+++ b/clippy_lints/src/lib.register_lints.rs
@@ -523,7 +523,7 @@ store.register_lints(&[
unit_types::UNIT_CMP,
unnamed_address::FN_ADDRESS_COMPARISONS,
unnamed_address::VTABLE_ADDRESS_COMPARISONS,
- unnecessary_owned_empty_string::UNNECESSARY_OWNED_EMPTY_STRING,
+ unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS,
unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS,
unnecessary_sort_by::UNNECESSARY_SORT_BY,
unnecessary_wraps::UNNECESSARY_WRAPS,
diff --git a/clippy_lints/src/lib.register_style.rs b/clippy_lints/src/lib.register_style.rs
index b014c11f0f4a..f52fe97ed237 100644
--- a/clippy_lints/src/lib.register_style.rs
+++ b/clippy_lints/src/lib.register_style.rs
@@ -106,7 +106,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS),
LintId::of(tabs_in_doc_comments::TABS_IN_DOC_COMMENTS),
LintId::of(to_digit_is_some::TO_DIGIT_IS_SOME),
- LintId::of(unnecessary_owned_empty_string::UNNECESSARY_OWNED_EMPTY_STRING),
+ LintId::of(unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS),
LintId::of(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME),
LintId::of(unused_unit::UNUSED_UNIT),
LintId::of(upper_case_acronyms::UPPER_CASE_ACRONYMS),
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index 576a66112675..74ade422dc88 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -383,7 +383,7 @@ mod unit_hash;
mod unit_return_expecting_ord;
mod unit_types;
mod unnamed_address;
-mod unnecessary_owned_empty_string;
+mod unnecessary_owned_empty_strings;
mod unnecessary_self_imports;
mod unnecessary_sort_by;
mod unnecessary_wraps;
@@ -869,7 +869,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
});
store.register_early_pass(|| Box::new(crate_in_macro_def::CrateInMacroDef));
store.register_early_pass(|| Box::new(empty_structs_with_brackets::EmptyStructsWithBrackets));
- store.register_late_pass(|| Box::new(unnecessary_owned_empty_string::UnnecessaryOwnedEmptyString));
+ store.register_late_pass(|| Box::new(unnecessary_owned_empty_strings::UnnecessaryOwnedEmptyStrings));
// add lints here, do not remove this comment, it's used in `new_lint`
}
diff --git a/clippy_lints/src/unnecessary_owned_empty_string.rs b/clippy_lints/src/unnecessary_owned_empty_strings.rs
similarity index 90%
rename from clippy_lints/src/unnecessary_owned_empty_string.rs
rename to clippy_lints/src/unnecessary_owned_empty_strings.rs
index bb42c7816e7b..8a4f4c0ad971 100644
--- a/clippy_lints/src/unnecessary_owned_empty_string.rs
+++ b/clippy_lints/src/unnecessary_owned_empty_strings.rs
@@ -27,13 +27,13 @@ declare_clippy_lint! {
/// vec!["1", "2", "3"].join("");
/// ```
#[clippy::version = "1.62.0"]
- pub UNNECESSARY_OWNED_EMPTY_STRING,
+ pub UNNECESSARY_OWNED_EMPTY_STRINGS,
style,
"detects cases of references to owned empty strings being passed as an argument to a function expecting `&str`"
}
-declare_lint_pass!(UnnecessaryOwnedEmptyString => [UNNECESSARY_OWNED_EMPTY_STRING]);
+declare_lint_pass!(UnnecessaryOwnedEmptyStrings => [UNNECESSARY_OWNED_EMPTY_STRINGS]);
-impl<'tcx> LateLintPass<'tcx> for UnnecessaryOwnedEmptyString {
+impl<'tcx> LateLintPass<'tcx> for UnnecessaryOwnedEmptyStrings {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
if_chain! {
if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner_expr) = expr.kind;
@@ -46,7 +46,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryOwnedEmptyString {
if match_def_path(cx, fun_def_id, &paths::STRING_NEW) {
span_lint_and_sugg(
cx,
- UNNECESSARY_OWNED_EMPTY_STRING,
+ UNNECESSARY_OWNED_EMPTY_STRINGS,
expr.span,
"usage of `&String::new()` for a function expecting a `&str` argument",
"try",
@@ -65,7 +65,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryOwnedEmptyString {
then {
span_lint_and_sugg(
cx,
- UNNECESSARY_OWNED_EMPTY_STRING,
+ UNNECESSARY_OWNED_EMPTY_STRINGS,
expr.span,
"usage of `&String::from(\"\")` for a function expecting a `&str` argument",
"try",
diff --git a/tests/ui/unnecessary_owned_empty_string.fixed b/tests/ui/unnecessary_owned_empty_strings.fixed
similarity index 87%
rename from tests/ui/unnecessary_owned_empty_string.fixed
rename to tests/ui/unnecessary_owned_empty_strings.fixed
index cd69e956fd4d..f95f91329a2f 100644
--- a/tests/ui/unnecessary_owned_empty_string.fixed
+++ b/tests/ui/unnecessary_owned_empty_strings.fixed
@@ -1,6 +1,6 @@
// run-rustfix
-#![warn(clippy::unnecessary_owned_empty_string)]
+#![warn(clippy::unnecessary_owned_empty_strings)]
fn ref_str_argument(_value: &str) {}
diff --git a/tests/ui/unnecessary_owned_empty_string.rs b/tests/ui/unnecessary_owned_empty_strings.rs
similarity index 88%
rename from tests/ui/unnecessary_owned_empty_string.rs
rename to tests/ui/unnecessary_owned_empty_strings.rs
index 3fbba156240c..0cbdc151ed9b 100644
--- a/tests/ui/unnecessary_owned_empty_string.rs
+++ b/tests/ui/unnecessary_owned_empty_strings.rs
@@ -1,6 +1,6 @@
// run-rustfix
-#![warn(clippy::unnecessary_owned_empty_string)]
+#![warn(clippy::unnecessary_owned_empty_strings)]
fn ref_str_argument(_value: &str) {}
diff --git a/tests/ui/unnecessary_owned_empty_string.stderr b/tests/ui/unnecessary_owned_empty_strings.stderr
similarity index 69%
rename from tests/ui/unnecessary_owned_empty_string.stderr
rename to tests/ui/unnecessary_owned_empty_strings.stderr
index c32372290d1e..46bc4597b335 100644
--- a/tests/ui/unnecessary_owned_empty_string.stderr
+++ b/tests/ui/unnecessary_owned_empty_strings.stderr
@@ -1,13 +1,13 @@
error: usage of `&String::new()` for a function expecting a `&str` argument
- --> $DIR/unnecessary_owned_empty_string.rs:12:22
+ --> $DIR/unnecessary_owned_empty_strings.rs:12:22
|
LL | ref_str_argument(&String::new());
| ^^^^^^^^^^^^^^ help: try: `""`
|
- = note: `-D clippy::unnecessary-owned-empty-string` implied by `-D warnings`
+ = note: `-D clippy::unnecessary-owned-empty-strings` implied by `-D warnings`
error: usage of `&String::from("")` for a function expecting a `&str` argument
- --> $DIR/unnecessary_owned_empty_string.rs:15:22
+ --> $DIR/unnecessary_owned_empty_strings.rs:15:22
|
LL | ref_str_argument(&String::from(""));
| ^^^^^^^^^^^^^^^^^ help: try: `""`
From 62d912e24dc1ec3816dfcddba96c792b660edc9b Mon Sep 17 00:00:00 2001
From: Jakob Degen
Date: Tue, 5 Apr 2022 17:14:59 -0400
Subject: [PATCH 027/128] Add new `Deinit` statement kind
---
clippy_utils/src/qualify_min_const_fn.rs | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs
index 891531951c1a..fe4112204848 100644
--- a/clippy_utils/src/qualify_min_const_fn.rs
+++ b/clippy_utils/src/qualify_min_const_fn.rs
@@ -211,7 +211,8 @@ fn check_statement<'tcx>(
StatementKind::FakeRead(box (_, place)) => check_place(tcx, *place, span, body),
// just an assignment
- StatementKind::SetDiscriminant { place, .. } => check_place(tcx, **place, span, body),
+ StatementKind::SetDiscriminant { place, .. } | StatementKind::Deinit(place) =>
+ check_place(tcx, **place, span, body),
StatementKind::CopyNonOverlapping(box rustc_middle::mir::CopyNonOverlapping { dst, src, count }) => {
check_operand(tcx, dst, span, body)?;
From c82dd0f36ebeba506d3722f9def3ec17ab03b528 Mon Sep 17 00:00:00 2001
From: Jason Newcomb
Date: Mon, 11 Apr 2022 13:18:27 -0400
Subject: [PATCH 028/128] Fix ICE in `undocumented_unsafe_blocks`
---
clippy_lints/src/undocumented_unsafe_blocks.rs | 7 ++++---
tests/ui/crashes/auxiliary/ice-8681-aux.rs | 6 ++++++
tests/ui/crashes/ice-8681.rs | 10 ++++++++++
3 files changed, 20 insertions(+), 3 deletions(-)
create mode 100644 tests/ui/crashes/auxiliary/ice-8681-aux.rs
create mode 100644 tests/ui/crashes/ice-8681.rs
diff --git a/clippy_lints/src/undocumented_unsafe_blocks.rs b/clippy_lints/src/undocumented_unsafe_blocks.rs
index c0947685fa1d..bc57e9b992c6 100644
--- a/clippy_lints/src/undocumented_unsafe_blocks.rs
+++ b/clippy_lints/src/undocumented_unsafe_blocks.rs
@@ -156,8 +156,9 @@ fn text_has_safety_comment(src: &str, line_starts: &[BytePos], offset: usize) ->
.array_windows::<2>()
.rev()
.map_while(|[start, end]| {
- src.get(start.to_usize() - offset..end.to_usize() - offset)
- .map(|text| (start.to_usize(), text.trim_start()))
+ let start = start.to_usize() - offset;
+ let end = end.to_usize() - offset;
+ src.get(start..end).map(|text| (start, text.trim_start()))
})
.filter(|(_, text)| !text.is_empty());
@@ -182,7 +183,7 @@ fn text_has_safety_comment(src: &str, line_starts: &[BytePos], offset: usize) ->
let (mut line_start, mut line) = (line_start, line);
loop {
if line.starts_with("/*") {
- let src = src[line_start..line_starts.last().unwrap().to_usize()].trim_start();
+ let src = src[line_start..line_starts.last().unwrap().to_usize() - offset].trim_start();
let mut tokens = tokenize(src);
return src[..tokens.next().unwrap().len]
.to_ascii_uppercase()
diff --git a/tests/ui/crashes/auxiliary/ice-8681-aux.rs b/tests/ui/crashes/auxiliary/ice-8681-aux.rs
new file mode 100644
index 000000000000..95b631513252
--- /dev/null
+++ b/tests/ui/crashes/auxiliary/ice-8681-aux.rs
@@ -0,0 +1,6 @@
+pub fn foo(x: &u32) -> u32 {
+ /* Safety:
+ * This is totally ok.
+ */
+ unsafe { *(x as *const u32) }
+}
diff --git a/tests/ui/crashes/ice-8681.rs b/tests/ui/crashes/ice-8681.rs
new file mode 100644
index 000000000000..ee14f011f631
--- /dev/null
+++ b/tests/ui/crashes/ice-8681.rs
@@ -0,0 +1,10 @@
+// aux-build: ice-8681-aux.rs
+
+#![warn(clippy::undocumented_unsafe_blocks)]
+
+#[path = "auxiliary/ice-8681-aux.rs"]
+mod ice_8681_aux;
+
+fn main() {
+ let _ = ice_8681_aux::foo(&0u32);
+}
From 4a21082da22f6ff48a77f1ded2a6b5f02f31aa78 Mon Sep 17 00:00:00 2001
From: Alex Macleod
Date: Mon, 11 Apr 2022 18:54:44 +0100
Subject: [PATCH 029/128] Fix subtraction overflow in
`cast_possible_truncation`
---
.../src/casts/cast_possible_truncation.rs | 20 +++++++++----------
tests/ui/cast.rs | 14 ++++++++++---
tests/ui/cast.stderr | 14 ++++++++++++-
3 files changed, 33 insertions(+), 15 deletions(-)
diff --git a/clippy_lints/src/casts/cast_possible_truncation.rs b/clippy_lints/src/casts/cast_possible_truncation.rs
index 421bd6f53f71..64f87c80f8d1 100644
--- a/clippy_lints/src/casts/cast_possible_truncation.rs
+++ b/clippy_lints/src/casts/cast_possible_truncation.rs
@@ -29,21 +29,19 @@ fn apply_reductions(cx: &LateContext<'_>, nbits: u64, expr: &Expr<'_>, signed: b
ExprKind::Block(block, _) => block.expr.map_or(nbits, |e| apply_reductions(cx, nbits, e, signed)),
ExprKind::Binary(op, left, right) => match op.node {
BinOpKind::Div => {
- apply_reductions(cx, nbits, left, signed)
- - (if signed {
- 0 // let's be conservative here
- } else {
- // by dividing by 1, we remove 0 bits, etc.
- get_constant_bits(cx, right).map_or(0, |b| b.saturating_sub(1))
- })
+ apply_reductions(cx, nbits, left, signed).saturating_sub(if signed {
+ // let's be conservative here
+ 0
+ } else {
+ // by dividing by 1, we remove 0 bits, etc.
+ get_constant_bits(cx, right).map_or(0, |b| b.saturating_sub(1))
+ })
},
BinOpKind::Rem | BinOpKind::BitAnd => get_constant_bits(cx, right)
.unwrap_or(u64::max_value())
.min(apply_reductions(cx, nbits, left, signed)),
- BinOpKind::Shr => {
- apply_reductions(cx, nbits, left, signed)
- - constant_int(cx, right).map_or(0, |s| u64::try_from(s).expect("shift too high"))
- },
+ BinOpKind::Shr => apply_reductions(cx, nbits, left, signed)
+ .saturating_sub(constant_int(cx, right).map_or(0, |s| u64::try_from(s).expect("shift too high"))),
_ => nbits,
},
ExprKind::MethodCall(method, [left, right], _) => {
diff --git a/tests/ui/cast.rs b/tests/ui/cast.rs
index cf85a5ca931d..e6031e9adaeb 100644
--- a/tests/ui/cast.rs
+++ b/tests/ui/cast.rs
@@ -1,13 +1,13 @@
#![feature(repr128)]
#![allow(incomplete_features)]
-
-#[warn(
+#![warn(
clippy::cast_precision_loss,
clippy::cast_possible_truncation,
clippy::cast_sign_loss,
clippy::cast_possible_wrap
)]
-#[allow(clippy::cast_abs_to_unsigned, clippy::no_effect, clippy::unnecessary_operation)]
+#![allow(clippy::cast_abs_to_unsigned, clippy::no_effect, clippy::unnecessary_operation)]
+
fn main() {
// Test clippy::cast_precision_loss
let x0 = 1i32;
@@ -252,3 +252,11 @@ fn main() {
}
}
}
+
+fn avoid_subtract_overflow(q: u32) {
+ let c = (q >> 16) as u8;
+ c as usize;
+
+ let c = (q / 1000) as u8;
+ c as usize;
+}
diff --git a/tests/ui/cast.stderr b/tests/ui/cast.stderr
index 7a68c0984f14..0c63b4af3086 100644
--- a/tests/ui/cast.stderr
+++ b/tests/ui/cast.stderr
@@ -194,5 +194,17 @@ error: casting `main::E10` to `u16` may truncate the value
LL | let _ = self as u16;
| ^^^^^^^^^^^
-error: aborting due to 31 previous errors
+error: casting `u32` to `u8` may truncate the value
+ --> $DIR/cast.rs:257:13
+ |
+LL | let c = (q >> 16) as u8;
+ | ^^^^^^^^^^^^^^^
+
+error: casting `u32` to `u8` may truncate the value
+ --> $DIR/cast.rs:260:13
+ |
+LL | let c = (q / 1000) as u8;
+ | ^^^^^^^^^^^^^^^^
+
+error: aborting due to 33 previous errors
From 9716a9eff0d68c92fd064b293f0ffbd083645fa5 Mon Sep 17 00:00:00 2001
From: kyoto7250 <50972773+kyoto7250@users.noreply.github.com>
Date: Tue, 12 Apr 2022 04:03:48 +0900
Subject: [PATCH 030/128] adding condition for map_clone message
if msrv < 1.36, the message tells , but the suggestion is
---
clippy_lints/src/map_clone.rs | 2 +-
tests/ui-toml/min_rust_version/min_rust_version.stderr | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/clippy_lints/src/map_clone.rs b/clippy_lints/src/map_clone.rs
index e233300e26ab..a03d5d540653 100644
--- a/clippy_lints/src/map_clone.rs
+++ b/clippy_lints/src/map_clone.rs
@@ -143,7 +143,7 @@ fn lint_needless_cloning(cx: &LateContext<'_>, root: Span, receiver: Span) {
impl MapClone {
fn lint_explicit_closure(&self, cx: &LateContext<'_>, replace: Span, root: Span, is_copy: bool) {
let mut applicability = Applicability::MachineApplicable;
- let message = if is_copy {
+ let message = if is_copy && meets_msrv(self.msrv.as_ref(), &msrvs::ITERATOR_COPIED) {
"you are using an explicit closure for copying elements"
} else {
"you are using an explicit closure for cloning elements"
diff --git a/tests/ui-toml/min_rust_version/min_rust_version.stderr b/tests/ui-toml/min_rust_version/min_rust_version.stderr
index a1e7361c0cb3..5dae5af7eb5a 100644
--- a/tests/ui-toml/min_rust_version/min_rust_version.stderr
+++ b/tests/ui-toml/min_rust_version/min_rust_version.stderr
@@ -1,4 +1,4 @@
-error: you are using an explicit closure for copying elements
+error: you are using an explicit closure for cloning elements
--> $DIR/min_rust_version.rs:74:26
|
LL | let _: Option = Some(&16).map(|b| *b);
From 40224f46c0158ff8bf6657f2272dba9ec2ee96d7 Mon Sep 17 00:00:00 2001
From: kyoto7250 <50972773+kyoto7250@users.noreply.github.com>
Date: Tue, 12 Apr 2022 09:49:00 +0900
Subject: [PATCH 031/128] refactor: Put together an if statement
---
clippy_lints/src/map_clone.rs | 12 ++++--------
1 file changed, 4 insertions(+), 8 deletions(-)
diff --git a/clippy_lints/src/map_clone.rs b/clippy_lints/src/map_clone.rs
index a03d5d540653..ceb66947d02c 100644
--- a/clippy_lints/src/map_clone.rs
+++ b/clippy_lints/src/map_clone.rs
@@ -143,15 +143,11 @@ fn lint_needless_cloning(cx: &LateContext<'_>, root: Span, receiver: Span) {
impl MapClone {
fn lint_explicit_closure(&self, cx: &LateContext<'_>, replace: Span, root: Span, is_copy: bool) {
let mut applicability = Applicability::MachineApplicable;
- let message = if is_copy && meets_msrv(self.msrv.as_ref(), &msrvs::ITERATOR_COPIED) {
- "you are using an explicit closure for copying elements"
+
+ let (message, sugg_method) = if is_copy && meets_msrv(self.msrv.as_ref(), &msrvs::ITERATOR_COPIED) {
+ ("you are using an explicit closure for copying elements", "copied")
} else {
- "you are using an explicit closure for cloning elements"
- };
- let sugg_method = if is_copy && meets_msrv(self.msrv.as_ref(), &msrvs::ITERATOR_COPIED) {
- "copied"
- } else {
- "cloned"
+ ("you are using an explicit closure for cloning elements", "cloned")
};
span_lint_and_sugg(
From 739f273739a1e4da98d1222f3316d831ae187bc2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bruno=20A=2E=20Muci=C3=B1o?=
Date: Mon, 11 Apr 2022 22:47:04 -0500
Subject: [PATCH 032/128] Do not apply `rest_pat_in_fully_bound_structs` on
`#[non_exhaustive]` structs
---
.../src/matches/rest_pat_in_fully_bound_struct.rs | 1 +
tests/ui/rest_pat_in_fully_bound_structs.rs | 15 +++++++++++++++
2 files changed, 16 insertions(+)
diff --git a/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs b/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs
index 5076239a57c4..0aadb482acdd 100644
--- a/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs
+++ b/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs
@@ -14,6 +14,7 @@ pub(crate) fn check(cx: &LateContext<'_>, pat: &Pat<'_>) {
if let ty::Adt(def, _) = ty.kind();
if def.is_struct() || def.is_union();
if fields.len() == def.non_enum_variant().fields.len();
+ if !def.non_enum_variant().is_field_list_non_exhaustive();
then {
span_lint_and_help(
diff --git a/tests/ui/rest_pat_in_fully_bound_structs.rs b/tests/ui/rest_pat_in_fully_bound_structs.rs
index 38fc9969804f..086331af6b56 100644
--- a/tests/ui/rest_pat_in_fully_bound_structs.rs
+++ b/tests/ui/rest_pat_in_fully_bound_structs.rs
@@ -39,4 +39,19 @@ fn main() {
// No lint
foo!(a_struct);
+
+ #[non_exhaustive]
+ struct B {
+ a: u32,
+ b: u32,
+ c: u64,
+ }
+
+ let b_struct = B { a: 5, b: 42, c: 342 };
+
+ match b_struct {
+ B { a: 5, b: 42, .. } => {},
+ B { a: 0, b: 0, c: 128, .. } => {}, // No Lint
+ _ => {},
+ }
}
From 214fba7ed4a8e2199d42733cc191b956f0c2b017 Mon Sep 17 00:00:00 2001
From: flip1995
Date: Tue, 12 Apr 2022 15:27:28 +0200
Subject: [PATCH 033/128] Prevent infinite (exponential) recursion in
only_used_in_recursion
This simplifies the visitor code a bit and prevents checking expressions
multiple times. I still think this lint should be removed for now,
because its code isn't really tested.
---
clippy_lints/src/only_used_in_recursion.rs | 47 ++++++++--------------
1 file changed, 16 insertions(+), 31 deletions(-)
diff --git a/clippy_lints/src/only_used_in_recursion.rs b/clippy_lints/src/only_used_in_recursion.rs
index 8e61f2347767..f946fc111928 100644
--- a/clippy_lints/src/only_used_in_recursion.rs
+++ b/clippy_lints/src/only_used_in_recursion.rs
@@ -8,7 +8,7 @@ use rustc_errors::Applicability;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::DefId;
use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData};
-use rustc_hir::intravisit::{walk_expr, FnKind, Visitor};
+use rustc_hir::intravisit::{walk_expr, walk_stmt, FnKind, Visitor};
use rustc_hir::{
Arm, Block, Body, Expr, ExprKind, Guard, HirId, ImplicitSelfKind, Let, Local, Pat, PatKind, Path, PathSegment,
QPath, Stmt, StmtKind, TyKind, UnOp,
@@ -145,7 +145,8 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion {
is_method: matches!(kind, FnKind::Method(..)),
has_self,
ty_res,
- ty_ctx: cx.tcx,
+ tcx: cx.tcx,
+ visited_exprs: FxHashSet::default(),
};
visitor.visit_expr(&body.value);
@@ -206,19 +207,13 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion {
}
pub fn is_primitive(ty: Ty<'_>) -> bool {
- match ty.kind() {
- ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => true,
- ty::Ref(_, t, _) => is_primitive(*t),
- _ => false,
- }
+ let ty = ty.peel_refs();
+ ty.is_primitive() || ty.is_str()
}
pub fn is_array(ty: Ty<'_>) -> bool {
- match ty.kind() {
- ty::Array(..) | ty::Slice(..) => true,
- ty::Ref(_, t, _) => is_array(*t),
- _ => false,
- }
+ let ty = ty.peel_refs();
+ ty.is_array() || ty.is_array_slice()
}
/// This builds the graph of side effect.
@@ -250,40 +245,30 @@ pub struct SideEffectVisit<'tcx> {
is_method: bool,
has_self: bool,
ty_res: &'tcx TypeckResults<'tcx>,
- ty_ctx: TyCtxt<'tcx>,
+ tcx: TyCtxt<'tcx>,
+ visited_exprs: FxHashSet,
}
impl<'tcx> Visitor<'tcx> for SideEffectVisit<'tcx> {
- fn visit_block(&mut self, b: &'tcx Block<'tcx>) {
- b.stmts.iter().for_each(|stmt| {
- self.visit_stmt(stmt);
- self.ret_vars.clear();
- });
- walk_list!(self, visit_expr, b.expr);
- }
-
fn visit_stmt(&mut self, s: &'tcx Stmt<'tcx>) {
match s.kind {
StmtKind::Local(Local {
pat, init: Some(init), ..
}) => {
self.visit_pat_expr(pat, init, false);
- self.ret_vars.clear();
},
- StmtKind::Item(i) => {
- let item = self.ty_ctx.hir().item(i);
- self.visit_item(item);
- self.ret_vars.clear();
- },
- StmtKind::Expr(e) | StmtKind::Semi(e) => {
- self.visit_expr(e);
- self.ret_vars.clear();
+ StmtKind::Item(_) | StmtKind::Expr(_) | StmtKind::Semi(_) => {
+ walk_stmt(self, s);
},
StmtKind::Local(_) => {},
}
+ self.ret_vars.clear();
}
fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
+ if !self.visited_exprs.insert(ex.hir_id) {
+ return;
+ }
match ex.kind {
ExprKind::Array(exprs) | ExprKind::Tup(exprs) => {
self.ret_vars = exprs
@@ -307,7 +292,7 @@ impl<'tcx> Visitor<'tcx> for SideEffectVisit<'tcx> {
ExprKind::Match(expr, arms, _) => self.visit_match(expr, arms),
// since analysing the closure is not easy, just set all variables in it to side-effect
ExprKind::Closure(_, _, body_id, _, _) => {
- let body = self.ty_ctx.hir().body(body_id);
+ let body = self.tcx.hir().body(body_id);
self.visit_body(body);
let vars = std::mem::take(&mut self.ret_vars);
self.add_side_effect(vars);
From dfdc5ad7d8f5d3787406a00bab2d1e049a57d1e2 Mon Sep 17 00:00:00 2001
From: kyoto7250 <50972773+kyoto7250@users.noreply.github.com>
Date: Tue, 12 Apr 2022 22:55:48 +0900
Subject: [PATCH 034/128] fix unnecessary_to_owned about msrv
---
clippy_lints/src/methods/mod.rs | 2 +-
.../src/methods/unnecessary_to_owned.rs | 23 +-
tests/ui/unnecessary_to_owned.fixed | 12 ++
tests/ui/unnecessary_to_owned.rs | 12 ++
tests/ui/unnecessary_to_owned.stderr | 196 ++++++++++--------
5 files changed, 148 insertions(+), 97 deletions(-)
diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs
index 56a8782cee42..e2abce86791b 100644
--- a/clippy_lints/src/methods/mod.rs
+++ b/clippy_lints/src/methods/mod.rs
@@ -2254,7 +2254,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
single_char_add_str::check(cx, expr, args);
into_iter_on_ref::check(cx, expr, method_span, method_call.ident.name, args);
single_char_pattern::check(cx, expr, method_call.ident.name, args);
- unnecessary_to_owned::check(cx, expr, method_call.ident.name, args);
+ unnecessary_to_owned::check(cx, expr, method_call.ident.name, args, self.msrv.as_ref());
},
hir::ExprKind::Binary(op, lhs, rhs) if op.node == hir::BinOpKind::Eq || op.node == hir::BinOpKind::Ne => {
let mut info = BinaryExprInfo {
diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs
index 1555758fc4ad..02b882e8b55e 100644
--- a/clippy_lints/src/methods/unnecessary_to_owned.rs
+++ b/clippy_lints/src/methods/unnecessary_to_owned.rs
@@ -5,6 +5,8 @@ use clippy_utils::source::snippet_opt;
use clippy_utils::ty::{
contains_ty, get_associated_type, get_iterator_item_ty, implements_trait, is_copy, peel_mid_ty_refs,
};
+use clippy_utils::{meets_msrv, msrvs};
+
use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item};
use rustc_errors::Applicability;
use rustc_hir::{def_id::DefId, BorrowKind, Expr, ExprKind};
@@ -13,12 +15,19 @@ use rustc_middle::mir::Mutability;
use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref};
use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef};
use rustc_middle::ty::{self, PredicateKind, ProjectionPredicate, TraitPredicate, Ty};
+use rustc_semver::RustcVersion;
use rustc_span::{sym, Symbol};
use std::cmp::max;
use super::UNNECESSARY_TO_OWNED;
-pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: Symbol, args: &'tcx [Expr<'tcx>]) {
+pub fn check<'tcx>(
+ cx: &LateContext<'tcx>,
+ expr: &'tcx Expr<'tcx>,
+ method_name: Symbol,
+ args: &'tcx [Expr<'tcx>],
+ msrv: Option<&RustcVersion>,
+) {
if_chain! {
if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
if let [receiver] = args;
@@ -33,7 +42,7 @@ pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name:
if check_addr_of_expr(cx, expr, method_name, method_def_id, receiver) {
return;
}
- if check_into_iter_call_arg(cx, expr, method_name, receiver) {
+ if check_into_iter_call_arg(cx, expr, method_name, receiver, msrv) {
return;
}
check_other_call_arg(cx, expr, method_name, receiver);
@@ -178,7 +187,13 @@ fn check_addr_of_expr(
/// Checks whether `expr` is an argument in an `into_iter` call and, if so, determines whether its
/// call of a `to_owned`-like function is unnecessary.
-fn check_into_iter_call_arg(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symbol, receiver: &Expr<'_>) -> bool {
+fn check_into_iter_call_arg(
+ cx: &LateContext<'_>,
+ expr: &Expr<'_>,
+ method_name: Symbol,
+ receiver: &Expr<'_>,
+ msrv: Option<&RustcVersion>,
+) -> bool {
if_chain! {
if let Some(parent) = get_parent_expr(cx, expr);
if let Some(callee_def_id) = fn_def_id(cx, parent);
@@ -192,7 +207,7 @@ fn check_into_iter_call_arg(cx: &LateContext<'_>, expr: &Expr<'_>, method_name:
if unnecessary_iter_cloned::check_for_loop_iter(cx, parent, method_name, receiver, true) {
return true;
}
- let cloned_or_copied = if is_copy(cx, item_ty) { "copied" } else { "cloned" };
+ let cloned_or_copied = if is_copy(cx, item_ty) && meets_msrv(msrv, &msrvs::ITERATOR_COPIED) { "copied" } else { "cloned" };
// The next suggestion may be incorrect because the removal of the `to_owned`-like
// function could cause the iterator to hold a reference to a resource that is used
// mutably. See https://github.com/rust-lang/rust-clippy/issues/8148.
diff --git a/tests/ui/unnecessary_to_owned.fixed b/tests/ui/unnecessary_to_owned.fixed
index 38ba41ac54ec..7455e22d49b2 100644
--- a/tests/ui/unnecessary_to_owned.fixed
+++ b/tests/ui/unnecessary_to_owned.fixed
@@ -2,6 +2,7 @@
#![allow(clippy::ptr_arg)]
#![warn(clippy::unnecessary_to_owned)]
+#![feature(custom_inner_attributes)]
use std::borrow::Cow;
use std::ffi::{CStr, CString, OsStr, OsString};
@@ -213,6 +214,17 @@ fn get_file_path(_file_type: &FileType) -> Result Result