@@ -208,6 +208,8 @@ pub(crate) fn options() -> Result<Options, OptionError> {
208208 child_args,
209209 & subst_mappings,
210210 require_explicit_unstable_features,
211+ None ,
212+ None ,
211213 ) ?;
212214 // Split the executable path from the rest of the arguments.
213215 let ( exec_path, args) = child_args. split_first ( ) . ok_or_else ( || {
@@ -270,58 +272,89 @@ fn prepare_arg(mut arg: String, subst_mappings: &[(String, String)]) -> String {
270272 arg
271273}
272274
273- /// Apply substitutions to the given param file. Returns the new filename and whether any
274- /// allow-features flags were found.
275+ /// Apply substitutions to the given param file. Returns true iff any allow-features flags were found.
275276fn prepare_param_file (
276277 filename : & str ,
277278 subst_mappings : & [ ( String , String ) ] ,
278- ) -> Result < ( String , bool ) , OptionError > {
279- let expanded_file = format ! ( "{filename}.expanded" ) ;
280- let format_err = |err : io:: Error | {
281- OptionError :: Generic ( format ! (
282- "{} writing path: {:?}, current directory: {:?}" ,
283- err,
284- expanded_file,
285- std:: env:: current_dir( )
286- ) )
287- } ;
288- let mut out = io:: BufWriter :: new ( File :: create ( & expanded_file) . map_err ( format_err) ?) ;
279+ read_file : & mut impl FnMut ( & str ) -> Result < Vec < String > , OptionError > ,
280+ write_to_file : & mut impl FnMut ( & str ) -> Result < ( ) , OptionError > ,
281+ ) -> Result < bool , OptionError > {
289282 fn process_file (
290283 filename : & str ,
291- out : & mut io:: BufWriter < File > ,
292284 subst_mappings : & [ ( String , String ) ] ,
293- format_err : & impl Fn ( io:: Error ) -> OptionError ,
285+ read_file : & mut impl FnMut ( & str ) -> Result < Vec < String > , OptionError > ,
286+ write_to_file : & mut impl FnMut ( & str ) -> Result < ( ) , OptionError > ,
294287 ) -> Result < bool , OptionError > {
295288 let mut has_allow_features_flag = false ;
296- for arg in read_file_to_array ( filename) . map_err ( OptionError :: Generic ) ? {
289+ for arg in read_file ( filename) ? {
297290 let arg = prepare_arg ( arg, subst_mappings) ;
298291 has_allow_features_flag |= is_allow_features_flag ( & arg) ;
299292 if let Some ( arg_file) = arg. strip_prefix ( '@' ) {
300- has_allow_features_flag |= process_file ( arg_file, out, subst_mappings, format_err) ?;
293+ has_allow_features_flag |=
294+ process_file ( arg_file, subst_mappings, read_file, write_to_file) ?;
301295 } else {
302- writeln ! ( out , "{ arg}" ) . map_err ( format_err ) ?;
296+ write_to_file ( & arg) ?;
303297 }
304298 }
305299 Ok ( has_allow_features_flag)
306300 }
307- let has_allow_features_flag = process_file ( filename, & mut out , subst_mappings , & format_err ) ?;
308- Ok ( ( expanded_file , has_allow_features_flag) )
301+ let has_allow_features_flag = process_file ( filename, subst_mappings , read_file , write_to_file ) ?;
302+ Ok ( has_allow_features_flag)
309303}
310304
311305/// Apply substitutions to the provided arguments, recursing into param files.
306+ #[ allow( clippy:: type_complexity) ]
312307fn prepare_args (
313308 args : Vec < String > ,
314309 subst_mappings : & [ ( String , String ) ] ,
315310 require_explicit_unstable_features : bool ,
311+ read_file : Option < & mut dyn FnMut ( & str ) -> Result < Vec < String > , OptionError > > ,
312+ mut write_file : Option < & mut dyn FnMut ( & str , & str ) -> Result < ( ) , OptionError > > ,
316313) -> Result < Vec < String > , OptionError > {
317314 let mut allowed_features = false ;
318315 let mut processed_args = Vec :: < String > :: new ( ) ;
316+
317+ let mut read_file_wrapper = |s : & str | read_file_to_array ( s) . map_err ( OptionError :: Generic ) ;
318+ let mut read_file = read_file. unwrap_or ( & mut read_file_wrapper) ;
319+
319320 for arg in args. into_iter ( ) {
320321 let arg = prepare_arg ( arg, subst_mappings) ;
321322 if let Some ( param_file) = arg. strip_prefix ( '@' ) {
323+ let expanded_file = format ! ( "{param_file}.expanded" ) ;
324+ let format_err = |err : io:: Error | {
325+ OptionError :: Generic ( format ! (
326+ "{} writing path: {:?}, current directory: {:?}" ,
327+ err,
328+ expanded_file,
329+ std:: env:: current_dir( )
330+ ) )
331+ } ;
332+
333+ enum Writer < ' f , F : FnMut ( & str , & str ) -> Result < ( ) , OptionError > > {
334+ Function ( & ' f mut F ) ,
335+ BufWriter ( io:: BufWriter < File > ) ,
336+ }
337+ let mut out = match write_file {
338+ Some ( ref mut f) => Writer :: Function ( f) ,
339+ None => Writer :: BufWriter ( io:: BufWriter :: new (
340+ File :: create ( & expanded_file) . map_err ( format_err) ?,
341+ ) ) ,
342+ } ;
343+ let mut write_to_file = |s : & str | -> Result < ( ) , OptionError > {
344+ match out {
345+ Writer :: Function ( ref mut f) => f ( & expanded_file, s) ,
346+ Writer :: BufWriter ( ref mut bw) => writeln ! ( bw, "{s}" ) . map_err ( format_err) ,
347+ }
348+ } ;
349+
322350 // Note that substitutions may also apply to the param file path!
323- let ( file, allowed) = prepare_param_file ( param_file, subst_mappings)
324- . map ( |( filename, af) | ( format ! ( "@{filename}" ) , af) ) ?;
351+ let ( file, allowed) = prepare_param_file (
352+ param_file,
353+ subst_mappings,
354+ & mut read_file,
355+ & mut write_to_file,
356+ )
357+ . map ( |af| ( format ! ( "@{expanded_file}" ) , af) ) ?;
325358 allowed_features |= allowed;
326359 processed_args. push ( file) ;
327360 } else {
@@ -373,7 +406,7 @@ mod test {
373406 fn test_enforce_allow_features_flag_user_didnt_say ( ) {
374407 let args = vec ! [ "rustc" . to_string( ) ] ;
375408 let subst_mappings: Vec < ( String , String ) > = vec ! [ ] ;
376- let args = prepare_args ( args, & subst_mappings) . unwrap ( ) ;
409+ let args = prepare_args ( args, & subst_mappings, true , None , None ) . unwrap ( ) ;
377410 assert_eq ! (
378411 args,
379412 vec![ "rustc" . to_string( ) , "-Zallow-features=" . to_string( ) , ]
@@ -387,7 +420,7 @@ mod test {
387420 "-Zallow-features=whitespace_instead_of_curly_braces" . to_string( ) ,
388421 ] ;
389422 let subst_mappings: Vec < ( String , String ) > = vec ! [ ] ;
390- let args = prepare_args ( args, & subst_mappings) . unwrap ( ) ;
423+ let args = prepare_args ( args, & subst_mappings, true , None , None ) . unwrap ( ) ;
391424 assert_eq ! (
392425 args,
393426 vec![
@@ -396,4 +429,53 @@ mod test {
396429 ]
397430 ) ;
398431 }
432+
433+ #[ test]
434+ fn test_enforce_allow_features_flag_user_requested_something_in_param_file ( ) {
435+ let mut written_files = HashMap :: < String , String > :: new ( ) ;
436+ let mut read_files = HashMap :: < String , Vec < String > > :: new ( ) ;
437+ read_files. insert (
438+ "rustc_params" . to_string ( ) ,
439+ vec ! [ "-Zallow-features=whitespace_instead_of_curly_braces" . to_string( ) ] ,
440+ ) ;
441+
442+ let mut read_file = |filename : & str | -> Result < Vec < String > , OptionError > {
443+ read_files
444+ . get ( filename)
445+ . cloned ( )
446+ . ok_or_else ( || OptionError :: Generic ( format ! ( "file not found: {}" , filename) ) )
447+ } ;
448+ let mut write_file = |filename : & str , content : & str | -> Result < ( ) , OptionError > {
449+ if let Some ( v) = written_files. get_mut ( filename) {
450+ v. push_str ( content) ;
451+ } else {
452+ written_files. insert ( filename. to_owned ( ) , content. to_owned ( ) ) ;
453+ }
454+ Ok ( ( ) )
455+ } ;
456+
457+ let args = vec ! [ "rustc" . to_string( ) , "@rustc_params" . to_string( ) ] ;
458+ let subst_mappings: Vec < ( String , String ) > = vec ! [ ] ;
459+
460+ let args = prepare_args (
461+ args,
462+ & subst_mappings,
463+ true ,
464+ Some ( & mut read_file) ,
465+ Some ( & mut write_file) ,
466+ ) ;
467+
468+ assert_eq ! (
469+ args. unwrap( ) ,
470+ vec![ "rustc" . to_string( ) , "@rustc_params.expanded" . to_string( ) , ]
471+ ) ;
472+
473+ assert_eq ! (
474+ written_files,
475+ HashMap :: <String , String >:: from( [ (
476+ "rustc_params.expanded" . to_string( ) ,
477+ "-Zallow-features=whitespace_instead_of_curly_braces" . to_string( )
478+ ) ] )
479+ ) ;
480+ }
399481}
0 commit comments