1 use clippy_utils
::diagnostics
::span_lint_and_sugg
;
2 use clippy_utils
::ty
::is_type_diagnostic_item
;
3 use rustc_ast
::ast
::LitKind
;
4 use rustc_errors
::Applicability
;
5 use rustc_hir
::intravisit
::{walk_expr, Visitor}
;
6 use rustc_hir
::{Arm, Expr, ExprKind, MatchSource, PatKind}
;
7 use rustc_lint
::{LateContext, LateLintPass}
;
8 use rustc_middle
::lint
::in_external_macro
;
10 use rustc_session
::{declare_lint_pass, declare_tool_lint}
;
11 use rustc_span
::symbol
::Symbol
;
12 use rustc_span
::{sym, Span}
;
14 declare_clippy_lint
! {
16 /// Checks for `match` expressions modifying the case of a string with non-compliant arms
18 /// ### Why is this bad?
19 /// The arm is unreachable, which is likely a mistake
23 /// # let text = "Foo";
25 /// match &*text.to_ascii_lowercase() {
33 /// # let text = "Foo";
35 /// match &*text.to_ascii_lowercase() {
41 #[clippy::version = "1.58.0"]
42 pub MATCH_STR_CASE_MISMATCH
,
44 "creation of a case altering match expression with non-compliant arms"
47 declare_lint_pass
!(MatchStrCaseMismatch
=> [MATCH_STR_CASE_MISMATCH
]);
57 impl<'tcx
> LateLintPass
<'tcx
> for MatchStrCaseMismatch
{
58 fn check_expr(&mut self, cx
: &LateContext
<'tcx
>, expr
: &'tcx Expr
<'_
>) {
60 if !in_external_macro(cx
.tcx
.sess
, expr
.span
);
61 if let ExprKind
::Match(match_expr
, arms
, MatchSource
::Normal
) = expr
.kind
;
62 if let ty
::Ref(_
, ty
, _
) = cx
.typeck_results().expr_ty(match_expr
).kind();
63 if let ty
::Str
= ty
.kind();
65 let mut visitor
= MatchExprVisitor
{
70 visitor
.visit_expr(match_expr
);
72 if let Some(case_method
) = visitor
.case_method
{
73 if let Some((bad_case_span
, bad_case_sym
)) = verify_case(&case_method
, arms
) {
74 lint(cx
, &case_method
, bad_case_span
, bad_case_sym
.as_str());
82 struct MatchExprVisitor
<'a
, 'tcx
> {
83 cx
: &'a LateContext
<'tcx
>,
84 case_method
: Option
<CaseMethod
>,
87 impl<'a
, 'tcx
> Visitor
<'tcx
> for MatchExprVisitor
<'a
, 'tcx
> {
88 fn visit_expr(&mut self, ex
: &'tcx Expr
<'_
>) {
90 ExprKind
::MethodCall(segment
, [receiver
], _
) if self.case_altered(segment
.ident
.as_str(), receiver
) => {}
,
91 _
=> walk_expr(self, ex
),
96 impl<'a
, 'tcx
> MatchExprVisitor
<'a
, 'tcx
> {
97 fn case_altered(&mut self, segment_ident
: &str, receiver
: &Expr
<'_
>) -> bool
{
98 if let Some(case_method
) = get_case_method(segment_ident
) {
99 let ty
= self.cx
.typeck_results().expr_ty(receiver
).peel_refs();
101 if is_type_diagnostic_item(self.cx
, ty
, sym
::String
) || ty
.kind() == &ty
::Str
{
102 self.case_method
= Some(case_method
);
111 fn get_case_method(segment_ident_str
: &str) -> Option
<CaseMethod
> {
112 match segment_ident_str
{
113 "to_lowercase" => Some(CaseMethod
::LowerCase
),
114 "to_ascii_lowercase" => Some(CaseMethod
::AsciiLowerCase
),
115 "to_uppercase" => Some(CaseMethod
::UpperCase
),
116 "to_ascii_uppercase" => Some(CaseMethod
::AsciiUppercase
),
121 fn verify_case
<'a
>(case_method
: &'a CaseMethod
, arms
: &'a
[Arm
<'_
>]) -> Option
<(Span
, Symbol
)> {
122 let case_check
= match case_method
{
123 CaseMethod
::LowerCase
=> |input
: &str| -> bool { input.chars().all(|c| c.to_lowercase().next() == Some(c)) }
,
124 CaseMethod
::AsciiLowerCase
=> |input
: &str| -> bool { !input.chars().any(|c| c.is_ascii_uppercase()) }
,
125 CaseMethod
::UpperCase
=> |input
: &str| -> bool { input.chars().all(|c| c.to_uppercase().next() == Some(c)) }
,
126 CaseMethod
::AsciiUppercase
=> |input
: &str| -> bool { !input.chars().any(|c| c.is_ascii_lowercase()) }
,
131 if let PatKind
::Lit(Expr
{
132 kind
: ExprKind
::Lit(lit
),
135 if let LitKind
::Str(symbol
, _
) = lit
.node
;
136 let input
= symbol
.as_str();
137 if !case_check(input
);
139 return Some((lit
.span
, symbol
));
147 fn lint(cx
: &LateContext
<'_
>, case_method
: &CaseMethod
, bad_case_span
: Span
, bad_case_str
: &str) {
148 let (method_str
, suggestion
) = match case_method
{
149 CaseMethod
::LowerCase
=> ("to_lowercase", bad_case_str
.to_lowercase()),
150 CaseMethod
::AsciiLowerCase
=> ("to_ascii_lowercase", bad_case_str
.to_ascii_lowercase()),
151 CaseMethod
::UpperCase
=> ("to_uppercase", bad_case_str
.to_uppercase()),
152 CaseMethod
::AsciiUppercase
=> ("to_ascii_uppercase", bad_case_str
.to_ascii_uppercase()),
157 MATCH_STR_CASE_MISMATCH
,
159 "this `match` arm has a differing case than its expression",
160 &*format
!("consider changing the case of this arm to respect `{}`", method_str
),
161 format
!("\"{}\"", suggestion
),
162 Applicability
::MachineApplicable
,