]> git.lizzy.rs Git - rust.git/commitdiff
Move `MatchStrCaseMismatch` into `Matches` lint pass
authorJason Newcomb <jsnewcomb@pm.me>
Fri, 3 Jun 2022 17:16:02 +0000 (13:16 -0400)
committerJason Newcomb <jsnewcomb@pm.me>
Fri, 3 Jun 2022 23:09:49 +0000 (19:09 -0400)
clippy_lints/src/lib.register_all.rs
clippy_lints/src/lib.register_correctness.rs
clippy_lints/src/lib.register_lints.rs
clippy_lints/src/lib.rs
clippy_lints/src/match_str_case_mismatch.rs [deleted file]
clippy_lints/src/matches/match_str_case_mismatch.rs [new file with mode: 0644]
clippy_lints/src/matches/mod.rs

index 46af1d8fb09c3539eccfa0a893250dfee6ac5057..15e744229a1cbbbbaf48152cee58bc11ca13f4f6 100644 (file)
     LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN),
     LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN),
     LintId::of(match_result_ok::MATCH_RESULT_OK),
-    LintId::of(match_str_case_mismatch::MATCH_STR_CASE_MISMATCH),
     LintId::of(matches::COLLAPSIBLE_MATCH),
     LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH),
     LintId::of(matches::MANUAL_UNWRAP_OR),
     LintId::of(matches::MATCH_OVERLAPPING_ARM),
     LintId::of(matches::MATCH_REF_PATS),
     LintId::of(matches::MATCH_SINGLE_BINDING),
+    LintId::of(matches::MATCH_STR_CASE_MISMATCH),
     LintId::of(matches::NEEDLESS_MATCH),
     LintId::of(matches::REDUNDANT_PATTERN_MATCHING),
     LintId::of(matches::SINGLE_MATCH),
index 6bf2c4bbaedc024ad45ac7970c6fc030be7c9d67..50cdd0af923051f2453dc36a62f3506f94a6bbac 100644 (file)
@@ -39,7 +39,7 @@
     LintId::of(loops::ITER_NEXT_LOOP),
     LintId::of(loops::NEVER_LOOP),
     LintId::of(loops::WHILE_IMMUTABLE_CONDITION),
-    LintId::of(match_str_case_mismatch::MATCH_STR_CASE_MISMATCH),
+    LintId::of(matches::MATCH_STR_CASE_MISMATCH),
     LintId::of(mem_replace::MEM_REPLACE_WITH_UNINIT),
     LintId::of(methods::CLONE_DOUBLE_REF),
     LintId::of(methods::ITERATOR_STEP_BY_ZERO),
index fb44df57de676594d9a45c73e34b999e6b4bd149..59ba295a88732242d137e2960c3eef82bde505de 100644 (file)
     map_unit_fn::OPTION_MAP_UNIT_FN,
     map_unit_fn::RESULT_MAP_UNIT_FN,
     match_result_ok::MATCH_RESULT_OK,
-    match_str_case_mismatch::MATCH_STR_CASE_MISMATCH,
     matches::COLLAPSIBLE_MATCH,
     matches::INFALLIBLE_DESTRUCTURING_MATCH,
     matches::MANUAL_UNWRAP_OR,
     matches::MATCH_REF_PATS,
     matches::MATCH_SAME_ARMS,
     matches::MATCH_SINGLE_BINDING,
+    matches::MATCH_STR_CASE_MISMATCH,
     matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
     matches::MATCH_WILD_ERR_ARM,
     matches::NEEDLESS_MATCH,
index 50b67918fc00d3b2f58cc73fc4ef35a2b17b93ad..7335b4a35c8c6993ac7a3d9b08f76c69d1d53c38 100644 (file)
@@ -287,7 +287,6 @@ macro_rules! declare_clippy_lint {
 mod map_err_ignore;
 mod map_unit_fn;
 mod match_result_ok;
-mod match_str_case_mismatch;
 mod matches;
 mod mem_forget;
 mod mem_replace;
@@ -875,7 +874,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         ))
     });
     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));
     store.register_early_pass(|| Box::new(octal_escapes::OctalEscapes));
