1
- use clippy_utils:: diagnostics:: { span_lint_and_note , span_lint_and_sugg} ;
1
+ use clippy_utils:: diagnostics:: span_lint_and_sugg;
2
2
use clippy_utils:: macros:: { root_macro_call, FormatArgsExpn } ;
3
3
use clippy_utils:: sugg:: Sugg ;
4
4
use clippy_utils:: ty:: is_type_diagnostic_item;
@@ -54,60 +54,41 @@ impl<'tcx> LateLintPass<'tcx> for PathFromFormat {
54
54
then {
55
55
let format_string_parts = format_args. format_string_parts;
56
56
let format_value_args = format_args. value_args;
57
- let mut string_parts: Vec <& str > = format_string_parts. iter( ) . map( rustc_span:: Symbol :: as_str) . collect( ) ;
58
- string_parts. push( "" ) ;
57
+ let string_parts: Vec <& str > = format_string_parts. iter( ) . map( rustc_span:: Symbol :: as_str) . collect( ) ;
59
58
let mut applicability = Applicability :: MachineApplicable ;
60
59
let real_vars: Vec <Sugg <' _>> = format_value_args. iter( ) . map( |x| Sugg :: hir_with_applicability( cx, x, ".." , & mut applicability) ) . collect( ) ;
61
60
let order_of_real_vars: Vec <usize > = format_args. formatters. iter( ) . map( |( x, _) | * x) . collect( ) ;
61
+ let mut arguments_in_order = Vec :: new( ) ;
62
+ for n in 0 ..real_vars. len( ) {
63
+ arguments_in_order. push( real_vars[ order_of_real_vars[ n] ] . clone( ) ) ;
64
+ }
65
+ let mut paths_zip = string_parts. iter( ) . take( arguments_in_order. len( ) ) . zip( arguments_in_order) ;
62
66
let mut sugg = String :: new( ) ;
63
67
for n in 0 ..real_vars. len( ) {
64
- if !(
65
- string_parts[ n] . is_empty( )
66
- || string_parts[ n] . ends_with( '/' )
67
- || string_parts[ n] . ends_with( '\\' )
68
- )
69
- || !(
70
- string_parts[ n+1 ] . is_empty( )
71
- || string_parts[ n+1 ] . starts_with( '/' )
72
- || string_parts[ n+1 ] . starts_with( '\\' )
73
- ) {
74
- span_lint_and_note(
75
- cx,
76
- PATH_FROM_FORMAT ,
77
- expr. span,
78
- "`format!(..)` used to form `PathBuf`" ,
79
- None ,
80
- "if it fits your use case, you may want to consider using `Path::new()` and `.join()` to make it OS-agnostic and improve code readability" ,
81
- ) ;
82
- return ;
83
- }
84
- if n == 0 {
85
- if string_parts[ 0 ] . is_empty( ) {
86
- sugg = format!( "Path::new({})" , real_vars[ order_of_real_vars[ 0 ] ] ) ;
68
+ if let Some ( ( part, arg) ) = paths_zip. next( ) {
69
+ if is_valid_use_case( string_parts[ n] , string_parts. get( n+1 ) . unwrap_or( & "" ) ) {
70
+ return ;
87
71
}
88
- else {
89
- push_comps( & mut sugg, Path :: new( string_parts[ 0 ] ) ) ;
90
- let _ = write!( sugg, ".join({})" , real_vars[ order_of_real_vars[ 0 ] ] ) ;
72
+ if n == 0 {
73
+ if part. is_empty( ) {
74
+ sugg = format!( "Path::new({})" , arg) ;
75
+ }
76
+ else {
77
+ push_comps( & mut sugg, part, false ) ;
78
+ let _ = write!( sugg, ".join({})" , arg) ;
79
+ }
91
80
}
92
- }
93
- else if string_parts[ n] . is_empty( ) {
94
- sugg = format!( "{sugg}.join({})" , real_vars[ order_of_real_vars[ n] ] ) ;
95
- }
96
- else {
97
- let mut string = String :: from( string_parts[ n] ) ;
98
- if string. starts_with( '/' ) || string. starts_with( '\\' ) {
99
- string. remove( 0 ) ;
81
+ else if n < real_vars. len( ) {
82
+ push_comps( & mut sugg, & part. to_string( ) , true ) ;
83
+ let _ = write!( sugg, ".join({})" , arg) ;
84
+ }
85
+ else {
86
+ sugg = format!( "{sugg}.join({})" , arg) ;
100
87
}
101
- push_comps( & mut sugg, Path :: new( & string) ) ;
102
- let _ = write!( sugg, ".join({})" , real_vars[ order_of_real_vars[ n] ] ) ;
103
88
}
104
89
}
105
- if !string_parts[ real_vars. len( ) ] . is_empty( ) {
106
- let mut string = String :: from( string_parts[ real_vars. len( ) ] ) ;
107
- if string. starts_with( '/' ) || string. starts_with( '\\' ) {
108
- string. remove( 0 ) ;
109
- }
110
- push_comps( & mut sugg, Path :: new( & string) ) ;
90
+ if real_vars. len( ) < string_parts. len( ) {
91
+ push_comps( & mut sugg, & string_parts[ real_vars. len( ) ] , true ) ;
111
92
}
112
93
span_lint_and_sugg(
113
94
cx,
@@ -123,7 +104,12 @@ impl<'tcx> LateLintPass<'tcx> for PathFromFormat {
123
104
}
124
105
}
125
106
126
- fn push_comps ( string : & mut String , path : & Path ) {
107
+ fn push_comps ( string : & mut String , path : & str , trim_first_slash : bool ) {
108
+ let mut path = path. to_string ( ) ;
109
+ if ( path. starts_with ( '/' ) || path. starts_with ( '\\' ) ) && trim_first_slash {
110
+ path. remove ( 0 ) ;
111
+ }
112
+ let path = Path :: new ( & path) ;
127
113
let comps = path. components ( ) ;
128
114
for n in comps {
129
115
let x = n. as_os_str ( ) . to_string_lossy ( ) . to_string ( ) ;
@@ -134,3 +120,16 @@ fn push_comps(string: &mut String, path: &Path) {
134
120
}
135
121
}
136
122
}
123
+
124
+ fn is_valid_use_case ( string : & str , string2 : & str ) -> bool {
125
+ !(
126
+ string. is_empty ( )
127
+ || string. ends_with ( '/' )
128
+ || string. ends_with ( '\\' )
129
+ )
130
+ || !(
131
+ string2. is_empty ( )
132
+ || string2. starts_with ( '/' )
133
+ || string2. starts_with ( '\\' )
134
+ )
135
+ }
0 commit comments