1
- use clippy_utils:: {
2
- diagnostics :: span_lint_and_then , get_parent_expr , is_from_proc_macro , match_def_path , path_res , paths:: PATH_NEW ,
3
- ty:: is_type_diagnostic_item,
4
- } ;
5
- use rustc_ast:: LitKind ;
1
+ use clippy_utils:: diagnostics :: span_lint_and_then ;
2
+ use clippy_utils :: paths:: PATH_NEW ;
3
+ use clippy_utils :: ty:: is_type_diagnostic_item;
4
+ use clippy_utils :: { get_parent_expr , is_from_proc_macro , match_def_path , path_res } ;
5
+ use rustc_ast:: { LitKind , StrStyle } ;
6
6
use rustc_errors:: Applicability ;
7
7
use rustc_hir:: def_id:: DefId ;
8
8
use rustc_hir:: { Expr , ExprKind , QPath } ;
9
9
use rustc_lint:: { LateContext , LateLintPass , LintContext } ;
10
- use rustc_middle:: { lint:: in_external_macro, ty} ;
10
+ use rustc_middle:: lint:: in_external_macro;
11
+ use rustc_middle:: ty;
11
12
use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
12
13
use rustc_span:: { sym, Symbol } ;
14
+ use std:: borrow:: Cow ;
13
15
14
16
declare_clippy_lint ! {
15
17
/// ### What it does
@@ -41,7 +43,7 @@ impl<'tcx> LateLintPass<'tcx> for BareDosDeviceNames {
41
43
fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > ) {
42
44
if !in_external_macro ( cx. sess ( ) , expr. span )
43
45
&& let ExprKind :: Lit ( arg) = expr. kind
44
- && let LitKind :: Str ( str_sym, _ ) = arg. node
46
+ && let LitKind :: Str ( str_sym, str_style ) = arg. node
45
47
&& matches ! (
46
48
& * str_sym. as_str( ) . to_ascii_lowercase( ) ,
47
49
"aux"
@@ -55,6 +57,9 @@ impl<'tcx> LateLintPass<'tcx> for BareDosDeviceNames {
55
57
// Using `CONIN$` and `CONOUT$` is common enough in other languages that it may
56
58
// trip up a couple newbies coming to rust. Besides, it's unlikely someone will
57
59
// ever use `CONIN$` as a filename.
60
+ //
61
+ // TODO: Perhaps `com10` etc. are also DOS device names? `com42` is used in
62
+ // `starship-rs` so perhaps they are. But this needs confirmation.
58
63
| "com1"
59
64
| "com2"
60
65
| "com3"
@@ -77,7 +82,7 @@ impl<'tcx> LateLintPass<'tcx> for BareDosDeviceNames {
77
82
| "prn"
78
83
)
79
84
&& let Some ( parent) = get_parent_expr ( cx, expr)
80
- && ( is_path_constructor ( cx, parent) || is_path_ty ( cx, expr, parent) )
85
+ && ( is_path_like_constructor ( cx, parent) || is_path_like_ty ( cx, expr, parent) )
81
86
&& !is_from_proc_macro ( cx, expr)
82
87
{
83
88
span_lint_and_then (
@@ -86,20 +91,26 @@ impl<'tcx> LateLintPass<'tcx> for BareDosDeviceNames {
86
91
expr. span ,
87
92
"this path refers to a DOS device" ,
88
93
|diag| {
94
+ // Keep `r###` and `###`
95
+ let ( prefix, hashes) = if let StrStyle :: Raw ( num) = str_style {
96
+ ( Cow :: Borrowed ( "r" ) , "#" . repeat ( num as usize ) . into ( ) )
97
+ } else {
98
+ ( Cow :: Borrowed ( "" ) , Cow :: Borrowed ( "" ) )
99
+ } ;
100
+
89
101
// Suggest making current behavior explicit
90
102
diag. span_suggestion_verbose (
91
103
expr. span ,
92
- "if this is intended, try" ,
93
- // FIXME: I have zero clue why it normalizes this. `\` -> `/`
94
- format ! ( r#"r"\\.\{str_sym}"\"# ) ,
104
+ "if this is intended, use" ,
105
+ format ! ( r#"r{hashes}"\\.\{str_sym}"{hashes}"# ) ,
95
106
Applicability :: MaybeIncorrect ,
96
107
) ;
97
108
98
109
// Suggest making the code refer to a file or folder in the current directory
99
110
diag. span_suggestion_verbose (
100
111
expr. span ,
101
- "if this was intended to point to a file or folder, try " ,
102
- format ! ( " \ " ./{str_sym}\" " ) ,
112
+ "if this was intended to point to a file or folder, use " ,
113
+ format ! ( r#"{prefix}{hashes} "./{str_sym}"{hashes}"# ) ,
103
114
Applicability :: MaybeIncorrect ,
104
115
) ;
105
116
}
@@ -112,9 +123,7 @@ impl<'tcx> LateLintPass<'tcx> for BareDosDeviceNames {
112
123
/// parent `Expr`, for performance's sake.
113
124
///
114
125
/// 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 {
126
+ fn is_path_like_constructor ( cx : & LateContext < ' _ > , parent : & Expr < ' _ > ) -> bool {
118
127
enum DefPathOrTyAndName {
119
128
/// Something from `clippy_utils::paths`.
120
129
DefPath ( & ' static [ & ' static str ] ) ,
@@ -136,21 +145,25 @@ fn is_path_constructor(cx: &LateContext<'_>, parent: &Expr<'_>) -> bool {
136
145
&& let QPath :: TypeRelative ( ty, last_segment) = qpath
137
146
&& let Some ( call_def_id) = path_res ( cx, path) . opt_def_id ( )
138
147
&& let Some ( ty_def_id) = path_res ( cx, ty) . opt_def_id ( )
148
+ && LINTED_METHODS . iter ( ) . any ( |method| match method {
149
+ DefPath ( path) => match_def_path ( cx, call_def_id, path) ,
150
+ TyAndName ( ( ty_name, method_name) ) => {
151
+ cx. tcx . is_diagnostic_item ( * ty_name, ty_def_id) && last_segment. ident . name == * method_name
152
+ } ,
153
+ } )
139
154
{
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
- } ) ;
155
+ return true ;
148
156
}
149
157
150
158
false
151
159
}
152
160
153
161
/// Gets the `DefId` and arguments of `expr`, if it's a `Call` or `MethodCall`
162
+ ///
163
+ /// TODO: Move this to `clippy_utils` and extend it to give more info (not just `DefId` and
164
+ /// arguments). There are many lints that often need this sorta functionality. Most recently
165
+ /// `incorrect_partial_ord_impl_on_ord_type`, but basically all `methods` lints can use this to lint
166
+ /// `Self::method(self)` as well.
154
167
fn get_def_id_and_args < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > ) -> Option < ( DefId , & ' tcx [ Expr < ' tcx > ] ) > {
155
168
match expr. kind {
156
169
ExprKind :: Call ( path, args) => Some ( ( path_res ( cx, path) . opt_def_id ( ) ?, args) ) ,
@@ -161,16 +174,14 @@ fn get_def_id_and_args<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) ->
161
174
162
175
/// Given a `Ty`, returns whether it is likely a path type, like `Path` or `PathBuf`. Also returns
163
176
/// true if it's `impl AsRef<Path>`, `T: AsRef<Path>`, etc. You get the idea.
164
- fn is_path_ty < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > , parent : & ' tcx Expr < ' tcx > ) -> bool {
177
+ fn is_path_like_ty < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > , parent : & ' tcx Expr < ' tcx > ) -> bool {
165
178
const LINTED_TRAITS : & [ ( Symbol , Symbol ) ] = & [
166
179
( sym:: AsRef , sym:: Path ) ,
167
180
( sym:: AsMut , sym:: Path ) ,
168
- // Basically useless, but let's lint these anyway
169
181
( sym:: AsRef , sym:: PathBuf ) ,
170
182
( sym:: AsMut , sym:: PathBuf ) ,
171
183
( sym:: Into , sym:: Path ) ,
172
184
( sym:: Into , sym:: PathBuf ) ,
173
- // Never seen `From` used in a generic context before, but let's lint these anyway
174
185
( sym:: From , sym:: Path ) ,
175
186
( sym:: From , sym:: PathBuf ) ,
176
187
// TODO: Let's add more traits here.
@@ -204,14 +215,11 @@ fn is_path_ty<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, parent: &'tc
204
215
// I believe `0` is always `Self`, i.e., `T` or `impl <trait>` so get `1` instead
205
216
&& let [ _, subst] = trit. trait_ref . substs . as_slice ( )
206
217
&& let Some ( as_ref_ty) = subst. as_type ( )
207
- {
208
- for ( trait_sym, ty_sym) in LINTED_TRAITS {
209
- if cx. tcx . is_diagnostic_item ( * trait_sym, trit. trait_ref . def_id )
218
+ && LINTED_TRAITS . iter ( ) . any ( |( trait_sym, ty_sym) | {
219
+ cx. tcx . is_diagnostic_item ( * trait_sym, trit. trait_ref . def_id )
210
220
&& is_type_diagnostic_item ( cx, as_ref_ty, * ty_sym)
211
- {
212
- return true ;
213
- }
214
- }
221
+ } ) {
222
+ return true ;
215
223
}
216
224
}
217
225
0 commit comments