@@ -3,18 +3,20 @@ use rustc_ast::ptr::P;
3
3
use rustc_ast:: token;
4
4
use rustc_ast:: tokenstream:: TokenStream ;
5
5
use rustc_ast_pretty:: pprust;
6
+ use rustc_data_structures:: sync:: Lrc ;
6
7
use rustc_expand:: base:: {
7
- check_zero_tts, get_single_str_from_tts, parse_expr , resolve_path , DummyResult , ExtCtxt ,
8
- MacEager , MacResult ,
8
+ check_zero_tts, get_single_str_from_tts, get_single_str_spanned_from_tts , parse_expr ,
9
+ resolve_path , DummyResult , ExtCtxt , MacEager , MacResult ,
9
10
} ;
10
11
use rustc_expand:: module:: DirOwnership ;
11
12
use rustc_parse:: new_parser_from_file;
12
13
use rustc_parse:: parser:: { ForceCollect , Parser } ;
13
14
use rustc_session:: lint:: builtin:: INCOMPLETE_INCLUDE ;
15
+ use rustc_span:: source_map:: SourceMap ;
14
16
use rustc_span:: symbol:: Symbol ;
15
17
use rustc_span:: { Pos , Span } ;
16
-
17
18
use smallvec:: SmallVec ;
19
+ use std:: path:: { Path , PathBuf } ;
18
20
use std:: rc:: Rc ;
19
21
20
22
// These macros all relate to the file system; they either return
@@ -180,32 +182,22 @@ pub fn expand_include_str(
180
182
tts : TokenStream ,
181
183
) -> Box < dyn MacResult + ' static > {
182
184
let sp = cx. with_def_site_ctxt ( sp) ;
183
- let file = match get_single_str_from_tts ( cx, sp, tts, "include_str!" ) {
184
- Ok ( file ) => file ,
185
+ let ( path , path_span ) = match get_single_str_spanned_from_tts ( cx, sp, tts, "include_str!" ) {
186
+ Ok ( res ) => res ,
185
187
Err ( guar) => return DummyResult :: any ( sp, guar) ,
186
188
} ;
187
- let file = match resolve_path ( & cx. sess , file. as_str ( ) , sp) {
188
- Ok ( f) => f,
189
- Err ( err) => {
190
- let guar = err. emit ( ) ;
191
- return DummyResult :: any ( sp, guar) ;
192
- }
193
- } ;
194
- match cx. source_map ( ) . load_binary_file ( & file) {
189
+ match load_binary_file ( cx, path. as_str ( ) . as_ref ( ) , sp, path_span) {
195
190
Ok ( bytes) => match std:: str:: from_utf8 ( & bytes) {
196
191
Ok ( src) => {
197
192
let interned_src = Symbol :: intern ( src) ;
198
193
MacEager :: expr ( cx. expr_str ( sp, interned_src) )
199
194
}
200
195
Err ( _) => {
201
- let guar = cx. dcx ( ) . span_err ( sp, format ! ( "{} wasn't a utf-8 file" , file . display ( ) ) ) ;
196
+ let guar = cx. dcx ( ) . span_err ( sp, format ! ( "`{path}` wasn't a utf-8 file" ) ) ;
202
197
DummyResult :: any ( sp, guar)
203
198
}
204
199
} ,
205
- Err ( e) => {
206
- let guar = cx. dcx ( ) . span_err ( sp, format ! ( "couldn't read {}: {}" , file. display( ) , e) ) ;
207
- DummyResult :: any ( sp, guar)
208
- }
200
+ Err ( dummy) => dummy,
209
201
}
210
202
}
211
203
@@ -215,25 +207,123 @@ pub fn expand_include_bytes(
215
207
tts : TokenStream ,
216
208
) -> Box < dyn MacResult + ' static > {
217
209
let sp = cx. with_def_site_ctxt ( sp) ;
218
- let file = match get_single_str_from_tts ( cx, sp, tts, "include_bytes!" ) {
219
- Ok ( file ) => file ,
210
+ let ( path , path_span ) = match get_single_str_spanned_from_tts ( cx, sp, tts, "include_bytes!" ) {
211
+ Ok ( res ) => res ,
220
212
Err ( guar) => return DummyResult :: any ( sp, guar) ,
221
213
} ;
222
- let file = match resolve_path ( & cx. sess , file. as_str ( ) , sp) {
223
- Ok ( f) => f,
214
+ match load_binary_file ( cx, path. as_str ( ) . as_ref ( ) , sp, path_span) {
215
+ Ok ( bytes) => {
216
+ let expr = cx. expr ( sp, ast:: ExprKind :: IncludedBytes ( bytes) ) ;
217
+ MacEager :: expr ( expr)
218
+ }
219
+ Err ( dummy) => dummy,
220
+ }
221
+ }
222
+
223
+ fn load_binary_file (
224
+ cx : & mut ExtCtxt < ' _ > ,
225
+ original_path : & Path ,
226
+ macro_span : Span ,
227
+ path_span : Span ,
228
+ ) -> Result < Lrc < [ u8 ] > , Box < dyn MacResult > > {
229
+ let resolved_path = match resolve_path ( & cx. sess , original_path, macro_span) {
230
+ Ok ( path) => path,
224
231
Err ( err) => {
225
232
let guar = err. emit ( ) ;
226
- return DummyResult :: any ( sp , guar) ;
233
+ return Err ( DummyResult :: any ( macro_span , guar) ) ;
227
234
}
228
235
} ;
229
- match cx. source_map ( ) . load_binary_file ( & file) {
230
- Ok ( bytes) => {
231
- let expr = cx. expr ( sp, ast:: ExprKind :: IncludedBytes ( bytes) ) ;
232
- MacEager :: expr ( expr)
236
+ match cx. source_map ( ) . load_binary_file ( & resolved_path) {
237
+ Ok ( data) => Ok ( data) ,
238
+ Err ( io_err) => {
239
+ let mut err = cx. dcx ( ) . struct_span_err (
240
+ macro_span,
241
+ format ! ( "couldn't read `{}`: {io_err}" , resolved_path. display( ) ) ,
242
+ ) ;
243
+
244
+ if original_path. is_relative ( ) {
245
+ let source_map = cx. sess . source_map ( ) ;
246
+ let new_path = source_map
247
+ . span_to_filename ( macro_span. source_callsite ( ) )
248
+ . into_local_path ( )
249
+ . and_then ( |src| find_path_suggestion ( source_map, src. parent ( ) ?, original_path) )
250
+ . and_then ( |path| path. into_os_string ( ) . into_string ( ) . ok ( ) ) ;
251
+
252
+ if let Some ( new_path) = new_path {
253
+ err. span_suggestion (
254
+ path_span,
255
+ "there is a file with the same name in a different directory" ,
256
+ format ! ( "\" {}\" " , new_path. escape_debug( ) ) ,
257
+ rustc_lint_defs:: Applicability :: MachineApplicable ,
258
+ ) ;
259
+ }
260
+ }
261
+ let guar = err. emit ( ) ;
262
+ Err ( DummyResult :: any ( macro_span, guar) )
233
263
}
234
- Err ( e) => {
235
- let guar = cx. dcx ( ) . span_err ( sp, format ! ( "couldn't read {}: {}" , file. display( ) , e) ) ;
236
- DummyResult :: any ( sp, guar)
264
+ }
265
+ }
266
+
267
+ fn find_path_suggestion (
268
+ source_map : & SourceMap ,
269
+ base_dir : & Path ,
270
+ wanted_path : & Path ,
271
+ ) -> Option < PathBuf > {
272
+ // Fix paths that assume they're relative to cargo manifest dir
273
+ let mut base_c = base_dir. components ( ) ;
274
+ let mut wanted_c = wanted_path. components ( ) ;
275
+ let mut without_base = None ;
276
+ while let Some ( wanted_next) = wanted_c. next ( ) {
277
+ if wanted_c. as_path ( ) . file_name ( ) . is_none ( ) {
278
+ break ;
279
+ }
280
+ // base_dir may be absolute
281
+ while let Some ( base_next) = base_c. next ( ) {
282
+ if base_next == wanted_next {
283
+ without_base = Some ( wanted_c. as_path ( ) ) ;
284
+ break ;
285
+ }
286
+ }
287
+ }
288
+ let root_absolute = without_base. into_iter ( ) . map ( PathBuf :: from) ;
289
+
290
+ let base_dir_components = base_dir. components ( ) . count ( ) ;
291
+ // Avoid going all the way to the root dir
292
+ let max_parent_components = if base_dir. is_relative ( ) {
293
+ base_dir_components + 1
294
+ } else {
295
+ base_dir_components. saturating_sub ( 1 )
296
+ } ;
297
+
298
+ // Try with additional leading ../
299
+ let mut prefix = PathBuf :: new ( ) ;
300
+ let add = std:: iter:: from_fn ( || {
301
+ prefix. push ( ".." ) ;
302
+ Some ( prefix. join ( wanted_path) )
303
+ } )
304
+ . take ( max_parent_components. min ( 3 ) ) ;
305
+
306
+ // Try without leading directories
307
+ let mut trimmed_path = wanted_path;
308
+ let remove = std:: iter:: from_fn ( || {
309
+ let mut components = trimmed_path. components ( ) ;
310
+ let removed = components. next ( ) ?;
311
+ trimmed_path = components. as_path ( ) ;
312
+ let _ = trimmed_path. file_name ( ) ?; // ensure there is a file name left
313
+ Some ( [
314
+ Some ( trimmed_path. to_path_buf ( ) ) ,
315
+ ( removed != std:: path:: Component :: ParentDir )
316
+ . then ( || Path :: new ( ".." ) . join ( trimmed_path) ) ,
317
+ ] )
318
+ } )
319
+ . flatten ( )
320
+ . flatten ( )
321
+ . take ( 4 ) ;
322
+
323
+ for new_path in root_absolute. chain ( add) . chain ( remove) {
324
+ if source_map. file_exists ( & base_dir. join ( & new_path) ) {
325
+ return Some ( new_path) ;
237
326
}
238
327
}
328
+ None
239
329
}
0 commit comments