From c70f1e0f8f3419f67dc31b5bbbc47db1e829128c Mon Sep 17 00:00:00 2001
From: Peter Jaszkowiak
Date: Fri, 1 Apr 2022 22:36:30 -0600
Subject: [PATCH 001/129] 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 1d4fe9cfd3cf4..790ffe618d71d 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 46463a29e9b20..c223b5bc711b2 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 8ce0738bfc27b..04cd11036e4e0 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 de424c3fdb8f2..453c66cbba8fc 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/129] 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 7e65812669029..b5fa847451d1f 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 0d05c83ffe45e..6ba9ba0753d49 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 1d6f7acab139b..be46b791aa4b6 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/129] 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 4e48b670457e8..b010149626cf8 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 39a2601fee9ac..07270bd76362a 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 6095f134d55e0..0e142ac8f20e7 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/129] 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 | 30 ++++++-------
6 files changed, 135 insertions(+), 77 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 b010149626cf8..f15b00ecad1e6 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 c8b57956b1b62..9812cfde3ec78 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 0000000000000..e10dc0e1bfeb7
--- /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 24a0c81229198..325f63a64dd90 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 ea64234c680d3..a21b4a242880b 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 8b132a7838470..54e12d5fae5ea 100644
--- a/tests/ui/rename.stderr
+++ b/tests/ui/rename.stderr
@@ -12,17 +12,17 @@ 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`
+error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity`
--> $DIR/rename.rs:37:9
|
-LL | #![warn(clippy::const_static_lifetime)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes`
+LL | #![warn(clippy::cyclomatic_complexity)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity`
-error: lint `clippy::cyclomatic_complexity` has been renamed to `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::cyclomatic_complexity)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity`
+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/129] 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 b5fa847451d1f..c0b3e95b18523 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/129] 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 8dab039f24fe7..b4a70988286ae 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 33d1bb2985f43..7b4b8d6bffa0c 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,55 +63,84 @@ 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;
- }
-
- check_manual_non_exhaustive_struct(cx, item, variant_data);
- },
- _ => {},
+ 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);
}
}
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)
+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 mut markers = variants.iter().filter(|v| is_non_exhaustive_marker(v));
+ 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 let Some(marker) = markers.next();
- if markers.count() == 0 && variants.len() > 1;
+ 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,
@@ -116,7 +150,7 @@ fn check_manual_non_exhaustive_enum(cx: &EarlyContext<'_>, item: &Item, variants
|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, '{');
+ let header_span = find_header_span(cx, item, data);
if let Some(snippet) = snippet_opt(cx, header_span);
then {
diag.span_suggestion(
@@ -127,60 +161,79 @@ fn check_manual_non_exhaustive_enum(cx: &EarlyContext<'_>, item: &Item, variants
);
}
}
- diag.span_help(marker.span, "remove this variant");
+ diag.span_help(marker.span, "remove this field");
});
}
}
}
-fn check_manual_non_exhaustive_struct(cx: &EarlyContext<'_>, item: &Item, data: &VariantData) {
- fn is_private(field: &FieldDef) -> bool {
- matches!(field.vis.kind, VisibilityKind::Inherited)
- }
+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;
+ }
- 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('_'))
+ 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 find_header_span(cx: &EarlyContext<'_>, item: &Item, data: &VariantData) -> Span {
- let delimiter = match data {
- VariantData::Struct(..) => '{',
- VariantData::Tuple(..) => '(',
- VariantData::Unit(_) => unreachable!("`VariantData::Unit` is already handled above"),
- };
-
- cx.sess().source_map().span_until_char(item.span, delimiter)
+ 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));
+ }
}
- 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 {
+ 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,
- item.span,
+ enum_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 {
+ 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(marker.span, "remove this field");
- });
+ 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 0000000000000..f23c6d69b4c6a
--- /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 0000000000000..317a45d2cbd59
--- /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 7a788f4852072..498eee4447b88 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 613c5e8ca1d45..e0766c17b7580 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/129] 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 7b4b8d6bffa0c..2501dd5bed4b7 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/129] 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 ba1997e70e131..4ecde8f49581e 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 40451b17a9c63..3db64b2535398 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 a9a04c8f56b94..370dbd5882161 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 4787999920bc2..b76d6a13ffb9c 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/129] 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 f359b606e4548..0e07a0b0392eb 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 cd5a7127791a8..5a431bee04a45 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/129] 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 b4097ea86a518..c5c93b1132783 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 df780747a0c75..e3e31c5863edf 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 28d0c75fde6ba..e72031e48778f 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 14ca93b5f3c14..aa8e3b535b15d 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 532590aaa5a3d..7436f31125b1e 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 3114afac8863e..2d98802cb1401 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 0000000000000..ad333df2f2d5d
--- /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 70d021a1668eb..6a7841a90dfa9 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 d955fad7d41a2..6860b60acbdb4 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 0d0c88b02c78b..e3bc40c4b4914 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 0424e06720263..134fd1ce505a0 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 908ff822712ff..b92d42e83232c 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 0000000000000..c0ba647d70791
--- /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 0000000000000..68e3f3243d96d
--- /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 0000000000000..dc5cb2913ae15
--- /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 19184df0becb5..3c5e964271465 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 45a6728ebf578..4f247c06ceeda 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 177d3ccd3e23d..10a1b393a3906 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/129] 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 4ecde8f49581e..aa7222266eb5a 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/129] 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 | 63 +-
clippy_dev/src/main.rs | 20 +-
clippy_dev/src/new_lint.rs | 24 +-
clippy_dev/src/update_lints.rs | 654 ++++++++----------
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 | 309 ++++-----
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 | 8 +-
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 | 170 ++++-
tests/ui/needless_match.rs | 202 ++++--
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 | 148 ++--
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, 3113 insertions(+), 1486 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 88f71931d92b5..b4097ea86a518 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 123af23881b62..dd6518d5241b5 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 d133e8cddabc7..c2ebba0683ca9 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 b0fb39e816996..8e5c739afe05a 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 59fde44754714..9c6d754b400fc 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 b8287980a4bac..1bc1a39542db5 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());
- process::exit(code.unwrap_or(1));
+ // 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);
+ }
}
diff --git a/clippy_dev/src/main.rs b/clippy_dev/src/main.rs
index 30a241c8ba151..b1fe35a0243f0 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 59658b42c79b1..7a3fd1317619e 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 d368ef1f46a2a..59db51fbfac51 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();
- }
+ for (rel_path, file) in WalkDir::new(&root_path)
+ .into_iter()
+ .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)
+ };
- let module = rel_path
- .components()
- .map(|c| c.as_os_str().to_str().unwrap())
- .collect::>()
- .join("::");
+ if module == "deprecated_lints" {
+ parse_deprecated_contents(&contents, &mut deprecated_lints);
+ } else {
+ parse_contents(&contents, module, &mut lints);
+ }
+ }
+ (lints, deprecated_lints)
+}
- parse_contents(&content, &module)
+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,)?)*) }
+ }
+ }
}
-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()
+/// 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));
+ }
}
-/// 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)
- .into_iter()
- .filter_map(Result::ok)
- .filter(|f| f.path().extension() == Some(OsStr::new("rs")))
+/// 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));
+ }
}
-/// Whether a file has had its text changed or not
-#[derive(PartialEq, Debug)]
-struct FileChange {
- changed: bool,
- new_lines: String,
+/// 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);
-
- 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());
- }
- } else if start.is_match(line) {
- if !replace_start {
- new_lines.push(line.to_string());
+ 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()),
+ };
+
+ 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);
}
- 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"
}
-declare_clippy_lint!{
- #[clippy::version = "Test version"]
- pub DOC_MARKDOWN,
- pedantic,
- "single line"
-}
-
-/// 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);
+/// 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)?;
+
+ 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);
+
+ 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()]
- });
- assert_eq!(expected, result);
- }
+ fn test_parse_contents() {
+ static CONTENTS: &str = r#"
+ declare_clippy_lint! {
+ #[clippy::version = "Hello Clippy!"]
+ pub PTR_ARG,
+ style,
+ "really long \
+ text"
+ }
- #[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()]
- });
+ 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_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);
+ 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"
+ }
+ "#;
+
+ 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 66e61660d313a..aebf9a87cabd2 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 0000000000000..e9b0f1f672de0
--- /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 a4ef1344ab951..d476a1a7646c0 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 be59145afa003..55c1f085657bb 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 470c8c7ea26a6..af56ec11ef8ac 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 0000000000000..fc141b4a6e3af
--- /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 92cf82bcd6a34..28d0c75fde6ba 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 5c4b35fd4b9d2..88c54828da834 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 0000000000000..fdeac8d82557f
--- /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 f824f20ca40a0..4d6bef89bea7f 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 9ead4bb27a588..4ba7477add82a 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 132a466267626..14ca93b5f3c14 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 a2ce69065f94d..10369a855ae6e 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 df63f84463dba..6bf2c4bbaedc0 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 21f1ef562b5a3..532590aaa5a3d 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 6ab139b2fb67b..4802dd877e99d 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 dcf399cf9562f..3114afac8863e 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 fa3a88e1368ce..82f45b5fd58b9 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 f2a0799914448..c9b836f95808a 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 36ecd83f7d643..a0bd7ad0ac647 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 0f6ac47843241..f552d5c1afab9 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 ff85623acf49b..e93b494653fc0 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 76131d307d777..2105a03e03a30 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 {
- 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;
+ for i in 0..pats.len() {
+ if !pat_same_as_expr(&pats[i], &exprs[i]) {
+ return false;
}
}
- false
+ true
}
diff --git a/clippy_lints/src/methods/bytes_nth.rs b/clippy_lints/src/methods/bytes_nth.rs
index 76eaedea8a0d2..44857d61fef8f 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 0000000000000..be9d4ad94fb8e
--- /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 c98cdfbca434e..9651a52be4e72 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 b93f1399eaeed..54c9ca435a447 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 f112b500d3d29..862a9578e6ff2 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 9d4e1fa399401..70d021a1668eb 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 0000000000000..7030baf19ff5c
--- /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 b8dfe99688066..0a393657267b0 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 9d3d7d1f24cbc..0000000000000
--- 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 6ef6b9a20aa4b..2f3007658ea62 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 5f453dc165558..48a2666a2e0ce 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 02569bd3a476e..342f23f030cd0 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 3eb07b68992a8..9e1823c373bfd 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 7570bc2a7a8f0..786e7bfc56f6e 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 e42c6c63ede0b..c8912a18f1854 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 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 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 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);
- }
- }
+ span_lint_and_help(
+ cx,
+ UNDOCUMENTED_UNSAFE_BLOCKS,
+ span,
+ "unsafe block missing a safety comment",
+ None,
+ "consider adding a safety comment on the preceding line",
+ );
}
}
+}
- 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;
- }
- }
+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"))
}
-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),
+/// 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
}
}
-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;
- }
- },
+/// 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,
}
-
- 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');
+ // 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);
}
-
- 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,
- );
+ 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 db652766705c4..0000000000000
--- 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 09d671e11184d..f8e1021af0ea1 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 680b2eb1da723..271c3a3dd181c 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 b3b241392fed5..25d74b8c49939 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 b3fad6ce7b651..ca03b8010dd82 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 d3ed8da4499f8..0b1fd95c3453d 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 62e144398012d..a275bac4ce63d 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 fce93153d96ec..0424e06720263 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 6f56f8d51365a..79e6e92dc0aaf 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 afe3033c288cf..c4f8f98938428 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 5befb856a0234..bb29c71e9f455 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 bc1b0d7457559..00dc916b217ca 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 af4c298b31085..e2010e9981315 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 11e15db7fb96b..f91940209383f 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 9302e02ccb9c4..67e1a07b7f5fa 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 242984680e163..32dd80246fab4 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 0000000000000..3c40f77469b82
--- /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 8a7afa934502d..9851d4791d8db 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 68719c2bc6d05..0d65071af15ed 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 2e31ad3172ee8..cf85a5ca931dd 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 0000000000000..4ec2465be06db
--- /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 0000000000000..59b9c8c367883
--- /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 0000000000000..eb12857357a44
--- /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 659591fffbecd..e4e7290a30e9e 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 aedd368445554..5df2b5b1094be 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 d44b0fae82001..88cfa1f923c0b 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 8d9a1af8ff118..4fe92d356c44d 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 0e2ab1a39b82f..9cbafc716b500 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 111350a6280dc..1a33e647588f1 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 676564b2445d5..376ff97ba6036 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 a41bcb33b4460..6f9d98bbfe7f3 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 0000000000000..ac456108e4ab1
--- /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 0000000000000..9ac5937dcc063
--- /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 1b0e7544e79c6..e0b4a2f694239 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 e9687777bbd0b..50bbb6eec6c70 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 55c082fcb19fb..bded9e2c0e801 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 e0a4828ce9ff6..3fceefa551c78 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 9ddd6d64701a6..7c7a9ecff67f5 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 01de0be7caea9..88228afae89c0 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 0000000000000..5a0ebde82c5d4
--- /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 0000000000000..f73068901c503
--- /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 e1a15c609fd23..7de0b0bbdf9ae 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 0000000000000..80f07603b8d4f
--- /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 0000000000000..1d1ed4c769025
--- /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 0000000000000..0308cb5571af2
--- /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 0000000000000..7e18d70bae400
--- /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 0000000000000..bf8c3c9fb8c98
--- /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 0000000000000..ffd97e00a5c09
--- /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 1442ee08e7546..f805bcc9ba8af 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 0000000000000..7580cf95ebfa8
--- /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 0000000000000..03fb00960a447
--- /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 c49e6756a6c5b..6c8c4c9c0edec 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 12bbda71f4348..edc3fe1aec13a 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 0103cf5457e81..706f01a3dd6c4 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 639fecb8927bd..2549c9f32f904 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 ca8ca53c80c3f..45a430edcb589 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 76ecec3348400..83a36f407d5d8 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 b54147c94d192..f23671c26e4cc 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 b92c7d18adb4f..7c968d4984571 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 a9041671101b5..56761ebbcb80b 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 dd04e33a4b3ae..98321d889b582 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 e36b0e36fbdf9..0582700fd16a8 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 e4a2e9df4d7ba..7601b5c66fa35 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 e3561863c1e1f..bb162adc9adb2 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 a842e872a37b1..1edb77c748bfb 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 136cc96be70ca..b7e46a4a8ccc2 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 ddc453ffdb750..b05429da66225 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 05d6c56f2aca0..7d68978216c9c 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 09f62c69b71de..b937fe6f977e5 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 4a1452b25f343..2256e51f2d09c 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 65c7e6e1ea554..ccfdc9ea76d52 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 e4a0320cbda55..b6a77281f6de2 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 9a74da4e3b8b6..e7f07b50f3ab1 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 c5f221220ece7..f83c3e0e281ca 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 6b3fdb0844b49..de225eb740d03 100644
--- a/tests/ui/min_rust_version_attr.stderr
+++ b/tests/ui/min_rust_version_attr.stderr
@@ -1,12 +1,12 @@
error: stripping a prefix manually
- --> $DIR/min_rust_version_attr.rs:186:24
+ --> $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:185:9
+ --> $DIR/min_rust_version_attr.rs:197:9
|
LL | if s.starts_with("hello, ") {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -17,13 +17,13 @@ LL ~ assert_eq!(.to_uppercase(), "WORLD!");
|
error: stripping a prefix manually
- --> $DIR/min_rust_version_attr.rs:198:24
+ --> $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:197:9
+ --> $DIR/min_rust_version_attr.rs:209:9
|
LL | if s.starts_with("hello, ") {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/missing_inline.rs b/tests/ui/missing_inline.rs
index b73b24b8e0a3b..07f8e3888c998 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 f5908cb5701fb..ebaa77cc283e0 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 bdd217a969c05..3f343a3e43018 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 047a29fa1e327..3ebe46bc5be7c 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 64335f35f0f82..11b5f77461ba2 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 ad0d694a2174a..02b43cce2bd4c 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 f3eafe8e9279e..1456204ca8692 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 ece18ad737fda..9ccccaa1725a6 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 do_something() {}
+
+ // Don't trigger
+ 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(x: Result<(), i32>) {
- let _: Result<(), i32> = x;
- let _: Result<(), i32> = x;
+fn if_let_result() {
+ let x: Result = Ok(1);
+ let _: Result = x;
+ let _: Result = x;
// Input type mismatch, don't trigger
- let _: Result<(), i32> = if let Err(e) = Ok(1) { Err(e) } else { x };
+ let _: Result = if let Err(e) = Ok(1) { Err(e) } else { x };
}
-fn if_let_custom_enum(x: Choice) {
- let _: Choice = x;
+fn if_let_custom_enum(x: Simple) {
+ let _: Simple = x;
+
// Don't trigger
- let _: Choice = if let Choice::A = x {
- Choice::A
+ 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 36649de35a603..d210ecff7f163 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),
};
+ // 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 {
- if let Some(a) = Some(1) { Some(a) } else { None }
+fn if_let_option() {
+ let _ = if let Some(a) = Some(1) { Some(a) } else { None };
+
+ fn do_something() {}
+
+ // Don't trigger
+ 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(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 };
+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<(), i32> = if let Err(e) = Ok(1) { Err(e) } else { x };
+ let _: Result = 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
+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 _: Choice = if let Choice::A = x {
- Choice::A
+ 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 = 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 ad1525406ade7..34c5226f06057 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 d721452ae8880..c09b07db3dca9 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 bb15512adf6aa..c3ba27ecccf22 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 5dd507b4a71e8..bc07db5b38ed3 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 5427c88faf348..7ece66a1ccb6f 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 7d29445e66c8b..1290bd8efebd2 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 b6f834f686f9f..f3e5b62c65b7f 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 8abdbcafb6e93..ab2a294a060f0 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 27d4b795a5eeb..6e0d5a87f6807 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 0dab5ae2f1c04..e406a71d46d00 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 7338064646244..12a0c776ae2fd 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 bfd1c7a380149..4ceb6d1440f61 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 03dd938a2339e..814bbc7af713b 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 9241bf7ed7402..f72fc77ab9977 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 52fbc91e32555..80f94e5f3cbbb 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 ef089b25f47fd..e7ed84731c02e 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 fefa87721d720..de763f98b5c89 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 16b40dcd90286..1525f6a93dfdd 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 bd3d7365229fb..2f82aefd92830 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 921249606ad27..acc8f1e25b6ed 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 4d4b249d076ff..f2f0f78659c93 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 631042c616bc0..14c331f67e739 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 679eb2326268c..8b0fca9ece1a3 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 4f3a8c6b79239..782febd526441 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 9d420ec672a26..99964f0de075c 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 c307afffcb863..63d31ff83f9b5 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 2c0c03b721199..2cda5a329d254 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 f52ca8c5a9b0c..0aeb8da1a2e23 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 21de19a260147..f5ca91143af25 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 9b681a79aae7a..5b688ce4d644f 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 86537153e3228..72a386e8fa618 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 380303d8152aa..afa337c45f417 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 f69d0da54e0d6..856a07fd31685 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
+ = help: consider adding a safety comment on the preceding line
+
+error: unsafe block missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:264:14
|
-LL ~ // SAFETY: ...
-LL + unsafe {}
+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:219:5
+ --> $DIR/undocumented_unsafe_blocks.rs:264:29
|
LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^^^^^
|
-help: consider adding a safety comment
+ = 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 ~ // SAFETY: ...
-LL + let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
+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:268:18
|
LL | let _ = (42, unsafe {}, "test", unsafe {});
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^
|
-help: consider adding a safety comment
+ = help: consider adding a safety comment on the preceding line
+
+error: unsafe block missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:268:37
|
-LL ~ // SAFETY: ...
-LL + let _ = (42, unsafe {}, "test", unsafe {});
+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: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 b77c19f2ba596..62c3e9636866a 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 3332f49c80c97..36800c5340db2 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 ec01e93877927..d4b6bb952ab35 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 690d705573d3f..bafca91917aa4 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 a1f616733bd92..cde4e96d668c2 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 7a4bbdda1ab27..08bf58fec7c3e 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 9d216f56ae60c..3e62ffe74fedd 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 5f604fe25e416..da2faddee12a7 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 a5fcde768f183..ce58a80347b55 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 0396d39e3d54e..c82bb9ba07fd7 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 77102b8cac0c9..38498ebdcf2c1 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/129] 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 c2ebba0683ca9..81faa5fe5e148 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/129] 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 df780747a0c75..a8f9c189adec8 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/129] 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 22b4589695512..c76061867145e 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 c32c3dd9880f6..cf06eb32e0c7f 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/129] 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 c76061867145e..a01e2f2db3afb 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/129] 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 e93b494653fc0..854cf06ed8198 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 dbad607c58ea3..beed73268037e 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 0000000000000..8ea631f2bbd42
--- /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 b624a41a29b2d..82387f3d80b77 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 21ea704b62ab5..7756c6f204e67 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/129] Don't lint `iter_with_drain` on references
---
clippy_lints/src/methods/iter_with_drain.rs | 91 ++++++++-------------
tests/ui/iter_with_drain.fixed | 9 ++
tests/ui/iter_with_drain.rs | 9 ++
3 files changed, 51 insertions(+), 58 deletions(-)
diff --git a/clippy_lints/src/methods/iter_with_drain.rs b/clippy_lints/src/methods/iter_with_drain.rs
index 958c3773087b6..152072e09c772 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 !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,
+ );
+ };
+}
- 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,
- );
+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 aea4dba9dd5a0..0330d5549264a 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 271878cffb44c..993936fb8de3d 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/129] 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 d5113df569a08..24ac364ffd9f7 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 80e02952dbd07..85c0c7d69778b 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/129] 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 b1fe35a0243f0..833b7393ff8b9 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/129] 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 88c54828da834..25014bfa1a5b1 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 f73068901c503..30121033de7ea 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/129] 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 00594f4d42add..20206ce82a72d 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 7f28f02579045..3d2295912c9fd 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 363a03846d236..71e82910ef752 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/129] 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 845863bd209c6..c2e32f1d9a23e 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 405e306359bc9..4236e3aae2fbd 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 5aedbea381f23..873ed689a5be7 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 5fdf7fb977169..4cb58eec94cd7 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 cda84982c9b75..d1ae889d6f360 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 c09b07db3dca9..acd22c6bb4337 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 c3ba27ecccf22..61eda5052a21e 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/129] 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 | 86 ++++-----
tests/ui/needless_splitn.fixed | 20 ++
tests/ui/needless_splitn.rs | 20 ++
tests/ui/needless_splitn.stderr | 44 ++++-
9 files changed, 216 insertions(+), 259 deletions(-)
diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs
index 021337280d139..7047aa4c69b5d 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 8125930b30461..6eaa9eb2ef0ec 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 04ea445665652..8ed8f3b3a0642 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 d5113df569a08..8621ddc746724 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 80e02952dbd07..c8a1d7b91e5d1 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 af9c7a2d41bff..9635e6c5e4b46 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)`
+LL | let _ = s.splitn(2, '=').skip(1).next()?;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=')?.1`
-error: manual implementation of `split_once`
- --> $DIR/manual_split_once.rs:32:17
+error: manual implementation of `rsplit_once`
+ --> $DIR/manual_split_once.rs:30:17
|
-LL | let _ = s.splitn(2, "key=value").nth(1)?;
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once("key=value")?.1`
+LL | let _ = s.rsplitn(2, '=').nth(1)?;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit_once('=')?.0`
-error: manual implementation of `split_once`
- --> $DIR/manual_split_once.rs:33:17
+error: manual implementation of `rsplit_once`
+ --> $DIR/manual_split_once.rs:31:17
|
-LL | let _ = s.splitn(2, "key=value").skip(1).next()?;
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once("key=value")?.1`
+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:42:13
+ --> $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
+ --> $DIR/manual_split_once.rs:40:18
|
-LL | let _ = "key=value".rsplitn(2, '=').nth(0);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').map(|x| x.1)`
+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:44:18
+ --> $DIR/manual_split_once.rs:41:13
|
-LL | let (_, _) = "key=value".rsplitn(2, '=').next_tuple().unwrap();
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').map(|(x, y)| (y, x))`
+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 f6a4b2f17d33d..61f5fc4e679ed 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 6ba32255bb2d7..71d9a7077faa6 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 66de2256554e3..f112b29e7f206 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/129] 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 b4097ea86a518..e48413dc30c5d 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 14ca93b5f3c14..b601e37a4c876 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 532590aaa5a3d..0a23e60fd2c4c 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 3114afac8863e..b014c11f0f4a3 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 c9b836f95808a..576a661126757 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 0000000000000..bb42c7816e7b2
--- /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 79e6e92dc0aaf..deb548daf2d0c 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 0000000000000..cd69e956fd4d3
--- /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 0000000000000..3fbba156240c7
--- /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 0000000000000..c32372290d1e9
--- /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/129] 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 e48413dc30c5d..44a36870108d5 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 b601e37a4c876..02ba7835639e7 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 0a23e60fd2c4c..704e79885cfb4 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 b014c11f0f4a3..f52fe97ed237c 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 576a661126757..74ade422dc887 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 bb42c7816e7b2..8a4f4c0ad9719 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 cd69e956fd4d3..f95f91329a2fa 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 3fbba156240c7..0cbdc151ed9b1 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 c32372290d1e9..46bc4597b335f 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/129] 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 891531951c1a0..fe41122048489 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/129] 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 c0947685fa1d5..bc57e9b992c61 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 0000000000000..95b6315132526
--- /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 0000000000000..ee14f011f631b
--- /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/129] 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 421bd6f53f71b..64f87c80f8d14 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 cf85a5ca931dd..e6031e9adaeb6 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 7a68c0984f140..0c63b4af30865 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/129] 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 e233300e26ab8..a03d5d540653d 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 a1e7361c0cb32..5dae5af7eb5a9 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/129] 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 a03d5d540653d..ceb66947d02c6 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"
- } 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"
+
+ 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 {
- "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/129] 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 5076239a57c4d..0aadb482acdda 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 38fc9969804fa..086331af6b567 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/129] 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 8e61f2347767d..f946fc1119281 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/129] 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 | 176 ++++++++++--------
5 files changed, 138 insertions(+), 87 deletions(-)
diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs
index 56a8782cee427..e2abce86791b6 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 1555758fc4ad8..02b882e8b55e0 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 38ba41ac54ecb..7455e22d49b21 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 $DIR/unnecessary_to_owned.rs:150:64
+ --> $DIR/unnecessary_to_owned.rs:151:64
|
LL | require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned());
| ^^^^^^^^^^^ help: remove this
|
= note: `-D clippy::redundant-clone` implied by `-D warnings`
note: this value is dropped without further use
- --> $DIR/unnecessary_to_owned.rs:150:20
+ --> $DIR/unnecessary_to_owned.rs:151:20
|
LL | require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: redundant clone
- --> $DIR/unnecessary_to_owned.rs:151:40
+ --> $DIR/unnecessary_to_owned.rs:152:40
|
LL | require_os_str(&OsString::from("x").to_os_string());
| ^^^^^^^^^^^^^^^ help: remove this
|
note: this value is dropped without further use
- --> $DIR/unnecessary_to_owned.rs:151:21
+ --> $DIR/unnecessary_to_owned.rs:152:21
|
LL | require_os_str(&OsString::from("x").to_os_string());
| ^^^^^^^^^^^^^^^^^^^
error: redundant clone
- --> $DIR/unnecessary_to_owned.rs:152:48
+ --> $DIR/unnecessary_to_owned.rs:153:48
|
LL | require_path(&std::path::PathBuf::from("x").to_path_buf());
| ^^^^^^^^^^^^^^ help: remove this
|
note: this value is dropped without further use
- --> $DIR/unnecessary_to_owned.rs:152:19
+ --> $DIR/unnecessary_to_owned.rs:153:19
|
LL | require_path(&std::path::PathBuf::from("x").to_path_buf());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: redundant clone
- --> $DIR/unnecessary_to_owned.rs:153:35
+ --> $DIR/unnecessary_to_owned.rs:154:35
|
LL | require_str(&String::from("x").to_string());
| ^^^^^^^^^^^^ help: remove this
|
note: this value is dropped without further use
- --> $DIR/unnecessary_to_owned.rs:153:18
+ --> $DIR/unnecessary_to_owned.rs:154:18
|
LL | require_str(&String::from("x").to_string());
| ^^^^^^^^^^^^^^^^^
error: unnecessary use of `into_owned`
- --> $DIR/unnecessary_to_owned.rs:59:36
+ --> $DIR/unnecessary_to_owned.rs:60:36
|
LL | require_c_str(&Cow::from(c_str).into_owned());
| ^^^^^^^^^^^^^ help: remove this
@@ -56,427 +56,427 @@ LL | require_c_str(&Cow::from(c_str).into_owned());
= note: `-D clippy::unnecessary-to-owned` implied by `-D warnings`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:60:19
+ --> $DIR/unnecessary_to_owned.rs:61:19
|
LL | require_c_str(&c_str.to_owned());
| ^^^^^^^^^^^^^^^^^ help: use: `c_str`
error: unnecessary use of `to_os_string`
- --> $DIR/unnecessary_to_owned.rs:62:20
+ --> $DIR/unnecessary_to_owned.rs:63:20
|
LL | require_os_str(&os_str.to_os_string());
| ^^^^^^^^^^^^^^^^^^^^^^ help: use: `os_str`
error: unnecessary use of `into_owned`
- --> $DIR/unnecessary_to_owned.rs:63:38
+ --> $DIR/unnecessary_to_owned.rs:64:38
|
LL | require_os_str(&Cow::from(os_str).into_owned());
| ^^^^^^^^^^^^^ help: remove this
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:64:20
+ --> $DIR/unnecessary_to_owned.rs:65:20
|
LL | require_os_str(&os_str.to_owned());
| ^^^^^^^^^^^^^^^^^^ help: use: `os_str`
error: unnecessary use of `to_path_buf`
- --> $DIR/unnecessary_to_owned.rs:66:18
+ --> $DIR/unnecessary_to_owned.rs:67:18
|
LL | require_path(&path.to_path_buf());
| ^^^^^^^^^^^^^^^^^^^ help: use: `path`
error: unnecessary use of `into_owned`
- --> $DIR/unnecessary_to_owned.rs:67:34
+ --> $DIR/unnecessary_to_owned.rs:68:34
|
LL | require_path(&Cow::from(path).into_owned());
| ^^^^^^^^^^^^^ help: remove this
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:68:18
+ --> $DIR/unnecessary_to_owned.rs:69:18
|
LL | require_path(&path.to_owned());
| ^^^^^^^^^^^^^^^^ help: use: `path`
error: unnecessary use of `to_string`
- --> $DIR/unnecessary_to_owned.rs:70:17
+ --> $DIR/unnecessary_to_owned.rs:71:17
|
LL | require_str(&s.to_string());
| ^^^^^^^^^^^^^^ help: use: `s`
error: unnecessary use of `into_owned`
- --> $DIR/unnecessary_to_owned.rs:71:30
+ --> $DIR/unnecessary_to_owned.rs:72:30
|
LL | require_str(&Cow::from(s).into_owned());
| ^^^^^^^^^^^^^ help: remove this
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:72:17
+ --> $DIR/unnecessary_to_owned.rs:73:17
|
LL | require_str(&s.to_owned());
| ^^^^^^^^^^^^^ help: use: `s`
error: unnecessary use of `to_string`
- --> $DIR/unnecessary_to_owned.rs:73:17
+ --> $DIR/unnecessary_to_owned.rs:74:17
|
LL | require_str(&x_ref.to_string());
| ^^^^^^^^^^^^^^^^^^ help: use: `x_ref.as_ref()`
error: unnecessary use of `to_vec`
- --> $DIR/unnecessary_to_owned.rs:75:19
+ --> $DIR/unnecessary_to_owned.rs:76:19
|
LL | require_slice(&slice.to_vec());
| ^^^^^^^^^^^^^^^ help: use: `slice`
error: unnecessary use of `into_owned`
- --> $DIR/unnecessary_to_owned.rs:76:36
+ --> $DIR/unnecessary_to_owned.rs:77:36
|
LL | require_slice(&Cow::from(slice).into_owned());
| ^^^^^^^^^^^^^ help: remove this
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:77:19
+ --> $DIR/unnecessary_to_owned.rs:78:19
|
LL | require_slice(&array.to_owned());
| ^^^^^^^^^^^^^^^^^ help: use: `array.as_ref()`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:78:19
+ --> $DIR/unnecessary_to_owned.rs:79:19
|
LL | require_slice(&array_ref.to_owned());
| ^^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref.as_ref()`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:79:19
+ --> $DIR/unnecessary_to_owned.rs:80:19
|
LL | require_slice(&slice.to_owned());
| ^^^^^^^^^^^^^^^^^ help: use: `slice`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:80:19
+ --> $DIR/unnecessary_to_owned.rs:81:19
|
LL | require_slice(&x_ref.to_owned());
| ^^^^^^^^^^^^^^^^^ help: use: `x_ref`
error: unnecessary use of `into_owned`
- --> $DIR/unnecessary_to_owned.rs:82:42
+ --> $DIR/unnecessary_to_owned.rs:83:42
|
LL | require_x(&Cow::::Owned(x.clone()).into_owned());
| ^^^^^^^^^^^^^ help: remove this
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:83:15
+ --> $DIR/unnecessary_to_owned.rs:84:15
|
LL | require_x(&x_ref.to_owned());
| ^^^^^^^^^^^^^^^^^ help: use: `x_ref`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:85:25
+ --> $DIR/unnecessary_to_owned.rs:86:25
|
LL | require_deref_c_str(c_str.to_owned());
| ^^^^^^^^^^^^^^^^ help: use: `c_str`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:86:26
+ --> $DIR/unnecessary_to_owned.rs:87:26
|
LL | require_deref_os_str(os_str.to_owned());
| ^^^^^^^^^^^^^^^^^ help: use: `os_str`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:87:24
+ --> $DIR/unnecessary_to_owned.rs:88:24
|
LL | require_deref_path(path.to_owned());
| ^^^^^^^^^^^^^^^ help: use: `path`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:88:23
+ --> $DIR/unnecessary_to_owned.rs:89:23
|
LL | require_deref_str(s.to_owned());
| ^^^^^^^^^^^^ help: use: `s`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:89:25
+ --> $DIR/unnecessary_to_owned.rs:90:25
|
LL | require_deref_slice(slice.to_owned());
| ^^^^^^^^^^^^^^^^ help: use: `slice`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:91:30
+ --> $DIR/unnecessary_to_owned.rs:92:30
|
LL | require_impl_deref_c_str(c_str.to_owned());
| ^^^^^^^^^^^^^^^^ help: use: `c_str`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:92:31
+ --> $DIR/unnecessary_to_owned.rs:93:31
|
LL | require_impl_deref_os_str(os_str.to_owned());
| ^^^^^^^^^^^^^^^^^ help: use: `os_str`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:93:29
+ --> $DIR/unnecessary_to_owned.rs:94:29
|
LL | require_impl_deref_path(path.to_owned());
| ^^^^^^^^^^^^^^^ help: use: `path`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:94:28
+ --> $DIR/unnecessary_to_owned.rs:95:28
|
LL | require_impl_deref_str(s.to_owned());
| ^^^^^^^^^^^^ help: use: `s`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:95:30
+ --> $DIR/unnecessary_to_owned.rs:96:30
|
LL | require_impl_deref_slice(slice.to_owned());
| ^^^^^^^^^^^^^^^^ help: use: `slice`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:97:29
+ --> $DIR/unnecessary_to_owned.rs:98:29
|
LL | require_deref_str_slice(s.to_owned(), slice.to_owned());
| ^^^^^^^^^^^^ help: use: `s`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:97:43
+ --> $DIR/unnecessary_to_owned.rs:98:43
|
LL | require_deref_str_slice(s.to_owned(), slice.to_owned());
| ^^^^^^^^^^^^^^^^ help: use: `slice`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:98:29
+ --> $DIR/unnecessary_to_owned.rs:99:29
|
LL | require_deref_slice_str(slice.to_owned(), s.to_owned());
| ^^^^^^^^^^^^^^^^ help: use: `slice`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:98:47
+ --> $DIR/unnecessary_to_owned.rs:99:47
|
LL | require_deref_slice_str(slice.to_owned(), s.to_owned());
| ^^^^^^^^^^^^ help: use: `s`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:100:26
+ --> $DIR/unnecessary_to_owned.rs:101:26
|
LL | require_as_ref_c_str(c_str.to_owned());
| ^^^^^^^^^^^^^^^^ help: use: `c_str`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:101:27
+ --> $DIR/unnecessary_to_owned.rs:102:27
|
LL | require_as_ref_os_str(os_str.to_owned());
| ^^^^^^^^^^^^^^^^^ help: use: `os_str`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:102:25
+ --> $DIR/unnecessary_to_owned.rs:103:25
|
LL | require_as_ref_path(path.to_owned());
| ^^^^^^^^^^^^^^^ help: use: `path`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:103:24
+ --> $DIR/unnecessary_to_owned.rs:104:24
|
LL | require_as_ref_str(s.to_owned());
| ^^^^^^^^^^^^ help: use: `s`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:104:24
+ --> $DIR/unnecessary_to_owned.rs:105:24
|
LL | require_as_ref_str(x.to_owned());
| ^^^^^^^^^^^^ help: use: `&x`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:105:26
+ --> $DIR/unnecessary_to_owned.rs:106:26
|
LL | require_as_ref_slice(array.to_owned());
| ^^^^^^^^^^^^^^^^ help: use: `array`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:106:26
+ --> $DIR/unnecessary_to_owned.rs:107:26
|
LL | require_as_ref_slice(array_ref.to_owned());
| ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:107:26
+ --> $DIR/unnecessary_to_owned.rs:108:26
|
LL | require_as_ref_slice(slice.to_owned());
| ^^^^^^^^^^^^^^^^ help: use: `slice`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:109:31
+ --> $DIR/unnecessary_to_owned.rs:110:31
|
LL | require_impl_as_ref_c_str(c_str.to_owned());
| ^^^^^^^^^^^^^^^^ help: use: `c_str`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:110:32
+ --> $DIR/unnecessary_to_owned.rs:111:32
|
LL | require_impl_as_ref_os_str(os_str.to_owned());
| ^^^^^^^^^^^^^^^^^ help: use: `os_str`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:111:30
+ --> $DIR/unnecessary_to_owned.rs:112:30
|
LL | require_impl_as_ref_path(path.to_owned());
| ^^^^^^^^^^^^^^^ help: use: `path`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:112:29
+ --> $DIR/unnecessary_to_owned.rs:113:29
|
LL | require_impl_as_ref_str(s.to_owned());
| ^^^^^^^^^^^^ help: use: `s`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:113:29
+ --> $DIR/unnecessary_to_owned.rs:114:29
|
LL | require_impl_as_ref_str(x.to_owned());
| ^^^^^^^^^^^^ help: use: `&x`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:114:31
+ --> $DIR/unnecessary_to_owned.rs:115:31
|
LL | require_impl_as_ref_slice(array.to_owned());
| ^^^^^^^^^^^^^^^^ help: use: `array`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:115:31
+ --> $DIR/unnecessary_to_owned.rs:116:31
|
LL | require_impl_as_ref_slice(array_ref.to_owned());
| ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:116:31
+ --> $DIR/unnecessary_to_owned.rs:117:31
|
LL | require_impl_as_ref_slice(slice.to_owned());
| ^^^^^^^^^^^^^^^^ help: use: `slice`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:118:30
+ --> $DIR/unnecessary_to_owned.rs:119:30
|
LL | require_as_ref_str_slice(s.to_owned(), array.to_owned());
| ^^^^^^^^^^^^ help: use: `s`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:118:44
+ --> $DIR/unnecessary_to_owned.rs:119:44
|
LL | require_as_ref_str_slice(s.to_owned(), array.to_owned());
| ^^^^^^^^^^^^^^^^ help: use: `array`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:119:30
+ --> $DIR/unnecessary_to_owned.rs:120:30
|
LL | require_as_ref_str_slice(s.to_owned(), array_ref.to_owned());
| ^^^^^^^^^^^^ help: use: `s`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:119:44
+ --> $DIR/unnecessary_to_owned.rs:120:44
|
LL | require_as_ref_str_slice(s.to_owned(), array_ref.to_owned());
| ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:120:30
+ --> $DIR/unnecessary_to_owned.rs:121:30
|
LL | require_as_ref_str_slice(s.to_owned(), slice.to_owned());
| ^^^^^^^^^^^^ help: use: `s`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:120:44
+ --> $DIR/unnecessary_to_owned.rs:121:44
|
LL | require_as_ref_str_slice(s.to_owned(), slice.to_owned());
| ^^^^^^^^^^^^^^^^ help: use: `slice`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:121:30
+ --> $DIR/unnecessary_to_owned.rs:122:30
|
LL | require_as_ref_slice_str(array.to_owned(), s.to_owned());
| ^^^^^^^^^^^^^^^^ help: use: `array`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:121:48
+ --> $DIR/unnecessary_to_owned.rs:122:48
|
LL | require_as_ref_slice_str(array.to_owned(), s.to_owned());
| ^^^^^^^^^^^^ help: use: `s`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:122:30
+ --> $DIR/unnecessary_to_owned.rs:123:30
|
LL | require_as_ref_slice_str(array_ref.to_owned(), s.to_owned());
| ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:122:52
+ --> $DIR/unnecessary_to_owned.rs:123:52
|
LL | require_as_ref_slice_str(array_ref.to_owned(), s.to_owned());
| ^^^^^^^^^^^^ help: use: `s`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:123:30
+ --> $DIR/unnecessary_to_owned.rs:124:30
|
LL | require_as_ref_slice_str(slice.to_owned(), s.to_owned());
| ^^^^^^^^^^^^^^^^ help: use: `slice`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:123:48
+ --> $DIR/unnecessary_to_owned.rs:124:48
|
LL | require_as_ref_slice_str(slice.to_owned(), s.to_owned());
| ^^^^^^^^^^^^ help: use: `s`
error: unnecessary use of `to_string`
- --> $DIR/unnecessary_to_owned.rs:125:20
+ --> $DIR/unnecessary_to_owned.rs:126:20
|
LL | let _ = x.join(&x_ref.to_string());
| ^^^^^^^^^^^^^^^^^^ help: use: `x_ref`
error: unnecessary use of `to_vec`
- --> $DIR/unnecessary_to_owned.rs:127:13
+ --> $DIR/unnecessary_to_owned.rs:128:13
|
LL | let _ = slice.to_vec().into_iter();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:128:13
+ --> $DIR/unnecessary_to_owned.rs:129:13
|
LL | let _ = slice.to_owned().into_iter();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()`
error: unnecessary use of `to_vec`
- --> $DIR/unnecessary_to_owned.rs:129:13
+ --> $DIR/unnecessary_to_owned.rs:130:13
|
LL | let _ = [std::path::PathBuf::new()][..].to_vec().into_iter();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:130:13
+ --> $DIR/unnecessary_to_owned.rs:131:13
|
LL | let _ = [std::path::PathBuf::new()][..].to_owned().into_iter();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()`
error: unnecessary use of `to_vec`
- --> $DIR/unnecessary_to_owned.rs:132:13
+ --> $DIR/unnecessary_to_owned.rs:133:13
|
LL | let _ = IntoIterator::into_iter(slice.to_vec());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:133:13
+ --> $DIR/unnecessary_to_owned.rs:134:13
|
LL | let _ = IntoIterator::into_iter(slice.to_owned());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()`
error: unnecessary use of `to_vec`
- --> $DIR/unnecessary_to_owned.rs:134:13
+ --> $DIR/unnecessary_to_owned.rs:135:13
|
LL | let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_vec());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:135:13
+ --> $DIR/unnecessary_to_owned.rs:136:13
|
LL | let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_owned());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()`
error: unnecessary use of `to_vec`
- --> $DIR/unnecessary_to_owned.rs:196:14
+ --> $DIR/unnecessary_to_owned.rs:197:14
|
LL | for t in file_types.to_vec() {
| ^^^^^^^^^^^^^^^^^^^
@@ -491,11 +491,23 @@ LL - let path = match get_file_path(&t) {
LL + let path = match get_file_path(t) {
|
+error: unnecessary use of `to_vec`
+ --> $DIR/unnecessary_to_owned.rs:220:14
+ |
+LL | let _ = &["x"][..].to_vec().into_iter();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `["x"][..].iter().cloned()`
+
+error: unnecessary use of `to_vec`
+ --> $DIR/unnecessary_to_owned.rs:225:14
+ |
+LL | let _ = &["x"][..].to_vec().into_iter();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `["x"][..].iter().copied()`
+
error: unnecessary use of `to_string`
- --> $DIR/unnecessary_to_owned.rs:260:24
+ --> $DIR/unnecessary_to_owned.rs:272:24
|
LL | Box::new(build(y.to_string()))
| ^^^^^^^^^^^^^ help: use: `y`
-error: aborting due to 77 previous errors
+error: aborting due to 79 previous errors
From 5b7df246cffdfeeadb95412a0143442a36c08496 Mon Sep 17 00:00:00 2001
From: David Wood
Date: Tue, 12 Apr 2022 09:34:40 +0100
Subject: [PATCH 035/129] errors: lazily load fallback fluent bundle
Loading the fallback bundle in compilation sessions that won't go on to
emit any errors unnecessarily degrades compile time performance, so
lazily create the Fluent bundle when it is first required.
Signed-off-by: David Wood
---
clippy_lints/src/doc.rs | 6 ++++--
src/driver.rs | 3 ++-
2 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs
index 28d0c75fde6ba..503cef76775e4 100644
--- a/clippy_lints/src/doc.rs
+++ b/clippy_lints/src/doc.rs
@@ -621,8 +621,10 @@ 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(
+ rustc_errors::DEFAULT_LOCALE_RESOURCES,
+ false
+ );
let emitter = EmitterWriter::new(
Box::new(io::sink()),
None,
diff --git a/src/driver.rs b/src/driver.rs
index 00dc916b217ca..32a09fdb9d9ff 100644
--- a/src/driver.rs
+++ b/src/driver.rs
@@ -165,7 +165,8 @@ 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(rustc_errors::DEFAULT_LOCALE_RESOURCES, false);
let emitter = Box::new(rustc_errors::emitter::EmitterWriter::stderr(
rustc_errors::ColorConfig::Auto,
None,
From 66d253f0f219578264ac7cb4e1c10f01dcfe8ad7 Mon Sep 17 00:00:00 2001
From: Yoav Lavi
Date: Tue, 12 Apr 2022 11:09:34 +0200
Subject: [PATCH 036/129] pub_use
---
CHANGELOG.md | 1 +
clippy_lints/src/lib.register_lints.rs | 1 +
clippy_lints/src/lib.register_restriction.rs | 1 +
clippy_lints/src/lib.rs | 2 +
clippy_lints/src/pub_use.rs | 56 ++++++++++++++++++++
tests/ui/pub_use.rs | 14 +++++
tests/ui/pub_use.stderr | 11 ++++
7 files changed, 86 insertions(+)
create mode 100644 clippy_lints/src/pub_use.rs
create mode 100644 tests/ui/pub_use.rs
create mode 100644 tests/ui/pub_use.stderr
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 44a36870108d5..444c649c84f01 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3527,6 +3527,7 @@ Released 2018-09-13
[`ptr_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_eq
[`ptr_offset_with_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_offset_with_cast
[`pub_enum_variant_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_enum_variant_names
+[`pub_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_use
[`question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#question_mark
[`range_minus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_minus_one
[`range_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_plus_one
diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs
index 704e79885cfb4..4132802c9c6e6 100644
--- a/clippy_lints/src/lib.register_lints.rs
+++ b/clippy_lints/src/lib.register_lints.rs
@@ -432,6 +432,7 @@ store.register_lints(&[
ptr::PTR_ARG,
ptr_eq::PTR_EQ,
ptr_offset_with_cast::PTR_OFFSET_WITH_CAST,
+ pub_use::PUB_USE,
question_mark::QUESTION_MARK,
ranges::MANUAL_RANGE_CONTAINS,
ranges::RANGE_MINUS_ONE,
diff --git a/clippy_lints/src/lib.register_restriction.rs b/clippy_lints/src/lib.register_restriction.rs
index 4802dd877e99d..555a8ad841fec 100644
--- a/clippy_lints/src/lib.register_restriction.rs
+++ b/clippy_lints/src/lib.register_restriction.rs
@@ -53,6 +53,7 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve
LintId::of(panic_unimplemented::UNIMPLEMENTED),
LintId::of(panic_unimplemented::UNREACHABLE),
LintId::of(pattern_type_mismatch::PATTERN_TYPE_MISMATCH),
+ LintId::of(pub_use::PUB_USE),
LintId::of(redundant_slicing::DEREF_BY_SLICING),
LintId::of(same_name_method::SAME_NAME_METHOD),
LintId::of(shadow::SHADOW_REUSE),
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index 74ade422dc887..17552873b0a09 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -336,6 +336,7 @@ mod precedence;
mod ptr;
mod ptr_eq;
mod ptr_offset_with_cast;
+mod pub_use;
mod question_mark;
mod ranges;
mod redundant_clone;
@@ -870,6 +871,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_strings::UnnecessaryOwnedEmptyStrings));
+ store.register_early_pass(|| Box::new(pub_use::PubUse));
// add lints here, do not remove this comment, it's used in `new_lint`
}
diff --git a/clippy_lints/src/pub_use.rs b/clippy_lints/src/pub_use.rs
new file mode 100644
index 0000000000000..9d2b0cedb60a1
--- /dev/null
+++ b/clippy_lints/src/pub_use.rs
@@ -0,0 +1,56 @@
+use clippy_utils::diagnostics::span_lint_and_help;
+use rustc_ast::ast::{Item, ItemKind, VisibilityKind};
+use rustc_lint::{EarlyContext, EarlyLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+ /// ### What it does
+ ///
+ /// Restricts the usage of `pub use ...`
+ ///
+ /// ### Why is this bad?
+ ///
+ /// `pub use` is usually fine, but a project may wish to limit `pub use` instances to prevent
+ /// unintentional exports or to encourage placing exported items directly in public modules
+ ///
+ /// ### Example
+ /// ```rust
+ /// pub mod outer {
+ /// mod inner {
+ /// pub struct Test {}
+ /// }
+ /// pub use inner::Test;
+ /// }
+ ///
+ /// use outer::Test;
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// pub mod outer {
+ /// pub struct Test {}
+ /// }
+ ///
+ /// use outer::Test;
+ /// ```
+ #[clippy::version = "1.62.0"]
+ pub PUB_USE,
+ restriction,
+ "restricts the usage of `pub use`"
+}
+declare_lint_pass!(PubUse => [PUB_USE]);
+
+impl EarlyLintPass for PubUse {
+ fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
+ if let ItemKind::Use(_) = item.kind &&
+ let VisibilityKind::Public = item.vis.kind {
+ span_lint_and_help(
+ cx,
+ PUB_USE,
+ item.span,
+ "using `pub use`",
+ None,
+ "move the exported item to a public module instead",
+ );
+ }
+ }
+}
diff --git a/tests/ui/pub_use.rs b/tests/ui/pub_use.rs
new file mode 100644
index 0000000000000..65542bedec7bc
--- /dev/null
+++ b/tests/ui/pub_use.rs
@@ -0,0 +1,14 @@
+#![warn(clippy::pub_use)]
+#![allow(unused_imports)]
+#![no_main]
+
+pub mod outer {
+ mod inner {
+ pub struct Test {}
+ }
+ // should be linted
+ pub use inner::Test;
+}
+
+// should not be linted
+use std::fmt;
diff --git a/tests/ui/pub_use.stderr b/tests/ui/pub_use.stderr
new file mode 100644
index 0000000000000..9ab710df818ca
--- /dev/null
+++ b/tests/ui/pub_use.stderr
@@ -0,0 +1,11 @@
+error: using `pub use`
+ --> $DIR/pub_use.rs:10:5
+ |
+LL | pub use inner::Test;
+ | ^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::pub-use` implied by `-D warnings`
+ = help: move the exported item to a public module instead
+
+error: aborting due to previous error
+
From ba15cdd3c159b5feda929b7af0d929e17a6f5c6d Mon Sep 17 00:00:00 2001
From: xFrednet