1+ use std:: borrow:: Cow ;
2+ use std:: ops:: Range ;
3+
14use crate :: utils:: { snippet_with_applicability, span_lint, span_lint_and_sugg, span_lint_and_then} ;
25use rustc:: lint:: { EarlyContext , EarlyLintPass , LintArray , LintPass } ;
36use rustc:: { declare_lint_pass, declare_tool_lint} ;
47use rustc_errors:: Applicability ;
5- use std :: borrow :: Cow ;
8+ use rustc_lexer :: unescape :: { self , EscapeError } ;
69use syntax:: ast:: * ;
710use syntax:: parse:: { parser, token} ;
811use syntax:: tokenstream:: TokenStream ;
@@ -201,7 +204,7 @@ impl EarlyLintPass for Write {
201204 } else if mac. path == sym ! ( print) {
202205 span_lint ( cx, PRINT_STDOUT , mac. span , "use of `print!`" ) ;
203206 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 ) {
205208 span_lint_and_then (
206209 cx,
207210 PRINT_WITH_NEWLINE ,
@@ -222,7 +225,7 @@ impl EarlyLintPass for Write {
222225 }
223226 } else if mac. path == sym ! ( write) {
224227 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 ) {
226229 span_lint_and_then (
227230 cx,
228231 WRITE_WITH_NEWLINE ,
@@ -440,38 +443,31 @@ fn check_tts<'a>(cx: &EarlyContext<'a>, tts: &TokenStream, is_write: bool) -> (O
440443 }
441444}
442445
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.
444447///
445448/// 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 ;
448453
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 ( ) ;
458456
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 ;
469463 }
470- escaping = false ;
471- } else if byte == b'\\' {
472- escaping = true ;
473464 }
465+ } ;
466+
467+ match style {
468+ StrStyle :: Cooked => unescape:: unescape_str ( contents, & mut cb) ,
469+ StrStyle :: Raw ( _) => unescape:: unescape_raw_str ( contents, & mut cb) ,
474470 }
475471
476- false
472+ should_lint
477473}
0 commit comments