Skip to content

Commit c24a422

Browse files
committed
Auto merge of #4543 - xiongmao86:issue4503, r=flip1995
Fix issue4503 Fixes #4503. changelog: Add a lint checking user are using FileType.is_file() method and suggest using !FileType.is_dir(). - [x] Followed [lint naming conventions][lint_naming] - [x] Added passing UI tests (including committed `.stderr` file) - [x] `cargo test` passes locally - [x] Executed `./util/dev update_lints` - [x] Added lint documentation - [x] Run `./util/dev fmt`
2 parents 05cb0df + bba4688 commit c24a422

File tree

9 files changed

+132
-3
lines changed

9 files changed

+132
-3
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -1093,6 +1093,7 @@ Released 2018-09-13
10931093
[`extend_from_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#extend_from_slice
10941094
[`extra_unused_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#extra_unused_lifetimes
10951095
[`fallible_impl_from`]: https://rust-lang.github.io/rust-clippy/master/index.html#fallible_impl_from
1096+
[`filetype_is_file`]: https://rust-lang.github.io/rust-clippy/master/index.html#filetype_is_file
10961097
[`filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_map
10971098
[`filter_map_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_map_next
10981099
[`filter_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_next

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code.
88

9-
[There are 346 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
9+
[There are 347 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
1010

1111
We have a bunch of lint categories to allow you to choose how much Clippy is supposed to ~~annoy~~ help you:
1212

clippy_lints/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -614,6 +614,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
614614
&methods::CLONE_ON_COPY,
615615
&methods::CLONE_ON_REF_PTR,
616616
&methods::EXPECT_FUN_CALL,
617+
&methods::FILETYPE_IS_FILE,
617618
&methods::FILTER_MAP,
618619
&methods::FILTER_MAP_NEXT,
619620
&methods::FILTER_NEXT,
@@ -1007,6 +1008,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
10071008
LintId::of(&matches::WILDCARD_ENUM_MATCH_ARM),
10081009
LintId::of(&mem_forget::MEM_FORGET),
10091010
LintId::of(&methods::CLONE_ON_REF_PTR),
1011+
LintId::of(&methods::FILETYPE_IS_FILE),
10101012
LintId::of(&methods::GET_UNWRAP),
10111013
LintId::of(&methods::OPTION_EXPECT_USED),
10121014
LintId::of(&methods::OPTION_UNWRAP_USED),

clippy_lints/src/methods/mod.rs

+68
Original file line numberDiff line numberDiff line change
@@ -1131,6 +1131,40 @@ declare_clippy_lint! {
11311131
"Check for offset calculations on raw pointers to zero-sized types"
11321132
}
11331133

1134+
declare_clippy_lint! {
1135+
/// **What it does:** Checks for `FileType::is_file()`.
1136+
///
1137+
/// **Why is this bad?** When people testing a file type with `FileType::is_file`
1138+
/// they are testing whether a path is something they can get bytes from. But
1139+
/// `is_file` doesn't cover special file types in unix-like systems, and doesn't cover
1140+
/// symlink in windows. Using `!FileType::is_dir()` is a better way to that intention.
1141+
///
1142+
/// **Example:**
1143+
///
1144+
/// ```rust,ignore
1145+
/// let metadata = std::fs::metadata("foo.txt")?;
1146+
/// let filetype = metadata.file_type();
1147+
///
1148+
/// if filetype.is_file() {
1149+
/// // read file
1150+
/// }
1151+
/// ```
1152+
///
1153+
/// should be written as:
1154+
///
1155+
/// ```rust,ignore
1156+
/// let metadata = std::fs::metadata("foo.txt")?;
1157+
/// let filetype = metadata.file_type();
1158+
///
1159+
/// if !filetype.is_dir() {
1160+
/// // read file
1161+
/// }
1162+
/// ```
1163+
pub FILETYPE_IS_FILE,
1164+
restriction,
1165+
"`FileType::is_file` is not recommended to test for readable file type"
1166+
}
1167+
11341168
declare_lint_pass!(Methods => [
11351169
OPTION_UNWRAP_USED,
11361170
RESULT_UNWRAP_USED,
@@ -1178,6 +1212,7 @@ declare_lint_pass!(Methods => [
11781212
UNINIT_ASSUMED_INIT,
11791213
MANUAL_SATURATING_ARITHMETIC,
11801214
ZST_OFFSET,
1215+
FILETYPE_IS_FILE,
11811216
]);
11821217

11831218
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Methods {
@@ -1241,6 +1276,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Methods {
12411276
["add"] | ["offset"] | ["sub"] | ["wrapping_offset"] | ["wrapping_add"] | ["wrapping_sub"] => {
12421277
check_pointer_offset(cx, expr, arg_lists[0])
12431278
},
1279+
["is_file", ..] => lint_filetype_is_file(cx, expr, arg_lists[0]),
12441280
_ => {},
12451281
}
12461282

@@ -3225,3 +3261,35 @@ fn check_pointer_offset(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, args: &[
32253261
}
32263262
}
32273263
}
3264+
3265+
fn lint_filetype_is_file(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
3266+
let ty = cx.tables.expr_ty(&args[0]);
3267+
3268+
if !match_type(cx, ty, &paths::FILE_TYPE) {
3269+
return;
3270+
}
3271+
3272+
let span: Span;
3273+
let verb: &str;
3274+
let lint_unary: &str;
3275+
let help_unary: &str;
3276+
if_chain! {
3277+
if let Some(parent) = get_parent_expr(cx, expr);
3278+
if let hir::ExprKind::Unary(op, _) = parent.kind;
3279+
if op == hir::UnOp::UnNot;
3280+
then {
3281+
lint_unary = "!";
3282+
verb = "denies";
3283+
help_unary = "";
3284+
span = parent.span;
3285+
} else {
3286+
lint_unary = "";
3287+
verb = "covers";
3288+
help_unary = "!";
3289+
span = expr.span;
3290+
}
3291+
}
3292+
let lint_msg = format!("`{}FileType::is_file()` only {} regular files", lint_unary, verb);
3293+
let help_msg = format!("use `{}FileType::is_dir()` instead", help_unary);
3294+
span_help_and_lint(cx, FILETYPE_IS_FILE, span, &lint_msg, &help_msg);
3295+
}

clippy_lints/src/utils/paths.rs

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ pub const DROP_TRAIT: [&str; 4] = ["core", "ops", "drop", "Drop"];
2828
pub const DURATION: [&str; 3] = ["core", "time", "Duration"];
2929
pub const EARLY_CONTEXT: [&str; 4] = ["rustc", "lint", "context", "EarlyContext"];
3030
pub const EXIT: [&str; 3] = ["std", "process", "exit"];
31+
pub const FILE_TYPE: [&str; 3] = ["std", "fs", "FileType"];
3132
pub const FMT_ARGUMENTS_NEW_V1: [&str; 4] = ["core", "fmt", "Arguments", "new_v1"];
3233
pub const FMT_ARGUMENTS_NEW_V1_FORMATTED: [&str; 4] = ["core", "fmt", "Arguments", "new_v1_formatted"];
3334
pub const FMT_ARGUMENTV1_NEW: [&str; 4] = ["core", "fmt", "ArgumentV1", "new"];

src/lintlist/mod.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ pub use lint::Lint;
66
pub use lint::LINT_LEVELS;
77

88
// begin lint list, do not remove this comment, it’s used in `update_lints`
9-
pub const ALL_LINTS: [Lint; 346] = [
9+
pub const ALL_LINTS: [Lint; 347] = [
1010
Lint {
1111
name: "absurd_extreme_comparisons",
1212
group: "correctness",
@@ -560,6 +560,13 @@ pub const ALL_LINTS: [Lint; 346] = [
560560
deprecation: None,
561561
module: "fallible_impl_from",
562562
},
563+
Lint {
564+
name: "filetype_is_file",
565+
group: "restriction",
566+
desc: "`FileType::is_file` is not recommended to test for readable file type",
567+
deprecation: None,
568+
module: "methods",
569+
},
563570
Lint {
564571
name: "filter_map",
565572
group: "pedantic",

tests/compile-test.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ fn run_ui_toml_tests(config: &compiletest::Config, mut tests: Vec<test::TestDesc
121121
for file in fs::read_dir(&dir_path)? {
122122
let file = file?;
123123
let file_path = file.path();
124-
if !file.file_type()?.is_file() {
124+
if file.file_type()?.is_dir() {
125125
continue;
126126
}
127127
if file_path.extension() != Some(OsStr::new("rs")) {

tests/ui/filetype_is_file.rs

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#![warn(clippy::filetype_is_file)]
2+
3+
fn main() -> std::io::Result<()> {
4+
use std::fs;
5+
use std::ops::BitOr;
6+
7+
// !filetype.is_dir()
8+
if fs::metadata("foo.txt")?.file_type().is_file() {
9+
// read file
10+
}
11+
12+
// positive of filetype.is_dir()
13+
if !fs::metadata("foo.txt")?.file_type().is_file() {
14+
// handle dir
15+
}
16+
17+
// false positive of filetype.is_dir()
18+
if !fs::metadata("foo.txt")?.file_type().is_file().bitor(true) {
19+
// ...
20+
}
21+
22+
Ok(())
23+
}

tests/ui/filetype_is_file.stderr

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
error: `FileType::is_file()` only covers regular files
2+
--> $DIR/filetype_is_file.rs:8:8
3+
|
4+
LL | if fs::metadata("foo.txt")?.file_type().is_file() {
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: `-D clippy::filetype-is-file` implied by `-D warnings`
8+
= help: use `!FileType::is_dir()` instead
9+
10+
error: `!FileType::is_file()` only denies regular files
11+
--> $DIR/filetype_is_file.rs:13:8
12+
|
13+
LL | if !fs::metadata("foo.txt")?.file_type().is_file() {
14+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
15+
|
16+
= help: use `FileType::is_dir()` instead
17+
18+
error: `FileType::is_file()` only covers regular files
19+
--> $DIR/filetype_is_file.rs:18:9
20+
|
21+
LL | if !fs::metadata("foo.txt")?.file_type().is_file().bitor(true) {
22+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
23+
|
24+
= help: use `!FileType::is_dir()` instead
25+
26+
error: aborting due to 3 previous errors
27+

0 commit comments

Comments
 (0)