]> git.lizzy.rs Git - rust.git/blobdiff - clippy_lints/src/write.rs
Last PR adjustments
[rust.git] / clippy_lints / src / write.rs
index 06e7d70170171c97c0ffa5292fd738903407f830..df3350388817ed9ca3765d3bd6c1a45882611353 100644 (file)
@@ -1,12 +1,13 @@
 use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
 use clippy_utils::macros::{root_macro_call_first_node, FormatArgsExpn, MacroCall};
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::{expand_past_previous_comma, snippet_opt};
+use clippy_utils::{is_in_cfg_test, is_in_test_function};
 use rustc_ast::LitKind;
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind, HirIdMap, Impl, Item, ItemKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_session::{declare_tool_lint, impl_lint_pass};
-use rustc_span::{sym, BytePos, Span};
+use rustc_span::{sym, BytePos};
 
 declare_clippy_lint! {
     /// ### What it does
 #[derive(Default)]
 pub struct Write {
     in_debug_impl: bool,
+    allow_print_in_tests: bool,
+}
+
+impl Write {
+    pub fn new(allow_print_in_tests: bool) -> Self {
+        Self {
+            allow_print_in_tests,
+            ..Default::default()
+        }
+    }
 }
 
 impl_lint_pass!(Write => [
@@ -271,13 +282,15 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
             .as_ref()
             .map_or(false, |crate_name| crate_name == "build_script_build");
 
+        let allowed_in_tests = self.allow_print_in_tests
+            && (is_in_test_function(cx.tcx, expr.hir_id) || is_in_cfg_test(cx.tcx, expr.hir_id));
         match diag_name {
-            sym::print_macro | sym::println_macro => {
+            sym::print_macro | sym::println_macro if !allowed_in_tests => {
                 if !is_build_script {
                     span_lint(cx, PRINT_STDOUT, macro_call.span, &format!("use of `{name}!`"));
                 }
             },
-            sym::eprint_macro | sym::eprintln_macro => {
+            sym::eprint_macro | sym::eprintln_macro if !allowed_in_tests => {
                 span_lint(cx, PRINT_STDERR, macro_call.span, &format!("use of `{name}!`"));
             },
             sym::write_macro | sym::writeln_macro => {},
@@ -364,7 +377,7 @@ fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, macro_c
                     // print!("\n"), write!(f, "\n")
 
                     diag.multipart_suggestion(
-                        &format!("use `{name}ln!` instead"),
+                        format!("use `{name}ln!` instead"),
                         vec![(name_span, format!("{name}ln")), (format_string_span, String::new())],
                         Applicability::MachineApplicable,
                     );
@@ -375,7 +388,7 @@ fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, macro_c
                     let newline_span = format_string_span.with_lo(hi - BytePos(3)).with_hi(hi - BytePos(1));
 
                     diag.multipart_suggestion(
-                        &format!("use `{name}ln!` instead"),
+                        format!("use `{name}ln!` instead"),
                         vec![(name_span, format!("{name}ln")), (newline_span, String::new())],
                         Applicability::MachineApplicable,
                     );
@@ -475,11 +488,11 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, name: &
                 value.span,
                 "literal with an empty format string",
                 |diag| {
-                    if let Some(replacement) = replacement {
+                    if let Some(replacement) = replacement
                         // `format!("{}", "a")`, `format!("{named}", named = "b")
                         //              ~~~~~                      ~~~~~~~~~~~~~
-                        let value_span = expand_past_previous_comma(cx, value.span);
-
+                        && let Some(value_span) = format_args.value_with_prev_comma_span(value.hir_id)
+                    {
                         let replacement = replacement.replace('{', "{{").replace('}', "}}");
                         diag.multipart_suggestion(
                             "try this",
@@ -542,10 +555,3 @@ fn conservative_unescape(literal: &str) -> Result<String, UnescapeErr> {
 
     if err { Err(UnescapeErr::Lint) } else { Ok(unescaped) }
 }
-
-// Expand from `writeln!(o, "")` to `writeln!(o, "")`
-//                          ^^                 ^^^^
-fn expand_past_previous_comma(cx: &LateContext<'_>, span: Span) -> Span {
-    let extended = cx.sess().source_map().span_extend_to_prev_char(span, ',', true);
-    extended.with_lo(extended.lo() - BytePos(1))
-}