@@ -225,6 +225,47 @@ impl<'a> StringReader<'a> {
225
225
self . byte_offset ( end) . to_uint ( ) ) )
226
226
}
227
227
228
+ /// Converts CRLF to LF in the given string, raising an error on bare CR.
229
+ fn translate_crlf < ' a > ( & self , start : BytePos ,
230
+ s : & ' a str , errmsg : & ' a str ) -> str:: MaybeOwned < ' a > {
231
+ let mut i = 0 u;
232
+ while i < s. len ( ) {
233
+ let str:: CharRange { ch, next } = s. char_range_at ( i) ;
234
+ if ch == '\r' {
235
+ if next < s. len ( ) && s. char_at ( next) == '\n' {
236
+ return translate_crlf_ ( self , start, s, errmsg, i) . into_maybe_owned ( ) ;
237
+ }
238
+ let pos = start + BytePos ( i as u32 ) ;
239
+ let end_pos = start + BytePos ( next as u32 ) ;
240
+ self . err_span_ ( pos, end_pos, errmsg) ;
241
+ }
242
+ i = next;
243
+ }
244
+ return s. into_maybe_owned ( ) ;
245
+
246
+ fn translate_crlf_ ( rdr : & StringReader , start : BytePos ,
247
+ s : & str , errmsg : & str , mut i : uint ) -> String {
248
+ let mut buf = String :: with_capacity ( s. len ( ) ) ;
249
+ let mut j = 0 ;
250
+ while i < s. len ( ) {
251
+ let str:: CharRange { ch, next } = s. char_range_at ( i) ;
252
+ if ch == '\r' {
253
+ if j < i { buf. push_str ( s. slice ( j, i) ) ; }
254
+ j = next;
255
+ if next >= s. len ( ) || s. char_at ( next) != '\n' {
256
+ let pos = start + BytePos ( i as u32 ) ;
257
+ let end_pos = start + BytePos ( next as u32 ) ;
258
+ rdr. err_span_ ( pos, end_pos, errmsg) ;
259
+ }
260
+ }
261
+ i = next;
262
+ }
263
+ if j < s. len ( ) { buf. push_str ( s. slice_from ( j) ) ; }
264
+ buf
265
+ }
266
+ }
267
+
268
+
228
269
/// Advance the StringReader by one character. If a newline is
229
270
/// discovered, add it to the FileMap's list of line start offsets.
230
271
pub fn bump ( & mut self ) {
@@ -305,7 +346,20 @@ impl<'a> StringReader<'a> {
305
346
// line comments starting with "///" or "//!" are doc-comments
306
347
if self . curr_is ( '/' ) || self . curr_is ( '!' ) {
307
348
let start_bpos = self . pos - BytePos ( 3 ) ;
308
- while !self . curr_is ( '\n' ) && !self . is_eof ( ) {
349
+ while !self . is_eof ( ) {
350
+ match self . curr . unwrap ( ) {
351
+ '\n' => break ,
352
+ '\r' => {
353
+ if self . nextch_is ( '\n' ) {
354
+ // CRLF
355
+ break
356
+ } else {
357
+ self . err_span_ ( self . last_pos , self . pos ,
358
+ "bare CR not allowed in doc-comment" ) ;
359
+ }
360
+ }
361
+ _ => ( )
362
+ }
309
363
self . bump ( ) ;
310
364
}
311
365
let ret = self . with_str_from ( start_bpos, |string| {
@@ -370,6 +424,7 @@ impl<'a> StringReader<'a> {
370
424
let start_bpos = self . last_pos - BytePos ( 2 ) ;
371
425
372
426
let mut level: int = 1 ;
427
+ let mut has_cr = false ;
373
428
while level > 0 {
374
429
if self . is_eof ( ) {
375
430
let msg = if is_doc_comment {
@@ -379,25 +434,35 @@ impl<'a> StringReader<'a> {
379
434
} ;
380
435
let last_bpos = self . last_pos ;
381
436
self . fatal_span_ ( start_bpos, last_bpos, msg) ;
382
- } else if self . curr_is ( '/' ) && self . nextch_is ( '*' ) {
383
- level += 1 ;
384
- self . bump ( ) ;
385
- self . bump ( ) ;
386
- } else if self . curr_is ( '*' ) && self . nextch_is ( '/' ) {
387
- level -= 1 ;
388
- self . bump ( ) ;
389
- self . bump ( ) ;
390
- } else {
391
- self . bump ( ) ;
392
437
}
438
+ let n = self . curr . unwrap ( ) ;
439
+ match n {
440
+ '/' if self . nextch_is ( '*' ) => {
441
+ level += 1 ;
442
+ self . bump ( ) ;
443
+ }
444
+ '*' if self . nextch_is ( '/' ) => {
445
+ level -= 1 ;
446
+ self . bump ( ) ;
447
+ }
448
+ '\r' => {
449
+ has_cr = true ;
450
+ }
451
+ _ => ( )
452
+ }
453
+ self . bump ( ) ;
393
454
}
394
455
395
456
let res = if is_doc_comment {
396
457
self . with_str_from ( start_bpos, |string| {
397
458
// but comments with only "*"s between two "/"s are not
398
459
if !is_block_non_doc_comment ( string) {
460
+ let string = if has_cr {
461
+ self . translate_crlf ( start_bpos, string,
462
+ "bare CR not allowed in block doc-comment" )
463
+ } else { string. into_maybe_owned ( ) } ;
399
464
Some ( TokenAndSpan {
400
- tok : token:: DOC_COMMENT ( str_to_ident ( string) ) ,
465
+ tok : token:: DOC_COMMENT ( str_to_ident ( string. as_slice ( ) ) ) ,
401
466
sp : codemap:: mk_sp ( start_bpos, self . last_pos )
402
467
} )
403
468
} else {
@@ -675,6 +740,10 @@ impl<'a> StringReader<'a> {
675
740
self . consume_whitespace ( ) ;
676
741
return None
677
742
} ,
743
+ '\r' if delim == '"' && self . curr_is ( '\n' ) => {
744
+ self . consume_whitespace ( ) ;
745
+ return None
746
+ }
678
747
c => {
679
748
let last_pos = self . last_pos ;
680
749
self . err_span_char (
@@ -696,6 +765,15 @@ impl<'a> StringReader<'a> {
696
765
else { "character constant must be escaped" } ,
697
766
first_source_char) ;
698
767
}
768
+ '\r' => {
769
+ if self . curr_is ( '\n' ) {
770
+ self . bump ( ) ;
771
+ return Some ( '\n' ) ;
772
+ } else {
773
+ self . err_span_ ( start, self . last_pos ,
774
+ "bare CR not allowed in string, use \\ r instead" ) ;
775
+ }
776
+ }
699
777
_ => if ascii_only && first_source_char > '\x7F' {
700
778
let last_pos = self . last_pos ;
701
779
self . err_span_char (
@@ -1042,28 +1120,45 @@ impl<'a> StringReader<'a> {
1042
1120
self . bump ( ) ;
1043
1121
let content_start_bpos = self . last_pos ;
1044
1122
let mut content_end_bpos;
1123
+ let mut has_cr = false ;
1045
1124
' outer: loop {
1046
1125
if self . is_eof ( ) {
1047
1126
let last_bpos = self . last_pos ;
1048
1127
self . fatal_span_ ( start_bpos, last_bpos, "unterminated raw string" ) ;
1049
1128
}
1050
- if self . curr_is ( '"' ) {
1051
- content_end_bpos = self . last_pos ;
1052
- for _ in range ( 0 , hash_count) {
1053
- self . bump ( ) ;
1054
- if !self . curr_is ( '#' ) {
1055
- continue ' outer;
1129
+ //if self.curr_is('"') {
1130
+ //content_end_bpos = self.last_pos;
1131
+ //for _ in range(0, hash_count) {
1132
+ //self.bump();
1133
+ //if !self.curr_is('#') {
1134
+ //continue 'outer;
1135
+ let c = self . curr . unwrap ( ) ;
1136
+ match c {
1137
+ '"' => {
1138
+ content_end_bpos = self . last_pos ;
1139
+ for _ in range ( 0 , hash_count) {
1140
+ self . bump ( ) ;
1141
+ if !self . curr_is ( '#' ) {
1142
+ continue ' outer;
1143
+ }
1056
1144
}
1145
+ break ;
1146
+ }
1147
+ '\r' => {
1148
+ has_cr = true ;
1057
1149
}
1058
- break ;
1150
+ _ => ( )
1059
1151
}
1060
1152
self . bump ( ) ;
1061
1153
}
1062
1154
self . bump ( ) ;
1063
- let str_content = self . with_str_from_to (
1064
- content_start_bpos,
1065
- content_end_bpos,
1066
- str_to_ident) ;
1155
+ let str_content = self . with_str_from_to ( content_start_bpos, content_end_bpos, |string| {
1156
+ let string = if has_cr {
1157
+ self . translate_crlf ( content_start_bpos, string,
1158
+ "bare CR not allowed in raw string" )
1159
+ } else { string. into_maybe_owned ( ) } ;
1160
+ str_to_ident ( string. as_slice ( ) )
1161
+ } ) ;
1067
1162
return token:: LIT_STR_RAW ( str_content, hash_count) ;
1068
1163
}
1069
1164
'-' => {
0 commit comments