@@ -5,39 +5,97 @@ use std::io;
55
66use rustc_session:: EarlyErrorHandler ;
77
8- fn arg_expand ( arg : String ) -> Result < Vec < String > , Error > {
9- if let Some ( path) = arg. strip_prefix ( '@' ) {
10- let file = match fs:: read_to_string ( path) {
11- Ok ( file) => file,
12- Err ( ref err) if err. kind ( ) == io:: ErrorKind :: InvalidData => {
13- return Err ( Error :: Utf8Error ( Some ( path. to_string ( ) ) ) ) ;
8+ /// Expands argfiles in command line arguments.
9+ #[ derive( Default ) ]
10+ struct Expander {
11+ shell_argfiles : bool ,
12+ next_is_unstable_option : bool ,
13+ expanded : Vec < String > ,
14+ }
15+
16+ impl Expander {
17+ /// Handles the next argument. If the argument is an argfile, it is expanded
18+ /// inline.
19+ fn arg ( & mut self , arg : & str ) -> Result < ( ) , Error > {
20+ if let Some ( argfile) = arg. strip_prefix ( '@' ) {
21+ match argfile. split_once ( ':' ) {
22+ Some ( ( "shell" , path) ) if self . shell_argfiles => {
23+ shlex:: split ( & Self :: read_file ( path) ?)
24+ . ok_or_else ( || Error :: ShellParseError ( path. to_string ( ) ) ) ?
25+ . into_iter ( )
26+ . for_each ( |arg| self . push ( arg) ) ;
27+ }
28+ _ => {
29+ let contents = Self :: read_file ( argfile) ?;
30+ contents. lines ( ) . for_each ( |arg| self . push ( arg. to_string ( ) ) ) ;
31+ }
32+ }
33+ } else {
34+ self . push ( arg. to_string ( ) ) ;
35+ }
36+
37+ Ok ( ( ) )
38+ }
39+
40+ /// Adds a command line argument verbatim with no argfile expansion.
41+ fn push ( & mut self , arg : String ) {
42+ if self . next_is_unstable_option {
43+ self . inspect_unstable_option ( & arg) ;
44+ self . next_is_unstable_option = false ;
45+ } else if let Some ( unstable_option) = arg. strip_prefix ( "-Z" ) {
46+ if unstable_option. is_empty ( ) {
47+ self . next_is_unstable_option = true ;
48+ } else {
49+ self . inspect_unstable_option ( unstable_option) ;
50+ }
51+ }
52+
53+ self . expanded . push ( arg) ;
54+ }
55+
56+ /// Consumes the `Expander`, returning the expanded arguments.
57+ fn finish ( self ) -> Vec < String > {
58+ self . expanded
59+ }
60+
61+ /// Parses any relevant unstable flags specified on the command line.
62+ fn inspect_unstable_option ( & mut self , option : & str ) {
63+ match option {
64+ "shell-argfiles" => self . shell_argfiles = true ,
65+ _ => ( ) ,
66+ }
67+ }
68+
69+ /// Reads the contents of a file as UTF-8.
70+ fn read_file ( path : & str ) -> Result < String , Error > {
71+ fs:: read_to_string ( path) . map_err ( |e| {
72+ if e. kind ( ) == io:: ErrorKind :: InvalidData {
73+ Error :: Utf8Error ( Some ( path. to_string ( ) ) )
74+ } else {
75+ Error :: IOError ( path. to_string ( ) , e)
1476 }
15- Err ( err) => return Err ( Error :: IOError ( path. to_string ( ) , err) ) ,
16- } ;
17- Ok ( file. lines ( ) . map ( ToString :: to_string) . collect ( ) )
18- } else {
19- Ok ( vec ! [ arg] )
77+ } )
2078 }
2179}
2280
2381/// **Note:** This function doesn't interpret argument 0 in any special way.
2482/// If this function is intended to be used with command line arguments,
2583/// `argv[0]` must be removed prior to calling it manually.
2684pub fn arg_expand_all ( handler : & EarlyErrorHandler , at_args : & [ String ] ) -> Vec < String > {
27- let mut args = Vec :: new ( ) ;
85+ let mut expander = Expander :: default ( ) ;
2886 for arg in at_args {
29- match arg_expand ( arg. clone ( ) ) {
30- Ok ( arg) => args. extend ( arg) ,
31- Err ( err) => handler. early_error ( format ! ( "Failed to load argument file: {err}" ) ) ,
87+ if let Err ( err) = expander. arg ( arg) {
88+ handler. early_error ( format ! ( "Failed to load argument file: {err}" ) ) ;
3289 }
3390 }
34- args
91+ expander . finish ( )
3592}
3693
3794#[ derive( Debug ) ]
3895pub enum Error {
3996 Utf8Error ( Option < String > ) ,
4097 IOError ( String , io:: Error ) ,
98+ ShellParseError ( String ) ,
4199}
42100
43101impl fmt:: Display for Error {
@@ -46,6 +104,7 @@ impl fmt::Display for Error {
46104 Error :: Utf8Error ( None ) => write ! ( fmt, "Utf8 error" ) ,
47105 Error :: Utf8Error ( Some ( path) ) => write ! ( fmt, "Utf8 error in {path}" ) ,
48106 Error :: IOError ( path, err) => write ! ( fmt, "IO Error: {path}: {err}" ) ,
107+ Error :: ShellParseError ( path) => write ! ( fmt, "Invalid shell-style arguments in {path}" ) ,
49108 }
50109 }
51110}
0 commit comments