@@ -12,10 +12,11 @@ use rustc_expand::module::DirOwnership;
12
12
use rustc_parse:: new_parser_from_file;
13
13
use rustc_parse:: parser:: { ForceCollect , Parser } ;
14
14
use rustc_session:: lint:: builtin:: INCOMPLETE_INCLUDE ;
15
+ use rustc_span:: source_map:: SourceMap ;
15
16
use rustc_span:: symbol:: Symbol ;
16
17
use rustc_span:: { Pos , Span } ;
17
18
use smallvec:: SmallVec ;
18
- use std:: path:: Path ;
19
+ use std:: path:: { Path , PathBuf } ;
19
20
use std:: rc:: Rc ;
20
21
21
22
// These macros all relate to the file system; they either return
@@ -185,7 +186,7 @@ pub fn expand_include_str(
185
186
Ok ( res) => res,
186
187
Err ( guar) => return DummyResult :: any ( sp, guar) ,
187
188
} ;
188
- match load_binary_file ( cx, path. as_str ( ) , sp, path_span) {
189
+ match load_binary_file ( cx, path. as_str ( ) . as_ref ( ) , sp, path_span) {
189
190
Ok ( bytes) => match std:: str:: from_utf8 ( & bytes) {
190
191
Ok ( src) => {
191
192
let interned_src = Symbol :: intern ( src) ;
@@ -210,7 +211,7 @@ pub fn expand_include_bytes(
210
211
Ok ( res) => res,
211
212
Err ( guar) => return DummyResult :: any ( sp, guar) ,
212
213
} ;
213
- match load_binary_file ( cx, path. as_str ( ) , sp, path_span) {
214
+ match load_binary_file ( cx, path. as_str ( ) . as_ref ( ) , sp, path_span) {
214
215
Ok ( bytes) => {
215
216
let expr = cx. expr ( sp, ast:: ExprKind :: IncludedBytes ( bytes) ) ;
216
217
MacEager :: expr ( expr)
@@ -221,7 +222,7 @@ pub fn expand_include_bytes(
221
222
222
223
fn load_binary_file (
223
224
cx : & mut ExtCtxt < ' _ > ,
224
- original_path : & str ,
225
+ original_path : & Path ,
225
226
macro_span : Span ,
226
227
path_span : Span ,
227
228
) -> Result < Lrc < [ u8 ] > , Box < dyn MacResult > > {
@@ -239,24 +240,90 @@ fn load_binary_file(
239
240
macro_span,
240
241
format ! ( "couldn't read {}: {io_err}" , resolved_path. display( ) ) ,
241
242
) ;
242
- if Path :: new ( original_path) . is_relative ( ) {
243
- for prefix in [ ".." , "../.." ] {
244
- let parent_path = Path :: new ( prefix) . join ( original_path) ;
245
- if resolve_path ( & cx. sess , & parent_path, macro_span)
246
- . map_or ( false , |p| p. exists ( ) )
247
- {
248
- err. span_suggestion (
249
- path_span,
250
- "it's in a parent directory" ,
251
- format ! ( "\" {}\" " , parent_path. display( ) . to_string( ) . escape_debug( ) ) ,
252
- rustc_lint_defs:: Applicability :: MachineApplicable ,
253
- ) ;
254
- break ;
255
- }
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 in another directory" ,
256
+ format ! ( "\" {}\" " , new_path. escape_debug( ) ) ,
257
+ rustc_lint_defs:: Applicability :: MachineApplicable ,
258
+ ) ;
256
259
}
257
260
}
258
261
let guar = err. emit ( ) ;
259
262
Err ( DummyResult :: any ( macro_span, guar) )
260
263
}
261
264
}
262
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) ;
326
+ }
327
+ }
328
+ None
329
+ }
0 commit comments