]> git.lizzy.rs Git - rust.git/blobdiff - clippy_lints/src/write.rs
Auto merge of #3680 - g-bartoszek:needless-bool-else-if-brackets, r=oli-obk
[rust.git] / clippy_lints / src / write.rs
index 76e07a2d3b3f5ff78703018e62fe29fd36ba6766..c8c291c8cc873a87d5b69d13531e568e8ff3a42d 100644 (file)
@@ -1,21 +1,11 @@
-// Copyright 2014-2018 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-
-use crate::rustc::lint::{EarlyContext, EarlyLintPass, LintArray, LintPass};
-use crate::rustc::{declare_tool_lint, lint_array};
-use crate::rustc_errors::Applicability;
-use crate::syntax::ast::*;
-use crate::syntax::parse::{parser, token};
-use crate::syntax::tokenstream::{ThinTokenStream, TokenStream};
-use crate::utils::{snippet, span_lint, span_lint_and_sugg};
+use crate::utils::{snippet_with_applicability, span_lint, span_lint_and_sugg};
+use rustc::lint::{EarlyContext, EarlyLintPass, LintArray, LintPass};
+use rustc::{declare_tool_lint, lint_array};
+use rustc_errors::Applicability;
 use std::borrow::Cow;
+use syntax::ast::*;
+use syntax::parse::{parser, token};
+use syntax::tokenstream::TokenStream;
 
 /// **What it does:** This lint warns when you use `println!("")` to
 /// print a newline.
