Skip to content

Commit

Permalink
Add a suggestion
Browse files Browse the repository at this point in the history
  • Loading branch information
Serial-ATA committed Oct 12, 2021
1 parent 99bfee7 commit 0c99de0
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 57 deletions.
49 changes: 27 additions & 22 deletions clippy_lints/src/match_str_case_mismatch.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
use clippy_utils::diagnostics::span_lint_and_help;
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, NestedVisitorMap, Visitor};
use rustc_hir::{Arm, Expr, ExprKind, MatchSource, PatKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::hir::map::Map;
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::symbol::SymbolStr;
use rustc_span::{sym, Span};

declare_clippy_lint! {
Expand All @@ -18,15 +20,19 @@ declare_clippy_lint! {
/// The arm is unreachable, which is likely a mistake
///
/// ### Example
/// ```rust,no_run
/// ```rust
/// # let text = "Foo";
///
/// match &*text.to_ascii_lowercase() {
/// "foo" => {},
/// "Bar" => {},
/// _ => {},
/// }
/// ```
/// Use instead:
/// ```rust,no_run
/// ```rust
/// # let text = "Foo";
///
/// match &*text.to_ascii_lowercase() {
/// "foo" => {},
/// "bar" => {},
Expand Down Expand Up @@ -64,8 +70,8 @@ impl LateLintPass<'_> for MatchStrCaseMismatch {
visitor.visit_expr(match_expr);

if let Some(case_method) = visitor.case_method {
if let Some(bad_case) = verify_case(&case_method, arms) {
lint(cx, expr.span, &case_method, bad_case);
if let Some((bad_case_span, bad_case_str)) = verify_case(&case_method, arms) {
lint(cx, &case_method, bad_case_span, &bad_case_str);
}
}
}
Expand Down Expand Up @@ -119,9 +125,7 @@ fn get_case_method(segment_ident_str: &str) -> Option<CaseMethod> {
}
}

fn verify_case(case_method: &CaseMethod, arms: &'_ [Arm<'_>]) -> Option<Span> {
let mut bad_case = None;

fn verify_case<'a>(case_method: &'a CaseMethod, arms: &'a [Arm<'_>]) -> Option<(Span, SymbolStr)> {
let case_check = match case_method {
CaseMethod::LowerCase => |input: &str| -> bool { input.chars().all(char::is_lowercase) },
CaseMethod::AsciiLowerCase => |input: &str| -> bool { input.chars().all(|c| matches!(c, 'a'..='z')) },
Expand All @@ -136,31 +140,32 @@ fn verify_case(case_method: &CaseMethod, arms: &'_ [Arm<'_>]) -> Option<Span> {
..
}) = arm.pat.kind;
if let LitKind::Str(symbol, _) = lit.node;
if !case_check(&symbol.as_str());
let input = symbol.as_str();
if !case_check(&input);
then {
bad_case = Some(lit.span);
break;
return Some((lit.span, input));
}
}
}

bad_case
None
}

fn lint(cx: &LateContext<'_>, expr_span: Span, case_method: &CaseMethod, bad_case_span: Span) {
let method_str = match case_method {
CaseMethod::LowerCase => "to_lower_case",
CaseMethod::AsciiLowerCase => "to_ascii_lowercase",
CaseMethod::UpperCase => "to_uppercase",
CaseMethod::AsciiUppercase => "to_ascii_uppercase",
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_lower_case", 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_help(
span_lint_and_sugg(
cx,
MATCH_STR_CASE_MISMATCH,
expr_span,
"this `match` expression alters case, but has non-compliant arms",
Some(bad_case_span),
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,
);
}
53 changes: 18 additions & 35 deletions tests/ui/match_str_case_mismatch.stderr
Original file line number Diff line number Diff line change
@@ -1,53 +1,36 @@
error: this `match` expression alters case, but has non-compliant arms
--> $DIR/match_str_case_mismatch.rs:66:5
|
LL | / match var.to_ascii_lowercase().as_str() {
LL | | "foo" => {},
LL | | "Bar" => {},
LL | | _ => {},
LL | | }
| |_____^
|
= note: `-D clippy::match-str-case-mismatch` implied by `-D warnings`
help: consider changing the case of this arm to respect `to_ascii_lowercase`
error: this `match` arm has a differing case than its expression
--> $DIR/match_str_case_mismatch.rs:68:9
|
LL | "Bar" => {},
| ^^^^^

error: this `match` expression alters case, but has non-compliant arms
--> $DIR/match_str_case_mismatch.rs:76:5
|
LL | / match &*var.to_ascii_lowercase() {
LL | | "foo" => {},
LL | | "Bar" => {},
LL | | _ => {},
LL | | }
| |_____^
|
= note: `-D clippy::match-str-case-mismatch` implied by `-D warnings`
help: consider changing the case of this arm to respect `to_ascii_lowercase`
|
LL | "bar" => {},
| ~~~~~

error: this `match` arm has a differing case than its expression
--> $DIR/match_str_case_mismatch.rs:78:9
|
LL | "Bar" => {},
| ^^^^^

error: this `match` expression alters case, but has non-compliant arms
--> $DIR/match_str_case_mismatch.rs:86:5
|
LL | / match &*var
LL | | .to_ascii_lowercase()
LL | | .to_uppercase()
LL | | .to_lowercase()
... |
LL | | _ => {},
LL | | }
| |_____^
|
help: consider changing the case of this arm to respect `to_ascii_uppercase`
help: consider changing the case of this arm to respect `to_ascii_lowercase`
|
LL | "bar" => {},
| ~~~~~

error: this `match` arm has a differing case than its expression
--> $DIR/match_str_case_mismatch.rs:93:9
|
LL | "bAR" => {},
| ^^^^^
|
help: consider changing the case of this arm to respect `to_ascii_uppercase`
|
LL | "BAR" => {},
| ~~~~~

error: aborting due to 3 previous errors

0 comments on commit 0c99de0

Please sign in to comment.