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