@@ -200,17 +190,14 @@ fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &Mac) {
                         "using `println!(\"\")`",
                         "replace it with",
                         "println!()".to_string(),
-                        Applicability::Unspecified,
+                        Applicability::MachineApplicable,
                     );
                 }
             }
         } else if mac.node.path == "print" {
             span_lint(cx, PRINT_STDOUT, mac.span, "use of `print!`");
             if let Some(fmtstr) = check_tts(cx, &mac.node.tts, false).0 {
-                if fmtstr.ends_with("\\n") &&
-                   // don't warn about strings with several `\n`s (#3126)
-                   fmtstr.matches("\\n").count() == 1
-                {
+                if check_newlines(&fmtstr) {
                     span_lint(
                         cx,
                         PRINT_WITH_NEWLINE,
@@ -222,10 +209,7 @@ fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &Mac) {
             }
         } else if mac.node.path == "write" {
             if let Some(fmtstr) = check_tts(cx, &mac.node.tts, true).0 {
-                if fmtstr.ends_with("\\n") &&
-                   // don't warn about strings with several `\n`s (#3126)
-                   fmtstr.matches("\\n").count() == 1
-                {
+                if check_newlines(&fmtstr) {
                     span_lint(
                         cx,
                         WRITE_WITH_NEWLINE,
@@ -239,9 +223,14 @@ fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &Mac) {
             let check_tts = check_tts(cx, &mac.node.tts, true);
             if let Some(fmtstr) = check_tts.0 {
                 if fmtstr == "" {
-                    let suggestion = check_tts
-                        .1
-                        .map_or(Cow::Borrowed("v"), |expr| snippet(cx, expr.span, "v"));
+                    let mut applicability = Applicability::MachineApplicable;
+                    let suggestion = check_tts.1.map_or_else(
+                        move || {
+                            applicability = Applicability::HasPlaceholders;
+                            Cow::Borrowed("v")
+                        },
+                        move |expr| snippet_with_applicability(cx, expr.span, "v", &mut applicability),
+                    );
 
                     span_lint_and_sugg(
                         cx,
@@ -250,7 +239,7 @@ fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &Mac) {
                         format!("using `writeln!({}, \"\")`", suggestion).as_str(),
                         "replace it with",
                         format!("writeln!({})", suggestion),
-                        Applicability::Unspecified,
+                        applicability,
                     );
                 }
             }
@@ -259,8 +248,8 @@ fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &Mac) {
 }
 
 /// Checks the arguments of `print[ln]!` and `write[ln]!` calls. It will return a tuple of two
-/// options. The first part of the tuple is format_str of the macros. The secund part of the tuple
-/// is in the `write[ln]!` case the expression the format_str should be written to.
+/// options. The first part of the tuple is `format_str` of the macros. The second part of the tuple
+/// is in the `write[ln]!` case the expression the `format_str` should be written to.
 ///
 /// Example:
 ///
@@ -272,9 +261,9 @@ fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &Mac) {
 /// ```rust,ignore
 /// (Some("string to write: {}"), Some(buf))
 /// ```
-fn check_tts<'a>(cx: &EarlyContext<'a>, tts: &ThinTokenStream, is_write: bool) -> (Option<String>, Option<Expr>) {
-    use crate::fmt_macros::*;
-    let tts = TokenStream::from(tts.clone());
+fn check_tts<'a>(cx: &EarlyContext<'a>, tts: &TokenStream, is_write: bool) -> (Option<String>, Option<Expr>) {
+    use fmt_macros::*;
+    let tts = tts.clone();
     let mut parser = parser::Parser::new(&cx.sess.parse_sess, tts, None, false, false);
     let mut expr: Option<Expr> = None;
     if is_write {
@@ -294,7 +283,7 @@ fn check_tts<'a>(cx: &EarlyContext<'a>, tts: &ThinTokenStream, is_write: bool) -
     };
     let tmp = fmtstr.clone();
     let mut args = vec![];
-    let mut fmt_parser = Parser::new(&tmp, None);
+    let mut fmt_parser = Parser::new(&tmp, None, Vec::new(), false);
     while let Some(piece) = fmt_parser.next() {
         if !fmt_parser.errors.is_empty() {
             return (None, expr);
@@ -331,9 +320,11 @@ fn check_tts<'a>(cx: &EarlyContext<'a>, tts: &ThinTokenStream, is_write: bool) -
                 let mut seen = false;
                 for arg in &args {
                     match arg.position {
-                        ArgumentImplicitlyIs(n) | ArgumentIs(n) => if n == idx {
-                            all_simple &= arg.format == SIMPLE;
-                            seen = true;
+                        ArgumentImplicitlyIs(n) | ArgumentIs(n) => {
+                            if n == idx {
+                                all_simple &= arg.format == SIMPLE;
+                                seen = true;
+                            }
                         },
                         ArgumentNamed(_) => {},
                     }
@@ -351,9 +342,11 @@ fn check_tts<'a>(cx: &EarlyContext<'a>, tts: &ThinTokenStream, is_write: bool) -
                         for arg in &args {
                             match arg.position {
                                 ArgumentImplicitlyIs(_) | ArgumentIs(_) => {},
-                                ArgumentNamed(name) => if *p == name {
-                                    seen = true;
-                                    all_simple &= arg.format == SIMPLE;
+                                ArgumentNamed(name) => {
+                                    if *p == name {
+                                        seen = true;
+                                        all_simple &= arg.format == SIMPLE;
+                                    }
                                 },
                             }
                         }
@@ -367,3 +360,29 @@ fn check_tts<'a>(cx: &EarlyContext<'a>, tts: &ThinTokenStream, is_write: bool) -
         }
     }
 }
+
+// Checks if `s` constains a single newline that terminates it
+fn check_newlines(s: &str) -> bool {
+    if s.len() < 2 {
+        return false;
+    }
+
+    let bytes = s.as_bytes();
+    if bytes[bytes.len() - 2] != b'\\' || bytes[bytes.len() - 1] != b'n' {
+        return false;
+    }
+
+    let mut escaping = false;
+    for (index, &byte) in bytes.iter().enumerate() {
+        if escaping {
+            if byte == b'n' {
+                return index == bytes.len() - 1;
+            }
+            escaping = false;
+        } else if byte == b'\\' {
+            escaping = true;
+        }
+    }
+
+    false
+}