@@ -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