@@ -873,8 +873,6 @@ impl<'source> Lexer<'source> {
873873 terminated = true ;
874874 self . next ( ) ;
875875 break ;
876- } else if byte == b'\n' {
877- break ; // unterminated string literal across newline, handled below
878876 }
879877
880878 self . next ( ) ;
@@ -888,7 +886,7 @@ impl<'source> Lexer<'source> {
888886 }
889887
890888 if !terminated {
891- self . error_unterminated_string ( start_offset, length ) ;
889+ self . error_unterminated_string ( start_offset, 1 ) ;
892890 }
893891
894892 Token {
@@ -911,8 +909,6 @@ impl<'source> Lexer<'source> {
911909 terminated = true ;
912910 self . next ( ) ;
913911 break ;
914- } else if byte == b'\n' {
915- break ;
916912 } else if byte == 0 {
917913 self . error_disallowed_byte_in_raw_string ( self . current_offset , byte) ;
918914 self . next ( ) ;
@@ -925,7 +921,7 @@ impl<'source> Lexer<'source> {
925921 let length = end_offset - start_offset;
926922
927923 if !terminated {
928- self . error_unterminated_raw_string ( start_offset, length ) ;
924+ self . error_unterminated_raw_string ( start_offset, 2 ) ;
929925 }
930926
931927 Token {
@@ -1163,14 +1159,49 @@ impl<'source> Lexer<'source> {
11631159 None
11641160 }
11651161
1162+ // Caller has just consumed `{` of the broken interpolation, so we start
1163+ // inside it (depth=1). Newlines are not a recovery boundary now that
1164+ // f-string text spans them, so we balance braces and skip past quoted
1165+ // strings to avoid stopping at the first inner `"`.
11661166 fn skip_to_format_string_end ( & mut self ) {
1167+ let mut depth = 1 ;
11671168 while !self . at_eof ( ) {
11681169 match self . current_byte ( ) {
1169- b'"' => {
1170+ b'\\' => {
1171+ self . next ( ) ;
1172+ if !self . at_eof ( ) {
1173+ self . next ( ) ;
1174+ }
1175+ }
1176+ b'"' if depth == 0 => {
11701177 self . next ( ) ;
11711178 return ;
11721179 }
1173- b'\n' => return ,
1180+ b'"' => {
1181+ self . next ( ) ;
1182+ while !self . at_eof ( ) && self . current_byte ( ) != b'"' {
1183+ if self . current_byte ( ) == b'\\' {
1184+ self . next ( ) ;
1185+ if self . at_eof ( ) {
1186+ break ;
1187+ }
1188+ }
1189+ self . next ( ) ;
1190+ }
1191+ if !self . at_eof ( ) {
1192+ self . next ( ) ;
1193+ }
1194+ }
1195+ b'{' => {
1196+ depth += 1 ;
1197+ self . next ( ) ;
1198+ }
1199+ b'}' => {
1200+ if depth > 0 {
1201+ depth -= 1 ;
1202+ }
1203+ self . next ( ) ;
1204+ }
11741205 _ => self . next ( ) ,
11751206 }
11761207 }
@@ -1234,12 +1265,6 @@ impl<'source> Lexer<'source> {
12341265 return tokens;
12351266 }
12361267
1237- b'\n' => {
1238- let length = self . current_offset . saturating_sub ( start_offset) ;
1239- self . error_unterminated_format_string ( start_offset, length) ;
1240- return tokens;
1241- }
1242-
12431268 b'{' => {
12441269 self . push_format_string_text_if_needed ( & mut tokens, text_segment_start) ;
12451270
@@ -1258,8 +1283,7 @@ impl<'source> Lexer<'source> {
12581283 }
12591284 }
12601285
1261- let length = self . current_offset . saturating_sub ( start_offset) ;
1262- self . error_unterminated_format_string ( start_offset, length) ;
1286+ self . error_unterminated_format_string ( start_offset, 2 ) ;
12631287 tokens
12641288 }
12651289
0 commit comments