@@ -50,6 +50,46 @@ pub(crate) struct GlobalTestOptions {
50
50
pub ( crate ) args_file : PathBuf ,
51
51
}
52
52
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
+
53
93
pub ( crate ) fn generate_args_file ( file_path : & Path , options : & RustdocOptions ) -> Result < ( ) , String > {
54
94
let mut file = File :: create ( file_path)
55
95
. 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) ->
79
119
}
80
120
81
121
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) ) ;
90
123
}
91
124
92
125
let content = content. join ( "\n " ) ;
@@ -1003,6 +1036,29 @@ fn doctest_run_fn(
1003
1036
Ok ( ( ) )
1004
1037
}
1005
1038
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
+
1006
1062
#[ cfg( test) ] // used in tests
1007
1063
impl DocTestVisitor for Vec < usize > {
1008
1064
fn visit_test ( & mut self , _test : String , _config : LangString , rel_line : MdRelLine ) {
0 commit comments