@@ -50,6 +50,46 @@ pub(crate) struct GlobalTestOptions {
5050 pub ( crate ) args_file : PathBuf ,
5151}
5252
53+ /// Function used to split command line arguments just like a shell would.
54+ fn split_args ( args : & str ) -> Vec < String > {
55+ let mut out = Vec :: new ( ) ;
56+ let mut iter = args. chars ( ) ;
57+ let mut current = String :: new ( ) ;
58+
59+ while let Some ( c) = iter. next ( ) {
60+ if c == '\\' {
61+ if let Some ( c) = iter. next ( ) {
62+ // If it's escaped, even a quote or a whitespace will be ignored.
63+ current. push ( c) ;
64+ }
65+ } else if c == '"' || c == '\'' {
66+ while let Some ( new_c) = iter. next ( ) {
67+ if new_c == c {
68+ break ;
69+ } else if new_c == '\\' {
70+ if let Some ( c) = iter. next ( ) {
71+ // If it's escaped, even a quote will be ignored.
72+ current. push ( c) ;
73+ }
74+ } else {
75+ current. push ( new_c) ;
76+ }
77+ }
78+ } else if " \n \t \r " . contains ( c) {
79+ if !current. is_empty ( ) {
80+ out. push ( current. clone ( ) ) ;
81+ current. clear ( ) ;
82+ }
83+ } else {
84+ current. push ( c) ;
85+ }
86+ }
87+ if !current. is_empty ( ) {
88+ out. push ( current) ;
89+ }
90+ out
91+ }
92+
5393pub ( crate ) fn generate_args_file ( file_path : & Path , options : & RustdocOptions ) -> Result < ( ) , String > {
5494 let mut file = File :: create ( file_path)
5595 . map_err ( |error| format ! ( "failed to create args file: {error:?}" ) ) ?;
@@ -79,14 +119,7 @@ pub(crate) fn generate_args_file(file_path: &Path, options: &RustdocOptions) ->
79119 }
80120
81121 for compilation_args in & options. doctest_compilation_args {
82- for flag in compilation_args
83- . split_whitespace ( )
84- . map ( |flag| flag. trim ( ) )
85- . filter ( |flag| !flag. is_empty ( ) )
86- {
87- // Very simple parsing implementation. Might be a good idea to correctly handle strings.
88- content. push ( flag. to_string ( ) ) ;
89- }
122+ content. extend ( split_args ( compilation_args) ) ;
90123 }
91124
92125 let content = content. join ( "\n " ) ;
@@ -1003,6 +1036,29 @@ fn doctest_run_fn(
10031036 Ok ( ( ) )
10041037}
10051038
1039+ #[ cfg( test) ]
1040+ #[ test]
1041+ fn check_split_args ( ) {
1042+ fn compare ( input : & str , expected : & [ & str ] ) {
1043+ let output = split_args ( input) ;
1044+ let expected = expected. iter ( ) . map ( |s| s. to_string ( ) ) . collect :: < Vec < _ > > ( ) ;
1045+ assert_eq ! ( expected, output, "test failed for {input:?}" ) ;
1046+ }
1047+
1048+ compare ( "'a' \" b\" c" , & [ "a" , "bc" ] ) ;
1049+ compare ( "'a' \" b \" c d" , & [ "a" , "b c" , "d" ] ) ;
1050+ compare ( "'a' \" b\\ \" c\" " , & [ "a" , "b\" c" ] ) ;
1051+ compare ( "'a\" '" , & [ "a\" " ] ) ;
1052+ compare ( "\" a'\" " , & [ "a'" ] ) ;
1053+ compare ( "\\ a" , & [ " a" ] ) ;
1054+ compare ( "\\ \\ " , & [ "\\ " ] ) ;
1055+ compare ( "a'" , & [ "a" ] ) ;
1056+ compare ( "a " , & [ "a" ] ) ;
1057+ compare ( "a b" , & [ "a" , "b" ] ) ;
1058+ compare ( "a\n \t \r b" , & [ "a" , "b" ] ) ;
1059+ compare ( "a\n \t 1 \r b" , & [ "a" , "1" , "b" ] ) ;
1060+ }
1061+
10061062#[ cfg( test) ] // used in tests
10071063impl DocTestVisitor for Vec < usize > {
10081064 fn visit_test ( & mut self , _test : String , _config : LangString , rel_line : MdRelLine ) {
0 commit comments