Skip to content

Commit 4ef8f9d

Browse files
committed
limit extensions to 2-3 chars and add configuration
1 parent a0e0367 commit 4ef8f9d

File tree

10 files changed

+69
-3
lines changed

10 files changed

+69
-3
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5575,5 +5575,6 @@ Released 2018-09-13
55755575
[`allow-one-hash-in-raw-strings`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-one-hash-in-raw-strings
55765576
[`absolute-paths-max-segments`]: https://doc.rust-lang.org/clippy/lint_configuration.html#absolute-paths-max-segments
55775577
[`absolute-paths-allowed-crates`]: https://doc.rust-lang.org/clippy/lint_configuration.html#absolute-paths-allowed-crates
5578+
[`allowed-file-extensions`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-file-extensions
55785579
[`enforce-iter-loop-reborrow`]: https://doc.rust-lang.org/clippy/lint_configuration.html#enforce-iter-loop-reborrow
55795580
<!-- end autogenerated links to configuration documentation -->

book/src/lint_configuration.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -751,6 +751,16 @@ Which crates to allow absolute paths from
751751
* [`absolute_paths`](https://rust-lang.github.io/rust-clippy/master/index.html#absolute_paths)
752752

753753

754+
## `allowed-file-extensions`
755+
Additional file extensions to allow
756+
757+
**Default Value:** `{}` (`rustc_data_structures::fx::FxHashSet<String>`)
758+
759+
---
760+
**Affected lints:**
761+
* [`path_ends_with_ext`](https://rust-lang.github.io/rust-clippy/master/index.html#path_ends_with_ext)
762+
763+
754764
## `enforce-iter-loop-reborrow`
755765
#### Example
756766
```

clippy_lints/src/lib.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -661,12 +661,24 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
661661
let allow_unwrap_in_tests = conf.allow_unwrap_in_tests;
662662
let suppress_restriction_lint_in_const = conf.suppress_restriction_lint_in_const;
663663
store.register_late_pass(move |_| Box::new(approx_const::ApproxConstant::new(msrv())));
664+
let allowed_file_extensions = conf
665+
.allowed_file_extensions
666+
.iter()
667+
.cloned()
668+
.chain(
669+
methods::DEFAULT_ALLOWED_FILE_EXTENSIONS
670+
.iter()
671+
.copied()
672+
.map(ToOwned::to_owned),
673+
)
674+
.collect::<FxHashSet<_>>();
664675
store.register_late_pass(move |_| {
665676
Box::new(methods::Methods::new(
666677
avoid_breaking_exported_api,
667678
msrv(),
668679
allow_expect_in_tests,
669680
allow_unwrap_in_tests,
681+
allowed_file_extensions.clone(),
670682
))
671683
});
672684
store.register_late_pass(move |_| Box::new(matches::Matches::new(msrv())));

clippy_lints/src/methods/mod.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,8 @@ use clippy_utils::msrvs::{self, Msrv};
121121
use clippy_utils::ty::{contains_ty_adt_constructor_opaque, implements_trait, is_copy, is_type_diagnostic_item};
122122
use clippy_utils::{contains_return, is_bool, is_trait_method, iter_input_pats, peel_blocks, return_ty};
123123
use if_chain::if_chain;
124+
pub use path_ends_with_ext::DEFAULT_ALLOWED_FILE_EXTENSIONS;
125+
use rustc_data_structures::fx::FxHashSet;
124126
use rustc_hir as hir;
125127
use rustc_hir::{Expr, ExprKind, Node, Stmt, StmtKind, TraitItem, TraitItemKind};
126128
use rustc_hir_analysis::hir_ty_to_ty;
@@ -3568,6 +3570,13 @@ declare_clippy_lint! {
35683570
/// ### What it does
35693571
/// Looks for calls to `Path::ends_with` calls where the argument looks like a file extension.
35703572
///
3573+
/// By default, Clippy has a short list of known filenames that start with a dot
3574+
/// but aren't necessarily file extensions (e.g. the `.git` folder), which are allowed by default.
3575+
/// File extensions are often two to three characters long, so this only lints in those cases
3576+
/// in an attempt to avoid false positives.
3577+
/// The `allowed-file-extensions` configuration can be used to allow additional
3578+
/// file extensions that Clippy should not lint.
3579+
///
35713580
/// ### Why is this bad?
35723581
/// This doesn't actually compare file extensions. Rather, `ends_with` compares the given argument
35733582
/// to the last **component** of the path and checks if it matches exactly.
@@ -3597,6 +3606,7 @@ pub struct Methods {
35973606
msrv: Msrv,
35983607
allow_expect_in_tests: bool,
35993608
allow_unwrap_in_tests: bool,
3609+
allowed_file_extensions: FxHashSet<String>,
36003610
}
36013611

36023612
impl Methods {
@@ -3606,12 +3616,14 @@ impl Methods {
36063616
msrv: Msrv,
36073617
allow_expect_in_tests: bool,
36083618
allow_unwrap_in_tests: bool,
3619+
allowed_file_extensions: FxHashSet<String>,
36093620
) -> Self {
36103621
Self {
36113622
avoid_breaking_exported_api,
36123623
msrv,
36133624
allow_expect_in_tests,
36143625
allow_unwrap_in_tests,
3626+
allowed_file_extensions,
36153627
}
36163628
}
36173629
}
@@ -4008,7 +4020,7 @@ impl Methods {
40084020
if let ExprKind::MethodCall(.., span) = expr.kind {
40094021
case_sensitive_file_extension_comparisons::check(cx, expr, span, recv, arg);
40104022
}
4011-
path_ends_with_ext::check(cx, recv, arg, expr, &self.msrv);
4023+
path_ends_with_ext::check(cx, recv, arg, expr, &self.msrv, &self.allowed_file_extensions);
40124024
},
40134025
("expect", [_]) => {
40144026
match method_call(recv) {

clippy_lints/src/methods/path_ends_with_ext.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,30 @@ use clippy_utils::msrvs::Msrv;
55
use clippy_utils::source::snippet;
66
use clippy_utils::ty::is_type_diagnostic_item;
77
use rustc_ast::{LitKind, StrStyle};
8+
use rustc_data_structures::fx::FxHashSet;
89
use rustc_errors::Applicability;
910
use rustc_hir::{Expr, ExprKind};
1011
use rustc_lint::LateContext;
1112
use rustc_span::sym;
1213
use std::fmt::Write;
1314

14-
pub(super) fn check(cx: &LateContext<'_>, recv: &Expr<'_>, path: &Expr<'_>, expr: &Expr<'_>, msrv: &Msrv) {
15+
pub const DEFAULT_ALLOWED_FILE_EXTENSIONS: &[&str] = &["git", "svn", "gem", "npm", "vim", "env", "rnd", "ssh", "vnc"];
16+
17+
pub(super) fn check(
18+
cx: &LateContext<'_>,
19+
recv: &Expr<'_>,
20+
path: &Expr<'_>,
21+
expr: &Expr<'_>,
22+
msrv: &Msrv,
23+
allowed_paths: &FxHashSet<String>,
24+
) {
1525
if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv).peel_refs(), sym::Path)
1626
&& !path.span.from_expansion()
1727
&& let ExprKind::Lit(lit) = path.kind
1828
&& let LitKind::Str(path, StrStyle::Cooked) = lit.node
1929
&& let Some(path) = path.as_str().strip_prefix('.')
30+
&& (path.len() == 2 || path.len() == 3)
31+
&& !allowed_paths.contains(path)
2032
{
2133
let mut sugg = snippet(cx, recv.span, "..").into_owned();
2234
if msrv.meets(msrvs::OPTION_IS_SOME_AND) {

clippy_lints/src/utils/conf.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,11 @@ define_Conf! {
561561
/// Which crates to allow absolute paths from
562562
(absolute_paths_allowed_crates: rustc_data_structures::fx::FxHashSet<String> =
563563
rustc_data_structures::fx::FxHashSet::default()),
564+
/// Lint: PATH_ENDS_WITH_EXT.
565+
///
566+
/// Additional file extensions to allow
567+
(allowed_file_extensions: rustc_data_structures::fx::FxHashSet<String> =
568+
rustc_data_structures::fx::FxHashSet::default()),
564569
/// Lint: EXPLICIT_ITER_LOOP
565570
///
566571
/// Whether to recommend using implicit into iter for reborrowed values.

tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect
1010
allow-print-in-tests
1111
allow-private-module-inception
1212
allow-unwrap-in-tests
13+
allowed-file-extensions
1314
allowed-idents-below-min-chars
1415
allowed-scripts
1516
arithmetic-side-effects-allowed
@@ -82,6 +83,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect
8283
allow-print-in-tests
8384
allow-private-module-inception
8485
allow-unwrap-in-tests
86+
allowed-file-extensions
8587
allowed-idents-below-min-chars
8688
allowed-scripts
8789
arithmetic-side-effects-allowed

tests/ui/path_ends_with_ext.fixed

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ fn test(path: &Path) {
1111
path.extension().is_some_and(|ext| ext == "md");
1212
//~^ ERROR: this looks like a failed attempt at checking for the file extension
1313

14+
// some "extensions" are allowed by default
15+
path.ends_with(".git");
16+
17+
// most legitimate "dotfiles" are longer than 3 chars, so we allow them as well
18+
path.ends_with(".bashrc");
19+
1420
path.ends_with(arg!());
1521
}
1622

tests/ui/path_ends_with_ext.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ fn test(path: &Path) {
1111
path.ends_with(".md");
1212
//~^ ERROR: this looks like a failed attempt at checking for the file extension
1313

14+
// some "extensions" are allowed by default
15+
path.ends_with(".git");
16+
17+
// most legitimate "dotfiles" are longer than 3 chars, so we allow them as well
18+
path.ends_with(".bashrc");
19+
1420
path.ends_with(arg!());
1521
}
1622

tests/ui/path_ends_with_ext.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ LL | path.ends_with(".md");
88
= help: to override `-D warnings` add `#[allow(clippy::path_ends_with_ext)]`
99

1010
error: this looks like a failed attempt at checking for the file extension
11-
--> $DIR/path_ends_with_ext.rs:19:5
11+
--> $DIR/path_ends_with_ext.rs:25:5
1212
|
1313
LL | path.ends_with(".md")
1414
| ^^^^^^^^^^^^^^^^^^^^^ help: try: `path.extension().map_or(false, |ext| ext == "md")`

0 commit comments

Comments
 (0)