Skip to content

Commit d230fc8

Browse files
committed
Allow //~| comments to preceed //~v comments.
Disallow `//~|` commented to follow `//~v` and `//~` comments.
1 parent 3e63003 commit d230fc8

17 files changed

+489
-23
lines changed

src/parser.rs

Lines changed: 104 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,15 @@ pub(crate) enum ErrorMatchKind {
226226
Code(Spanned<String>),
227227
}
228228

229+
impl ErrorMatchKind {
230+
fn span(&self) -> &Span {
231+
match self {
232+
Self::Pattern { pattern, .. } => &pattern.span,
233+
Self::Code(code) => &code.span,
234+
}
235+
}
236+
}
237+
229238
#[derive(Debug, Clone)]
230239
pub(crate) struct ErrorMatch {
231240
pub(crate) kind: ErrorMatchKind,
@@ -254,6 +263,21 @@ impl Condition {
254263
}
255264
}
256265

266+
enum ParsePatternResult {
267+
Other,
268+
ErrorAbove {
269+
match_line: NonZeroUsize,
270+
},
271+
ErrorBelow {
272+
span: Span,
273+
match_line: NonZeroUsize,
274+
},
275+
Fallthrough {
276+
span: Span,
277+
idx: usize,
278+
},
279+
}
280+
257281
impl Comments {
258282
pub(crate) fn parse_file(
259283
comments: Comments,
@@ -279,6 +303,7 @@ impl Comments {
279303

280304
let defaults = std::mem::take(parser.comments.revisioned.get_mut(&[][..]).unwrap());
281305

306+
let mut delayed_fallthrough = Vec::new();
282307
let mut fallthrough_to = None; // The line that a `|` will refer to.
283308
let mut last_line = 0;
284309
for (l, line) in content.as_ref().lines().enumerate() {
@@ -291,8 +316,37 @@ impl Comments {
291316
col_start: NonZeroUsize::new(1).unwrap(),
292317
col_end: NonZeroUsize::new(line.chars().count() + 1).unwrap(),
293318
};
294-
match parser.parse_checked_line(&mut fallthrough_to, Spanned::new(line, span)) {
295-
Ok(()) => {}
319+
match parser.parse_checked_line(fallthrough_to, Spanned::new(line, span)) {
320+
Ok(ParsePatternResult::Other) => {
321+
fallthrough_to = None;
322+
}
323+
Ok(ParsePatternResult::ErrorAbove { match_line }) => {
324+
fallthrough_to = Some(match_line);
325+
}
326+
Ok(ParsePatternResult::Fallthrough { span, idx }) => {
327+
delayed_fallthrough.push((span, l, idx));
328+
}
329+
Ok(ParsePatternResult::ErrorBelow { span, match_line }) => {
330+
if fallthrough_to.is_some() {
331+
parser.error(
332+
span,
333+
"`//~v` comment immediately following a `//~^` comment chain",
334+
);
335+
}
336+
337+
for (span, line, idx) in delayed_fallthrough.drain(..) {
338+
if let Some(rev) = parser
339+
.comments
340+
.revisioned
341+
.values_mut()
342+
.find(|rev| rev.error_matches[idx].kind.span().line_start == line)
343+
{
344+
rev.error_matches[idx].line = match_line;
345+
} else {
346+
parser.error(span, "`//~|` comment not attached to anchoring matcher");
347+
}
348+
}
349+
}
296350
Err(e) => parser.error(e.span, format!("Comment is not utf8: {:?}", e.content)),
297351
}
298352
}
@@ -337,6 +391,10 @@ impl Comments {
337391
}
338392
}
339393

394+
for (span, ..) in delayed_fallthrough {
395+
parser.error(span, "`//~|` comment not attached to anchoring matcher");
396+
}
397+
340398
let Revisioned {
341399
span,
342400
ignore,
@@ -397,18 +455,18 @@ impl Comments {
397455
impl CommentParser<Comments> {
398456
fn parse_checked_line(
399457
&mut self,
400-
fallthrough_to: &mut Option<NonZeroUsize>,
458+
fallthrough_to: Option<NonZeroUsize>,
401459
line: Spanned<&[u8]>,
402-
) -> std::result::Result<(), Spanned<Utf8Error>> {
460+
) -> std::result::Result<ParsePatternResult, Spanned<Utf8Error>> {
461+
let mut res = ParsePatternResult::Other;
403462
if let Some(command) = line.strip_prefix(b"//@") {
404463
self.parse_command(command.to_str()?.trim())
405464
} else if let Some((_, pattern)) = line.split_once_str("//~") {
406465
let (revisions, pattern) = self.parse_revisions(pattern.to_str()?);
407466
self.revisioned(revisions, |this| {
408-
this.parse_pattern(pattern, fallthrough_to)
467+
res = this.parse_pattern(pattern, fallthrough_to);
409468
})
410469
} else {
411-
*fallthrough_to = None;
412470
for pos in line.clone().find_iter("//") {
413471
let (_, rest) = line.clone().to_str()?.split_at(pos + 2);
414472
for rest in std::iter::once(rest.clone()).chain(rest.strip_prefix(" ")) {
@@ -448,7 +506,7 @@ impl CommentParser<Comments> {
448506
}
449507
}
450508
}
451-
Ok(())
509+
Ok(res)
452510
}
453511
}
454512

@@ -835,15 +893,27 @@ impl CommentParser<&mut Revisioned> {
835893
// (\[[a-z]+(,[a-z]+)*\])?
836894
// (?P<offset>\||[\^]+)? *
837895
// ((?P<level>ERROR|HELP|WARN|NOTE): (?P<text>.*))|(?P<code>[a-z0-9_:]+)
838-
fn parse_pattern(&mut self, pattern: Spanned<&str>, fallthrough_to: &mut Option<NonZeroUsize>) {
896+
fn parse_pattern(
897+
&mut self,
898+
pattern: Spanned<&str>,
899+
fallthrough_to: Option<NonZeroUsize>,
900+
) -> ParsePatternResult {
839901
let c = pattern.chars().next();
902+
let mut res = ParsePatternResult::Other;
903+
840904
let (match_line, pattern) = match c {
841905
Some(Spanned { content: '|', span }) => (
842906
match fallthrough_to {
843-
Some(fallthrough) => *fallthrough,
907+
Some(match_line) => {
908+
res = ParsePatternResult::ErrorAbove { match_line };
909+
match_line
910+
}
844911
None => {
845-
self.error(span, "`//~|` pattern without preceding line");
846-
return;
912+
res = ParsePatternResult::Fallthrough {
913+
span,
914+
idx: self.error_matches.len(),
915+
};
916+
pattern.span.line_start
847917
}
848918
},
849919
pattern.split_at(1).1,
@@ -862,14 +932,19 @@ impl CommentParser<&mut Revisioned> {
862932
{
863933
// lines are one-indexed, so a target line of 0 is invalid, but also
864934
// prevented via `NonZeroUsize`
865-
Some(match_line) => (match_line, pattern.split_at(offset).1),
935+
Some(match_line) => {
936+
res = ParsePatternResult::ErrorAbove { match_line };
937+
(match_line, pattern.split_at(offset).1)
938+
}
866939
_ => {
867940
self.error(pattern.span(), format!(
868941
"//~^ pattern is trying to refer to {} lines above, but there are only {} lines above",
869942
offset,
870943
pattern.line().get() - 1,
871944
));
872-
return;
945+
return ParsePatternResult::ErrorAbove {
946+
match_line: pattern.span().line_start,
947+
};
873948
}
874949
}
875950
}
@@ -885,22 +960,31 @@ impl CommentParser<&mut Revisioned> {
885960
.checked_add(offset)
886961
.and_then(NonZeroUsize::new)
887962
{
888-
Some(match_line) => (match_line, pattern.split_at(offset).1),
963+
Some(match_line) => {
964+
res = ParsePatternResult::ErrorBelow {
965+
span: pattern.span(),
966+
match_line,
967+
};
968+
(match_line, pattern.split_at(offset).1)
969+
}
889970
_ => {
890971
// The line count of the file is not yet known so we can only check
891972
// if the resulting line is in the range of a usize.
892973
self.error(pattern.span(), format!(
893974
"//~v pattern is trying to refer to {} lines below, which is more than ui_test can count",
894975
offset,
895976
));
896-
return;
977+
return ParsePatternResult::ErrorBelow {
978+
span: pattern.span(),
979+
match_line: pattern.span().line_start,
980+
};
897981
}
898982
}
899983
}
900984
Some(_) => (pattern.span().line_start, pattern),
901985
None => {
902986
self.error(pattern.span(), "no pattern specified");
903-
return;
987+
return res;
904988
}
905989
};
906990

@@ -916,7 +1000,7 @@ impl CommentParser<&mut Revisioned> {
9161000
Ok(level) => level,
9171001
Err(msg) => {
9181002
self.error(level.span(), msg);
919-
return;
1003+
return res;
9201004
}
9211005
};
9221006

@@ -933,13 +1017,13 @@ impl CommentParser<&mut Revisioned> {
9331017
} else if (*level_or_code).parse::<Level>().is_ok() {
9341018
// Shouldn't conflict with any real diagnostic code
9351019
self.error(level_or_code.span(), "no `:` after level found");
936-
return;
1020+
return res;
9371021
} else if !pattern.trim_start().is_empty() {
9381022
self.error(
9391023
pattern.span(),
9401024
format!("text found after error code `{}`", *level_or_code),
9411025
);
942-
return;
1026+
return res;
9431027
} else {
9441028
self.error_matches.push(ErrorMatch {
9451029
kind: ErrorMatchKind::Code(Spanned::new(
@@ -950,7 +1034,7 @@ impl CommentParser<&mut Revisioned> {
9501034
});
9511035
};
9521036

953-
*fallthrough_to = Some(match_line);
1037+
res
9541038
}
9551039
}
9561040

0 commit comments

Comments
 (0)