diff --git a/crates/ty_test/src/assertion.rs b/crates/ty_test/src/assertion.rs index c62123b5fc..6f1e80f9c9 100644 --- a/crates/ty_test/src/assertion.rs +++ b/crates/ty_test/src/assertion.rs @@ -418,10 +418,14 @@ impl<'a> ErrorAssertionParser<'a> { // message text '"' => { - let comment_source = self.comment_source.trim(); + let comment_source = self.comment_source.trim_end(); return if comment_source.ends_with('"') { - let rest = &comment_source - [self.cursor.offset().to_usize()..comment_source.len() - 1]; + let start = self.cursor.offset().to_usize(); + let end = comment_source.len() - 1; + if start > end { + return Err(ErrorAssertionParseError::DanglingMessageQuote); + } + let rest = &comment_source[start..end]; Ok(ErrorAssertion { rule, column, @@ -489,6 +493,8 @@ pub(crate) enum ErrorAssertionParseError<'a> { MultipleRuleCodes, #[error("expected '\"' to be the final character in an assertion with an error message")] UnclosedMessage, + #[error("expected message text and closing '\"' after opening '\"'")] + DanglingMessageQuote, #[error( "unexpected character `{character}` at offset {offset} (relative to the `:` in the assertion comment)" )] diff --git a/crates/ty_test/src/matcher.rs b/crates/ty_test/src/matcher.rs index 06694fce55..ade91912f5 100644 --- a/crates/ty_test/src/matcher.rs +++ b/crates/ty_test/src/matcher.rs @@ -1395,4 +1395,28 @@ mod tests { )], ); } + + #[test] + fn trailing_quote_without_message_not_allowed() { + let source = "x # error: [some-rule]\""; + let result = get_result( + source, + vec![ExpectedDiagnostic::new( + DiagnosticId::lint("some-rule"), + "some message", + 0, + )], + ); + + assert_fail( + result, + &[( + 0, + &[ + "invalid assertion: expected message text and closing '\"' after opening '\"'", + r#"unexpected error: 1 [some-rule] "some message""#, + ], + )], + ); + } }