Skip to content

Commit 3af8b45

Browse files
authored
Merge pull request #33 from oli-obk/revise_revisions
Revise revisions
2 parents 16cbb46 + ba2d33b commit 3af8b45

25 files changed

+565
-107
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@ their command specifies, or the test will fail without even being run.
3232
* `//@error-pattern: XXX` make sure the stderr output contains `XXX`
3333
* `//@revisions: XXX YYY` runs the test once for each space separated name in the list
3434
* emits one stderr file per revision
35-
* `//~` comments can be restricted to specific revisions by adding the revision name before the `~` in square brackets: `//[XXX]~`
35+
* `//~` comments can be restricted to specific revisions by adding the revision name after the `~` in square brackets: `//~[XXX]`
36+
* `//@` comments can be restricted to specific revisions by adding the revision name after the `@` in square brackets: `//@[XXX]`
37+
* Note that you cannot add revisions to the `revisions` command.
3638
* `//@compile-flags: XXX` appends `XXX` to the command line arguments passed to the rustc driver
3739
* you can specify this multiple times, and all the flags will accumulate
3840
* `//@rustc-env: XXX=YYY` sets the env var `XXX` to `YYY` for the rustc driver execution.

src/lib.rs

Lines changed: 87 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use regex::bytes::Regex;
1515
use rustc_stderr::{Level, Message};
1616
use std::collections::VecDeque;
1717
use std::ffi::OsString;
18-
use std::fmt::{Display, Write as _};
18+
use std::fmt::Display;
1919
use std::io::Write as _;
2020
use std::num::NonZeroUsize;
2121
use std::path::{Path, PathBuf};
@@ -488,28 +488,22 @@ fn parse_and_test_file(path: PathBuf, config: &Config) -> Vec<TestRun> {
488488
}]
489489
}
490490
};
491-
// Ignore file if only/ignore rules do (not) apply
492-
if !test_file_conditions(&comments, config) {
493-
return vec![TestRun {
494-
result: TestResult::Ignored,
495-
path,
496-
revision: "".into(),
497-
}];
498-
}
499491
// Run the test for all revisions
500492
comments
501493
.revisions
502494
.clone()
503495
.unwrap_or_else(|| vec![String::new()])
504496
.into_iter()
505497
.map(|revision| {
506-
let (command, errors, stderr) = run_test(&path, config, &revision, &comments);
507-
508-
// Using a single `eprintln!` to prevent messages from threads from getting intermingled.
509-
let mut msg = format!("{}", path.display());
510-
if !revision.is_empty() {
511-
write!(msg, " (revision `{revision}`) ").unwrap();
498+
// Ignore file if only/ignore rules do (not) apply
499+
if !test_file_conditions(&comments, config, &revision) {
500+
return TestRun {
501+
result: TestResult::Ignored,
502+
path: path.clone(),
503+
revision,
504+
};
512505
}
506+
let (command, errors, stderr) = run_test(&path, config, &revision, &comments);
513507
let result = if errors.is_empty() {
514508
TestResult::Ok
515509
} else {
@@ -569,11 +563,19 @@ fn build_command(path: &Path, config: &Config, revision: &str, comments: &Commen
569563
if !revision.is_empty() {
570564
cmd.arg(format!("--cfg={revision}"));
571565
}
572-
for arg in &comments.compile_flags {
566+
for arg in comments
567+
.for_revision(revision)
568+
.flat_map(|r| r.compile_flags.iter())
569+
{
573570
cmd.arg(arg);
574571
}
575572
cmd.args(config.trailing_args.iter());
576-
cmd.envs(comments.env_vars.iter().map(|(k, v)| (k, v)));
573+
cmd.envs(
574+
comments
575+
.for_revision(revision)
576+
.flat_map(|r| r.env_vars.iter())
577+
.map(|(k, v)| (k, v)),
578+
);
577579

578580
cmd
579581
}
@@ -627,6 +629,7 @@ fn check_test_result(
627629
&config.stderr_filters,
628630
config,
629631
comments,
632+
revision,
630633
);
631634
check_output(
632635
stdout,
@@ -636,6 +639,7 @@ fn check_test_result(
636639
&config.stdout_filters,
637640
config,
638641
comments,
642+
revision,
639643
);
640644
// Check error annotations in the source against output
641645
check_annotations(
@@ -659,7 +663,17 @@ fn check_annotations(
659663
revision: &str,
660664
comments: &Comments,
661665
) {
662-
if let Some((ref error_pattern, definition_line)) = comments.error_pattern {
666+
let error_pattern = comments.find_one_for_revision(
667+
revision,
668+
|r| r.error_pattern.as_ref(),
669+
|(_, line)| {
670+
errors.push(Error::InvalidComment {
671+
msg: "same revision defines pattern twice".into(),
672+
line: *line,
673+
})
674+
},
675+
);
676+
if let Some((error_pattern, definition_line)) = error_pattern {
663677
// first check the diagnostics messages outside of our file. We check this first, so that
664678
// you can mix in-file annotations with //@error-pattern annotations, even if there is overlap
665679
// in the messages.
@@ -671,7 +685,7 @@ fn check_annotations(
671685
} else {
672686
errors.push(Error::PatternNotFound {
673687
pattern: error_pattern.clone(),
674-
definition_line,
688+
definition_line: *definition_line,
675689
});
676690
}
677691
}
@@ -680,20 +694,17 @@ fn check_annotations(
680694
// We will ensure that *all* diagnostics of level at least `lowest_annotation_level`
681695
// are matched.
682696
let mut lowest_annotation_level = Level::Error;
697+
let mut seen_error_match = false;
683698
for &ErrorMatch {
684699
ref pattern,
685-
revision: ref rev,
686700
definition_line,
687701
line,
688702
level,
689-
} in &comments.error_matches
703+
} in comments
704+
.for_revision(revision)
705+
.flat_map(|r| r.error_matches.iter())
690706
{
691-
if let Some(rev) = rev {
692-
if rev != revision {
693-
continue;
694-
}
695-
}
696-
707+
seen_error_match = true;
697708
// If we found a diagnostic with a level annotation, make sure that all
698709
// diagnostics of that level have annotations, even if we don't end up finding a matching diagnostic
699710
// for this pattern.
@@ -715,18 +726,21 @@ fn check_annotations(
715726
});
716727
}
717728

718-
let filter = |msgs: Vec<Message>| -> Vec<_> {
719-
msgs.into_iter()
720-
.filter(|msg| {
721-
msg.level
722-
>= comments
723-
.require_annotations_for_level
724-
.unwrap_or(lowest_annotation_level)
729+
let filter = |mut msgs: Vec<Message>, errors: &mut Vec<_>| -> Vec<_> {
730+
let error = |_| {
731+
errors.push(Error::InvalidComment {
732+
msg: "`require_annotations_for_level` specified twice for same revision".into(),
733+
line: 0,
725734
})
726-
.collect()
735+
};
736+
let required_annotation_level = comments
737+
.find_one_for_revision(revision, |r| r.require_annotations_for_level, error)
738+
.unwrap_or(lowest_annotation_level);
739+
msgs.retain(|msg| msg.level >= required_annotation_level);
740+
msgs
727741
};
728742

729-
let messages_from_unknown_file_or_line = filter(messages_from_unknown_file_or_line);
743+
let messages_from_unknown_file_or_line = filter(messages_from_unknown_file_or_line, errors);
730744
if !messages_from_unknown_file_or_line.is_empty() {
731745
errors.push(Error::ErrorsWithoutPattern {
732746
path: None,
@@ -735,7 +749,7 @@ fn check_annotations(
735749
}
736750

737751
for (line, msgs) in messages.into_iter().enumerate() {
738-
let msgs = filter(msgs);
752+
let msgs = filter(msgs, errors);
739753
if !msgs.is_empty() {
740754
errors.push(Error::ErrorsWithoutPattern {
741755
path: Some((path.to_path_buf(), line)),
@@ -744,10 +758,7 @@ fn check_annotations(
744758
}
745759
}
746760

747-
match (
748-
config.mode,
749-
comments.error_pattern.is_some() || !comments.error_matches.is_empty(),
750-
) {
761+
match (config.mode, error_pattern.is_some() || seen_error_match) {
751762
(Mode::Pass, true) | (Mode::Panic, true) => errors.push(Error::PatternFoundInPassTest),
752763
(
753764
Mode::Fail {
@@ -767,10 +778,11 @@ fn check_output(
767778
filters: &Filter,
768779
config: &Config,
769780
comments: &Comments,
781+
revision: &str,
770782
) {
771783
let target = config.target.as_ref().unwrap();
772-
let output = normalize(path, output, filters, comments);
773-
let path = output_path(path, comments, kind, target);
784+
let output = normalize(path, output, filters, comments, revision);
785+
let path = output_path(path, comments, kind, target, revision);
774786
match config.output_conflict_handling {
775787
OutputConflictHandling::Bless => {
776788
if output.is_empty() {
@@ -793,8 +805,17 @@ fn check_output(
793805
}
794806
}
795807

796-
fn output_path(path: &Path, comments: &Comments, kind: String, target: &str) -> PathBuf {
797-
if comments.stderr_per_bitwidth {
808+
fn output_path(
809+
path: &Path,
810+
comments: &Comments,
811+
kind: String,
812+
target: &str,
813+
revision: &str,
814+
) -> PathBuf {
815+
if comments
816+
.for_revision(revision)
817+
.any(|r| r.stderr_per_bitwidth)
818+
{
798819
return path.with_extension(format!("{}bit.{kind}", get_pointer_width(target)));
799820
}
800821
path.with_extension(kind)
@@ -810,11 +831,18 @@ fn test_condition(condition: &Condition, config: &Config) -> bool {
810831
}
811832

812833
/// Returns whether according to the in-file conditions, this file should be run.
813-
fn test_file_conditions(comments: &Comments, config: &Config) -> bool {
814-
if comments.ignore.iter().any(|c| test_condition(c, config)) {
834+
fn test_file_conditions(comments: &Comments, config: &Config, revision: &str) -> bool {
835+
if comments
836+
.for_revision(revision)
837+
.flat_map(|r| r.ignore.iter())
838+
.any(|c| test_condition(c, config))
839+
{
815840
return false;
816841
}
817-
comments.only.iter().all(|c| test_condition(c, config))
842+
comments
843+
.for_revision(revision)
844+
.flat_map(|r| r.only.iter())
845+
.all(|c| test_condition(c, config))
818846
}
819847

820848
// Taken 1:1 from compiletest-rs
@@ -830,7 +858,13 @@ fn get_pointer_width(triple: &str) -> u8 {
830858
}
831859
}
832860

833-
fn normalize(path: &Path, text: &[u8], filters: &Filter, comments: &Comments) -> Vec<u8> {
861+
fn normalize(
862+
path: &Path,
863+
text: &[u8],
864+
filters: &Filter,
865+
comments: &Comments,
866+
revision: &str,
867+
) -> Vec<u8> {
834868
// Useless paths
835869
let mut text = text.replace(&path.parent().unwrap().display().to_string(), "$DIR");
836870
if let Some(lib_path) = option_env!("RUSTC_LIB_PATH") {
@@ -841,7 +875,10 @@ fn normalize(path: &Path, text: &[u8], filters: &Filter, comments: &Comments) ->
841875
text = regex.replace_all(&text, *replacement).into_owned();
842876
}
843877

844-
for (from, to) in &comments.normalize_stderr {
878+
for (from, to) in comments
879+
.for_revision(revision)
880+
.flat_map(|r| r.normalize_stderr.iter())
881+
{
845882
text = from.replace_all(&text, to).into_owned();
846883
}
847884
text

0 commit comments

Comments
 (0)