@@ -5,26 +5,30 @@ use clippy_utils::{
5
5
use rustc_ast:: LitKind ;
6
6
use rustc_errors:: Applicability ;
7
7
use rustc_hir:: def_id:: DefId ;
8
- use rustc_hir:: * ;
8
+ use rustc_hir:: { Expr , ExprKind , QPath } ;
9
9
use rustc_lint:: { LateContext , LateLintPass , LintContext } ;
10
10
use rustc_middle:: { lint:: in_external_macro, ty} ;
11
11
use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
12
12
use rustc_span:: { sym, Symbol } ;
13
13
14
14
declare_clippy_lint ! {
15
15
/// ### What it does
16
- /// TODO please do soon
16
+ /// Checks for paths implicitly referring to DOS devices.
17
17
///
18
18
/// ### Why is this bad?
19
- /// TODO please
19
+ /// This will lead to unexpected path transformations on Windows. Usually, the programmer will
20
+ /// have intended to refer to a file/folder instead.
20
21
///
21
22
/// ### Example
22
- /// ```rust
23
- /// // TODO
23
+ /// ```rust,ignore
24
+ /// let _ = PathBuf::from("CON");
24
25
/// ```
25
26
/// Use instead:
26
- /// ```rust
27
- /// // TODO
27
+ /// ```rust,ignore
28
+ /// // If this was unintended:
29
+ /// let _ = PathBuf::from("./CON");
30
+ /// // To silence the lint:
31
+ /// let _ = PathBuf::from(r"\\.\CON");
28
32
/// ```
29
33
#[ clippy:: version = "1.72.0" ]
30
34
pub BARE_DOS_DEVICE_NAMES ,
@@ -73,7 +77,7 @@ impl<'tcx> LateLintPass<'tcx> for BareDosDeviceNames {
73
77
| "prn"
74
78
)
75
79
&& let Some ( parent) = get_parent_expr ( cx, expr)
76
- && ( is_path_buf_from_or_path_new ( cx, parent) || is_path_ty ( cx, expr, parent) )
80
+ && ( is_path_constructor ( cx, parent) || is_path_ty ( cx, expr, parent) )
77
81
&& !is_from_proc_macro ( cx, expr)
78
82
{
79
83
span_lint_and_then (
@@ -86,7 +90,8 @@ impl<'tcx> LateLintPass<'tcx> for BareDosDeviceNames {
86
90
diag. span_suggestion_verbose (
87
91
expr. span ,
88
92
"if this is intended, try" ,
89
- format ! ( r#""\\.\{str_sym}""# ) ,
93
+ // FIXME: I have zero clue why it normalizes this. `\` -> `/`
94
+ format ! ( r#"r"\\.\{str_sym}"\"# ) ,
90
95
Applicability :: MaybeIncorrect ,
91
96
) ;
92
97
@@ -103,22 +108,43 @@ impl<'tcx> LateLintPass<'tcx> for BareDosDeviceNames {
103
108
}
104
109
}
105
110
106
- /// Gets whether the `Expr` is an argument to `Path::new` or `PathBuf::from` . The caller must
107
- /// provide the parent `Expr`, for performance's sake.
111
+ /// Gets whether the `Expr` is an argument to path type constructors . The caller must provide the
112
+ /// parent `Expr`, for performance's sake.
108
113
///
109
- /// TODO: We can likely refactor this like we did with `LINTED_TRAITS`.
110
- fn is_path_buf_from_or_path_new ( cx : & LateContext < ' _ > , parent : & Expr < ' _ > ) -> bool {
114
+ /// We can't use `is_path_ty` as these take `AsRef<OsStr>` or similar.
115
+ ///
116
+ /// TODO: Should we lint `OsStr` too, in `is_path_ty`? I personally don't think so.
117
+ fn is_path_constructor ( cx : & LateContext < ' _ > , parent : & Expr < ' _ > ) -> bool {
118
+ enum DefPathOrTyAndName {
119
+ /// Something from `clippy_utils::paths`.
120
+ DefPath ( & ' static [ & ' static str ] ) ,
121
+ /// The type's name and the method's name. The type must be a diagnostic item and not its
122
+ /// constructor.
123
+ ///
124
+ /// Currently, this is only used for `PathBuf::from`. `PathBuf::from` is unfortunately
125
+ /// tricky, as all we end up having for `match_def_path` is `core::convert::From::from`,
126
+ /// not `std::path::PathBuf::from`. Basically useless.
127
+ TyAndName ( ( Symbol , Symbol ) ) ,
128
+ }
129
+ // Provides no additional clarity
130
+ use DefPathOrTyAndName :: { DefPath , TyAndName } ;
131
+
132
+ const LINTED_METHODS : & [ DefPathOrTyAndName ] = & [ DefPath ( & PATH_NEW ) , TyAndName ( ( sym:: PathBuf , sym:: from) ) ] ;
133
+
111
134
if let ExprKind :: Call ( path, _) = parent. kind
112
135
&& let ExprKind :: Path ( qpath) = path. kind
113
136
&& let QPath :: TypeRelative ( ty, last_segment) = qpath
114
137
&& let Some ( call_def_id) = path_res ( cx, path) . opt_def_id ( )
115
138
&& let Some ( ty_def_id) = path_res ( cx, ty) . opt_def_id ( )
116
- && ( match_def_path ( cx, call_def_id, & PATH_NEW )
117
- // `PathBuf::from` is unfortunately tricky, as all we end up having for `match_def_path`
118
- // is `core::convert::From::from`, not `std::path::PathBuf::from`. Basically useless.
119
- || cx. tcx . is_diagnostic_item ( sym:: PathBuf , ty_def_id) && last_segment. ident . as_str ( ) == "from" )
120
139
{
121
- return true ;
140
+ return LINTED_METHODS . iter ( ) . any ( |method| {
141
+ match method {
142
+ DefPath ( path) => match_def_path ( cx, call_def_id, path) ,
143
+ TyAndName ( ( ty_name, method_name) ) => {
144
+ cx. tcx . is_diagnostic_item ( * ty_name, ty_def_id) && last_segment. ident . name == * method_name
145
+ } ,
146
+ }
147
+ } ) ;
122
148
}
123
149
124
150
false
@@ -138,8 +164,15 @@ fn get_def_id_and_args<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) ->
138
164
fn is_path_ty < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > , parent : & ' tcx Expr < ' tcx > ) -> bool {
139
165
const LINTED_TRAITS : & [ ( Symbol , Symbol ) ] = & [
140
166
( sym:: AsRef , sym:: Path ) ,
141
- ( sym:: Into , sym:: PathBuf ) ,
167
+ ( sym:: AsMut , sym:: Path ) ,
168
+ // Basically useless, but let's lint these anyway
169
+ ( sym:: AsRef , sym:: PathBuf ) ,
170
+ ( sym:: AsMut , sym:: PathBuf ) ,
142
171
( sym:: Into , sym:: Path ) ,
172
+ ( sym:: Into , sym:: PathBuf ) ,
173
+ // Never seen `From` used in a generic context before, but let's lint these anyway
174
+ ( sym:: From , sym:: Path ) ,
175
+ ( sym:: From , sym:: PathBuf ) ,
143
176
// TODO: Let's add more traits here.
144
177
] ;
145
178
@@ -168,7 +201,7 @@ fn is_path_ty<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, parent: &'tc
168
201
{
169
202
if let ty:: ClauseKind :: Trait ( trit) = predicate
170
203
&& trit. trait_ref . self_ty ( ) == arg_ty
171
- // I believe `0` is always `Self`, so `T` or `impl <trait>`
204
+ // I believe `0` is always `Self`, i.e., `T` or `impl <trait>` so get `1` instead
172
205
&& let [ _, subst] = trit. trait_ref . substs . as_slice ( )
173
206
&& let Some ( as_ref_ty) = subst. as_type ( )
174
207
{
0 commit comments