@@ -4,7 +4,7 @@ use crate::symbol::{sym, Symbol};
4
4
use crate :: parse:: unescape;
5
5
use crate :: parse:: unescape_error_reporting:: { emit_unescape_error, push_escaped_char} ;
6
6
7
- use errors:: { FatalError , Diagnostic , DiagnosticBuilder } ;
7
+ use errors:: { Applicability , FatalError , Diagnostic , DiagnosticBuilder } ;
8
8
use syntax_pos:: { BytePos , Pos , Span , NO_EXPANSION } ;
9
9
use core:: unicode:: property:: Pattern_White_Space ;
10
10
@@ -145,19 +145,64 @@ impl<'a> StringReader<'a> {
145
145
self . ch . is_none ( )
146
146
}
147
147
148
- fn fail_unterminated_raw_string ( & self , pos : BytePos , hash_count : u16 ) -> ! {
149
- let mut err = self . struct_span_fatal ( pos, pos, "unterminated raw string" ) ;
150
- err. span_label ( self . mk_sp ( pos, pos) , "unterminated raw string" ) ;
148
+ fn fail_unterminated_raw_string ( & self , start : Span , hash_count : u16 , spans : Vec < Span > ) -> ! {
149
+ const SPAN_THRESHOLD : usize = 3 ;
150
+ const MSG_STR : & str = "you might have meant to end the raw string here" ;
151
+ let hash_str = format ! ( "\" {}" , "#" . repeat( hash_count as usize ) ) ;
152
+ let spans_len = spans. len ( ) ;
151
153
152
- if hash_count > 0 {
153
- err. note ( & format ! ( "this raw string should be terminated with `\" {}`" ,
154
- "#" . repeat( hash_count as usize ) ) ) ;
154
+ let mut err = self . sess . span_diagnostic . struct_span_fatal ( start, "unterminated raw string" ) ;
155
+ err. span_label ( start, "unterminated raw string" ) ;
156
+
157
+ for s in spans {
158
+ if spans_len < SPAN_THRESHOLD {
159
+ err. span_suggestion (
160
+ s,
161
+ MSG_STR ,
162
+ hash_str. clone ( ) ,
163
+ Applicability :: MaybeIncorrect
164
+ ) ;
165
+ } else {
166
+ err. tool_only_span_suggestion (
167
+ s,
168
+ MSG_STR ,
169
+ hash_str. clone ( ) ,
170
+ Applicability :: MaybeIncorrect
171
+ ) ;
172
+ }
173
+ }
174
+
175
+ if hash_count > 0 && spans_len >= SPAN_THRESHOLD {
176
+ err. note ( & format ! ( "this raw string should be terminated with `\" {}`" , hash_str) ) ;
155
177
}
156
178
157
179
err. emit ( ) ;
158
180
FatalError . raise ( ) ;
159
181
}
160
182
183
+ fn fail_incorrect_raw_string_delimiter ( & mut self , start : BytePos ) -> ! {
184
+ loop {
185
+ match self . ch {
186
+ Some ( '#' ) | Some ( '"' ) => break ,
187
+ _ => self . bump ( ) ,
188
+ }
189
+ }
190
+ let end = self . pos ;
191
+ let span = self . mk_sp ( start, end) ;
192
+ let mut err = self . sess . span_diagnostic . struct_span_fatal (
193
+ span,
194
+ "found invalid character; only `#` is allowed in raw string delimitation" ,
195
+ ) ;
196
+ err. span_suggestion_hidden (
197
+ span,
198
+ "replace with `#`" ,
199
+ format ! ( "{}" , "#" . repeat( ( end. 0 - start. 0 ) as usize ) ) ,
200
+ Applicability :: MachineApplicable ,
201
+ ) ;
202
+ err. emit ( ) ;
203
+ FatalError . raise ( ) ;
204
+ }
205
+
161
206
crate fn emit_fatal_errors ( & mut self ) {
162
207
for err in & mut self . fatal_errs {
163
208
err. emit ( ) ;
@@ -202,16 +247,6 @@ impl<'a> StringReader<'a> {
202
247
self . err_span ( self . mk_sp ( from_pos, to_pos) , m)
203
248
}
204
249
205
- /// Report a lexical error spanning [`from_pos`, `to_pos`), appending an
206
- /// escaped character to the error message
207
- fn fatal_span_char ( & self , from_pos : BytePos , to_pos : BytePos , m : & str , c : char ) -> FatalError {
208
- let mut m = m. to_string ( ) ;
209
- m. push_str ( ": " ) ;
210
- push_escaped_char ( & mut m, c) ;
211
-
212
- self . fatal_span_ ( from_pos, to_pos, & m[ ..] )
213
- }
214
-
215
250
fn struct_span_fatal ( & self , from_pos : BytePos , to_pos : BytePos , m : & str )
216
251
-> DiagnosticBuilder < ' a >
217
252
{
@@ -945,6 +980,7 @@ impl<'a> StringReader<'a> {
945
980
Ok ( TokenKind :: lit ( token:: Char , symbol, suffix) )
946
981
}
947
982
'b' => {
983
+ let start_bpos = self . pos ;
948
984
self . bump ( ) ;
949
985
let ( kind, symbol) = match self . ch {
950
986
Some ( '\'' ) => {
@@ -963,7 +999,7 @@ impl<'a> StringReader<'a> {
963
999
( token:: ByteStr , symbol)
964
1000
} ,
965
1001
Some ( 'r' ) => {
966
- let ( start, end, hash_count) = self . scan_raw_string ( ) ;
1002
+ let ( start, end, hash_count) = self . scan_raw_string ( start_bpos ) ;
967
1003
let symbol = self . symbol_from_to ( start, end) ;
968
1004
self . validate_raw_byte_str_escape ( start, end) ;
969
1005
@@ -984,7 +1020,7 @@ impl<'a> StringReader<'a> {
984
1020
Ok ( TokenKind :: lit ( token:: Str , symbol, suffix) )
985
1021
}
986
1022
'r' => {
987
- let ( start, end, hash_count) = self . scan_raw_string ( ) ;
1023
+ let ( start, end, hash_count) = self . scan_raw_string ( self . pos ) ;
988
1024
let symbol = self . symbol_from_to ( start, end) ;
989
1025
self . validate_raw_str_escape ( start, end) ;
990
1026
let suffix = self . scan_optional_raw_name ( ) ;
@@ -1145,8 +1181,7 @@ impl<'a> StringReader<'a> {
1145
1181
1146
1182
/// Scans a raw (byte) string, returning byte position range for `"<literal>"`
1147
1183
/// (including quotes) along with `#` character count in `(b)r##..."<literal>"##...`;
1148
- fn scan_raw_string ( & mut self ) -> ( BytePos , BytePos , u16 ) {
1149
- let start_bpos = self . pos ;
1184
+ fn scan_raw_string ( & mut self , start_bpos : BytePos ) -> ( BytePos , BytePos , u16 ) {
1150
1185
self . bump ( ) ;
1151
1186
let mut hash_count: u16 = 0 ;
1152
1187
while self . ch_is ( '#' ) {
@@ -1161,30 +1196,32 @@ impl<'a> StringReader<'a> {
1161
1196
hash_count += 1 ;
1162
1197
}
1163
1198
1164
- if self . is_eof ( ) {
1165
- self . fail_unterminated_raw_string ( start_bpos , hash_count ) ;
1166
- } else if ! self . ch_is ( '"' ) {
1167
- let last_bpos = self . pos ;
1168
- let curr_char = self . ch . unwrap ( ) ;
1169
- self . fatal_span_char ( start_bpos ,
1170
- last_bpos ,
1171
- "found invalid character; only `#` is allowed \
1172
- in raw string delimitation" ,
1173
- curr_char ) . raise ( ) ;
1199
+ let bpos_span = self . mk_sp ( start_bpos , self . pos ) ;
1200
+
1201
+ match self . ch {
1202
+ None => self . fail_unterminated_raw_string (
1203
+ bpos_span ,
1204
+ hash_count ,
1205
+ vec ! [ self . mk_sp ( self . pos , self . pos ) ]
1206
+ ) ,
1207
+ Some ( '"' ) => ( ) ,
1208
+ Some ( _ ) => self . fail_incorrect_raw_string_delimiter ( self . pos ) ,
1174
1209
}
1210
+
1175
1211
self . bump ( ) ;
1176
1212
let content_start_bpos = self . pos ;
1177
1213
let mut content_end_bpos;
1214
+ let mut spans = vec ! [ ] ;
1215
+
1178
1216
' outer: loop {
1179
1217
match self . ch {
1180
- None => {
1181
- self . fail_unterminated_raw_string ( start_bpos, hash_count) ;
1182
- }
1218
+ None => self . fail_unterminated_raw_string ( bpos_span, hash_count, spans) ,
1183
1219
Some ( '"' ) => {
1184
1220
content_end_bpos = self . pos ;
1185
1221
for _ in 0 ..hash_count {
1186
1222
self . bump ( ) ;
1187
1223
if !self . ch_is ( '#' ) {
1224
+ spans. push ( self . mk_sp ( content_end_bpos, self . pos ) ) ;
1188
1225
continue ' outer;
1189
1226
}
1190
1227
}
0 commit comments