Skip to content

Commit db0a7d6

Browse files
committed
improved diagnostic for raw string delimiter
1 parent 5f8e82a commit db0a7d6

File tree

1 file changed

+29
-10
lines changed
  • src/libsyntax/parse/lexer

1 file changed

+29
-10
lines changed

src/libsyntax/parse/lexer/mod.rs

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -165,11 +165,24 @@ impl<'a> StringReader<'a> {
165165
self.ch.is_none()
166166
}
167167

168-
fn fail_unterminated_raw_string(&self, pos: BytePos, hash_count: u16) {
169-
let mut err = self.struct_span_fatal(pos, pos, "unterminated raw string");
170-
err.span_label(self.mk_sp(pos, pos), "unterminated raw string");
168+
fn fail_unterminated_raw_string(&self, start: Span, hash_count: u16, spans: Vec<Span>) -> ! {
169+
const SPAN_THRESHOLD: usize = 3;
170+
const MSG_STR: &str = "Raw string could be meant to end here";
171+
let hash_str = format!("\"{}", "#".repeat(hash_count as usize));
172+
let spans_len = spans.len();
173+
174+
let mut err = self.sess.span_diagnostic.struct_span_fatal(start, "unterminated raw string");
175+
err.span_label(start, "unterminated raw string");
176+
177+
for s in spans {
178+
if spans_len < SPAN_THRESHOLD {
179+
err.span_suggestion(s, MSG_STR, hash_str.clone(), Applicability::MaybeIncorrect);
180+
} else {
181+
err.tool_only_span_suggestion(s, MSG_STR, hash_str.clone(), Applicability::MaybeIncorrect);
182+
}
183+
}
171184

172-
if hash_count > 0 {
185+
if hash_count > 0 && spans_len >= SPAN_THRESHOLD {
173186
err.note(&format!("this raw string should be terminated with `\"{}`",
174187
"#".repeat(hash_count as usize)));
175188
}
@@ -1111,6 +1124,7 @@ impl<'a> StringReader<'a> {
11111124
Ok(Token::lit(token::Char, symbol, suffix))
11121125
}
11131126
'b' => {
1127+
let start_bpos = self.pos;
11141128
self.bump();
11151129
let (kind, symbol) = match self.ch {
11161130
Some('\'') => {
@@ -1128,7 +1142,7 @@ impl<'a> StringReader<'a> {
11281142
self.validate_byte_str_escape(start_with_quote);
11291143
(token::ByteStr, symbol)
11301144
},
1131-
Some('r') => self.scan_raw_string(RawStringType::Byte),
1145+
Some('r') => self.scan_raw_string(start_bpos, RawStringType::Byte),
11321146
_ => unreachable!(), // Should have been a token::Ident above.
11331147
};
11341148
let suffix = self.scan_optional_raw_name();
@@ -1143,7 +1157,7 @@ impl<'a> StringReader<'a> {
11431157
Ok(Token::lit(token::Str, symbol, suffix))
11441158
}
11451159
'r' => {
1146-
let (lit, symbol) = self.scan_raw_string(RawStringType::Unicode);
1160+
let (lit, symbol) = self.scan_raw_string(self.pos, RawStringType::Unicode);
11471161
let suffix = self.scan_optional_raw_name();
11481162
Ok(Token::lit(lit, symbol, suffix))
11491163
}
@@ -1299,8 +1313,7 @@ impl<'a> StringReader<'a> {
12991313
id
13001314
}
13011315

1302-
fn scan_raw_string(&mut self, raw_type: RawStringType) -> (token::LitKind, Symbol) {
1303-
let start_bpos = self.pos;
1316+
fn scan_raw_string(&mut self, start_bpos: BytePos, raw_type: RawStringType) -> (token::LitKind, Symbol) {
13041317
self.bump();
13051318
let mut hash_count: u16 = 0;
13061319
while self.ch_is('#') {
@@ -1314,9 +1327,10 @@ impl<'a> StringReader<'a> {
13141327
self.bump();
13151328
hash_count += 1;
13161329
}
1330+
let bpos_span = self.mk_sp(start_bpos, self.pos);
13171331

13181332
match self.ch {
1319-
None => self.fail_unterminated_raw_string(start_bpos, hash_count, vec![]),
1333+
None => self.fail_unterminated_raw_string(bpos_span, hash_count, vec![self.mk_sp(self.pos, self.pos)]),
13201334
Some('"') => {},
13211335
Some(c) => {
13221336
let last_bpos = self.pos;
@@ -1332,15 +1346,20 @@ impl<'a> StringReader<'a> {
13321346
let content_start_bpos = self.pos;
13331347
let mut content_end_bpos;
13341348
let mut valid = true;
1349+
let mut spans = vec![];
13351350

13361351
'outer: loop {
13371352
match (self.ch, raw_type) {
1338-
(None, _) => self.fail_unterminated_raw_string(start_bpos, hash_count),
1353+
(None, _) => {
1354+
spans.push(self.mk_sp(self.pos, self.pos));
1355+
self.fail_unterminated_raw_string(bpos_span, hash_count, spans);
1356+
},
13391357
(Some('"'), _) => {
13401358
content_end_bpos = self.pos;
13411359
for _ in 0..hash_count {
13421360
self.bump();
13431361
if !self.ch_is('#') {
1362+
spans.push(self.mk_sp(content_end_bpos, self.pos));
13441363
continue 'outer;
13451364
}
13461365
}

0 commit comments

Comments
 (0)