Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix issue4503 #4543

Merged
merged 13 commits into from
Jan 13, 2020
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -1093,6 +1093,7 @@ Released 2018-09-13
[`extend_from_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#extend_from_slice
[`extra_unused_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#extra_unused_lifetimes
[`fallible_impl_from`]: https://rust-lang.github.io/rust-clippy/master/index.html#fallible_impl_from
[`filetype_is_file`]: https://rust-lang.github.io/rust-clippy/master/index.html#filetype_is_file
[`filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_map
[`filter_map_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_map_next
[`filter_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_next
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

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

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

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

Expand Down
2 changes: 2 additions & 0 deletions clippy_lints/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -612,6 +612,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf
&methods::CLONE_ON_COPY,
&methods::CLONE_ON_REF_PTR,
&methods::EXPECT_FUN_CALL,
&methods::FILETYPE_IS_FILE,
&methods::FILTER_MAP,
&methods::FILTER_MAP_NEXT,
&methods::FILTER_NEXT,
Expand Down Expand Up @@ -1005,6 +1006,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf
LintId::of(&matches::WILDCARD_ENUM_MATCH_ARM),
LintId::of(&mem_forget::MEM_FORGET),
LintId::of(&methods::CLONE_ON_REF_PTR),
LintId::of(&methods::FILETYPE_IS_FILE),
LintId::of(&methods::GET_UNWRAP),
LintId::of(&methods::OPTION_EXPECT_USED),
LintId::of(&methods::OPTION_UNWRAP_USED),
Expand Down
68 changes: 68 additions & 0 deletions clippy_lints/src/methods/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1130,6 +1130,40 @@ declare_clippy_lint! {
"Check for offset calculations on raw pointers to zero-sized types"
}

declare_clippy_lint! {
/// **What it does:** Checks for `FileType::is_file()`.
///
/// **Why is this bad?** When people testing a file type with `FileType::is_file`
/// they are testing whether a path is something they can get bytes from. But
/// `is_file` doesn't cover special file types in unix-like systems, and doesn't cover
/// symlink in windows. Using `!FileType::is_dir()` is a better way to that intention.
xiongmao86 marked this conversation as resolved.
Show resolved Hide resolved
///
/// **Example:**
///
/// ```rust,ignore
/// let metadata = std::fs::metadata("foo.txt")?;
/// let filetype = metadata.file_type();
///
/// if filetype.is_file() {
/// // read file
/// }
/// ```
///
/// should be written as:
///
/// ```rust,ignore
/// let metadata = std::fs::metadata("foo.txt")?;
/// let filetype = metadata.file_type();
///
/// if !filetype.is_dir() {
/// // read file
/// }
/// ```
pub FILETYPE_IS_FILE,
restriction,
"`FileType::is_file` is not recommended to test for readable file type"
}

declare_lint_pass!(Methods => [
OPTION_UNWRAP_USED,
RESULT_UNWRAP_USED,
Expand Down Expand Up @@ -1177,6 +1211,7 @@ declare_lint_pass!(Methods => [
UNINIT_ASSUMED_INIT,
MANUAL_SATURATING_ARITHMETIC,
ZST_OFFSET,
FILETYPE_IS_FILE,
]);

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Methods {
Expand Down Expand Up @@ -1240,6 +1275,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Methods {
["add"] | ["offset"] | ["sub"] | ["wrapping_offset"] | ["wrapping_add"] | ["wrapping_sub"] => {
check_pointer_offset(cx, expr, arg_lists[0])
},
["is_file", ..] => lint_filetype_is_file(cx, expr, arg_lists[0]),
_ => {},
}

Expand Down Expand Up @@ -3224,3 +3260,35 @@ fn check_pointer_offset(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, args: &[
}
}
}

fn lint_filetype_is_file(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
let ty = cx.tables.expr_ty(&args[0]);

if !match_type(cx, ty, &paths::FILE_TYPE) {
return;
}

let span: Span;
let verb: &str;
let lint_unary: &str;
let help_unary: &str;
if_chain! {
if let Some(parent) = get_parent_expr(cx, expr);
if let hir::ExprKind::Unary(op, _) = parent.kind;
if op == hir::UnOp::UnNot;
then {
lint_unary = "!";
verb = "denies";
help_unary = "";
span = parent.span;
} else {
lint_unary = "";
verb = "covers";
help_unary = "!";
span = expr.span;
}
}
let lint_msg = format!("`{}FileType::is_file()` only {} regular files", lint_unary, verb);
let help_msg = format!("use `{}FileType::is_dir()` instead", help_unary);
span_help_and_lint(cx, FILETYPE_IS_FILE, span, &lint_msg, &help_msg);
}
1 change: 1 addition & 0 deletions clippy_lints/src/utils/paths.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ pub const DROP_TRAIT: [&str; 4] = ["core", "ops", "drop", "Drop"];
pub const DURATION: [&str; 3] = ["core", "time", "Duration"];
pub const EARLY_CONTEXT: [&str; 4] = ["rustc", "lint", "context", "EarlyContext"];
pub const EXIT: [&str; 3] = ["std", "process", "exit"];
pub const FILE_TYPE: [&str; 3] = ["std", "fs", "FileType"];
pub const FMT_ARGUMENTS_NEW_V1: [&str; 4] = ["core", "fmt", "Arguments", "new_v1"];
pub const FMT_ARGUMENTS_NEW_V1_FORMATTED: [&str; 4] = ["core", "fmt", "Arguments", "new_v1_formatted"];
pub const FMT_ARGUMENTV1_NEW: [&str; 4] = ["core", "fmt", "ArgumentV1", "new"];
Expand Down
9 changes: 8 additions & 1 deletion src/lintlist/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ pub use lint::Lint;
pub use lint::LINT_LEVELS;

// begin lint list, do not remove this comment, it’s used in `update_lints`
pub const ALL_LINTS: [Lint; 346] = [
pub const ALL_LINTS: [Lint; 347] = [
Lint {
name: "absurd_extreme_comparisons",
group: "correctness",
Expand Down Expand Up @@ -560,6 +560,13 @@ pub const ALL_LINTS: [Lint; 346] = [
deprecation: None,
module: "fallible_impl_from",
},
Lint {
name: "filetype_is_file",
group: "restriction",
desc: "`FileType::is_file` is not recommended to test for readable file type",
deprecation: None,
module: "methods",
},
Lint {
name: "filter_map",
group: "pedantic",
Expand Down
2 changes: 1 addition & 1 deletion tests/compile-test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ fn run_ui_toml_tests(config: &compiletest::Config, mut tests: Vec<test::TestDesc
for file in fs::read_dir(&dir_path)? {
let file = file?;
let file_path = file.path();
if !file.file_type()?.is_file() {
if file.file_type()?.is_dir() {
continue;
}
if file_path.extension() != Some(OsStr::new("rs")) {
Expand Down
23 changes: 23 additions & 0 deletions tests/ui/filetype_is_file.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#![warn(clippy::filetype_is_file)]

fn main() -> std::io::Result<()> {
use std::fs;
use std::ops::BitOr;

// !filetype.is_dir()
if fs::metadata("foo.txt")?.file_type().is_file() {
flip1995 marked this conversation as resolved.
Show resolved Hide resolved
// read file
}

// positive of filetype.is_dir()
if !fs::metadata("foo.txt")?.file_type().is_file() {
// handle dir
}

// false positive of filetype.is_dir()
if !fs::metadata("foo.txt")?.file_type().is_file().bitor(true) {
// ...
}

Ok(())
}
27 changes: 27 additions & 0 deletions tests/ui/filetype_is_file.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
error: `FileType::is_file()` only covers regular files
--> $DIR/filetype_is_file.rs:8:8
|
LL | if fs::metadata("foo.txt")?.file_type().is_file() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `-D clippy::filetype-is-file` implied by `-D warnings`
= help: use `!FileType::is_dir()` instead

error: `!FileType::is_file()` only denies regular files
--> $DIR/filetype_is_file.rs:13:8
|
LL | if !fs::metadata("foo.txt")?.file_type().is_file() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: use `FileType::is_dir()` instead

error: `FileType::is_file()` only covers regular files
--> $DIR/filetype_is_file.rs:18:9
|
LL | if !fs::metadata("foo.txt")?.file_type().is_file().bitor(true) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: use `!FileType::is_dir()` instead

error: aborting due to 3 previous errors