Skip to content

Commit e3b7797

Browse files
committed
Move CaseSensitiveFileExtensionComparisons into Methods lint pass
1 parent ba6a459 commit e3b7797

6 files changed

+77
-91
lines changed

clippy_lints/src/case_sensitive_file_extension_comparisons.rs

Lines changed: 0 additions & 85 deletions
This file was deleted.

clippy_lints/src/lib.register_lints.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,6 @@ store.register_lints(&[
6464
cargo::NEGATIVE_FEATURE_NAMES,
6565
cargo::REDUNDANT_FEATURE_NAMES,
6666
cargo::WILDCARD_DEPENDENCIES,
67-
case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
6867
casts::AS_UNDERSCORE,
6968
casts::BORROW_AS_PTR,
7069
casts::CAST_ABS_TO_UNSIGNED,
@@ -285,6 +284,7 @@ store.register_lints(&[
285284
methods::BIND_INSTEAD_OF_MAP,
286285
methods::BYTES_COUNT_TO_LEN,
287286
methods::BYTES_NTH,
287+
methods::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
288288
methods::CHARS_LAST_CMP,
289289
methods::CHARS_NEXT_CMP,
290290
methods::CLONED_INSTEAD_OF_COPIED,

clippy_lints/src/lib.register_pedantic.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
66
LintId::of(attrs::INLINE_ALWAYS),
7-
LintId::of(case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS),
87
LintId::of(casts::BORROW_AS_PTR),
98
LintId::of(casts::CAST_LOSSLESS),
109
LintId::of(casts::CAST_POSSIBLE_TRUNCATION),
@@ -56,6 +55,7 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
5655
LintId::of(matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS),
5756
LintId::of(matches::MATCH_WILD_ERR_ARM),
5857
LintId::of(matches::SINGLE_MATCH_ELSE),
58+
LintId::of(methods::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS),
5959
LintId::of(methods::CLONED_INSTEAD_OF_COPIED),
6060
LintId::of(methods::FILTER_MAP_NEXT),
6161
LintId::of(methods::FLAT_MAP_OPTION),

clippy_lints/src/lib.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,6 @@ mod bool_assert_comparison;
181181
mod booleans;
182182
mod borrow_deref_ref;
183183
mod cargo;
184-
mod case_sensitive_file_extension_comparisons;
185184
mod casts;
186185
mod checked_conversions;
187186
mod cognitive_complexity;
@@ -852,9 +851,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
852851
store.register_late_pass(|| Box::new(strings::StringToString));
853852
store.register_late_pass(|| Box::new(zero_sized_map_values::ZeroSizedMapValues));
854853
store.register_late_pass(|| Box::new(vec_init_then_push::VecInitThenPush::default()));
855-
store.register_late_pass(|| {
856-
Box::new(case_sensitive_file_extension_comparisons::CaseSensitiveFileExtensionComparisons)
857-
});
858854
store.register_late_pass(|| Box::new(redundant_slicing::RedundantSlicing));
859855
store.register_late_pass(|| Box::new(from_str_radix_10::FromStrRadix10));
860856
store.register_late_pass(move || Box::new(if_then_some_else_none::IfThenSomeElseNone::new(msrv)));
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
use clippy_utils::diagnostics::span_lint_and_help;
2+
use clippy_utils::ty::is_type_diagnostic_item;
3+
use if_chain::if_chain;
4+
use rustc_ast::ast::LitKind;
5+
use rustc_hir::{Expr, ExprKind};
6+
use rustc_lint::LateContext;
7+
use rustc_span::{source_map::Spanned, symbol::sym, Span};
8+
9+
use super::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS;
10+
11+
pub(super) fn check<'tcx>(
12+
cx: &LateContext<'tcx>,
13+
expr: &'tcx Expr<'_>,
14+
call_span: Span,
15+
recv: &'tcx Expr<'_>,
16+
arg: &'tcx Expr<'_>,
17+
) {
18+
if_chain! {
19+
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
20+
if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
21+
if cx.tcx.type_of(impl_id).is_str();
22+
if let ExprKind::Lit(Spanned { node: LitKind::Str(ext_literal, ..), ..}) = arg.kind;
23+
if (2..=6).contains(&ext_literal.as_str().len());
24+
let ext_str = ext_literal.as_str();
25+
if ext_str.starts_with('.');
26+
if ext_str.chars().skip(1).all(|c| c.is_uppercase() || c.is_ascii_digit())
27+
|| ext_str.chars().skip(1).all(|c| c.is_lowercase() || c.is_ascii_digit());
28+
let recv_ty = cx.typeck_results().expr_ty(recv).peel_refs();
29+
if recv_ty.is_str() || is_type_diagnostic_item(cx, recv_ty, sym::String);
30+
then {
31+
span_lint_and_help(
32+
cx,
33+
CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
34+
call_span,
35+
"case-sensitive file extension comparison",
36+
None,
37+
"consider using a case-insensitive comparison instead",
38+
);
39+
}
40+
}
41+
}

clippy_lints/src/methods/mod.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ mod bind_instead_of_map;
22
mod bytecount;
33
mod bytes_count_to_len;
44
mod bytes_nth;
5+
mod case_sensitive_file_extension_comparisons;
56
mod chars_cmp;
67
mod chars_cmp_with_unwrap;
78
mod chars_last_cmp;
@@ -2428,6 +2429,34 @@ declare_clippy_lint! {
24282429
"Using `bytes().count()` when `len()` performs the same functionality"
24292430
}
24302431

2432+
declare_clippy_lint! {
2433+
/// ### What it does
2434+
/// Checks for calls to `ends_with` with possible file extensions
2435+
/// and suggests to use a case-insensitive approach instead.
2436+
///
2437+
/// ### Why is this bad?
2438+
/// `ends_with` is case-sensitive and may not detect files with a valid extension.
2439+
///
2440+
/// ### Example
2441+
/// ```rust
2442+
/// fn is_rust_file(filename: &str) -> bool {
2443+
/// filename.ends_with(".rs")
2444+
/// }
2445+
/// ```
2446+
/// Use instead:
2447+
/// ```rust
2448+
/// fn is_rust_file(filename: &str) -> bool {
2449+
/// let filename = std::path::Path::new(filename);
2450+
/// filename.extension()
2451+
/// .map_or(false, |ext| ext.eq_ignore_ascii_case("rs"))
2452+
/// }
2453+
/// ```
2454+
#[clippy::version = "1.51.0"]
2455+
pub CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
2456+
pedantic,
2457+
"Checks for calls to ends_with with case-sensitive file extensions"
2458+
}
2459+
24312460
pub struct Methods {
24322461
avoid_breaking_exported_api: bool,
24332462
msrv: Option<RustcVersion>,
@@ -2534,6 +2563,7 @@ impl_lint_pass!(Methods => [
25342563
ITER_ON_EMPTY_COLLECTIONS,
25352564
NAIVE_BYTECOUNT,
25362565
BYTES_COUNT_TO_LEN,
2566+
CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
25372567
]);
25382568

25392569
/// Extracts a method call name, args, and `Span` of the method name.
@@ -2801,6 +2831,10 @@ impl Methods {
28012831
("drain", [arg]) => {
28022832
iter_with_drain::check(cx, expr, recv, span, arg);
28032833
},
2834+
("ends_with", [arg]) => {
2835+
if let ExprKind::MethodCall(_, _, span) = expr.kind {
2836+
case_sensitive_file_extension_comparisons::check(cx, expr, span, recv, arg);
2837+
},
28042838
("expect", [_]) => match method_call(recv) {
28052839
Some(("ok", [recv], _)) => ok_expect::check(cx, expr, recv),
28062840
Some(("err", [recv], err_span)) => err_expect::check(cx, expr, recv, self.msrv, span, err_span),

0 commit comments

Comments
 (0)