diff --git a/clippy_lints/src/match_str_case_mismatch.rs b/clippy_lints/src/match_str_case_mismatch.rs
deleted file mode 100644 (file)
index d97a878..0000000
+++ /dev/null
@@ -1,162 +0,0 @@
-use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::ty::is_type_diagnostic_item;
-use rustc_ast::ast::LitKind;
-use rustc_errors::Applicability;
-use rustc_hir::intravisit::{walk_expr, Visitor};
-use rustc_hir::{Arm, Expr, ExprKind, MatchSource, PatKind};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::lint::in_external_macro;
-use rustc_middle::ty;
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::symbol::Symbol;
-use rustc_span::{sym, Span};
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for `match` expressions modifying the case of a string with non-compliant arms
-    ///
-    /// ### Why is this bad?
-    /// The arm is unreachable, which is likely a mistake
-    ///
-    /// ### Example
-    /// ```rust
-    /// # let text = "Foo";
-    /// match &*text.to_ascii_lowercase() {
-    ///     "foo" => {},
-    ///     "Bar" => {},
-    ///     _ => {},
-    /// }
-    /// ```
-    /// Use instead:
-    /// ```rust
-    /// # let text = "Foo";
-    /// match &*text.to_ascii_lowercase() {
-    ///     "foo" => {},
-    ///     "bar" => {},
-    ///     _ => {},
-    /// }
-    /// ```
-    #[clippy::version = "1.58.0"]
-    pub MATCH_STR_CASE_MISMATCH,
-    correctness,
-    "creation of a case altering match expression with non-compliant arms"
-}
-
-declare_lint_pass!(MatchStrCaseMismatch => [MATCH_STR_CASE_MISMATCH]);
-
-#[derive(Debug)]
-enum CaseMethod {
-    LowerCase,
-    AsciiLowerCase,
-    UpperCase,
-    AsciiUppercase,
-}
-
-impl<'tcx> LateLintPass<'tcx> for MatchStrCaseMismatch {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if_chain! {
-            if !in_external_macro(cx.tcx.sess, expr.span);
-            if let ExprKind::Match(match_expr, arms, MatchSource::Normal) = expr.kind;
-            if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty(match_expr).kind();
-            if let ty::Str = ty.kind();
-            then {
-                let mut visitor = MatchExprVisitor {
-                    cx,
-                    case_method: None,
-                };
-
-                visitor.visit_expr(match_expr);
-
-                if let Some(case_method) = visitor.case_method {
-                    if let Some((bad_case_span, bad_case_sym)) = verify_case(&case_method, arms) {
-                        lint(cx, &case_method, bad_case_span, bad_case_sym.as_str());
-                    }
-                }
-            }
-        }
-    }
-}
-
-struct MatchExprVisitor<'a, 'tcx> {
-    cx: &'a LateContext<'tcx>,
-    case_method: Option<CaseMethod>,
-}
-
-impl<'a, 'tcx> Visitor<'tcx> for MatchExprVisitor<'a, 'tcx> {
-    fn visit_expr(&mut self, ex: &'tcx Expr<'_>) {
-        match ex.kind {
-            ExprKind::MethodCall(segment, [receiver], _) if self.case_altered(segment.ident.as_str(), receiver) => {},
-            _ => walk_expr(self, ex),
-        }
-    }
-}
-
-impl<'a, 'tcx> MatchExprVisitor<'a, 'tcx> {
-    fn case_altered(&mut self, segment_ident: &str, receiver: &Expr<'_>) -> bool {
-        if let Some(case_method) = get_case_method(segment_ident) {
-            let ty = self.cx.typeck_results().expr_ty(receiver).peel_refs();
-
-            if is_type_diagnostic_item(self.cx, ty, sym::String) || ty.kind() == &ty::Str {
-                self.case_method = Some(case_method);
-                return true;
-            }
-        }
-
-        false
-    }
-}
-
-fn get_case_method(segment_ident_str: &str) -> Option<CaseMethod> {
-    match segment_ident_str {
-        "to_lowercase" => Some(CaseMethod::LowerCase),
-        "to_ascii_lowercase" => Some(CaseMethod::AsciiLowerCase),
-        "to_uppercase" => Some(CaseMethod::UpperCase),
-        "to_ascii_uppercase" => Some(CaseMethod::AsciiUppercase),
-        _ => None,
-    }
-}
-
-fn verify_case<'a>(case_method: &'a CaseMethod, arms: &'a [Arm<'_>]) -> Option<(Span, Symbol)> {
-    let case_check = match case_method {
-        CaseMethod::LowerCase => |input: &str| -> bool { input.chars().all(|c| c.to_lowercase().next() == Some(c)) },
-        CaseMethod::AsciiLowerCase => |input: &str| -> bool { !input.chars().any(|c| c.is_ascii_uppercase()) },
-        CaseMethod::UpperCase => |input: &str| -> bool { input.chars().all(|c| c.to_uppercase().next() == Some(c)) },
-        CaseMethod::AsciiUppercase => |input: &str| -> bool { !input.chars().any(|c| c.is_ascii_lowercase()) },
-    };
-
-    for arm in arms {
-        if_chain! {
-            if let PatKind::Lit(Expr {
-                                kind: ExprKind::Lit(lit),
-                                ..
-                            }) = arm.pat.kind;
-            if let LitKind::Str(symbol, _) = lit.node;
-            let input = symbol.as_str();
-            if !case_check(input);
-            then {
-                return Some((lit.span, symbol));
-            }
-        }
-    }
-
-    None
-}
-
-fn lint(cx: &LateContext<'_>, case_method: &CaseMethod, bad_case_span: Span, bad_case_str: &str) {
-    let (method_str, suggestion) = match case_method {
-        CaseMethod::LowerCase => ("to_lowercase", bad_case_str.to_lowercase()),
-        CaseMethod::AsciiLowerCase => ("to_ascii_lowercase", bad_case_str.to_ascii_lowercase()),
-        CaseMethod::UpperCase => ("to_uppercase", bad_case_str.to_uppercase()),
-        CaseMethod::AsciiUppercase => ("to_ascii_uppercase", bad_case_str.to_ascii_uppercase()),
-    };
-
-    span_lint_and_sugg(
-        cx,
-        MATCH_STR_CASE_MISMATCH,
-        bad_case_span,
-        "this `match` arm has a differing case than its expression",
-        &*format!("consider changing the case of this arm to respect `{}`", method_str),
-        format!("\"{}\"", suggestion),
-        Applicability::MachineApplicable,
-    );
-}
diff --git a/clippy_lints/src/matches/match_str_case_mismatch.rs b/clippy_lints/src/matches/match_str_case_mismatch.rs
new file mode 100644 (file)
index 0000000..8302ce4
--- /dev/null
@@ -0,0 +1,125 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::ty::is_type_diagnostic_item;
+use rustc_ast::ast::LitKind;
+use rustc_errors::Applicability;
+use rustc_hir::intravisit::{walk_expr, Visitor};
+use rustc_hir::{Arm, Expr, ExprKind, PatKind};
+use rustc_lint::LateContext;
+use rustc_middle::ty;
+use rustc_span::symbol::Symbol;
+use rustc_span::{sym, Span};
+
+use super::MATCH_STR_CASE_MISMATCH;
+
+#[derive(Debug)]
+enum CaseMethod {
+    LowerCase,
+    AsciiLowerCase,
+    UpperCase,
+    AsciiUppercase,
+}
+
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, scrutinee: &'tcx Expr<'_>, arms: &'tcx [Arm<'_>]) {
+    if_chain! {
+        if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty(scrutinee).kind();
+        if let ty::Str = ty.kind();
+        then {
+            let mut visitor = MatchExprVisitor {
+                cx,
+                case_method: None,
+            };
+
+            visitor.visit_expr(scrutinee);
+
+            if let Some(case_method) = visitor.case_method {
+                if let Some((bad_case_span, bad_case_sym)) = verify_case(&case_method, arms) {
+                    lint(cx, &case_method, bad_case_span, bad_case_sym.as_str());
+                }
+            }
+        }
+    }
+}
+
+struct MatchExprVisitor<'a, 'tcx> {
+    cx: &'a LateContext<'tcx>,
+    case_method: Option<CaseMethod>,
+}
+
+impl<'a, 'tcx> Visitor<'tcx> for MatchExprVisitor<'a, 'tcx> {
+    fn visit_expr(&mut self, ex: &'tcx Expr<'_>) {
+        match ex.kind {
+            ExprKind::MethodCall(segment, [receiver], _) if self.case_altered(segment.ident.as_str(), receiver) => {},
+            _ => walk_expr(self, ex),
+        }
+    }
+}
+
+impl<'a, 'tcx> MatchExprVisitor<'a, 'tcx> {
+    fn case_altered(&mut self, segment_ident: &str, receiver: &Expr<'_>) -> bool {
+        if let Some(case_method) = get_case_method(segment_ident) {
+            let ty = self.cx.typeck_results().expr_ty(receiver).peel_refs();
+
+            if is_type_diagnostic_item(self.cx, ty, sym::String) || ty.kind() == &ty::Str {
+                self.case_method = Some(case_method);
+                return true;
+            }
+        }
+
+        false
+    }
+}
+
+fn get_case_method(segment_ident_str: &str) -> Option<CaseMethod> {
+    match segment_ident_str {
+        "to_lowercase" => Some(CaseMethod::LowerCase),
+        "to_ascii_lowercase" => Some(CaseMethod::AsciiLowerCase),
+        "to_uppercase" => Some(CaseMethod::UpperCase),
+        "to_ascii_uppercase" => Some(CaseMethod::AsciiUppercase),
+        _ => None,
+    }
+}
+
+fn verify_case<'a>(case_method: &'a CaseMethod, arms: &'a [Arm<'_>]) -> Option<(Span, Symbol)> {
+    let case_check = match case_method {
+        CaseMethod::LowerCase => |input: &str| -> bool { input.chars().all(|c| c.to_lowercase().next() == Some(c)) },
+        CaseMethod::AsciiLowerCase => |input: &str| -> bool { !input.chars().any(|c| c.is_ascii_uppercase()) },
+        CaseMethod::UpperCase => |input: &str| -> bool { input.chars().all(|c| c.to_uppercase().next() == Some(c)) },
+        CaseMethod::AsciiUppercase => |input: &str| -> bool { !input.chars().any(|c| c.is_ascii_lowercase()) },
+    };
+
+    for arm in arms {
+        if_chain! {
+            if let PatKind::Lit(Expr {
+                                kind: ExprKind::Lit(lit),
+                                ..
+                            }) = arm.pat.kind;
+            if let LitKind::Str(symbol, _) = lit.node;
+            let input = symbol.as_str();
+            if !case_check(input);
+            then {
+                return Some((lit.span, symbol));
+            }
+        }
+    }
+
+    None
+}
+
+fn lint(cx: &LateContext<'_>, case_method: &CaseMethod, bad_case_span: Span, bad_case_str: &str) {
+    let (method_str, suggestion) = match case_method {
+        CaseMethod::LowerCase => ("to_lowercase", bad_case_str.to_lowercase()),
+        CaseMethod::AsciiLowerCase => ("to_ascii_lowercase", bad_case_str.to_ascii_lowercase()),
+        CaseMethod::UpperCase => ("to_uppercase", bad_case_str.to_uppercase()),
+        CaseMethod::AsciiUppercase => ("to_ascii_uppercase", bad_case_str.to_ascii_uppercase()),
+    };
+
+    span_lint_and_sugg(
+        cx,
+        MATCH_STR_CASE_MISMATCH,
+        bad_case_span,
+        "this `match` arm has a differing case than its expression",
+        &*format!("consider changing the case of this arm to respect `{}`", method_str),
+        format!("\"{}\"", suggestion),
+        Applicability::MachineApplicable,
+    );
+}
index 7fb449028e7a47620fe3e4523625e879399abb31..a58c71ca5f808a545efcb67e7a79c27683f06d31 100644 (file)
@@ -18,6 +18,7 @@
 mod match_ref_pats;
 mod match_same_arms;
 mod match_single_binding;
