mirror of
https://github.com/rust-lang/rust-clippy
synced 2024-11-27 15:11:30 +00:00
add unnested_or_patterns
lint
This commit is contained in:
parent
67ec96c8f9
commit
7b6dc7b33d
15 changed files with 1314 additions and 20 deletions
|
@ -1683,6 +1683,7 @@ Released 2018-09-13
|
|||
[`unnecessary_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap
|
||||
[`unneeded_field_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_field_pattern
|
||||
[`unneeded_wildcard_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_wildcard_pattern
|
||||
[`unnested_or_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnested_or_patterns
|
||||
[`unreachable`]: https://rust-lang.github.io/rust-clippy/master/index.html#unreachable
|
||||
[`unreadable_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#unreadable_literal
|
||||
[`unsafe_derive_deserialize`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_derive_deserialize
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
// error-pattern:cargo-clippy
|
||||
|
||||
#![feature(bindings_after_at)]
|
||||
#![feature(box_syntax)]
|
||||
#![feature(box_patterns)]
|
||||
#![feature(or_patterns)]
|
||||
|
@ -12,6 +13,7 @@
|
|||
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
|
||||
#![feature(crate_visibility_modifier)]
|
||||
#![feature(concat_idents)]
|
||||
#![feature(drain_filter)]
|
||||
|
||||
// FIXME: switch to something more ergonomic here, once available.
|
||||
// (Currently there is no way to opt into sysroot crates without `extern crate`.)
|
||||
|
@ -319,6 +321,7 @@ mod types;
|
|||
mod unicode;
|
||||
mod unnamed_address;
|
||||
mod unnecessary_sort_by;
|
||||
mod unnested_or_patterns;
|
||||
mod unsafe_removed_from_name;
|
||||
mod unused_io_amount;
|
||||
mod unused_self;
|
||||
|
@ -836,6 +839,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
&unnamed_address::FN_ADDRESS_COMPARISONS,
|
||||
&unnamed_address::VTABLE_ADDRESS_COMPARISONS,
|
||||
&unnecessary_sort_by::UNNECESSARY_SORT_BY,
|
||||
&unnested_or_patterns::UNNESTED_OR_PATTERNS,
|
||||
&unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME,
|
||||
&unused_io_amount::UNUSED_IO_AMOUNT,
|
||||
&unused_self::UNUSED_SELF,
|
||||
|
@ -1073,6 +1077,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_early_pass(move || box non_expressive_names::NonExpressiveNames {
|
||||
single_char_binding_names_threshold,
|
||||
});
|
||||
store.register_early_pass(|| box unnested_or_patterns::UnnestedOrPatterns);
|
||||
|
||||
store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
|
||||
LintId::of(&arithmetic::FLOAT_ARITHMETIC),
|
||||
|
@ -1433,6 +1438,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS),
|
||||
LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS),
|
||||
LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY),
|
||||
LintId::of(&unnested_or_patterns::UNNESTED_OR_PATTERNS),
|
||||
LintId::of(&unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME),
|
||||
LintId::of(&unused_io_amount::UNUSED_IO_AMOUNT),
|
||||
LintId::of(&unwrap::PANICKING_UNWRAP),
|
||||
|
@ -1616,6 +1622,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(&types::UNNECESSARY_CAST),
|
||||
LintId::of(&types::VEC_BOX),
|
||||
LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY),
|
||||
LintId::of(&unnested_or_patterns::UNNESTED_OR_PATTERNS),
|
||||
LintId::of(&unwrap::UNNECESSARY_UNWRAP),
|
||||
LintId::of(&useless_conversion::USELESS_CONVERSION),
|
||||
LintId::of(&zero_div_zero::ZERO_DIVIDED_BY_ZERO),
|
||||
|
|
407
clippy_lints/src/unnested_or_patterns.rs
Normal file
407
clippy_lints/src/unnested_or_patterns.rs
Normal file
|
@ -0,0 +1,407 @@
|
|||
#![allow(clippy::wildcard_imports, clippy::enum_glob_use)]
|
||||
|
||||
use crate::utils::ast_utils::{eq_field_pat, eq_id, eq_pat, eq_path};
|
||||
use crate::utils::{over, span_lint_and_then};
|
||||
use rustc_ast::ast::{self, Pat, PatKind, PatKind::*, DUMMY_NODE_ID};
|
||||
use rustc_ast::mut_visit::*;
|
||||
use rustc_ast::ptr::P;
|
||||
use rustc_ast_pretty::pprust;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::DUMMY_SP;
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::mem;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:**
|
||||
///
|
||||
/// Checks for unnested or-patterns, e.g., `Some(0) | Some(2)` and
|
||||
/// suggests replacing the pattern with a nested one, `Some(0 | 2)`.
|
||||
///
|
||||
/// Another way to think of this is that it rewrites patterns in
|
||||
/// *disjunctive normal form (DNF)* into *conjunctive normal form (CNF)*.
|
||||
///
|
||||
/// **Why is this bad?**
|
||||
///
|
||||
/// In the example above, `Some` is repeated, which unncessarily complicates the pattern.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// fn main() {
|
||||
/// if let Some(0) | Some(2) = Some(0) {}
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// #![feature(or_patterns)]
|
||||
///
|
||||
/// fn main() {
|
||||
/// if let Some(0 | 2) = Some(0) {}
|
||||
/// }
|
||||
/// ```
|
||||
pub UNNESTED_OR_PATTERNS,
|
||||
complexity,
|
||||
"unnested or-patterns, e.g., `Foo(Bar) | Foo(Baz) instead of `Foo(Bar | Baz)`"
|
||||
}
|
||||
|
||||
declare_lint_pass!(UnnestedOrPatterns => [UNNESTED_OR_PATTERNS]);
|
||||
|
||||
impl EarlyLintPass for UnnestedOrPatterns {
|
||||
fn check_arm(&mut self, cx: &EarlyContext<'_>, a: &ast::Arm) {
|
||||
lint_unnested_or_patterns(cx, &a.pat);
|
||||
}
|
||||
|
||||
fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
|
||||
if let ast::ExprKind::Let(pat, _) = &e.kind {
|
||||
lint_unnested_or_patterns(cx, pat);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_param(&mut self, cx: &EarlyContext<'_>, p: &ast::Param) {
|
||||
lint_unnested_or_patterns(cx, &p.pat);
|
||||
}
|
||||
|
||||
fn check_local(&mut self, cx: &EarlyContext<'_>, l: &ast::Local) {
|
||||
lint_unnested_or_patterns(cx, &l.pat);
|
||||
}
|
||||
}
|
||||
|
||||
fn lint_unnested_or_patterns(cx: &EarlyContext<'_>, pat: &Pat) {
|
||||
if !cx.sess.opts.unstable_features.is_nightly_build() {
|
||||
// User cannot do `#![feature(or_patterns)]`, so bail.
|
||||
return;
|
||||
}
|
||||
|
||||
if let Ident(.., None) | Lit(_) | Wild | Path(..) | Range(..) | Rest | MacCall(_) = pat.kind {
|
||||
// This is a leaf pattern, so cloning is unprofitable.
|
||||
return;
|
||||
}
|
||||
|
||||
let mut pat = P(pat.clone());
|
||||
|
||||
// Nix all the paren patterns everywhere so that they aren't in our way.
|
||||
remove_all_parens(&mut pat);
|
||||
|
||||
// Transform all unnested or-patterns into nested ones, and if there were none, quit.
|
||||
if !unnest_or_patterns(&mut pat) {
|
||||
return;
|
||||
}
|
||||
|
||||
span_lint_and_then(cx, UNNESTED_OR_PATTERNS, pat.span, "unnested or-patterns", |db| {
|
||||
insert_necessary_parens(&mut pat);
|
||||
db.span_suggestion_verbose(
|
||||
pat.span,
|
||||
"nest the patterns",
|
||||
pprust::pat_to_string(&pat),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/// Remove all `(p)` patterns in `pat`.
|
||||
fn remove_all_parens(pat: &mut P<Pat>) {
|
||||
struct Visitor;
|
||||
impl MutVisitor for Visitor {
|
||||
fn visit_pat(&mut self, pat: &mut P<Pat>) {
|
||||
noop_visit_pat(pat, self);
|
||||
let inner = match &mut pat.kind {
|
||||
Paren(i) => mem::replace(&mut i.kind, Wild),
|
||||
_ => return,
|
||||
};
|
||||
pat.kind = inner;
|
||||
}
|
||||
}
|
||||
Visitor.visit_pat(pat);
|
||||
}
|
||||
|
||||
/// Insert parens where necessary according to Rust's precedence rules for patterns.
|
||||
fn insert_necessary_parens(pat: &mut P<Pat>) {
|
||||
struct Visitor;
|
||||
impl MutVisitor for Visitor {
|
||||
fn visit_pat(&mut self, pat: &mut P<Pat>) {
|
||||
use ast::{BindingMode::*, Mutability::*};
|
||||
noop_visit_pat(pat, self);
|
||||
let target = match &mut pat.kind {
|
||||
// `i @ a | b`, `box a | b`, and `& mut? a | b`.
|
||||
Ident(.., Some(p)) | Box(p) | Ref(p, _) if matches!(&p.kind, Or(ps) if ps.len() > 1) => p,
|
||||
Ref(p, Not) if matches!(p.kind, Ident(ByValue(Mut), ..)) => p, // `&(mut x)`
|
||||
_ => return,
|
||||
};
|
||||
target.kind = Paren(P(take_pat(target)));
|
||||
}
|
||||
}
|
||||
Visitor.visit_pat(pat);
|
||||
}
|
||||
|
||||
/// Unnest or-patterns `p0 | ... | p1` in the pattern `pat`.
|
||||
/// For example, this would transform `Some(0) | FOO | Some(2)` into `Some(0 | 2) | FOO`.
|
||||
fn unnest_or_patterns(pat: &mut P<Pat>) -> bool {
|
||||
struct Visitor {
|
||||
changed: bool,
|
||||
}
|
||||
impl MutVisitor for Visitor {
|
||||
fn visit_pat(&mut self, p: &mut P<Pat>) {
|
||||
// This is a bottom up transformation, so recurse first.
|
||||
noop_visit_pat(p, self);
|
||||
|
||||
// Don't have an or-pattern? Just quit early on.
|
||||
let alternatives = match &mut p.kind {
|
||||
Or(ps) => ps,
|
||||
_ => return,
|
||||
};
|
||||
|
||||
// Collapse or-patterns directly nested in or-patterns.
|
||||
let mut idx = 0;
|
||||
let mut this_level_changed = false;
|
||||
while idx < alternatives.len() {
|
||||
let inner = if let Or(ps) = &mut alternatives[idx].kind {
|
||||
mem::take(ps)
|
||||
} else {
|
||||
idx += 1;
|
||||
continue;
|
||||
};
|
||||
this_level_changed = true;
|
||||
alternatives.splice(idx..=idx, inner);
|
||||
}
|
||||
|
||||
// Focus on `p_n` and then try to transform all `p_i` where `i > n`.
|
||||
let mut focus_idx = 0;
|
||||
while focus_idx < alternatives.len() {
|
||||
this_level_changed |= transform_with_focus_on_idx(alternatives, focus_idx);
|
||||
focus_idx += 1;
|
||||
}
|
||||
self.changed |= this_level_changed;
|
||||
|
||||
// Deal with `Some(Some(0)) | Some(Some(1))`.
|
||||
if this_level_changed {
|
||||
noop_visit_pat(p, self);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut visitor = Visitor { changed: false };
|
||||
visitor.visit_pat(pat);
|
||||
visitor.changed
|
||||
}
|
||||
|
||||
/// Match `$scrutinee` against `$pat` and extract `$then` from it.
|
||||
/// Panics if there is no match.
|
||||
macro_rules! always_pat {
|
||||
($scrutinee:expr, $pat:pat => $then:expr) => {
|
||||
match $scrutinee {
|
||||
$pat => $then,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Focus on `focus_idx` in `alternatives`,
|
||||
/// attempting to extend it with elements of the same constructor `C`
|
||||
/// in `alternatives[focus_idx + 1..]`.
|
||||
fn transform_with_focus_on_idx(alternatives: &mut Vec<P<Pat>>, focus_idx: usize) -> bool {
|
||||
// Extract the kind; we'll need to make some changes in it.
|
||||
let mut focus_kind = mem::replace(&mut alternatives[focus_idx].kind, PatKind::Wild);
|
||||
// We'll focus on `alternatives[focus_idx]`,
|
||||
// so we're draining from `alternatives[focus_idx + 1..]`.
|
||||
let start = focus_idx + 1;
|
||||
|
||||
// We're trying to find whatever kind (~"constructor") we found in `alternatives[start..]`.
|
||||
let changed = match &mut focus_kind {
|
||||
// These pattern forms are "leafs" and do not have sub-patterns.
|
||||
// Therefore they are not some form of constructor `C`,
|
||||
// with which a pattern `C(P0)` may be formed,
|
||||
// which we would want to join with other `C(Pj)`s.
|
||||
Ident(.., None) | Lit(_) | Wild | Path(..) | Range(..) | Rest | MacCall(_)
|
||||
// Dealt with elsewhere.
|
||||
| Or(_) | Paren(_) => false,
|
||||
// Transform `box x | ... | box y` into `box (x | y)`.
|
||||
//
|
||||
// The cases below until `Slice(...)` deal *singleton* products.
|
||||
// These patterns have the shape `C(p)`, and not e.g., `C(p0, ..., pn)`.
|
||||
Box(target) => extend_with_matching(
|
||||
target, start, alternatives,
|
||||
|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(
|
||||
target, start, alternatives,
|
||||
|k| matches!(k, Ref(_, m2) if m1 == m2), // Mutabilities must match.
|
||||
|k| always_pat!(k, Ref(p, _) => p),
|
||||
),
|
||||
// Transform `b @ p0 | ... b @ p1` into `b @ (p0 | p1)`.
|
||||
Ident(b1, i1, Some(target)) => extend_with_matching(
|
||||
target, start, alternatives,
|
||||
// Binding names must match.
|
||||
|k| matches!(k, Ident(b2, i2, Some(_)) if b1 == b2 && eq_id(*i1, *i2)),
|
||||
|k| always_pat!(k, Ident(_, _, Some(p)) => p),
|
||||
),
|
||||
// Transform `[pre, x, post] | ... | [pre, y, post]` into `[pre, x | y, post]`.
|
||||
Slice(ps1) => extend_with_matching_product(
|
||||
ps1, start, alternatives,
|
||||
|k, ps1, idx| matches!(k, Slice(ps2) if eq_pre_post(ps1, ps2, idx)),
|
||||
|k| always_pat!(k, Slice(ps) => ps),
|
||||
),
|
||||
// Transform `(pre, x, post) | ... | (pre, y, post)` into `(pre, x | y, post]`.
|
||||
Tuple(ps1) => extend_with_matching_product(
|
||||
ps1, start, alternatives,
|
||||
|k, ps1, idx| matches!(k, Tuple(ps2) if eq_pre_post(ps1, ps2, idx)),
|
||||
|k| always_pat!(k, Tuple(ps) => ps),
|
||||
),
|
||||
// Transform `S(pre, x, post) | ... | S(pre, y, post)` into `S(pre, x | y, post]`.
|
||||
TupleStruct(path1, ps1) => extend_with_matching_product(
|
||||
ps1, start, alternatives,
|
||||
|k, ps1, idx| matches!(
|
||||
k,
|
||||
TupleStruct(path2, ps2) if eq_path(path1, path2) && eq_pre_post(ps1, ps2, idx)
|
||||
),
|
||||
|k| always_pat!(k, TupleStruct(_, ps) => ps),
|
||||
),
|
||||
// Transform a record pattern `S { fp_0, ..., fp_n }`.
|
||||
Struct(path1, fps1, rest1) => extend_with_struct_pat(path1, fps1, *rest1, start, alternatives),
|
||||
};
|
||||
|
||||
alternatives[focus_idx].kind = focus_kind;
|
||||
changed
|
||||
}
|
||||
|
||||
/// Here we focusing on a record pattern `S { fp_0, ..., fp_n }`.
|
||||
/// In particular, for a record pattern, the order in which the field patterns is irrelevant.
|
||||
/// So when we fixate on some `ident_k: pat_k`, we try to find `ident_k` in the other pattern
|
||||
/// and check that all `fp_i` where `i ∈ ((0...n) \ k)` between two patterns are equal.
|
||||
fn extend_with_struct_pat(
|
||||
path1: &ast::Path,
|
||||
fps1: &mut Vec<ast::FieldPat>,
|
||||
rest1: bool,
|
||||
start: usize,
|
||||
alternatives: &mut Vec<P<Pat>>,
|
||||
) -> bool {
|
||||
(0..fps1.len()).any(|idx| {
|
||||
let pos_in_2 = Cell::new(None); // The element `k`.
|
||||
let tail_or = drain_matching(
|
||||
start,
|
||||
alternatives,
|
||||
|k| {
|
||||
matches!(k, Struct(path2, fps2, rest2)
|
||||
if rest1 == *rest2 // If one struct pattern has `..` so must the other.
|
||||
&& eq_path(path1, path2)
|
||||
&& fps1.len() == fps2.len()
|
||||
&& fps1.iter().enumerate().all(|(idx_1, fp1)| {
|
||||
if idx_1 == idx {
|
||||
// In the case of `k`, we merely require identical field names
|
||||
// so that we will transform into `ident_k: p1_k | p2_k`.
|
||||
let pos = fps2.iter().position(|fp2| eq_id(fp1.ident, fp2.ident));
|
||||
pos_in_2.set(pos);
|
||||
pos.is_some()
|
||||
} else {
|
||||
fps2.iter().any(|fp2| eq_field_pat(fp1, fp2))
|
||||
}
|
||||
}))
|
||||
},
|
||||
// Extract `p2_k`.
|
||||
|k| always_pat!(k, Struct(_, mut fps, _) => fps.swap_remove(pos_in_2.take().unwrap()).pat),
|
||||
);
|
||||
extend_with_tail_or(&mut fps1[idx].pat, tail_or)
|
||||
})
|
||||
}
|
||||
|
||||
/// Like `extend_with_matching` but for products with > 1 factor, e.g., `C(p_0, ..., p_n)`.
|
||||
/// Here, the idea is that we fixate on some `p_k` in `C`,
|
||||
/// allowing it to vary between two `targets` and `ps2` (returned by `extract`),
|
||||
/// while also requiring `ps1[..n] ~ ps2[..n]` (pre) and `ps1[n + 1..] ~ ps2[n + 1..]` (post),
|
||||
/// where `~` denotes semantic equality.
|
||||
fn extend_with_matching_product(
|
||||
targets: &mut Vec<P<Pat>>,
|
||||
start: usize,
|
||||
alternatives: &mut Vec<P<Pat>>,
|
||||
predicate: impl Fn(&PatKind, &[P<Pat>], usize) -> bool,
|
||||
extract: impl Fn(PatKind) -> Vec<P<Pat>>,
|
||||
) -> bool {
|
||||
(0..targets.len()).any(|idx| {
|
||||
let tail_or = drain_matching(
|
||||
start,
|
||||
alternatives,
|
||||
|k| predicate(k, targets, idx),
|
||||
|k| extract(k).swap_remove(idx),
|
||||
);
|
||||
extend_with_tail_or(&mut targets[idx], tail_or)
|
||||
})
|
||||
}
|
||||
|
||||
/// Extract the pattern from the given one and replace it with `Wild`.
|
||||
/// This is meant for temporarily swapping out the pattern for manipulation.
|
||||
fn take_pat(from: &mut Pat) -> Pat {
|
||||
let dummy = Pat {
|
||||
id: DUMMY_NODE_ID,
|
||||
kind: Wild,
|
||||
span: DUMMY_SP,
|
||||
};
|
||||
mem::replace(from, dummy)
|
||||
}
|
||||
|
||||
/// Extend `target` as an or-pattern with the alternatives
|
||||
/// in `tail_or` if there are any and return if there were.
|
||||
fn extend_with_tail_or(target: &mut Pat, tail_or: Vec<P<Pat>>) -> bool {
|
||||
fn extend(target: &mut Pat, mut tail_or: Vec<P<Pat>>) {
|
||||
match target {
|
||||
// On an existing or-pattern in the target, append to it.
|
||||
Pat { kind: Or(ps), .. } => ps.append(&mut tail_or),
|
||||
// Otherwise convert the target to an or-pattern.
|
||||
target => {
|
||||
let mut init_or = vec![P(take_pat(target))];
|
||||
init_or.append(&mut tail_or);
|
||||
target.kind = Or(init_or);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
let changed = !tail_or.is_empty();
|
||||
if changed {
|
||||
// Extend the target.
|
||||
extend(target, tail_or);
|
||||
}
|
||||
changed
|
||||
}
|
||||
|
||||
// Extract all inner patterns in `alternatives` matching our `predicate`.
|
||||
// Only elements beginning with `start` are considered for extraction.
|
||||
fn drain_matching(
|
||||
start: usize,
|
||||
alternatives: &mut Vec<P<Pat>>,
|
||||
predicate: impl Fn(&PatKind) -> bool,
|
||||
extract: impl Fn(PatKind) -> P<Pat>,
|
||||
) -> Vec<P<Pat>> {
|
||||
let mut tail_or = vec![];
|
||||
let mut idx = 0;
|
||||
for pat in alternatives.drain_filter(|p| {
|
||||
// Check if we should extract, but only if `idx >= start`.
|
||||
idx += 1;
|
||||
idx > start && predicate(&p.kind)
|
||||
}) {
|
||||
tail_or.push(extract(pat.into_inner().kind));
|
||||
}
|
||||
tail_or
|
||||
}
|
||||
|
||||
fn extend_with_matching(
|
||||
target: &mut Pat,
|
||||
start: usize,
|
||||
alternatives: &mut Vec<P<Pat>>,
|
||||
predicate: impl Fn(&PatKind) -> bool,
|
||||
extract: impl Fn(PatKind) -> P<Pat>,
|
||||
) -> bool {
|
||||
extend_with_tail_or(target, drain_matching(start, alternatives, predicate, extract))
|
||||
}
|
||||
|
||||
/// Are the patterns in `ps1` and `ps2` equal save for `ps1[idx]` compared to `ps2[idx]`?
|
||||
fn eq_pre_post(ps1: &[P<Pat>], ps2: &[P<Pat>], idx: usize) -> bool {
|
||||
ps1[idx].is_rest() == ps2[idx].is_rest() // Avoid `[x, ..] | [x, 0]` => `[x, .. | 0]`.
|
||||
&& ps1.len() == ps2.len()
|
||||
&& over(&ps1[..idx], &ps2[..idx], |l, r| eq_pat(l, r))
|
||||
&& over(&ps1[idx + 1..], &ps2[idx + 1..], |l, r| eq_pat(l, r))
|
||||
}
|
525
clippy_lints/src/utils/ast_utils.rs
Executable file
525
clippy_lints/src/utils/ast_utils.rs
Executable file
|
@ -0,0 +1,525 @@
|
|||
//! Utilities for manipulating and extracting information from `rustc_ast::ast`.
|
||||
//!
|
||||
//! - The `eq_foobar` functions test for semantic equality but ignores `NodeId`s and `Span`s.
|
||||
|
||||
#![allow(clippy::similar_names, clippy::wildcard_imports, clippy::enum_glob_use)]
|
||||
|
||||
use crate::utils::{both, over};
|
||||
use rustc_ast::ast::{self, *};
|
||||
use rustc_ast::ptr::P;
|
||||
use std::mem;
|
||||
|
||||
/// Checks if each element in the first slice is contained within the latter as per `eq_fn`.
|
||||
pub fn unordered_over<X>(left: &[X], right: &[X], mut eq_fn: impl FnMut(&X, &X) -> bool) -> bool {
|
||||
left.len() == right.len() && left.iter().all(|l| right.iter().any(|r| eq_fn(l, r)))
|
||||
}
|
||||
|
||||
pub fn eq_id(l: Ident, r: Ident) -> bool {
|
||||
l.name == r.name
|
||||
}
|
||||
|
||||
pub fn eq_pat(l: &Pat, r: &Pat) -> bool {
|
||||
use PatKind::*;
|
||||
match (&l.kind, &r.kind) {
|
||||
(Paren(l), _) => eq_pat(l, r),
|
||||
(_, Paren(r)) => eq_pat(l, r),
|
||||
(Wild, Wild) | (Rest, Rest) => true,
|
||||
(Lit(l), Lit(r)) => eq_expr(l, r),
|
||||
(Ident(b1, i1, s1), Ident(b2, i2, s2)) => b1 == b2 && eq_id(*i1, *i2) && both(s1, s2, |l, r| eq_pat(l, r)),
|
||||
(Range(lf, lt, le), Range(rf, rt, re)) => {
|
||||
eq_expr_opt(lf, rf) && eq_expr_opt(lt, rt) && eq_range_end(&le.node, &re.node)
|
||||
},
|
||||
(Box(l), Box(r))
|
||||
| (Ref(l, Mutability::Not), Ref(r, Mutability::Not))
|
||||
| (Ref(l, Mutability::Mut), Ref(r, Mutability::Mut)) => eq_pat(l, r),
|
||||
(Tuple(l), Tuple(r)) | (Slice(l), Slice(r)) => over(l, r, |l, r| eq_pat(l, r)),
|
||||
(Path(lq, lp), Path(rq, rp)) => both(lq, rq, |l, r| eq_qself(l, r)) && eq_path(lp, rp),
|
||||
(TupleStruct(lp, lfs), TupleStruct(rp, rfs)) => eq_path(lp, rp) && over(lfs, rfs, |l, r| eq_pat(l, r)),
|
||||
(Struct(lp, lfs, lr), Struct(rp, rfs, rr)) => {
|
||||
lr == rr && eq_path(lp, rp) && unordered_over(lfs, rfs, |lf, rf| eq_field_pat(lf, rf))
|
||||
},
|
||||
(Or(ls), Or(rs)) => unordered_over(ls, rs, |l, r| eq_pat(l, r)),
|
||||
(MacCall(l), MacCall(r)) => eq_mac_call(l, r),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eq_range_end(l: &RangeEnd, r: &RangeEnd) -> bool {
|
||||
match (l, r) {
|
||||
(RangeEnd::Excluded, RangeEnd::Excluded) => true,
|
||||
(RangeEnd::Included(l), RangeEnd::Included(r)) => {
|
||||
matches!(l, RangeSyntax::DotDotEq) == matches!(r, RangeSyntax::DotDotEq)
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eq_field_pat(l: &FieldPat, r: &FieldPat) -> bool {
|
||||
l.is_placeholder == r.is_placeholder
|
||||
&& eq_id(l.ident, r.ident)
|
||||
&& eq_pat(&l.pat, &r.pat)
|
||||
&& over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
|
||||
}
|
||||
|
||||
pub fn eq_qself(l: &QSelf, r: &QSelf) -> bool {
|
||||
l.position == r.position && eq_ty(&l.ty, &r.ty)
|
||||
}
|
||||
|
||||
pub fn eq_path(l: &Path, r: &Path) -> bool {
|
||||
over(&l.segments, &r.segments, |l, r| eq_path_seg(l, r))
|
||||
}
|
||||
|
||||
pub fn eq_path_seg(l: &PathSegment, r: &PathSegment) -> bool {
|
||||
eq_id(l.ident, r.ident) && both(&l.args, &r.args, |l, r| eq_generic_args(l, r))
|
||||
}
|
||||
|
||||
pub fn eq_generic_args(l: &GenericArgs, r: &GenericArgs) -> bool {
|
||||
match (l, r) {
|
||||
(GenericArgs::AngleBracketed(l), GenericArgs::AngleBracketed(r)) => {
|
||||
over(&l.args, &r.args, |l, r| eq_angle_arg(l, r))
|
||||
},
|
||||
(GenericArgs::Parenthesized(l), GenericArgs::Parenthesized(r)) => {
|
||||
over(&l.inputs, &r.inputs, |l, r| eq_ty(l, r)) && eq_fn_ret_ty(&l.output, &r.output)
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eq_angle_arg(l: &AngleBracketedArg, r: &AngleBracketedArg) -> bool {
|
||||
match (l, r) {
|
||||
(AngleBracketedArg::Arg(l), AngleBracketedArg::Arg(r)) => eq_generic_arg(l, r),
|
||||
(AngleBracketedArg::Constraint(l), AngleBracketedArg::Constraint(r)) => eq_assoc_constraint(l, r),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eq_generic_arg(l: &GenericArg, r: &GenericArg) -> bool {
|
||||
match (l, r) {
|
||||
(GenericArg::Lifetime(l), GenericArg::Lifetime(r)) => eq_id(l.ident, r.ident),
|
||||
(GenericArg::Type(l), GenericArg::Type(r)) => eq_ty(l, r),
|
||||
(GenericArg::Const(l), GenericArg::Const(r)) => eq_expr(&l.value, &r.value),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eq_expr_opt(l: &Option<P<Expr>>, r: &Option<P<Expr>>) -> bool {
|
||||
both(l, r, |l, r| eq_expr(l, r))
|
||||
}
|
||||
|
||||
pub fn eq_expr(l: &Expr, r: &Expr) -> bool {
|
||||
use ExprKind::*;
|
||||
if !over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r)) {
|
||||
return false;
|
||||
}
|
||||
match (&l.kind, &r.kind) {
|
||||
(Paren(l), _) => eq_expr(l, r),
|
||||
(_, Paren(r)) => eq_expr(l, r),
|
||||
(Err, Err) => true,
|
||||
(Box(l), Box(r)) | (Try(l), Try(r)) | (Await(l), Await(r)) => eq_expr(l, r),
|
||||
(Array(l), Array(r)) | (Tup(l), Tup(r)) => over(l, r, |l, r| eq_expr(l, r)),
|
||||
(Repeat(le, ls), Repeat(re, rs)) => eq_expr(le, re) && eq_expr(&ls.value, &rs.value),
|
||||
(Call(lc, la), Call(rc, ra)) => eq_expr(lc, rc) && over(la, ra, |l, r| eq_expr(l, r)),
|
||||
(MethodCall(lc, la), MethodCall(rc, ra)) => eq_path_seg(lc, rc) && over(la, ra, |l, r| eq_expr(l, r)),
|
||||
(Binary(lo, ll, lr), Binary(ro, rl, rr)) => lo.node == ro.node && eq_expr(ll, rl) && eq_expr(lr, rr),
|
||||
(Unary(lo, l), Unary(ro, r)) => mem::discriminant(lo) == mem::discriminant(ro) && eq_expr(l, r),
|
||||
(Lit(l), Lit(r)) => l.kind == r.kind,
|
||||
(Cast(l, lt), Cast(r, rt)) | (Type(l, lt), Type(r, rt)) => eq_expr(l, r) && eq_ty(lt, rt),
|
||||
(Let(lp, le), Let(rp, re)) => eq_pat(lp, rp) && eq_expr(le, re),
|
||||
(If(lc, lt, le), If(rc, rt, re)) => eq_expr(lc, rc) && eq_block(lt, rt) && eq_expr_opt(le, re),
|
||||
(While(lc, lt, ll), While(rc, rt, rl)) => eq_label(ll, rl) && eq_expr(lc, rc) && eq_block(lt, rt),
|
||||
(ForLoop(lp, li, lt, ll), ForLoop(rp, ri, rt, rl)) => {
|
||||
eq_label(ll, rl) && eq_pat(lp, rp) && eq_expr(li, ri) && eq_block(lt, rt)
|
||||
},
|
||||
(Loop(lt, ll), Loop(rt, rl)) => eq_label(ll, rl) && eq_block(lt, rt),
|
||||
(Block(lb, ll), Block(rb, rl)) => eq_label(ll, rl) && eq_block(lb, rb),
|
||||
(TryBlock(l), TryBlock(r)) => eq_block(l, r),
|
||||
(Yield(l), Yield(r)) | (Ret(l), Ret(r)) => eq_expr_opt(l, r),
|
||||
(Break(ll, le), Break(rl, re)) => eq_label(ll, rl) && eq_expr_opt(le, re),
|
||||
(Continue(ll), Continue(rl)) => eq_label(ll, rl),
|
||||
(Assign(l1, l2, _), Assign(r1, r2, _)) | (Index(l1, l2), Index(r1, r2)) => eq_expr(l1, r1) && eq_expr(l2, r2),
|
||||
(AssignOp(lo, lp, lv), AssignOp(ro, rp, rv)) => lo.node == ro.node && eq_expr(lp, rp) && eq_expr(lv, rv),
|
||||
(Field(lp, lf), Field(rp, rf)) => eq_id(*lf, *rf) && eq_expr(lp, rp),
|
||||
(Match(ls, la), Match(rs, ra)) => eq_expr(ls, rs) && over(la, ra, |l, r| eq_arm(l, r)),
|
||||
(Closure(lc, la, lm, lf, lb, _), Closure(rc, ra, rm, rf, rb, _)) => {
|
||||
lc == rc && la.is_async() == ra.is_async() && lm == rm && eq_fn_decl(lf, rf) && eq_expr(lb, rb)
|
||||
},
|
||||
(Async(lc, _, lb), Async(rc, _, rb)) => lc == rc && eq_block(lb, rb),
|
||||
(Range(lf, lt, ll), Range(rf, rt, rl)) => ll == rl && eq_expr_opt(lf, rf) && eq_expr_opt(lt, rt),
|
||||
(AddrOf(lbk, lm, le), AddrOf(rbk, rm, re)) => lbk == rbk && lm == rm && eq_expr(le, re),
|
||||
(Path(lq, lp), Path(rq, rp)) => both(lq, rq, |l, r| eq_qself(l, r)) && eq_path(lp, rp),
|
||||
(MacCall(l), MacCall(r)) => eq_mac_call(l, r),
|
||||
(Struct(lp, lfs, lb), Struct(rp, rfs, rb)) => {
|
||||
eq_path(lp, rp) && eq_expr_opt(lb, rb) && unordered_over(lfs, rfs, |l, r| eq_field(l, r))
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eq_field(l: &Field, r: &Field) -> bool {
|
||||
l.is_placeholder == r.is_placeholder
|
||||
&& eq_id(l.ident, r.ident)
|
||||
&& eq_expr(&l.expr, &r.expr)
|
||||
&& over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
|
||||
}
|
||||
|
||||
pub fn eq_arm(l: &Arm, r: &Arm) -> bool {
|
||||
l.is_placeholder == r.is_placeholder
|
||||
&& eq_pat(&l.pat, &r.pat)
|
||||
&& eq_expr(&l.body, &r.body)
|
||||
&& eq_expr_opt(&l.guard, &r.guard)
|
||||
&& over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
|
||||
}
|
||||
|
||||
pub fn eq_label(l: &Option<Label>, r: &Option<Label>) -> bool {
|
||||
both(l, r, |l, r| eq_id(l.ident, r.ident))
|
||||
}
|
||||
|
||||
pub fn eq_block(l: &Block, r: &Block) -> bool {
|
||||
l.rules == r.rules && over(&l.stmts, &r.stmts, |l, r| eq_stmt(l, r))
|
||||
}
|
||||
|
||||
pub fn eq_stmt(l: &Stmt, r: &Stmt) -> bool {
|
||||
use StmtKind::*;
|
||||
match (&l.kind, &r.kind) {
|
||||
(Local(l), Local(r)) => {
|
||||
eq_pat(&l.pat, &r.pat)
|
||||
&& both(&l.ty, &r.ty, |l, r| eq_ty(l, r))
|
||||
&& eq_expr_opt(&l.init, &r.init)
|
||||
&& over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
|
||||
},
|
||||
(Item(l), Item(r)) => eq_item(l, r, eq_item_kind),
|
||||
(Expr(l), Expr(r)) | (Semi(l), Semi(r)) => eq_expr(l, r),
|
||||
(Empty, Empty) => true,
|
||||
(MacCall(l), MacCall(r)) => l.1 == r.1 && eq_mac_call(&l.0, &r.0) && over(&l.2, &r.2, |l, r| eq_attr(l, r)),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eq_item<K>(l: &Item<K>, r: &Item<K>, mut eq_kind: impl FnMut(&K, &K) -> bool) -> bool {
|
||||
eq_id(l.ident, r.ident)
|
||||
&& over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
|
||||
&& eq_vis(&l.vis, &r.vis)
|
||||
&& eq_kind(&l.kind, &r.kind)
|
||||
}
|
||||
|
||||
pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool {
|
||||
use ItemKind::*;
|
||||
match (l, r) {
|
||||
(ExternCrate(l), ExternCrate(r)) => l == r,
|
||||
(Use(l), Use(r)) => eq_use_tree(l, r),
|
||||
(Static(lt, lm, le), Static(rt, rm, re)) => lm == rm && eq_ty(lt, rt) && eq_expr_opt(le, re),
|
||||
(Const(ld, lt, le), Const(rd, rt, re)) => eq_defaultness(*ld, *rd) && eq_ty(lt, rt) && eq_expr_opt(le, re),
|
||||
(Fn(ld, lf, lg, lb), Fn(rd, rf, rg, rb)) => {
|
||||
eq_defaultness(*ld, *rd) && eq_fn_sig(lf, rf) && eq_generics(lg, rg) && both(lb, rb, |l, r| eq_block(l, r))
|
||||
},
|
||||
(Mod(l), Mod(r)) => l.inline == r.inline && over(&l.items, &r.items, |l, r| eq_item(l, r, eq_item_kind)),
|
||||
(ForeignMod(l), ForeignMod(r)) => {
|
||||
both(&l.abi, &r.abi, |l, r| eq_str_lit(l, r))
|
||||
&& over(&l.items, &r.items, |l, r| eq_item(l, r, eq_foreign_item_kind))
|
||||
},
|
||||
(TyAlias(ld, lg, lb, lt), TyAlias(rd, rg, rb, rt)) => {
|
||||
eq_defaultness(*ld, *rd)
|
||||
&& eq_generics(lg, rg)
|
||||
&& over(lb, rb, |l, r| eq_generic_bound(l, r))
|
||||
&& both(lt, rt, |l, r| eq_ty(l, r))
|
||||
},
|
||||
(Enum(le, lg), Enum(re, rg)) => {
|
||||
over(&le.variants, &re.variants, |l, r| eq_variant(l, r)) && eq_generics(lg, rg)
|
||||
},
|
||||
(Struct(lv, lg), Struct(rv, rg)) | (Union(lv, lg), Union(rv, rg)) => {
|
||||
eq_variant_data(lv, rv) && eq_generics(lg, rg)
|
||||
},
|
||||
(Trait(la, lu, lg, lb, li), Trait(ra, ru, rg, rb, ri)) => {
|
||||
la == ra
|
||||
&& matches!(lu, Unsafe::No) == matches!(ru, Unsafe::No)
|
||||
&& eq_generics(lg, rg)
|
||||
&& over(lb, rb, |l, r| eq_generic_bound(l, r))
|
||||
&& over(li, ri, |l, r| eq_item(l, r, eq_assoc_item_kind))
|
||||
},
|
||||
(TraitAlias(lg, lb), TraitAlias(rg, rb)) => eq_generics(lg, rg) && over(lb, rb, |l, r| eq_generic_bound(l, r)),
|
||||
(
|
||||
Impl {
|
||||
unsafety: lu,
|
||||
polarity: lp,
|
||||
defaultness: ld,
|
||||
constness: lc,
|
||||
generics: lg,
|
||||
of_trait: lot,
|
||||
self_ty: lst,
|
||||
items: li,
|
||||
},
|
||||
Impl {
|
||||
unsafety: ru,
|
||||
polarity: rp,
|
||||
defaultness: rd,
|
||||
constness: rc,
|
||||
generics: rg,
|
||||
of_trait: rot,
|
||||
self_ty: rst,
|
||||
items: ri,
|
||||
},
|
||||
) => {
|
||||
matches!(lu, Unsafe::No) == matches!(ru, Unsafe::No)
|
||||
&& matches!(lp, ImplPolarity::Positive) == matches!(rp, ImplPolarity::Positive)
|
||||
&& eq_defaultness(*ld, *rd)
|
||||
&& matches!(lc, ast::Const::No) == matches!(rc, ast::Const::No)
|
||||
&& eq_generics(lg, rg)
|
||||
&& both(lot, rot, |l, r| eq_path(&l.path, &r.path))
|
||||
&& eq_ty(lst, rst)
|
||||
&& over(li, ri, |l, r| eq_item(l, r, eq_assoc_item_kind))
|
||||
},
|
||||
(MacCall(l), MacCall(r)) => eq_mac_call(l, r),
|
||||
(MacroDef(l), MacroDef(r)) => l.macro_rules == r.macro_rules && eq_mac_args(&l.body, &r.body),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eq_foreign_item_kind(l: &ForeignItemKind, r: &ForeignItemKind) -> bool {
|
||||
use ForeignItemKind::*;
|
||||
match (l, r) {
|
||||
(Static(lt, lm, le), Static(rt, rm, re)) => lm == rm && eq_ty(lt, rt) && eq_expr_opt(le, re),
|
||||
(Fn(ld, lf, lg, lb), Fn(rd, rf, rg, rb)) => {
|
||||
eq_defaultness(*ld, *rd) && eq_fn_sig(lf, rf) && eq_generics(lg, rg) && both(lb, rb, |l, r| eq_block(l, r))
|
||||
},
|
||||
(TyAlias(ld, lg, lb, lt), TyAlias(rd, rg, rb, rt)) => {
|
||||
eq_defaultness(*ld, *rd)
|
||||
&& eq_generics(lg, rg)
|
||||
&& over(lb, rb, |l, r| eq_generic_bound(l, r))
|
||||
&& both(lt, rt, |l, r| eq_ty(l, r))
|
||||
},
|
||||
(MacCall(l), MacCall(r)) => eq_mac_call(l, r),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eq_assoc_item_kind(l: &AssocItemKind, r: &AssocItemKind) -> bool {
|
||||
use AssocItemKind::*;
|
||||
match (l, r) {
|
||||
(Const(ld, lt, le), Const(rd, rt, re)) => eq_defaultness(*ld, *rd) && eq_ty(lt, rt) && eq_expr_opt(le, re),
|
||||
(Fn(ld, lf, lg, lb), Fn(rd, rf, rg, rb)) => {
|
||||
eq_defaultness(*ld, *rd) && eq_fn_sig(lf, rf) && eq_generics(lg, rg) && both(lb, rb, |l, r| eq_block(l, r))
|
||||
},
|
||||
(TyAlias(ld, lg, lb, lt), TyAlias(rd, rg, rb, rt)) => {
|
||||
eq_defaultness(*ld, *rd)
|
||||
&& eq_generics(lg, rg)
|
||||
&& over(lb, rb, |l, r| eq_generic_bound(l, r))
|
||||
&& both(lt, rt, |l, r| eq_ty(l, r))
|
||||
},
|
||||
(MacCall(l), MacCall(r)) => eq_mac_call(l, r),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eq_variant(l: &Variant, r: &Variant) -> bool {
|
||||
l.is_placeholder == r.is_placeholder
|
||||
&& over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
|
||||
&& eq_vis(&l.vis, &r.vis)
|
||||
&& eq_id(l.ident, r.ident)
|
||||
&& eq_variant_data(&l.data, &r.data)
|
||||
&& both(&l.disr_expr, &r.disr_expr, |l, r| eq_expr(&l.value, &r.value))
|
||||
}
|
||||
|
||||
pub fn eq_variant_data(l: &VariantData, r: &VariantData) -> bool {
|
||||
use VariantData::*;
|
||||
match (l, r) {
|
||||
(Unit(_), Unit(_)) => true,
|
||||
(Struct(l, _), Struct(r, _)) | (Tuple(l, _), Tuple(r, _)) => over(l, r, |l, r| eq_struct_field(l, r)),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eq_struct_field(l: &StructField, r: &StructField) -> bool {
|
||||
l.is_placeholder == r.is_placeholder
|
||||
&& over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
|
||||
&& eq_vis(&l.vis, &r.vis)
|
||||
&& both(&l.ident, &r.ident, |l, r| eq_id(*l, *r))
|
||||
&& eq_ty(&l.ty, &r.ty)
|
||||
}
|
||||
|
||||
pub fn eq_fn_sig(l: &FnSig, r: &FnSig) -> bool {
|
||||
eq_fn_decl(&l.decl, &r.decl) && eq_fn_header(&l.header, &r.header)
|
||||
}
|
||||
|
||||
pub fn eq_fn_header(l: &FnHeader, r: &FnHeader) -> bool {
|
||||
matches!(l.unsafety, Unsafe::No) == matches!(r.unsafety, Unsafe::No)
|
||||
&& l.asyncness.is_async() == r.asyncness.is_async()
|
||||
&& matches!(l.constness, Const::No) == matches!(r.constness, Const::No)
|
||||
&& eq_ext(&l.ext, &r.ext)
|
||||
}
|
||||
|
||||
pub fn eq_generics(l: &Generics, r: &Generics) -> bool {
|
||||
over(&l.params, &r.params, |l, r| eq_generic_param(l, r))
|
||||
&& over(&l.where_clause.predicates, &r.where_clause.predicates, |l, r| {
|
||||
eq_where_predicate(l, r)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn eq_where_predicate(l: &WherePredicate, r: &WherePredicate) -> bool {
|
||||
use WherePredicate::*;
|
||||
match (l, r) {
|
||||
(BoundPredicate(l), BoundPredicate(r)) => {
|
||||
over(&l.bound_generic_params, &r.bound_generic_params, |l, r| {
|
||||
eq_generic_param(l, r)
|
||||
}) && eq_ty(&l.bounded_ty, &r.bounded_ty)
|
||||
&& over(&l.bounds, &r.bounds, |l, r| eq_generic_bound(l, r))
|
||||
},
|
||||
(RegionPredicate(l), RegionPredicate(r)) => {
|
||||
eq_id(l.lifetime.ident, r.lifetime.ident) && over(&l.bounds, &r.bounds, |l, r| eq_generic_bound(l, r))
|
||||
},
|
||||
(EqPredicate(l), EqPredicate(r)) => eq_ty(&l.lhs_ty, &r.lhs_ty) && eq_ty(&l.rhs_ty, &r.rhs_ty),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eq_use_tree(l: &UseTree, r: &UseTree) -> bool {
|
||||
eq_path(&l.prefix, &r.prefix) && eq_use_tree_kind(&l.kind, &r.kind)
|
||||
}
|
||||
|
||||
pub fn eq_use_tree_kind(l: &UseTreeKind, r: &UseTreeKind) -> bool {
|
||||
use UseTreeKind::*;
|
||||
match (l, r) {
|
||||
(Glob, Glob) => true,
|
||||
(Simple(l, _, _), Simple(r, _, _)) => both(l, r, |l, r| eq_id(*l, *r)),
|
||||
(Nested(l), Nested(r)) => over(l, r, |(l, _), (r, _)| eq_use_tree(l, r)),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eq_defaultness(l: Defaultness, r: Defaultness) -> bool {
|
||||
match (l, r) {
|
||||
(Defaultness::Final, Defaultness::Final) | (Defaultness::Default(_), Defaultness::Default(_)) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eq_vis(l: &Visibility, r: &Visibility) -> bool {
|
||||
use VisibilityKind::*;
|
||||
match (&l.node, &r.node) {
|
||||
(Public, Public) | (Inherited, Inherited) | (Crate(_), Crate(_)) => true,
|
||||
(Restricted { path: l, .. }, Restricted { path: r, .. }) => eq_path(l, r),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eq_fn_decl(l: &FnDecl, r: &FnDecl) -> bool {
|
||||
eq_fn_ret_ty(&l.output, &r.output)
|
||||
&& over(&l.inputs, &r.inputs, |l, r| {
|
||||
l.is_placeholder == r.is_placeholder
|
||||
&& eq_pat(&l.pat, &r.pat)
|
||||
&& eq_ty(&l.ty, &r.ty)
|
||||
&& over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn eq_fn_ret_ty(l: &FnRetTy, r: &FnRetTy) -> bool {
|
||||
match (l, r) {
|
||||
(FnRetTy::Default(_), FnRetTy::Default(_)) => true,
|
||||
(FnRetTy::Ty(l), FnRetTy::Ty(r)) => eq_ty(l, r),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eq_ty(l: &Ty, r: &Ty) -> bool {
|
||||
use TyKind::*;
|
||||
match (&l.kind, &r.kind) {
|
||||
(Paren(l), _) => eq_ty(l, r),
|
||||
(_, Paren(r)) => eq_ty(l, r),
|
||||
(Never, Never) | (Infer, Infer) | (ImplicitSelf, ImplicitSelf) | (Err, Err) | (CVarArgs, CVarArgs) => true,
|
||||
(Slice(l), Slice(r)) => eq_ty(l, r),
|
||||
(Array(le, ls), Array(re, rs)) => eq_ty(le, re) && eq_expr(&ls.value, &rs.value),
|
||||
(Ptr(l), Ptr(r)) => l.mutbl == r.mutbl && eq_ty(&l.ty, &r.ty),
|
||||
(Rptr(ll, l), Rptr(rl, r)) => {
|
||||
both(ll, rl, |l, r| eq_id(l.ident, r.ident)) && l.mutbl == r.mutbl && eq_ty(&l.ty, &r.ty)
|
||||
},
|
||||
(BareFn(l), BareFn(r)) => {
|
||||
l.unsafety == r.unsafety
|
||||
&& eq_ext(&l.ext, &r.ext)
|
||||
&& over(&l.generic_params, &r.generic_params, |l, r| eq_generic_param(l, r))
|
||||
&& eq_fn_decl(&l.decl, &r.decl)
|
||||
},
|
||||
(Tup(l), Tup(r)) => over(l, r, |l, r| eq_ty(l, r)),
|
||||
(Path(lq, lp), Path(rq, rp)) => both(lq, rq, |l, r| eq_qself(l, r)) && eq_path(lp, rp),
|
||||
(TraitObject(lg, ls), TraitObject(rg, rs)) => ls == rs && over(lg, rg, |l, r| eq_generic_bound(l, r)),
|
||||
(ImplTrait(_, lg), ImplTrait(_, rg)) => over(lg, rg, |l, r| eq_generic_bound(l, r)),
|
||||
(Typeof(l), Typeof(r)) => eq_expr(&l.value, &r.value),
|
||||
(MacCall(l), MacCall(r)) => eq_mac_call(l, r),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eq_ext(l: &Extern, r: &Extern) -> bool {
|
||||
use Extern::*;
|
||||
match (l, r) {
|
||||
(None, None) | (Implicit, Implicit) => true,
|
||||
(Explicit(l), Explicit(r)) => eq_str_lit(l, r),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eq_str_lit(l: &StrLit, r: &StrLit) -> bool {
|
||||
l.style == r.style && l.symbol == r.symbol && l.suffix == r.suffix
|
||||
}
|
||||
|
||||
pub fn eq_poly_ref_trait(l: &PolyTraitRef, r: &PolyTraitRef) -> bool {
|
||||
eq_path(&l.trait_ref.path, &r.trait_ref.path)
|
||||
&& over(&l.bound_generic_params, &r.bound_generic_params, |l, r| {
|
||||
eq_generic_param(l, r)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn eq_generic_param(l: &GenericParam, r: &GenericParam) -> bool {
|
||||
use GenericParamKind::*;
|
||||
l.is_placeholder == r.is_placeholder
|
||||
&& eq_id(l.ident, r.ident)
|
||||
&& over(&l.bounds, &r.bounds, |l, r| eq_generic_bound(l, r))
|
||||
&& match (&l.kind, &r.kind) {
|
||||
(Lifetime, Lifetime) => true,
|
||||
(Type { default: l }, Type { default: r }) => both(l, r, |l, r| eq_ty(l, r)),
|
||||
(Const { ty: l }, Const { ty: r }) => eq_ty(l, r),
|
||||
_ => false,
|
||||
}
|
||||
&& over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
|
||||
}
|
||||
|
||||
pub fn eq_generic_bound(l: &GenericBound, r: &GenericBound) -> bool {
|
||||
use GenericBound::*;
|
||||
match (l, r) {
|
||||
(Trait(ptr1, tbm1), Trait(ptr2, tbm2)) => tbm1 == tbm2 && eq_poly_ref_trait(ptr1, ptr2),
|
||||
(Outlives(l), Outlives(r)) => eq_id(l.ident, r.ident),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eq_assoc_constraint(l: &AssocTyConstraint, r: &AssocTyConstraint) -> bool {
|
||||
use AssocTyConstraintKind::*;
|
||||
eq_id(l.ident, r.ident)
|
||||
&& match (&l.kind, &r.kind) {
|
||||
(Equality { ty: l }, Equality { ty: r }) => eq_ty(l, r),
|
||||
(Bound { bounds: l }, Bound { bounds: r }) => over(l, r, |l, r| eq_generic_bound(l, r)),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eq_mac_call(l: &MacCall, r: &MacCall) -> bool {
|
||||
eq_path(&l.path, &r.path) && eq_mac_args(&l.args, &r.args)
|
||||
}
|
||||
|
||||
pub fn eq_attr(l: &Attribute, r: &Attribute) -> bool {
|
||||
use AttrKind::*;
|
||||
l.style == r.style
|
||||
&& match (&l.kind, &r.kind) {
|
||||
(DocComment(l), DocComment(r)) => l == r,
|
||||
(Normal(l), Normal(r)) => eq_path(&l.path, &r.path) && eq_mac_args(&l.args, &r.args),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eq_mac_args(l: &MacArgs, r: &MacArgs) -> bool {
|
||||
use MacArgs::*;
|
||||
match (l, r) {
|
||||
(Empty, Empty) => true,
|
||||
(Delimited(_, ld, lts), Delimited(_, rd, rts)) => ld == rd && lts.eq_unspanned(rts),
|
||||
(Eq(_, lts), Eq(_, rts)) => lts.eq_unspanned(rts),
|
||||
_ => false,
|
||||
}
|
||||
}
|
|
@ -332,19 +332,13 @@ fn swap_binop<'a>(
|
|||
|
||||
/// Checks if the two `Option`s are both `None` or some equal values as per
|
||||
/// `eq_fn`.
|
||||
fn both<X, F>(l: &Option<X>, r: &Option<X>, mut eq_fn: F) -> bool
|
||||
where
|
||||
F: FnMut(&X, &X) -> bool,
|
||||
{
|
||||
pub fn both<X>(l: &Option<X>, r: &Option<X>, mut eq_fn: impl FnMut(&X, &X) -> bool) -> bool {
|
||||
l.as_ref()
|
||||
.map_or_else(|| r.is_none(), |x| r.as_ref().map_or(false, |y| eq_fn(x, y)))
|
||||
}
|
||||
|
||||
/// Checks if two slices are equal as per `eq_fn`.
|
||||
fn over<X, F>(left: &[X], right: &[X], mut eq_fn: F) -> bool
|
||||
where
|
||||
F: FnMut(&X, &X) -> bool,
|
||||
{
|
||||
pub fn over<X>(left: &[X], right: &[X], mut eq_fn: impl FnMut(&X, &X) -> bool) -> bool {
|
||||
left.len() == right.len() && left.iter().zip(right).all(|(x, y)| eq_fn(x, y))
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#[macro_use]
|
||||
pub mod sym;
|
||||
|
||||
pub mod ast_utils;
|
||||
pub mod attrs;
|
||||
pub mod author;
|
||||
pub mod camel_case;
|
||||
|
@ -19,7 +20,7 @@ pub mod sugg;
|
|||
pub mod usage;
|
||||
pub use self::attrs::*;
|
||||
pub use self::diagnostics::*;
|
||||
pub use self::hir_utils::{SpanlessEq, SpanlessHash};
|
||||
pub use self::hir_utils::{both, over, SpanlessEq, SpanlessHash};
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::mem;
|
||||
|
|
|
@ -2327,6 +2327,13 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
|
|||
deprecation: None,
|
||||
module: "misc_early",
|
||||
},
|
||||
Lint {
|
||||
name: "unnested_or_patterns",
|
||||
group: "complexity",
|
||||
desc: "unnested or-patterns, e.g., `Foo(Bar) | Foo(Baz) instead of `Foo(Bar | Baz)`",
|
||||
deprecation: None,
|
||||
module: "unnested_or_patterns",
|
||||
},
|
||||
Lint {
|
||||
name: "unreachable",
|
||||
group: "restriction",
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
use std::cmp::Ordering;
|
||||
|
||||
#[allow(clippy::unnested_or_patterns)]
|
||||
#[warn(clippy::neg_cmp_op_on_partial_ord)]
|
||||
fn main() {
|
||||
let a_value = 1.0;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
error: The use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable.
|
||||
--> $DIR/neg_cmp_op_on_partial_ord.rs:15:21
|
||||
--> $DIR/neg_cmp_op_on_partial_ord.rs:16:21
|
||||
|
|
||||
LL | let _not_less = !(a_value < another_value);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
@ -7,19 +7,19 @@ LL | let _not_less = !(a_value < another_value);
|
|||
= note: `-D clippy::neg-cmp-op-on-partial-ord` implied by `-D warnings`
|
||||
|
||||
error: The use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable.
|
||||
--> $DIR/neg_cmp_op_on_partial_ord.rs:18:30
|
||||
--> $DIR/neg_cmp_op_on_partial_ord.rs:19:30
|
||||
|
|
||||
LL | let _not_less_or_equal = !(a_value <= another_value);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: The use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable.
|
||||
--> $DIR/neg_cmp_op_on_partial_ord.rs:21:24
|
||||
--> $DIR/neg_cmp_op_on_partial_ord.rs:22:24
|
||||
|
|
||||
LL | let _not_greater = !(a_value > another_value);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: The use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable.
|
||||
--> $DIR/neg_cmp_op_on_partial_ord.rs:24:33
|
||||
--> $DIR/neg_cmp_op_on_partial_ord.rs:25:33
|
||||
|
|
||||
LL | let _not_greater_or_equal = !(a_value >= another_value);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
41
tests/ui/unnested_or_patterns.fixed
Normal file
41
tests/ui/unnested_or_patterns.fixed
Normal file
|
@ -0,0 +1,41 @@
|
|||
// run-rustfix
|
||||
|
||||
#![feature(or_patterns)]
|
||||
#![feature(box_patterns)]
|
||||
#![warn(clippy::unnested_or_patterns)]
|
||||
#![allow(clippy::cognitive_complexity, clippy::match_ref_pats)]
|
||||
#![allow(unreachable_patterns, irrefutable_let_patterns, unused_variables)]
|
||||
|
||||
fn main() {
|
||||
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 {}
|
||||
if let &mut (0 | 2) = &mut 0 {}
|
||||
if let x @ (0 | 2) = 0 {}
|
||||
if let (0, 1 | 2 | 3) = (0, 0) {}
|
||||
if let (1 | 2 | 3, 0) = (0, 0) {}
|
||||
if let (x, ..) | (x, 1 | 2) = (0, 1) {}
|
||||
if let [0 | 1] = [0] {}
|
||||
if let [x, 0 | 1] = [0, 1] {}
|
||||
if let [x, 0 | 1 | 2] = [0, 1] {}
|
||||
if let [x, ..] | [x, 1 | 2] = [0, 1] {}
|
||||
struct TS(u8, u8);
|
||||
if let TS(0 | 1, x) = TS(0, 0) {}
|
||||
if let TS(1 | 2 | 3, 0) = TS(0, 0) {}
|
||||
if let TS(x, ..) | TS(x, 1 | 2) = TS(0, 0) {}
|
||||
struct S {
|
||||
x: u8,
|
||||
y: u8,
|
||||
}
|
||||
if let S { x: 0 | 1, y } = (S { x: 0, y: 1 }) {}
|
||||
if let S { x: 0, y, .. } | S { y, x: 1 } = (S { x: 0, y: 1 }) {}
|
||||
if let Some(Some(0 | 1)) = None {}
|
||||
if let Some(Some(0 | 1 | 2)) = None {}
|
||||
if let Some(Some(0 | 1 | 2 | 3 | 4)) = None {}
|
||||
if let Some(Some(0 | 1 | 2)) = None {}
|
||||
if let ((0 | 1 | 2,),) = ((0,),) {}
|
||||
if let 0 | 1 | 2 = 0 {}
|
||||
if let box (0 | 1 | 2 | 3 | 4) = Box::new(0) {}
|
||||
if let box box (0 | 2 | 4) = Box::new(Box::new(0)) {}
|
||||
}
|
41
tests/ui/unnested_or_patterns.rs
Normal file
41
tests/ui/unnested_or_patterns.rs
Normal file
|
@ -0,0 +1,41 @@
|
|||
// run-rustfix
|
||||
|
||||
#![feature(or_patterns)]
|
||||
#![feature(box_patterns)]
|
||||
#![warn(clippy::unnested_or_patterns)]
|
||||
#![allow(clippy::cognitive_complexity, clippy::match_ref_pats)]
|
||||
#![allow(unreachable_patterns, irrefutable_let_patterns, unused_variables)]
|
||||
|
||||
fn main() {
|
||||
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 {}
|
||||
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) {}
|
||||
if let (1, 0) | (2, 0) | (3, 0) = (0, 0) {}
|
||||
if let (x, ..) | (x, 1) | (x, 2) = (0, 1) {}
|
||||
if let [0] | [1] = [0] {}
|
||||
if let [x, 0] | [x, 1] = [0, 1] {}
|
||||
if let [x, 0] | [x, 1] | [x, 2] = [0, 1] {}
|
||||
if let [x, ..] | [x, 1] | [x, 2] = [0, 1] {}
|
||||
struct TS(u8, u8);
|
||||
if let TS(0, x) | TS(1, x) = TS(0, 0) {}
|
||||
if let TS(1, 0) | TS(2, 0) | TS(3, 0) = TS(0, 0) {}
|
||||
if let TS(x, ..) | TS(x, 1) | TS(x, 2) = TS(0, 0) {}
|
||||
struct S {
|
||||
x: u8,
|
||||
y: u8,
|
||||
}
|
||||
if let S { x: 0, y } | S { y, x: 1 } = (S { x: 0, y: 1 }) {}
|
||||
if let S { x: 0, y, .. } | S { y, x: 1 } = (S { x: 0, y: 1 }) {}
|
||||
if let Some(Some(0)) | Some(Some(1)) = None {}
|
||||
if let Some(Some(0)) | Some(Some(1) | Some(2)) = None {}
|
||||
if let Some(Some(0 | 1) | Some(2)) | Some(Some(3) | Some(4)) = None {}
|
||||
if let Some(Some(0) | Some(1 | 2)) = None {}
|
||||
if let ((0,),) | ((1,) | (2,),) = ((0,),) {}
|
||||
if let 0 | (1 | 2) = 0 {}
|
||||
if let box (0 | 1) | (box 2 | box (3 | 4)) = Box::new(0) {}
|
||||
if let box box 0 | box (box 2 | box 4) = Box::new(Box::new(0)) {}
|
||||
}
|
267
tests/ui/unnested_or_patterns.stderr
Normal file
267
tests/ui/unnested_or_patterns.stderr
Normal file
|
@ -0,0 +1,267 @@
|
|||
error: unnested or-patterns
|
||||
--> $DIR/unnested_or_patterns.rs:10:12
|
||||
|
|
||||
LL | if let box 0 | box 2 = Box::new(0) {}
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
||||
= note: `-D clippy::unnested-or-patterns` implied by `-D warnings`
|
||||
help: nest the patterns
|
||||
|
|
||||
LL | if let box (0 | 2) = Box::new(0) {}
|
||||
| ^^^^^^^^^^^
|
||||
|
||||
error: unnested or-patterns
|
||||
--> $DIR/unnested_or_patterns.rs:11:12
|
||||
|
|
||||
LL | if let box ((0 | 1)) | box (2 | 3) | box 4 = Box::new(0) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: nest the patterns
|
||||
|
|
||||
LL | if let box (0 | 1 | 2 | 3 | 4) = Box::new(0) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: unnested or-patterns
|
||||
--> $DIR/unnested_or_patterns.rs:13:12
|
||||
|
|
||||
LL | if let &0 | C0 | &2 = &0 {}
|
||||
| ^^^^^^^^^^^^
|
||||
|
|
||||
help: nest the patterns
|
||||
|
|
||||
LL | if let &(0 | 2) | C0 = &0 {}
|
||||
| ^^^^^^^^^^^^^
|
||||
|
||||
error: unnested or-patterns
|
||||
--> $DIR/unnested_or_patterns.rs:14:12
|
||||
|
|
||||
LL | if let &mut 0 | &mut 2 = &mut 0 {}
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: nest the patterns
|
||||
|
|
||||
LL | if let &mut (0 | 2) = &mut 0 {}
|
||||
| ^^^^^^^^^^^^
|
||||
|
||||
error: unnested or-patterns
|
||||
--> $DIR/unnested_or_patterns.rs:15:12
|
||||
|
|
||||
LL | if let x @ 0 | x @ 2 = 0 {}
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
||||
help: nest the patterns
|
||||
|
|
||||
LL | if let x @ (0 | 2) = 0 {}
|
||||
| ^^^^^^^^^^^
|
||||
|
||||
error: unnested or-patterns
|
||||
--> $DIR/unnested_or_patterns.rs:16:12
|
||||
|
|
||||
LL | if let (0, 1) | (0, 2) | (0, 3) = (0, 0) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: nest the patterns
|
||||
|
|
||||
LL | if let (0, 1 | 2 | 3) = (0, 0) {}
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
||||
error: unnested or-patterns
|
||||
--> $DIR/unnested_or_patterns.rs:17:12
|
||||
|
|
||||
LL | if let (1, 0) | (2, 0) | (3, 0) = (0, 0) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: nest the patterns
|
||||
|
|
||||
LL | if let (1 | 2 | 3, 0) = (0, 0) {}
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
||||
error: unnested or-patterns
|
||||
--> $DIR/unnested_or_patterns.rs:18:12
|
||||
|
|
||||
LL | if let (x, ..) | (x, 1) | (x, 2) = (0, 1) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: nest the patterns
|
||||
|
|
||||
LL | if let (x, ..) | (x, 1 | 2) = (0, 1) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: unnested or-patterns
|
||||
--> $DIR/unnested_or_patterns.rs:19:12
|
||||
|
|
||||
LL | if let [0] | [1] = [0] {}
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
help: nest the patterns
|
||||
|
|
||||
LL | if let [0 | 1] = [0] {}
|
||||
| ^^^^^^^
|
||||
|
||||
error: unnested or-patterns
|
||||
--> $DIR/unnested_or_patterns.rs:20:12
|
||||
|
|
||||
LL | if let [x, 0] | [x, 1] = [0, 1] {}
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: nest the patterns
|
||||
|
|
||||
LL | if let [x, 0 | 1] = [0, 1] {}
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: unnested or-patterns
|
||||
--> $DIR/unnested_or_patterns.rs:21:12
|
||||
|
|
||||
LL | if let [x, 0] | [x, 1] | [x, 2] = [0, 1] {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: nest the patterns
|
||||
|
|
||||
LL | if let [x, 0 | 1 | 2] = [0, 1] {}
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
||||
error: unnested or-patterns
|
||||
--> $DIR/unnested_or_patterns.rs:22:12
|
||||
|
|
||||
LL | if let [x, ..] | [x, 1] | [x, 2] = [0, 1] {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: nest the patterns
|
||||
|
|
||||
LL | if let [x, ..] | [x, 1 | 2] = [0, 1] {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: unnested or-patterns
|
||||
--> $DIR/unnested_or_patterns.rs:24:12
|
||||
|
|
||||
LL | if let TS(0, x) | TS(1, x) = TS(0, 0) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: nest the patterns
|
||||
|
|
||||
LL | if let TS(0 | 1, x) = TS(0, 0) {}
|
||||
| ^^^^^^^^^^^^
|
||||
|
||||
error: unnested or-patterns
|
||||
--> $DIR/unnested_or_patterns.rs:25:12
|
||||
|
|
||||
LL | if let TS(1, 0) | TS(2, 0) | TS(3, 0) = TS(0, 0) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: nest the patterns
|
||||
|
|
||||
LL | if let TS(1 | 2 | 3, 0) = TS(0, 0) {}
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
||||
error: unnested or-patterns
|
||||
--> $DIR/unnested_or_patterns.rs:26:12
|
||||
|
|
||||
LL | if let TS(x, ..) | TS(x, 1) | TS(x, 2) = TS(0, 0) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: nest the patterns
|
||||
|
|
||||
LL | if let TS(x, ..) | TS(x, 1 | 2) = TS(0, 0) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: unnested or-patterns
|
||||
--> $DIR/unnested_or_patterns.rs:31:12
|
||||
|
|
||||
LL | if let S { x: 0, y } | S { y, x: 1 } = (S { x: 0, y: 1 }) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: nest the patterns
|
||||
|
|
||||
LL | if let S { x: 0 | 1, y } = (S { x: 0, y: 1 }) {}
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: unnested or-patterns
|
||||
--> $DIR/unnested_or_patterns.rs:33:12
|
||||
|
|
||||
LL | if let Some(Some(0)) | Some(Some(1)) = None {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: nest the patterns
|
||||
|
|
||||
LL | if let Some(Some(0 | 1)) = None {}
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: unnested or-patterns
|
||||
--> $DIR/unnested_or_patterns.rs:34:12
|
||||
|
|
||||
LL | if let Some(Some(0)) | Some(Some(1) | Some(2)) = None {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: nest the patterns
|
||||
|
|
||||
LL | if let Some(Some(0 | 1 | 2)) = None {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: unnested or-patterns
|
||||
--> $DIR/unnested_or_patterns.rs:35:12
|
||||
|
|
||||
LL | if let Some(Some(0 | 1) | Some(2)) | Some(Some(3) | Some(4)) = None {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: nest the patterns
|
||||
|
|
||||
LL | if let Some(Some(0 | 1 | 2 | 3 | 4)) = None {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: unnested or-patterns
|
||||
--> $DIR/unnested_or_patterns.rs:36:12
|
||||
|
|
||||
LL | if let Some(Some(0) | Some(1 | 2)) = None {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: nest the patterns
|
||||
|
|
||||
LL | if let Some(Some(0 | 1 | 2)) = None {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: unnested or-patterns
|
||||
--> $DIR/unnested_or_patterns.rs:37:12
|
||||
|
|
||||
LL | if let ((0,),) | ((1,) | (2,),) = ((0,),) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: nest the patterns
|
||||
|
|
||||
LL | if let ((0 | 1 | 2,),) = ((0,),) {}
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
||||
error: unnested or-patterns
|
||||
--> $DIR/unnested_or_patterns.rs:38:12
|
||||
|
|
||||
LL | if let 0 | (1 | 2) = 0 {}
|
||||
| ^^^^^^^^^^^
|
||||
|
|
||||
help: nest the patterns
|
||||
|
|
||||
LL | if let 0 | 1 | 2 = 0 {}
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: unnested or-patterns
|
||||
--> $DIR/unnested_or_patterns.rs:39:12
|
||||
|
|
||||
LL | if let box (0 | 1) | (box 2 | box (3 | 4)) = Box::new(0) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: nest the patterns
|
||||
|
|
||||
LL | if let box (0 | 1 | 2 | 3 | 4) = Box::new(0) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: unnested or-patterns
|
||||
--> $DIR/unnested_or_patterns.rs:40:12
|
||||
|
|
||||
LL | if let box box 0 | box (box 2 | box 4) = Box::new(Box::new(0)) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: nest the patterns
|
||||
|
|
||||
LL | if let box box (0 | 2 | 4) = Box::new(Box::new(0)) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 24 previous errors
|
||||
|
|
@ -6,7 +6,8 @@
|
|||
unused_variables,
|
||||
dead_code,
|
||||
clippy::single_match,
|
||||
clippy::wildcard_in_or_patterns
|
||||
clippy::wildcard_in_or_patterns,
|
||||
clippy::unnested_or_patterns
|
||||
)]
|
||||
|
||||
use std::io::ErrorKind;
|
||||
|
|
|
@ -6,7 +6,8 @@
|
|||
unused_variables,
|
||||
dead_code,
|
||||
clippy::single_match,
|
||||
clippy::wildcard_in_or_patterns
|
||||
clippy::wildcard_in_or_patterns,
|
||||
clippy::unnested_or_patterns
|
||||
)]
|
||||
|
||||
use std::io::ErrorKind;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
error: wildcard match will miss any future added variants
|
||||
--> $DIR/wildcard_enum_match_arm.rs:37:9
|
||||
--> $DIR/wildcard_enum_match_arm.rs:38:9
|
||||
|
|
||||
LL | _ => eprintln!("Not red"),
|
||||
| ^ help: try this: `Color::Green | Color::Blue | Color::Rgb(..) | Color::Cyan`
|
||||
|
@ -11,25 +11,25 @@ LL | #![deny(clippy::wildcard_enum_match_arm)]
|
|||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: wildcard match will miss any future added variants
|
||||
--> $DIR/wildcard_enum_match_arm.rs:41:9
|
||||
--> $DIR/wildcard_enum_match_arm.rs:42:9
|
||||
|
|
||||
LL | _not_red => eprintln!("Not red"),
|
||||
| ^^^^^^^^ help: try this: `_not_red @ Color::Green | _not_red @ Color::Blue | _not_red @ Color::Rgb(..) | _not_red @ Color::Cyan`
|
||||
|
||||
error: wildcard match will miss any future added variants
|
||||
--> $DIR/wildcard_enum_match_arm.rs:45:9
|
||||
--> $DIR/wildcard_enum_match_arm.rs:46:9
|
||||
|
|
||||
LL | not_red => format!("{:?}", not_red),
|
||||
| ^^^^^^^ help: try this: `not_red @ Color::Green | not_red @ Color::Blue | not_red @ Color::Rgb(..) | not_red @ Color::Cyan`
|
||||
|
||||
error: wildcard match will miss any future added variants
|
||||
--> $DIR/wildcard_enum_match_arm.rs:61:9
|
||||
--> $DIR/wildcard_enum_match_arm.rs:62:9
|
||||
|
|
||||
LL | _ => "No red",
|
||||
| ^ help: try this: `Color::Red | Color::Green | Color::Blue | Color::Rgb(..) | Color::Cyan`
|
||||
|
||||
error: match on non-exhaustive enum doesn't explicitly match all known variants
|
||||
--> $DIR/wildcard_enum_match_arm.rs:78:9
|
||||
--> $DIR/wildcard_enum_match_arm.rs:79:9
|
||||
|
|
||||
LL | _ => {},
|
||||
| ^ help: try this: `std::io::ErrorKind::PermissionDenied | std::io::ErrorKind::ConnectionRefused | std::io::ErrorKind::ConnectionReset | std::io::ErrorKind::ConnectionAborted | std::io::ErrorKind::NotConnected | std::io::ErrorKind::AddrInUse | std::io::ErrorKind::AddrNotAvailable | std::io::ErrorKind::BrokenPipe | std::io::ErrorKind::AlreadyExists | std::io::ErrorKind::WouldBlock | std::io::ErrorKind::InvalidInput | std::io::ErrorKind::InvalidData | std::io::ErrorKind::TimedOut | std::io::ErrorKind::WriteZero | std::io::ErrorKind::Interrupted | std::io::ErrorKind::Other | std::io::ErrorKind::UnexpectedEof | _`
|
||||
|
|
Loading…
Reference in a new issue