@@ -3,17 +3,22 @@ use rustc_ast::ptr::P;
33use  rustc_ast:: token; 
44use  rustc_ast:: tokenstream:: TokenStream ; 
55use  rustc_ast_pretty:: pprust; 
6- use  rustc_expand:: base:: { check_zero_tts,  get_single_str_from_tts,  parse_expr,  resolve_path} ; 
6+ use  rustc_data_structures:: sync:: Lrc ; 
7+ use  rustc_expand:: base:: { 
8+     check_zero_tts,  get_single_str_from_tts,  get_single_str_spanned_from_tts,  parse_expr, 
9+     resolve_path, 
10+ } ; 
711use  rustc_expand:: base:: { DummyResult ,  ExpandResult ,  ExtCtxt } ; 
812use  rustc_expand:: base:: { MacEager ,  MacResult ,  MacroExpanderResult } ; 
913use  rustc_expand:: module:: DirOwnership ; 
1014use  rustc_parse:: new_parser_from_file; 
1115use  rustc_parse:: parser:: { ForceCollect ,  Parser } ; 
1216use  rustc_session:: lint:: builtin:: INCOMPLETE_INCLUDE ; 
17+ use  rustc_span:: source_map:: SourceMap ; 
1318use  rustc_span:: symbol:: Symbol ; 
1419use  rustc_span:: { Pos ,  Span } ; 
15- 
1620use  smallvec:: SmallVec ; 
21+ use  std:: path:: { Path ,  PathBuf } ; 
1722use  std:: rc:: Rc ; 
1823
1924// These macros all relate to the file system; they either return 
@@ -182,35 +187,26 @@ pub fn expand_include_str(
182187    tts :  TokenStream , 
183188)  -> MacroExpanderResult < ' static >  { 
184189    let  sp = cx. with_def_site_ctxt ( sp) ; 
185-     let  ExpandResult :: Ready ( mac)  = get_single_str_from_tts ( cx,  sp,  tts,  "include_str!" )  else  { 
190+     let  ExpandResult :: Ready ( mac)  = get_single_str_spanned_from_tts ( cx,  sp,  tts,  "include_str!" ) 
191+     else  { 
186192        return  ExpandResult :: Retry ( ( ) ) ; 
187193    } ; 
188-     let  file  = match  mac { 
189-         Ok ( file )  => file , 
194+     let  ( path ,  path_span )  = match  mac { 
195+         Ok ( res )  => res , 
190196        Err ( guar)  => return  ExpandResult :: Ready ( DummyResult :: any ( sp,  guar) ) , 
191197    } ; 
192-     let  file = match  resolve_path ( & cx. sess ,  file. as_str ( ) ,  sp)  { 
193-         Ok ( f)  => f, 
194-         Err ( err)  => { 
195-             let  guar = err. emit ( ) ; 
196-             return  ExpandResult :: Ready ( DummyResult :: any ( sp,  guar) ) ; 
197-         } 
198-     } ; 
199-     ExpandResult :: Ready ( match  cx. source_map ( ) . load_binary_file ( & file)  { 
198+     ExpandResult :: Ready ( match  load_binary_file ( cx,  path. as_str ( ) . as_ref ( ) ,  sp,  path_span)  { 
200199        Ok ( bytes)  => match  std:: str:: from_utf8 ( & bytes)  { 
201200            Ok ( src)  => { 
202201                let  interned_src = Symbol :: intern ( src) ; 
203202                MacEager :: expr ( cx. expr_str ( sp,  interned_src) ) 
204203            } 
205204            Err ( _)  => { 
206-                 let  guar = cx. dcx ( ) . span_err ( sp,  format ! ( "{}  wasn't a utf-8 file" ,  file . display ( ) ) ) ; 
205+                 let  guar = cx. dcx ( ) . span_err ( sp,  format ! ( "`{path}`  wasn't a utf-8 file" ) ) ; 
207206                DummyResult :: any ( sp,  guar) 
208207            } 
209208        } , 
210-         Err ( e)  => { 
211-             let  guar = cx. dcx ( ) . span_err ( sp,  format ! ( "couldn't read {}: {}" ,  file. display( ) ,  e) ) ; 
212-             DummyResult :: any ( sp,  guar) 
213-         } 
209+         Err ( dummy)  => dummy, 
214210    } ) 
215211} 
216212
@@ -220,28 +216,127 @@ pub fn expand_include_bytes(
220216    tts :  TokenStream , 
221217)  -> MacroExpanderResult < ' static >  { 
222218    let  sp = cx. with_def_site_ctxt ( sp) ; 
223-     let  ExpandResult :: Ready ( mac)  = get_single_str_from_tts ( cx,  sp,  tts,  "include_bytes!" )  else  { 
219+     let  ExpandResult :: Ready ( mac)  = get_single_str_spanned_from_tts ( cx,  sp,  tts,  "include_bytes!" ) 
220+     else  { 
224221        return  ExpandResult :: Retry ( ( ) ) ; 
225222    } ; 
226-     let  file  = match  mac { 
227-         Ok ( file )  => file , 
223+     let  ( path ,  path_span )  = match  mac { 
224+         Ok ( res )  => res , 
228225        Err ( guar)  => return  ExpandResult :: Ready ( DummyResult :: any ( sp,  guar) ) , 
229226    } ; 
230-     let  file = match  resolve_path ( & cx. sess ,  file. as_str ( ) ,  sp)  { 
231-         Ok ( f)  => f, 
227+     ExpandResult :: Ready ( match  load_binary_file ( cx,  path. as_str ( ) . as_ref ( ) ,  sp,  path_span)  { 
228+         Ok ( bytes)  => { 
229+             let  expr = cx. expr ( sp,  ast:: ExprKind :: IncludedBytes ( bytes) ) ; 
230+             MacEager :: expr ( expr) 
231+         } 
232+         Err ( dummy)  => dummy, 
233+     } ) 
234+ } 
235+ 
236+ fn  load_binary_file ( 
237+     cx :  & mut  ExtCtxt < ' _ > , 
238+     original_path :  & Path , 
239+     macro_span :  Span , 
240+     path_span :  Span , 
241+ )  -> Result < Lrc < [ u8 ] > ,  Box < dyn  MacResult > >  { 
242+     let  resolved_path = match  resolve_path ( & cx. sess ,  original_path,  macro_span)  { 
243+         Ok ( path)  => path, 
232244        Err ( err)  => { 
233245            let  guar = err. emit ( ) ; 
234-             return  ExpandResult :: Ready ( DummyResult :: any ( sp ,  guar) ) ; 
246+             return  Err ( DummyResult :: any ( macro_span ,  guar) ) ; 
235247        } 
236248    } ; 
237-     ExpandResult :: Ready ( match  cx. source_map ( ) . load_binary_file ( & file)  { 
238-         Ok ( bytes)  => { 
239-             let  expr = cx. expr ( sp,  ast:: ExprKind :: IncludedBytes ( bytes) ) ; 
240-             MacEager :: expr ( expr) 
249+     match  cx. source_map ( ) . load_binary_file ( & resolved_path)  { 
250+         Ok ( data)  => Ok ( data) , 
251+         Err ( io_err)  => { 
252+             let  mut  err = cx. dcx ( ) . struct_span_err ( 
253+                 macro_span, 
254+                 format ! ( "couldn't read `{}`: {io_err}" ,  resolved_path. display( ) ) , 
255+             ) ; 
256+ 
257+             if  original_path. is_relative ( )  { 
258+                 let  source_map = cx. sess . source_map ( ) ; 
259+                 let  new_path = source_map
260+                     . span_to_filename ( macro_span. source_callsite ( ) ) 
261+                     . into_local_path ( ) 
262+                     . and_then ( |src| find_path_suggestion ( source_map,  src. parent ( ) ?,  original_path) ) 
263+                     . and_then ( |path| path. into_os_string ( ) . into_string ( ) . ok ( ) ) ; 
264+ 
265+                 if  let  Some ( new_path)  = new_path { 
266+                     err. span_suggestion ( 
267+                         path_span, 
268+                         "there is a file with the same name in a different directory" , 
269+                         format ! ( "\" {}\" " ,  new_path. escape_debug( ) ) , 
270+                         rustc_lint_defs:: Applicability :: MachineApplicable , 
271+                     ) ; 
272+                 } 
273+             } 
274+             let  guar = err. emit ( ) ; 
275+             Err ( DummyResult :: any ( macro_span,  guar) ) 
241276        } 
242-         Err ( e)  => { 
243-             let  guar = cx. dcx ( ) . span_err ( sp,  format ! ( "couldn't read {}: {}" ,  file. display( ) ,  e) ) ; 
244-             DummyResult :: any ( sp,  guar) 
277+     } 
278+ } 
279+ 
280+ fn  find_path_suggestion ( 
281+     source_map :  & SourceMap , 
282+     base_dir :  & Path , 
283+     wanted_path :  & Path , 
284+ )  -> Option < PathBuf >  { 
285+     // Fix paths that assume they're relative to cargo manifest dir 
286+     let  mut  base_c = base_dir. components ( ) ; 
287+     let  mut  wanted_c = wanted_path. components ( ) ; 
288+     let  mut  without_base = None ; 
289+     while  let  Some ( wanted_next)  = wanted_c. next ( )  { 
290+         if  wanted_c. as_path ( ) . file_name ( ) . is_none ( )  { 
291+             break ; 
245292        } 
293+         // base_dir may be absolute 
294+         while  let  Some ( base_next)  = base_c. next ( )  { 
295+             if  base_next == wanted_next { 
296+                 without_base = Some ( wanted_c. as_path ( ) ) ; 
297+                 break ; 
298+             } 
299+         } 
300+     } 
301+     let  root_absolute = without_base. into_iter ( ) . map ( PathBuf :: from) ; 
302+ 
303+     let  base_dir_components = base_dir. components ( ) . count ( ) ; 
304+     // Avoid going all the way to the root dir 
305+     let  max_parent_components = if  base_dir. is_relative ( )  { 
306+         base_dir_components + 1 
307+     }  else  { 
308+         base_dir_components. saturating_sub ( 1 ) 
309+     } ; 
310+ 
311+     // Try with additional leading ../ 
312+     let  mut  prefix = PathBuf :: new ( ) ; 
313+     let  add = std:: iter:: from_fn ( || { 
314+         prefix. push ( ".." ) ; 
315+         Some ( prefix. join ( wanted_path) ) 
246316    } ) 
317+     . take ( max_parent_components. min ( 3 ) ) ; 
318+ 
319+     // Try without leading directories 
320+     let  mut  trimmed_path = wanted_path; 
321+     let  remove = std:: iter:: from_fn ( || { 
322+         let  mut  components = trimmed_path. components ( ) ; 
323+         let  removed = components. next ( ) ?; 
324+         trimmed_path = components. as_path ( ) ; 
325+         let  _ = trimmed_path. file_name ( ) ?;  // ensure there is a file name left 
326+         Some ( [ 
327+             Some ( trimmed_path. to_path_buf ( ) ) , 
328+             ( removed != std:: path:: Component :: ParentDir ) 
329+                 . then ( || Path :: new ( ".." ) . join ( trimmed_path) ) , 
330+         ] ) 
331+     } ) 
332+     . flatten ( ) 
333+     . flatten ( ) 
334+     . take ( 4 ) ; 
335+ 
336+     for  new_path in  root_absolute. chain ( add) . chain ( remove)  { 
337+         if  source_map. file_exists ( & base_dir. join ( & new_path) )  { 
338+             return  Some ( new_path) ; 
339+         } 
340+     } 
341+     None 
247342} 
0 commit comments