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,25 @@ 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 ,
96- ) ;
97-
107+ )
98108 // Suggest making the code refer to a file or folder in the current directory
99- diag . span_suggestion_verbose (
109+ . span_suggestion_verbose (
100110 expr. span ,
101- "if this was intended to point to a file or folder, try " ,
102- format ! ( " \ " ./{str_sym}\" " ) ,
111+ "if this was intended to point to a file or folder, use " ,
112+ format ! ( r#"{prefix}{hashes} "./{str_sym}"{hashes}"# ) ,
103113 Applicability :: MaybeIncorrect ,
104114 ) ;
105115 }
@@ -112,9 +122,7 @@ impl<'tcx> LateLintPass<'tcx> for BareDosDeviceNames {
112122/// parent `Expr`, for performance's sake.
113123///
114124/// 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 {
125+ fn is_path_like_constructor ( cx : & LateContext < ' _ > , parent : & Expr < ' _ > ) -> bool {
118126 enum DefPathOrTyAndName {
119127 /// Something from `clippy_utils::paths`.
120128 DefPath ( & ' static [ & ' static str ] ) ,
@@ -136,21 +144,25 @@ fn is_path_constructor(cx: &LateContext<'_>, parent: &Expr<'_>) -> bool {
136144 && let QPath :: TypeRelative ( ty, last_segment) = qpath
137145 && let Some ( call_def_id) = path_res ( cx, path) . opt_def_id ( )
138146 && let Some ( ty_def_id) = path_res ( cx, ty) . opt_def_id ( )
147+ && LINTED_METHODS . iter ( ) . any ( |method| match method {
148+ DefPath ( path) => match_def_path ( cx, call_def_id, path) ,
149+ TyAndName ( ( ty_name, method_name) ) => {
150+ cx. tcx . is_diagnostic_item ( * ty_name, ty_def_id) && last_segment. ident . name == * method_name
151+ } ,
152+ } )
139153 {
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- } ) ;
154+ return true ;
148155 }
149156
150157 false
151158}
152159
153160/// Gets the `DefId` and arguments of `expr`, if it's a `Call` or `MethodCall`
161+ ///
162+ /// TODO: Move this to `clippy_utils` and extend it to give more info (not just `DefId` and
163+ /// arguments). There are many lints that often need this sorta functionality. Most recently
164+ /// `incorrect_partial_ord_impl_on_ord_type`, but basically all `methods` lints can use this to lint
165+ /// `Self::method(self)` as well.
154166fn get_def_id_and_args < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > ) -> Option < ( DefId , & ' tcx [ Expr < ' tcx > ] ) > {
155167 match expr. kind {
156168 ExprKind :: Call ( path, args) => Some ( ( path_res ( cx, path) . opt_def_id ( ) ?, args) ) ,
@@ -161,19 +173,16 @@ fn get_def_id_and_args<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) ->
161173
162174/// Given a `Ty`, returns whether it is likely a path type, like `Path` or `PathBuf`. Also returns
163175/// 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 {
176+ fn is_path_like_ty < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > , parent : & ' tcx Expr < ' tcx > ) -> bool {
165177 const LINTED_TRAITS : & [ ( Symbol , Symbol ) ] = & [
166178 ( sym:: AsRef , sym:: Path ) ,
167179 ( sym:: AsMut , sym:: Path ) ,
168- // Basically useless, but let's lint these anyway
169180 ( sym:: AsRef , sym:: PathBuf ) ,
170181 ( sym:: AsMut , sym:: PathBuf ) ,
171182 ( sym:: Into , sym:: Path ) ,
172183 ( sym:: Into , sym:: PathBuf ) ,
173- // Never seen `From` used in a generic context before, but let's lint these anyway
174184 ( sym:: From , sym:: Path ) ,
175185 ( sym:: From , sym:: PathBuf ) ,
176- // TODO: Let's add more traits here.
177186 ] ;
178187
179188 let Some ( ( callee, callee_args) ) = get_def_id_and_args ( cx, parent) else {
@@ -204,14 +213,11 @@ fn is_path_ty<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, parent: &'tc
204213 // I believe `0` is always `Self`, i.e., `T` or `impl <trait>` so get `1` instead
205214 && let [ _, subst] = trit. trait_ref . substs . as_slice ( )
206215 && 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 )
216+ && LINTED_TRAITS . iter ( ) . any ( |( trait_sym, ty_sym) | {
217+ cx. tcx . is_diagnostic_item ( * trait_sym, trit. trait_ref . def_id )
210218 && is_type_diagnostic_item ( cx, as_ref_ty, * ty_sym)
211- {
212- return true ;
213- }
214- }
219+ } ) {
220+ return true ;
215221 }
216222 }
217223
0 commit comments