New lint [redundant_rest_pattern]

This commit is contained in:
Catherine 2023-06-23 06:02:39 -05:00
parent ecdea8cdd3
commit 9a8347ded5
12 changed files with 158 additions and 5 deletions

View file

@ -5141,6 +5141,7 @@ Released 2018-09-13
[`redundant_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern
[`redundant_pattern_matching`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern_matching
[`redundant_pub_crate`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pub_crate
[`redundant_rest_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_rest_pattern
[`redundant_slicing`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_slicing
[`redundant_static_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes
[`redundant_type_annotations`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_type_annotations

View file

@ -431,6 +431,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::misc_early::DUPLICATE_UNDERSCORE_ARGUMENT_INFO,
crate::misc_early::MIXED_CASE_HEX_LITERALS_INFO,
crate::misc_early::REDUNDANT_PATTERN_INFO,
crate::misc_early::REDUNDANT_REST_PATTERN_INFO,
crate::misc_early::SEPARATED_LITERAL_SUFFIX_INFO,
crate::misc_early::UNNEEDED_FIELD_PATTERN_INFO,
crate::misc_early::UNNEEDED_WILDCARD_PATTERN_INFO,

View file

@ -322,7 +322,7 @@ impl<'tcx> Visitor<'tcx> for IterFunctionVisitor<'_, 'tcx> {
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
// Check function calls on our collection
if let ExprKind::MethodCall(method_name, recv, [args @ ..], _) = &expr.kind {
if let ExprKind::MethodCall(method_name, recv, args, _) = &expr.kind {
if method_name.ident.name == sym!(collect) && is_trait_method(self.cx, expr, sym::Iterator) {
self.current_mutably_captured_ids = get_captured_ids(self.cx, self.cx.typeck_results().expr_ty(recv));
self.visit_expr(recv);

View file

@ -289,7 +289,7 @@ fn parse_iter_usage<'tcx>(
) -> Option<IterUsage> {
let (kind, span) = match iter.next() {
Some((_, Node::Expr(e))) if e.span.ctxt() == ctxt => {
let ExprKind::MethodCall(name, _, [args @ ..], _) = e.kind else {
let ExprKind::MethodCall(name, _, args, _) = e.kind else {
return None;
};
let did = cx.typeck_results().type_dependent_def_id(e.hir_id)?;

View file

@ -3,6 +3,7 @@ mod double_neg;
mod literal_suffix;
mod mixed_case_hex_literals;
mod redundant_pattern;
mod redundant_rest_pattern;
mod unneeded_field_pattern;
mod unneeded_wildcard_pattern;
mod zero_prefixed_literal;
@ -318,6 +319,34 @@ declare_clippy_lint! {
"tuple patterns with a wildcard pattern (`_`) is next to a rest pattern (`..`)"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for `[all @ ..]` patterns.
///
/// ### Why is this bad?
/// In all cases, `all` works fine and can often make code simpler, as you possibly won't need
/// to convert from say a `Vec` to a slice by dereferencing.
///
/// ### Example
/// ```rust,ignore
/// if let [all @ ..] = &*v {
/// // NOTE: Type is a slice here
/// println!("all elements: {all:#?}");
/// }
/// ```
/// Use instead:
/// ```rust,ignore
/// if let all = v {
/// // NOTE: Type is a `Vec` here
/// println!("all elements: {all:#?}");
/// }
/// ```
#[clippy::version = "1.72.0"]
pub REDUNDANT_REST_PATTERN,
complexity,
"checks for `[all @ ..]` where `all` would suffice"
}
declare_lint_pass!(MiscEarlyLints => [
UNNEEDED_FIELD_PATTERN,
DUPLICATE_UNDERSCORE_ARGUMENT,
@ -329,6 +358,7 @@ declare_lint_pass!(MiscEarlyLints => [
BUILTIN_TYPE_SHADOW,
REDUNDANT_PATTERN,
UNNEEDED_WILDCARD_PATTERN,
REDUNDANT_REST_PATTERN,
]);
impl EarlyLintPass for MiscEarlyLints {
@ -345,6 +375,7 @@ impl EarlyLintPass for MiscEarlyLints {
unneeded_field_pattern::check(cx, pat);
redundant_pattern::check(cx, pat);
redundant_rest_pattern::check(cx, pat);
unneeded_wildcard_pattern::check(cx, pat);
}

View file

@ -0,0 +1,26 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use rustc_ast::{Pat, PatKind};
use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, LintContext};
use rustc_middle::lint::in_external_macro;
use super::REDUNDANT_REST_PATTERN;
pub(super) fn check(cx: &EarlyContext<'_>, pat: &Pat) {
if !in_external_macro(cx.sess(), pat.span)
&& let PatKind::Slice(slice) = &pat.kind
&& let [one] = &**slice
&& let PatKind::Ident(annotation, ident, Some(rest)) = &one.kind
&& let PatKind::Rest = rest.kind
{
span_lint_and_sugg(
cx,
REDUNDANT_REST_PATTERN,
pat.span,
"using a rest pattern to bind an entire slice to a local",
"this is better represented with just the binding",
format!("{}{ident}", annotation.prefix_str()),
Applicability::MachineApplicable,
);
}
}

View file

@ -942,7 +942,7 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> {
},
// item is used in a call
// i.e.: `Call`: `|x| please(x)` or `MethodCall`: `|x| [1, 2, 3].contains(x)`
ExprKind::Call(_, [call_args @ ..]) | ExprKind::MethodCall(_, _, [call_args @ ..], _) => {
ExprKind::Call(_, call_args) | ExprKind::MethodCall(_, _, call_args, _) => {
let expr = self.cx.tcx.hir().expect_expr(cmt.hir_id);
let arg_ty_kind = self.cx.typeck_results().expr_ty(expr).kind();

View file

@ -1,5 +1,5 @@
#![allow(unused_braces, unused_variables, dead_code)]
#![allow(clippy::collapsible_else_if, clippy::let_unit_value)]
#![allow(clippy::collapsible_else_if, clippy::let_unit_value, clippy::redundant_rest_pattern)]
#![warn(clippy::manual_let_else)]
// Ensure that we don't conflict with match -> if let lints
#![warn(clippy::single_match_else, clippy::single_match)]

View file

@ -1,5 +1,5 @@
#![warn(clippy::match_on_vec_items)]
#![allow(clippy::useless_vec)]
#![allow(clippy::redundant_rest_pattern, clippy::useless_vec)]
fn match_with_wildcard() {
let arr = vec![0, 1, 2, 3];

View file

@ -0,0 +1,27 @@
//@run-rustfix
//@aux-build:proc_macros.rs
#![allow(irrefutable_let_patterns, unused)]
#![warn(clippy::redundant_rest_pattern)]
#[macro_use]
extern crate proc_macros;
fn main() {
if let a = [()] {}
if let ref a = [()] {}
if let mut a = [()] {}
if let ref mut a = [()] {}
let v = vec![()];
if let a = &*v {}
let s = &[()];
if let a = s {}
// Don't lint
if let [..] = &*v {}
if let [a] = &*v {}
if let [()] = &*v {}
if let [first, rest @ ..] = &*v {}
if let a = [()] {}
external! {
if let [a @ ..] = [()] {}
}
}

View file

@ -0,0 +1,27 @@
//@run-rustfix
//@aux-build:proc_macros.rs
#![allow(irrefutable_let_patterns, unused)]
#![warn(clippy::redundant_rest_pattern)]
#[macro_use]
extern crate proc_macros;
fn main() {
if let [a @ ..] = [()] {}
if let [ref a @ ..] = [()] {}
if let [mut a @ ..] = [()] {}
if let [ref mut a @ ..] = [()] {}
let v = vec![()];
if let [a @ ..] = &*v {}
let s = &[()];
if let [a @ ..] = s {}
// Don't lint
if let [..] = &*v {}
if let [a] = &*v {}
if let [()] = &*v {}
if let [first, rest @ ..] = &*v {}
if let a = [()] {}
external! {
if let [a @ ..] = [()] {}
}
}

View file

@ -0,0 +1,40 @@
error: using a rest pattern to bind an entire slice to a local
--> $DIR/redundant_rest_pattern.rs:10:12
|
LL | if let [a @ ..] = [()] {}
| ^^^^^^^^ help: this is better represented with just the binding: `a`
|
= note: `-D clippy::redundant-rest-pattern` implied by `-D warnings`
error: using a rest pattern to bind an entire slice to a local
--> $DIR/redundant_rest_pattern.rs:11:12
|
LL | if let [ref a @ ..] = [()] {}
| ^^^^^^^^^^^^ help: this is better represented with just the binding: `ref a`
error: using a rest pattern to bind an entire slice to a local
--> $DIR/redundant_rest_pattern.rs:12:12
|
LL | if let [mut a @ ..] = [()] {}
| ^^^^^^^^^^^^ help: this is better represented with just the binding: `mut a`
error: using a rest pattern to bind an entire slice to a local
--> $DIR/redundant_rest_pattern.rs:13:12
|
LL | if let [ref mut a @ ..] = [()] {}
| ^^^^^^^^^^^^^^^^ help: this is better represented with just the binding: `ref mut a`
error: using a rest pattern to bind an entire slice to a local
--> $DIR/redundant_rest_pattern.rs:15:12
|
LL | if let [a @ ..] = &*v {}
| ^^^^^^^^ help: this is better represented with just the binding: `a`
error: using a rest pattern to bind an entire slice to a local
--> $DIR/redundant_rest_pattern.rs:17:12
|
LL | if let [a @ ..] = s {}
| ^^^^^^^^ help: this is better represented with just the binding: `a`
error: aborting due to 6 previous errors