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 } ;
66use rustc_errors:: Applicability ;
77use rustc_hir:: def_id:: DefId ;
88use rustc_hir:: { Expr , ExprKind , QPath } ;
99use 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;
1112use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
1213use rustc_span:: { sym, Symbol } ;
14+ use std:: borrow:: Cow ;
1315
1416declare_clippy_lint ! {
1517 /// ### What it does
@@ -41,7 +43,7 @@ impl<'tcx> LateLintPass<'tcx> for BareDosDeviceNames {
4143 fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > ) {
4244 if !in_external_macro ( cx. sess ( ) , expr. span )
4345 && let ExprKind :: Lit ( arg) = expr. kind
44- && let LitKind :: Str ( str_sym, _ ) = arg. node
46+ && let LitKind :: Str ( str_sym, str_style ) = arg. node
4547 && matches ! (
4648 & * str_sym. as_str( ) . to_ascii_lowercase( ) ,
4749 "aux"
@@ -55,6 +57,9 @@ impl<'tcx> LateLintPass<'tcx> for BareDosDeviceNames {
5557 // Using `CONIN$` and `CONOUT$` is common enough in other languages that it may
5658 // trip up a couple newbies coming to rust. Besides, it's unlikely someone will
5759 // 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.
5863 | "com1"
5964 | "com2"
6065 | "com3"
@@ -77,7 +82,7 @@ impl<'tcx> LateLintPass<'tcx> for BareDosDeviceNames {
7782 | "prn"
7883 )
7984 && 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) )
8186 && !is_from_proc_macro ( cx, expr)
8287 {
8388 span_lint_and_then (
@@ -86,20 +91,26 @@ impl<'tcx> LateLintPass<'tcx> for BareDosDeviceNames {
8691 expr. span ,
8792 "this path refers to a DOS device" ,
8893 |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+
89101 // Suggest making current behavior explicit
90102 diag. span_suggestion_verbose (
91103 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}"# ) ,
95106 Applicability :: MaybeIncorrect ,
96107 ) ;
97108
98109 // Suggest making the code refer to a file or folder in the current directory
99110 diag. span_suggestion_verbose (
100111 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}"# ) ,
103114 Applicability :: MaybeIncorrect ,
104115 ) ;
105116 }
@@ -112,9 +123,7 @@ impl<'tcx> LateLintPass<'tcx> for BareDosDeviceNames {
112123/// parent `Expr`, for performance's sake.
113124///
114125/// 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 {
118127 enum DefPathOrTyAndName {
119128 /// Something from `clippy_utils::paths`.
120129 DefPath ( & ' static [ & ' static str ] ) ,
@@ -136,21 +145,25 @@ fn is_path_constructor(cx: &LateContext<'_>, parent: &Expr<'_>) -> bool {
136145 && let QPath :: TypeRelative ( ty, last_segment) = qpath
137146 && let Some ( call_def_id) = path_res ( cx, path) . opt_def_id ( )
138147 && 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+ } )
139154 {
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 ;
148156 }
149157
150158 false
151159}
152160
153161/// 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.
154167fn get_def_id_and_args < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > ) -> Option < ( DefId , & ' tcx [ Expr < ' tcx > ] ) > {
155168 match expr. kind {
156169 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>) ->
161174
162175/// Given a `Ty`, returns whether it is likely a path type, like `Path` or `PathBuf`. Also returns
163176/// 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 {
165178 const LINTED_TRAITS : & [ ( Symbol , Symbol ) ] = & [
166179 ( sym:: AsRef , sym:: Path ) ,
167180 ( sym:: AsMut , sym:: Path ) ,
168- // Basically useless, but let's lint these anyway
169181 ( sym:: AsRef , sym:: PathBuf ) ,
170182 ( sym:: AsMut , sym:: PathBuf ) ,
171183 ( sym:: Into , sym:: Path ) ,
172184 ( sym:: Into , sym:: PathBuf ) ,
173- // Never seen `From` used in a generic context before, but let's lint these anyway
174185 ( sym:: From , sym:: Path ) ,
175186 ( sym:: From , sym:: PathBuf ) ,
176187 // 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
204215 // I believe `0` is always `Self`, i.e., `T` or `impl <trait>` so get `1` instead
205216 && let [ _, subst] = trit. trait_ref . substs . as_slice ( )
206217 && 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 )
210220 && is_type_diagnostic_item ( cx, as_ref_ty, * ty_sym)
211- {
212- return true ;
213- }
214- }
221+ } ) {
222+ return true ;
215223 }
216224 }
217225
0 commit comments