#![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 clippy_utils::ast_utils::{eq_field_pat, eq_id, eq_pat, eq_path};
+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_pretty::pprust;
use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_semver::RustcVersion;
+use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::DUMMY_SP;
use std::cell::Cell;
/// }
/// ```
pub UNNESTED_OR_PATTERNS,
- complexity,
+ pedantic,
"unnested or-patterns, e.g., `Foo(Bar) | Foo(Baz) instead of `Foo(Bar | Baz)`"
}
-declare_lint_pass!(UnnestedOrPatterns => [UNNESTED_OR_PATTERNS]);
+#[derive(Clone, Copy)]
+pub struct UnnestedOrPatterns {
+ msrv: Option<RustcVersion>,
+}
+
+impl UnnestedOrPatterns {
+ #[must_use]
+ pub fn new(msrv: Option<RustcVersion>) -> Self {
+ Self { msrv }
+ }
+}
+
+impl_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);
+ if meets_msrv(self.msrv.as_ref(), &msrvs::OR_PATTERNS) {
+ 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);
+ if meets_msrv(self.msrv.as_ref(), &msrvs::OR_PATTERNS) {
+ 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);
+ if meets_msrv(self.msrv.as_ref(), &msrvs::OR_PATTERNS) {
+ lint_unnested_or_patterns(cx, &p.pat);
+ }
}
fn check_local(&mut self, cx: &EarlyContext<'_>, l: &ast::Local) {
- lint_unnested_or_patterns(cx, &l.pat);
+ if meets_msrv(self.msrv.as_ref(), &msrvs::OR_PATTERNS) {
+ lint_unnested_or_patterns(cx, &l.pat);
+ }
}
+
+ extract_msrv_attr!(EarlyContext);
}
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 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.
+ // 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(_)
// Dealt with elsewhere.
| Or(_) | Paren(_) => false,
// Transform `box x | ... | box y` into `box (x | y)`.
//
- // The cases below until `Slice(...)` deal *singleton* products.
+ // The cases below until `Slice(...)` deal with *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)`.
+ // 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, 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]`.
+ // 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]`.
+ // 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!(
/// 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>,
+ fps1: &mut Vec<ast::PatField>,
rest1: bool,
start: usize,
alternatives: &mut Vec<P<Pat>>,
id: DUMMY_NODE_ID,
kind: Wild,
span: DUMMY_SP,
+ tokens: None,
};
mem::replace(from, dummy)
}
/// 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()
+ ps1.len() == ps2.len()
+ && ps1[idx].is_rest() == ps2[idx].is_rest() // Avoid `[x, ..] | [x, 0]` => `[x, .. | 0]`.
&& over(&ps1[..idx], &ps2[..idx], |l, r| eq_pat(l, r))
&& over(&ps1[idx + 1..], &ps2[idx + 1..], |l, r| eq_pat(l, r))
}