@@ -1131,6 +1131,40 @@ declare_clippy_lint! {
1131
1131
"Check for offset calculations on raw pointers to zero-sized types"
1132
1132
}
1133
1133
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
+
1134
1168
declare_lint_pass ! ( Methods => [
1135
1169
OPTION_UNWRAP_USED ,
1136
1170
RESULT_UNWRAP_USED ,
@@ -1178,6 +1212,7 @@ declare_lint_pass!(Methods => [
1178
1212
UNINIT_ASSUMED_INIT ,
1179
1213
MANUAL_SATURATING_ARITHMETIC ,
1180
1214
ZST_OFFSET ,
1215
+ FILETYPE_IS_FILE ,
1181
1216
] ) ;
1182
1217
1183
1218
impl < ' a , ' tcx > LateLintPass < ' a , ' tcx > for Methods {
@@ -1241,6 +1276,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Methods {
1241
1276
[ "add" ] | [ "offset" ] | [ "sub" ] | [ "wrapping_offset" ] | [ "wrapping_add" ] | [ "wrapping_sub" ] => {
1242
1277
check_pointer_offset ( cx, expr, arg_lists[ 0 ] )
1243
1278
} ,
1279
+ [ "is_file" , ..] => lint_filetype_is_file ( cx, expr, arg_lists[ 0 ] ) ,
1244
1280
_ => { } ,
1245
1281
}
1246
1282
@@ -3225,3 +3261,35 @@ fn check_pointer_offset(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, args: &[
3225
3261
}
3226
3262
}
3227
3263
}
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
+ }
0 commit comments