1
+ use std:: borrow:: Cow ;
2
+ use std:: ops:: Range ;
3
+
1
4
use crate :: utils:: { snippet_with_applicability, span_lint, span_lint_and_sugg, span_lint_and_then} ;
2
5
use rustc:: lint:: { EarlyContext , EarlyLintPass , LintArray , LintPass } ;
3
6
use rustc:: { declare_lint_pass, declare_tool_lint} ;
4
7
use rustc_errors:: Applicability ;
5
- use std :: borrow :: Cow ;
8
+ use rustc_lexer :: unescape :: { self , EscapeError } ;
6
9
use syntax:: ast:: * ;
7
10
use syntax:: parse:: { parser, token} ;
8
11
use syntax:: tokenstream:: TokenStream ;
@@ -201,7 +204,7 @@ impl EarlyLintPass for Write {
201
204
} else if mac. path == sym ! ( print) {
202
205
span_lint ( cx, PRINT_STDOUT , mac. span , "use of `print!`" ) ;
203
206
if let ( Some ( fmt_str) , _) = check_tts ( cx, & mac. tts , false ) {
204
- if check_newlines ( & fmt_str) {
207
+ if check_newlines ( & fmt_str. contents , fmt_str . style ) {
205
208
span_lint_and_then (
206
209
cx,
207
210
PRINT_WITH_NEWLINE ,
@@ -222,7 +225,7 @@ impl EarlyLintPass for Write {
222
225
}
223
226
} else if mac. path == sym ! ( write) {
224
227
if let ( Some ( fmt_str) , _) = check_tts ( cx, & mac. tts , true ) {
225
- if check_newlines ( & fmt_str) {
228
+ if check_newlines ( & fmt_str. contents , fmt_str . style ) {
226
229
span_lint_and_then (
227
230
cx,
228
231
WRITE_WITH_NEWLINE ,
@@ -440,38 +443,31 @@ fn check_tts<'a>(cx: &EarlyContext<'a>, tts: &TokenStream, is_write: bool) -> (O
440
443
}
441
444
}
442
445
443
- /// Checks if the format string constains a single newline that terminates it.
446
+ /// Checks if the format string contains a single newline that terminates it.
444
447
///
445
448
/// Literal and escaped newlines are both checked (only literal for raw strings).
446
- fn check_newlines ( fmt_str : & FmtStr ) -> bool {
447
- let s = & fmt_str. contents ;
449
+ fn check_newlines ( contents : & str , style : StrStyle ) -> bool {
450
+ let mut has_internal_newline = false ;
451
+ let mut last_was_cr = false ;
452
+ let mut should_lint = false ;
448
453
449
- if s. ends_with ( '\n' ) {
450
- return true ;
451
- } else if let StrStyle :: Raw ( _) = fmt_str. style {
452
- return false ;
453
- }
454
-
455
- if s. len ( ) < 2 {
456
- return false ;
457
- }
454
+ let mut cb = |r : Range < usize > , c : Result < char , EscapeError > | {
455
+ let c = c. unwrap ( ) ;
458
456
459
- let bytes = s. as_bytes ( ) ;
460
- if bytes[ bytes. len ( ) - 2 ] != b'\\' || bytes[ bytes. len ( ) - 1 ] != b'n' {
461
- return false ;
462
- }
463
-
464
- let mut escaping = false ;
465
- for ( index, & byte) in bytes. iter ( ) . enumerate ( ) {
466
- if escaping {
467
- if byte == b'n' {
468
- return index == bytes. len ( ) - 1 ;
457
+ if r. end == contents. len ( ) && c == '\n' && !last_was_cr && !has_internal_newline {
458
+ should_lint = true ;
459
+ } else {
460
+ last_was_cr = c == '\r' ;
461
+ if c == '\n' {
462
+ has_internal_newline = true ;
469
463
}
470
- escaping = false ;
471
- } else if byte == b'\\' {
472
- escaping = true ;
473
464
}
465
+ } ;
466
+
467
+ match style {
468
+ StrStyle :: Cooked => unescape:: unescape_str ( contents, & mut cb) ,
469
+ StrStyle :: Raw ( _) => unescape:: unescape_raw_str ( contents, & mut cb) ,
474
470
}
475
471
476
- false
472
+ should_lint
477
473
}
0 commit comments