@@ -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,88 @@ 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.
312306fn prepare_args (
313307 args : Vec < String > ,
314308 subst_mappings : & [ ( String , String ) ] ,
315309 require_explicit_unstable_features : bool ,
310+ read_file : Option < & mut dyn FnMut ( & str ) -> Result < Vec < String > , OptionError > > ,
311+ mut write_file : Option < & mut dyn FnMut ( & str , & str ) -> Result < ( ) , OptionError > > ,
316312) -> Result < Vec < String > , OptionError > {
317313 let mut allowed_features = false ;
318314 let mut processed_args = Vec :: < String > :: new ( ) ;
315+
316+ let mut read_file_wrapper = |s : & str | read_file_to_array ( s) . map_err ( OptionError :: Generic ) ;
317+ let mut read_file = read_file. unwrap_or ( & mut read_file_wrapper) ;
318+
319319 for arg in args. into_iter ( ) {
320320 let arg = prepare_arg ( arg, subst_mappings) ;
321321 if let Some ( param_file) = arg. strip_prefix ( '@' ) {
322+ let expanded_file = format ! ( "{param_file}.expanded" ) ;
323+ let format_err = |err : io:: Error | {
324+ OptionError :: Generic ( format ! (
325+ "{} writing path: {:?}, current directory: {:?}" ,
326+ err,
327+ expanded_file,
328+ std:: env:: current_dir( )
329+ ) )
330+ } ;
331+
332+ enum Writer < ' f , F : FnMut ( & str , & str ) -> Result < ( ) , OptionError > > {
333+ Function ( & ' f mut F ) ,
334+ BufWriter ( io:: BufWriter < File > ) ,
335+ }
336+ let mut out = match write_file {
337+ Some ( ref mut f) => Writer :: Function ( f) ,
338+ None => Writer :: BufWriter ( io:: BufWriter :: new (
339+ File :: create ( & expanded_file) . map_err ( format_err) ?,
340+ ) ) ,
341+ } ;
342+ let mut write_to_file = |s : & str | -> Result < ( ) , OptionError > {
343+ match out {
344+ Writer :: Function ( ref mut f) => f ( & expanded_file, & s) ,
345+ Writer :: BufWriter ( ref mut bw) => writeln ! ( bw, "{s}" ) . map_err ( format_err) ,
346+ }
347+ } ;
348+
322349 // 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) ) ?;
350+ let ( file, allowed) = prepare_param_file (
351+ param_file,
352+ subst_mappings,
353+ & mut read_file,
354+ & mut write_to_file,
355+ )
356+ . map ( |af| ( format ! ( "@{expanded_file}" ) , af) ) ?;
325357 allowed_features |= allowed;
326358 processed_args. push ( file) ;
327359 } else {
@@ -373,7 +405,7 @@ mod test {
373405 fn test_enforce_allow_features_flag_user_didnt_say ( ) {
374406 let args = vec ! [ "rustc" . to_string( ) ] ;
375407 let subst_mappings: Vec < ( String , String ) > = vec ! [ ] ;
376- let args = prepare_args ( args, & subst_mappings) . unwrap ( ) ;
408+ let args = prepare_args ( args, & subst_mappings, true , None , None ) . unwrap ( ) ;
377409 assert_eq ! (
378410 args,
379411 vec![ "rustc" . to_string( ) , "-Zallow-features=" . to_string( ) , ]
@@ -387,7 +419,7 @@ mod test {
387419 "-Zallow-features=whitespace_instead_of_curly_braces" . to_string( ) ,
388420 ] ;
389421 let subst_mappings: Vec < ( String , String ) > = vec ! [ ] ;
390- let args = prepare_args ( args, & subst_mappings) . unwrap ( ) ;
422+ let args = prepare_args ( args, & subst_mappings, true , None , None ) . unwrap ( ) ;
391423 assert_eq ! (
392424 args,
393425 vec![
@@ -396,4 +428,53 @@ mod test {
396428 ]
397429 ) ;
398430 }
431+
432+ #[ test]
433+ fn test_enforce_allow_features_flag_user_requested_something_in_param_file ( ) {
434+ let mut written_files = HashMap :: < String , String > :: new ( ) ;
435+ let mut read_files = HashMap :: < String , Vec < String > > :: new ( ) ;
436+ read_files. insert (
437+ "rustc_params" . to_string ( ) ,
438+ vec ! [ "-Zallow-features=whitespace_instead_of_curly_braces" . to_string( ) ] ,
439+ ) ;
440+
441+ let mut read_file = |filename : & str | -> Result < Vec < String > , OptionError > {
442+ read_files
443+ . get ( filename)
444+ . map ( Vec :: clone)
445+ . ok_or_else ( || OptionError :: Generic ( format ! ( "file not found: {}" , filename) ) )
446+ } ;
447+ let mut write_file = |filename : & str , content : & str | -> Result < ( ) , OptionError > {
448+ if let Some ( v) = written_files. get_mut ( filename) {
449+ v. push_str ( content) ;
450+ } else {
451+ written_files. insert ( filename. to_owned ( ) , content. to_owned ( ) ) ;
452+ }
453+ Ok ( ( ) )
454+ } ;
455+
456+ let args = vec ! [ "rustc" . to_string( ) , "@rustc_params" . to_string( ) ] ;
457+ let subst_mappings: Vec < ( String , String ) > = vec ! [ ] ;
458+
459+ let args = prepare_args (
460+ args,
461+ & subst_mappings,
462+ true ,
463+ Some ( & mut read_file) ,
464+ Some ( & mut write_file) ,
465+ ) ;
466+
467+ assert_eq ! (
468+ args. unwrap( ) ,
469+ vec![ "rustc" . to_string( ) , "@rustc_params.expanded" . to_string( ) , ]
470+ ) ;
471+
472+ assert_eq ! (
473+ written_files,
474+ HashMap :: <String , String >:: from( [ (
475+ "rustc_params.expanded" . to_string( ) ,
476+ "-Zallow-features=whitespace_instead_of_curly_braces" . to_string( )
477+ ) ] )
478+ ) ;
479+ }
399480}
0 commit comments