+mod match_str_case_mismatch;
 mod match_wild_enum;
 mod match_wild_err_arm;
 mod needless_match;
     "matching on vector elements can panic"
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for `match` expressions modifying the case of a string with non-compliant arms
+    ///
+    /// ### Why is this bad?
+    /// The arm is unreachable, which is likely a mistake
+    ///
+    /// ### Example
+    /// ```rust
+    /// # let text = "Foo";
+    /// match &*text.to_ascii_lowercase() {
+    ///     "foo" => {},
+    ///     "Bar" => {},
+    ///     _ => {},
+    /// }
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// # let text = "Foo";
+    /// match &*text.to_ascii_lowercase() {
+    ///     "foo" => {},
+    ///     "bar" => {},
+    ///     _ => {},
+    /// }
+    /// ```
+    #[clippy::version = "1.58.0"]
+    pub MATCH_STR_CASE_MISMATCH,
+    correctness,
+    "creation of a case altering match expression with non-compliant arms"
+}
+
 #[derive(Default)]
 pub struct Matches {
     msrv: Option<RustcVersion>,
@@ -753,6 +785,7 @@ pub fn new(msrv: Option<RustcVersion>) -> Self {
     COLLAPSIBLE_MATCH,
     MANUAL_UNWRAP_OR,
     MATCH_ON_VEC_ITEMS,
+    MATCH_STR_CASE_MISMATCH,
 ]);
 
 impl<'tcx> LateLintPass<'tcx> for Matches {
@@ -790,6 +823,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
                     match_as_ref::check(cx, ex, arms, expr);
                     needless_match::check_match(cx, ex, arms, expr);
                     match_on_vec_items::check(cx, ex);
+                    match_str_case_mismatch::check(cx, ex, arms);
 
                     if !in_constant(cx, expr.hir_id) {
                         manual_unwrap_or::check(cx, expr, ex, arms);