@@ -165,11 +165,24 @@ impl<'a> StringReader<'a> {
165
165
self . ch . is_none ( )
166
166
}
167
167
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
+ }
171
184
172
- if hash_count > 0 {
185
+ if hash_count > 0 && spans_len >= SPAN_THRESHOLD {
173
186
err. note ( & format ! ( "this raw string should be terminated with `\" {}`" ,
174
187
"#" . repeat( hash_count as usize ) ) ) ;
175
188
}
@@ -1111,6 +1124,7 @@ impl<'a> StringReader<'a> {
1111
1124
Ok ( Token :: lit ( token:: Char , symbol, suffix) )
1112
1125
}
1113
1126
'b' => {
1127
+ let start_bpos = self . pos ;
1114
1128
self . bump ( ) ;
1115
1129
let ( kind, symbol) = match self . ch {
1116
1130
Some ( '\'' ) => {
@@ -1128,7 +1142,7 @@ impl<'a> StringReader<'a> {
1128
1142
self . validate_byte_str_escape ( start_with_quote) ;
1129
1143
( token:: ByteStr , symbol)
1130
1144
} ,
1131
- Some ( 'r' ) => self . scan_raw_string ( RawStringType :: Byte ) ,
1145
+ Some ( 'r' ) => self . scan_raw_string ( start_bpos , RawStringType :: Byte ) ,
1132
1146
_ => unreachable ! ( ) , // Should have been a token::Ident above.
1133
1147
} ;
1134
1148
let suffix = self . scan_optional_raw_name ( ) ;
@@ -1143,7 +1157,7 @@ impl<'a> StringReader<'a> {
1143
1157
Ok ( Token :: lit ( token:: Str , symbol, suffix) )
1144
1158
}
1145
1159
'r' => {
1146
- let ( lit, symbol) = self . scan_raw_string ( RawStringType :: Unicode ) ;
1160
+ let ( lit, symbol) = self . scan_raw_string ( self . pos , RawStringType :: Unicode ) ;
1147
1161
let suffix = self . scan_optional_raw_name ( ) ;
1148
1162
Ok ( Token :: lit ( lit, symbol, suffix) )
1149
1163
}
@@ -1299,8 +1313,7 @@ impl<'a> StringReader<'a> {
1299
1313
id
1300
1314
}
1301
1315
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 ) {
1304
1317
self . bump ( ) ;
1305
1318
let mut hash_count: u16 = 0 ;
1306
1319
while self . ch_is ( '#' ) {
@@ -1314,9 +1327,10 @@ impl<'a> StringReader<'a> {
1314
1327
self . bump ( ) ;
1315
1328
hash_count += 1 ;
1316
1329
}
1330
+ let bpos_span = self . mk_sp ( start_bpos, self . pos ) ;
1317
1331
1318
1332
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 ) ] ) ,
1320
1334
Some ( '"' ) => { } ,
1321
1335
Some ( c) => {
1322
1336
let last_bpos = self . pos ;
@@ -1332,15 +1346,20 @@ impl<'a> StringReader<'a> {
1332
1346
let content_start_bpos = self . pos ;
1333
1347
let mut content_end_bpos;
1334
1348
let mut valid = true ;
1349
+ let mut spans = vec ! [ ] ;
1335
1350
1336
1351
' outer: loop {
1337
1352
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
+ } ,
1339
1357
( Some ( '"' ) , _) => {
1340
1358
content_end_bpos = self . pos ;
1341
1359
for _ in 0 ..hash_count {
1342
1360
self . bump ( ) ;
1343
1361
if !self . ch_is ( '#' ) {
1362
+ spans. push ( self . mk_sp ( content_end_bpos, self . pos ) ) ;
1344
1363
continue ' outer;
1345
1364
}
1346
1365
}
0 commit comments