@@ -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