@@ -3,18 +3,20 @@ use rustc_ast::ptr::P;
33use  rustc_ast:: token; 
44use  rustc_ast:: tokenstream:: TokenStream ; 
55use  rustc_ast_pretty:: pprust; 
6+ use  rustc_data_structures:: sync:: Lrc ; 
67use  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 , 
910} ; 
1011use  rustc_expand:: module:: DirOwnership ; 
1112use  rustc_parse:: new_parser_from_file; 
1213use  rustc_parse:: parser:: { ForceCollect ,  Parser } ; 
1314use  rustc_session:: lint:: builtin:: INCOMPLETE_INCLUDE ; 
15+ use  rustc_span:: source_map:: SourceMap ; 
1416use  rustc_span:: symbol:: Symbol ; 
1517use  rustc_span:: { Pos ,  Span } ; 
16- 
1718use  smallvec:: SmallVec ; 
19+ use  std:: path:: { Path ,  PathBuf } ; 
1820use  std:: rc:: Rc ; 
1921
2022// These macros all relate to the file system; they either return 
@@ -180,32 +182,22 @@ pub fn expand_include_str(
180182    tts :  TokenStream , 
181183)  -> Box < dyn  MacResult  + ' static >  { 
182184    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 , 
185187        Err ( guar)  => return  DummyResult :: any ( sp,  guar) , 
186188    } ; 
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)  { 
195190        Ok ( bytes)  => match  std:: str:: from_utf8 ( & bytes)  { 
196191            Ok ( src)  => { 
197192                let  interned_src = Symbol :: intern ( src) ; 
198193                MacEager :: expr ( cx. expr_str ( sp,  interned_src) ) 
199194            } 
200195            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" ) ) ; 
202197                DummyResult :: any ( sp,  guar) 
203198            } 
204199        } , 
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, 
209201    } 
210202} 
211203
@@ -215,25 +207,123 @@ pub fn expand_include_bytes(
215207    tts :  TokenStream , 
216208)  -> Box < dyn  MacResult  + ' static >  { 
217209    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 , 
220212        Err ( guar)  => return  DummyResult :: any ( sp,  guar) , 
221213    } ; 
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, 
224231        Err ( err)  => { 
225232            let  guar = err. emit ( ) ; 
226-             return  DummyResult :: any ( sp ,  guar) ; 
233+             return  Err ( DummyResult :: any ( macro_span ,  guar) ) ; 
227234        } 
228235    } ; 
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) ) 
233263        } 
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) ; 
237326        } 
238327    } 
328+     None 
239329} 
0 commit comments