@@ -15,6 +15,7 @@ use crate::directives::auxiliary::{AuxProps, parse_and_update_aux};
1515use  crate :: directives:: directive_names:: { 
1616    KNOWN_DIRECTIVE_NAMES ,  KNOWN_HTMLDOCCK_DIRECTIVE_NAMES ,  KNOWN_JSONDOCCK_DIRECTIVE_NAMES , 
1717} ; 
18+ use  crate :: directives:: line:: { DirectiveLine ,  line_directive} ; 
1819use  crate :: directives:: needs:: CachedNeedsConditions ; 
1920use  crate :: edition:: { Edition ,  parse_edition} ; 
2021use  crate :: errors:: ErrorKind ; 
@@ -25,6 +26,7 @@ use crate::{fatal, help};
2526pub ( crate )  mod  auxiliary; 
2627mod  cfg; 
2728mod  directive_names; 
29+ mod  line; 
2830mod  needs; 
2931#[ cfg( test) ]  
3032mod  tests; 
@@ -824,70 +826,6 @@ impl TestProps {
824826    } 
825827} 
826828
827- /// If the given line begins with the appropriate comment prefix for a directive, 
828- /// returns a struct containing various parts of the directive. 
829- fn  line_directive < ' line > ( 
830-     line_number :  usize , 
831-     original_line :  & ' line  str , 
832- )  -> Option < DirectiveLine < ' line > >  { 
833-     // Ignore lines that don't start with the comment prefix. 
834-     let  after_comment =
835-         original_line. trim_start ( ) . strip_prefix ( COMPILETEST_DIRECTIVE_PREFIX ) ?. trim_start ( ) ; 
836- 
837-     let  revision; 
838-     let  raw_directive; 
839- 
840-     if  let  Some ( after_open_bracket)  = after_comment. strip_prefix ( '[' )  { 
841-         // A comment like `//@[foo]` only applies to revision `foo`. 
842-         let  Some ( ( line_revision,  after_close_bracket) )  = after_open_bracket. split_once ( ']' )  else  { 
843-             panic ! ( 
844-                 "malformed condition directive: expected `{COMPILETEST_DIRECTIVE_PREFIX}[foo]`, found `{original_line}`" 
845-             ) 
846-         } ; 
847- 
848-         revision = Some ( line_revision) ; 
849-         raw_directive = after_close_bracket. trim_start ( ) ; 
850-     }  else  { 
851-         revision = None ; 
852-         raw_directive = after_comment; 
853-     } ; 
854- 
855-     Some ( DirectiveLine  {  line_number,  revision,  raw_directive } ) 
856- } 
857- 
858- /// The (partly) broken-down contents of a line containing a test directive, 
859- /// which [`iter_directives`] passes to its callback function. 
860- /// 
861- /// For example: 
862- /// 
863- /// ```text 
864- /// //@ compile-flags: -O 
865- ///     ^^^^^^^^^^^^^^^^^ raw_directive 
866- /// 
867- /// //@ [foo] compile-flags: -O 
868- ///      ^^^                    revision 
869- ///           ^^^^^^^^^^^^^^^^^ raw_directive 
870- /// ``` 
871- struct  DirectiveLine < ' ln >  { 
872-     line_number :  usize , 
873-     /// Some test directives start with a revision name in square brackets 
874-      /// (e.g. `[foo]`), and only apply to that revision of the test. 
875-      /// If present, this field contains the revision name (e.g. `foo`). 
876-      revision :  Option < & ' ln  str > , 
877-     /// The main part of the directive, after removing the comment prefix 
878-      /// and the optional revision specifier. 
879-      /// 
880-      /// This is "raw" because the directive's name and colon-separated value 
881-      /// (if present) have not yet been extracted or checked. 
882-      raw_directive :  & ' ln  str , 
883- } 
884- 
885- impl < ' ln >  DirectiveLine < ' ln >  { 
886-     fn  applies_to_test_revision ( & self ,  test_revision :  Option < & str > )  -> bool  { 
887-         self . revision . is_none ( )  || self . revision  == test_revision
888-     } 
889- } 
890- 
891829pub ( crate )  struct  CheckDirectiveResult < ' ln >  { 
892830    is_known_directive :  bool , 
893831    trailing_directive :  Option < & ' ln  str > , 
@@ -897,9 +835,7 @@ fn check_directive<'a>(
897835    directive_ln :  & DirectiveLine < ' a > , 
898836    mode :  TestMode , 
899837)  -> CheckDirectiveResult < ' a >  { 
900-     let  & DirectiveLine  {  raw_directive :  directive_ln,  .. }  = directive_ln; 
901- 
902-     let  ( directive_name,  post)  = directive_ln. split_once ( [ ':' ,  ' ' ] ) . unwrap_or ( ( directive_ln,  "" ) ) ; 
838+     let  & DirectiveLine  {  name :  directive_name,  .. }  = directive_ln; 
903839
904840    let  is_known_directive = KNOWN_DIRECTIVE_NAMES . contains ( & directive_name) 
905841        || match  mode { 
@@ -908,20 +844,17 @@ fn check_directive<'a>(
908844            _ => false , 
909845        } ; 
910846
911-     let  trailing = post. trim ( ) . split_once ( ' ' ) . map ( |( pre,  _) | pre) . unwrap_or ( post) ; 
912-     let  trailing_directive = { 
913-         // 1. is the directive name followed by a space? (to exclude `:`) 
914-         directive_ln. get ( directive_name. len ( ) ..) . is_some_and ( |s| s. starts_with ( ' ' ) ) 
915-             // 2. is what is after that directive also a directive (ex: "only-x86 only-arm") 
916-             && KNOWN_DIRECTIVE_NAMES . contains ( & trailing) 
917-     } 
918-     . then_some ( trailing) ; 
847+     // If it looks like the user tried to put two directives on the same line 
848+     // (e.g. `//@ only-linux only-x86_64`), signal an error, because the 
849+     // second "directive" would actually be ignored with no effect. 
850+     let  trailing_directive = directive_ln
851+         . remark_after_space ( ) 
852+         . map ( |remark| remark. trim_start ( ) . split ( ' ' ) . next ( ) . unwrap ( ) ) 
853+         . filter ( |token| KNOWN_DIRECTIVE_NAMES . contains ( token) ) ; 
919854
920855    CheckDirectiveResult  {  is_known_directive,  trailing_directive } 
921856} 
922857
923- const  COMPILETEST_DIRECTIVE_PREFIX :  & str  = "//@" ; 
924- 
925858fn  iter_directives ( 
926859    mode :  TestMode , 
927860    poisoned :  & mut  bool , 
@@ -939,15 +872,17 @@ fn iter_directives(
939872    // FIXME(jieyouxu): I feel like there's a better way to do this, leaving for later. 
940873    if  mode == TestMode :: CoverageRun  { 
941874        let  extra_directives:  & [ & str ]  = & [ 
942-             "needs-profiler-runtime" , 
875+             "//@  needs-profiler-runtime" , 
943876            // FIXME(pietroalbini): this test currently does not work on cross-compiled targets 
944877            // because remote-test is not capable of sending back the *.profraw files generated by 
945878            // the LLVM instrumentation. 
946-             "ignore-cross-compile" , 
879+             "//@  ignore-cross-compile" , 
947880        ] ; 
948881        // Process the extra implied directives, with a dummy line number of 0. 
949-         for  raw_directive in  extra_directives { 
950-             it ( DirectiveLine  {  line_number :  0 ,  revision :  None ,  raw_directive } ) ; 
882+         for  directive_str in  extra_directives { 
883+             let  directive_line = line_directive ( 0 ,  directive_str) 
884+                 . unwrap_or_else ( || panic ! ( "bad extra-directive line: {directive_str:?}" ) ) ; 
885+             it ( directive_line) ; 
951886        } 
952887    } 
953888
@@ -977,7 +912,7 @@ fn iter_directives(
977912
978913                error ! ( 
979914                    "{testfile}:{line_number}: detected unknown compiletest test directive `{}`" , 
980-                     directive_line. raw_directive , 
915+                     directive_line. display ( ) , 
981916                ) ; 
982917
983918                return ; 
@@ -1073,35 +1008,30 @@ impl Config {
10731008    } 
10741009
10751010    fn  parse_custom_normalization ( & self ,  line :  & DirectiveLine < ' _ > )  -> Option < NormalizeRule >  { 
1076-         let  & DirectiveLine  {  raw_directive,  .. }  = line; 
1077- 
1078-         // FIXME(Zalathar): Integrate name/value splitting into `DirectiveLine` 
1079-         // instead of doing it here. 
1080-         let  ( directive_name,  raw_value)  = raw_directive. split_once ( ':' ) ?; 
1011+         let  & DirectiveLine  {  name,  .. }  = line; 
10811012
1082-         let  kind = match  directive_name  { 
1013+         let  kind = match  name  { 
10831014            "normalize-stdout"  => NormalizeKind :: Stdout , 
10841015            "normalize-stderr"  => NormalizeKind :: Stderr , 
10851016            "normalize-stderr-32bit"  => NormalizeKind :: Stderr32bit , 
10861017            "normalize-stderr-64bit"  => NormalizeKind :: Stderr64bit , 
10871018            _ => return  None , 
10881019        } ; 
10891020
1090-         let  Some ( ( regex,  replacement) )  = parse_normalize_rule ( raw_value)  else  { 
1091-             error ! ( "couldn't parse custom normalization rule: `{raw_directive}`" ) ; 
1092-             help ! ( "expected syntax is: `{directive_name}: \" REGEX\"  -> \" REPLACEMENT\" `" ) ; 
1021+         let  Some ( ( regex,  replacement) )  = line. value_after_colon ( ) . and_then ( parse_normalize_rule) 
1022+         else  { 
1023+             error ! ( "couldn't parse custom normalization rule: `{}`" ,  line. display( ) ) ; 
1024+             help ! ( "expected syntax is: `{name}: \" REGEX\"  -> \" REPLACEMENT\" `" ) ; 
10931025            panic ! ( "invalid normalization rule detected" ) ; 
10941026        } ; 
10951027        Some ( NormalizeRule  {  kind,  regex,  replacement } ) 
10961028    } 
10971029
10981030    fn  parse_name_directive ( & self ,  line :  & DirectiveLine < ' _ > ,  directive :  & str )  -> bool  { 
1099-         let  & DirectiveLine  {  raw_directive :  line,  .. }  = line; 
1100- 
1101-         // Ensure the directive is a whole word. Do not match "ignore-x86" when 
1102-         // the line says "ignore-x86_64". 
1103-         line. starts_with ( directive) 
1104-             && matches ! ( line. as_bytes( ) . get( directive. len( ) ) ,  None  | Some ( & b' ' )  | Some ( & b':' ) ) 
1031+         // FIXME(Zalathar): Ideally, this should raise an error if a name-only 
1032+         // directive is followed by a colon, since that's the wrong syntax. 
1033+         // But we would need to fix tests that rely on the current behaviour. 
1034+         line. name  == directive
11051035    } 
11061036
11071037    fn  parse_name_value_directive ( 
@@ -1110,22 +1040,26 @@ impl Config {
11101040        directive :  & str , 
11111041        testfile :  & Utf8Path , 
11121042    )  -> Option < String >  { 
1113-         let  & DirectiveLine  {  line_number,  raw_directive :  line,  .. }  = line; 
1114- 
1115-         let  colon = directive. len ( ) ; 
1116-         if  line. starts_with ( directive)  && line. as_bytes ( ) . get ( colon)  == Some ( & b':' )  { 
1117-             let  value = line[ ( colon + 1 ) ..] . to_owned ( ) ; 
1118-             debug ! ( "{}: {}" ,  directive,  value) ; 
1119-             let  value = expand_variables ( value,  self ) ; 
1120-             if  value. is_empty ( )  { 
1121-                 error ! ( "{testfile}:{line_number}: empty value for directive `{directive}`" ) ; 
1122-                 help ! ( "expected syntax is: `{directive}: value`" ) ; 
1123-                 panic ! ( "empty directive value detected" ) ; 
1124-             } 
1125-             Some ( value) 
1126-         }  else  { 
1127-             None 
1043+         let  & DirectiveLine  {  line_number,  .. }  = line; 
1044+ 
1045+         if  line. name  != directive { 
1046+             return  None ; 
1047+         } ; 
1048+ 
1049+         // FIXME(Zalathar): This silently discards directives with a matching 
1050+         // name but no colon. Unfortunately, some directives (e.g. "pp-exact") 
1051+         // currently rely on _not_ panicking here. 
1052+         let  value = line. value_after_colon ( ) ?; 
1053+         debug ! ( "{}: {}" ,  directive,  value) ; 
1054+         let  value = expand_variables ( value. to_owned ( ) ,  self ) ; 
1055+ 
1056+         if  value. is_empty ( )  { 
1057+             error ! ( "{testfile}:{line_number}: empty value for directive `{directive}`" ) ; 
1058+             help ! ( "expected syntax is: `{directive}: value`" ) ; 
1059+             panic ! ( "empty directive value detected" ) ; 
11281060        } 
1061+ 
1062+         Some ( value) 
11291063    } 
11301064
11311065    fn  set_name_directive ( & self ,  line :  & DirectiveLine < ' _ > ,  directive :  & str ,  value :  & mut  bool )  { 
@@ -1515,14 +1449,14 @@ pub(crate) fn make_test_description<R: Read>(
15151449} 
15161450
15171451fn  ignore_cdb ( config :  & Config ,  line :  & DirectiveLine < ' _ > )  -> IgnoreDecision  { 
1518-     let  & DirectiveLine  {  raw_directive :  line,  .. }  = line; 
1519- 
15201452    if  config. debugger  != Some ( Debugger :: Cdb )  { 
15211453        return  IgnoreDecision :: Continue ; 
15221454    } 
15231455
15241456    if  let  Some ( actual_version)  = config. cdb_version  { 
1525-         if  let  Some ( rest)  = line. strip_prefix ( "min-cdb-version:" ) . map ( str:: trim)  { 
1457+         if  line. name  == "min-cdb-version" 
1458+             && let  Some ( rest)  = line. value_after_colon ( ) . map ( str:: trim) 
1459+         { 
15261460            let  min_version = extract_cdb_version ( rest) . unwrap_or_else ( || { 
15271461                panic ! ( "couldn't parse version range: {:?}" ,  rest) ; 
15281462            } ) ; 
@@ -1540,14 +1474,14 @@ fn ignore_cdb(config: &Config, line: &DirectiveLine<'_>) -> IgnoreDecision {
15401474} 
15411475
15421476fn  ignore_gdb ( config :  & Config ,  line :  & DirectiveLine < ' _ > )  -> IgnoreDecision  { 
1543-     let  & DirectiveLine  {  raw_directive :  line,  .. }  = line; 
1544- 
15451477    if  config. debugger  != Some ( Debugger :: Gdb )  { 
15461478        return  IgnoreDecision :: Continue ; 
15471479    } 
15481480
15491481    if  let  Some ( actual_version)  = config. gdb_version  { 
1550-         if  let  Some ( rest)  = line. strip_prefix ( "min-gdb-version:" ) . map ( str:: trim)  { 
1482+         if  line. name  == "min-gdb-version" 
1483+             && let  Some ( rest)  = line. value_after_colon ( ) . map ( str:: trim) 
1484+         { 
15511485            let  ( start_ver,  end_ver)  = extract_version_range ( rest,  extract_gdb_version) 
15521486                . unwrap_or_else ( || { 
15531487                    panic ! ( "couldn't parse version range: {:?}" ,  rest) ; 
@@ -1563,7 +1497,9 @@ fn ignore_gdb(config: &Config, line: &DirectiveLine<'_>) -> IgnoreDecision {
15631497                    reason :  format ! ( "ignored when the GDB version is lower than {rest}" ) , 
15641498                } ; 
15651499            } 
1566-         }  else  if  let  Some ( rest)  = line. strip_prefix ( "ignore-gdb-version:" ) . map ( str:: trim)  { 
1500+         }  else  if  line. name  == "ignore-gdb-version" 
1501+             && let  Some ( rest)  = line. value_after_colon ( ) . map ( str:: trim) 
1502+         { 
15671503            let  ( min_version,  max_version)  = extract_version_range ( rest,  extract_gdb_version) 
15681504                . unwrap_or_else ( || { 
15691505                    panic ! ( "couldn't parse version range: {:?}" ,  rest) ; 
@@ -1590,14 +1526,14 @@ fn ignore_gdb(config: &Config, line: &DirectiveLine<'_>) -> IgnoreDecision {
15901526} 
15911527
15921528fn  ignore_lldb ( config :  & Config ,  line :  & DirectiveLine < ' _ > )  -> IgnoreDecision  { 
1593-     let  & DirectiveLine  {  raw_directive :  line,  .. }  = line; 
1594- 
15951529    if  config. debugger  != Some ( Debugger :: Lldb )  { 
15961530        return  IgnoreDecision :: Continue ; 
15971531    } 
15981532
15991533    if  let  Some ( actual_version)  = config. lldb_version  { 
1600-         if  let  Some ( rest)  = line. strip_prefix ( "min-lldb-version:" ) . map ( str:: trim)  { 
1534+         if  line. name  == "min-lldb-version" 
1535+             && let  Some ( rest)  = line. value_after_colon ( ) . map ( str:: trim) 
1536+         { 
16011537            let  min_version = rest. parse ( ) . unwrap_or_else ( |e| { 
16021538                panic ! ( "Unexpected format of LLDB version string: {}\n {:?}" ,  rest,  e) ; 
16031539            } ) ; 
0 commit comments