From 6fc6d87fd069470893b6a539a46fa1e2f10ae906 Mon Sep 17 00:00:00 2001
From: Alex Macleod <alex@macleod.io>
Date: Tue, 30 Aug 2022 12:20:49 +0000
Subject: [PATCH] Migrate write.rs to a late pass

---
 clippy_lints/src/format.rs                    |   2 +-
 clippy_lints/src/format_args.rs               |   2 +-
 clippy_lints/src/lib.register_all.rs          |   1 -
 clippy_lints/src/lib.register_lints.rs        |   1 -
 clippy_lints/src/lib.register_suspicious.rs   |   1 -
 clippy_lints/src/lib.rs                       |   3 +-
 clippy_lints/src/renamed_lints.rs             |   1 +
 clippy_lints/src/utils/conf.rs                |   2 +-
 clippy_lints/src/write.rs                     | 813 ++++++------------
 clippy_utils/src/macros.rs                    |  11 +-
 src/docs.rs                                   |   1 -
 .../positional_named_format_parameters.txt    |  15 -
 src/docs/print_literal.txt                    |   4 -
 src/docs/print_stderr.txt                     |   8 +-
 src/docs/print_stdout.txt                     |   8 +-
 src/docs/write_literal.txt                    |   4 -
 tests/ui/eprint_with_newline.rs               |  10 +-
 tests/ui/eprint_with_newline.stderr           |  18 +-
 tests/ui/format.fixed                         |   4 -
 tests/ui/format.rs                            |   4 -
 tests/ui/format.stderr                        |  44 +-
 .../positional_named_format_parameters.fixed  |  56 --
 .../ui/positional_named_format_parameters.rs  |  56 --
 .../positional_named_format_parameters.stderr | 418 ---------
 tests/ui/print_literal.rs                     |   2 +
 tests/ui/print_literal.stderr                 |  86 +-
 tests/ui/print_with_newline.rs                |  10 +-
 tests/ui/print_with_newline.stderr            |  18 +-
 tests/ui/println_empty_string.stderr          |  24 +-
 tests/ui/rename.fixed                         |   2 +
 tests/ui/rename.rs                            |   2 +
 tests/ui/rename.stderr                        |  82 +-
 tests/ui/write_literal.rs                     |   2 +
 tests/ui/write_literal.stderr                 |  86 +-
 tests/ui/write_literal_2.rs                   |   9 +-
 tests/ui/write_literal_2.stderr               |  80 +-
 tests/ui/write_with_newline.rs                |   8 +
 tests/ui/write_with_newline.stderr            |  38 +-
 tests/ui/writeln_empty_string.stderr          |  12 +-
 39 files changed, 597 insertions(+), 1351 deletions(-)
 delete mode 100644 src/docs/positional_named_format_parameters.txt
 delete mode 100644 tests/ui/positional_named_format_parameters.fixed
 delete mode 100644 tests/ui/positional_named_format_parameters.rs
 delete mode 100644 tests/ui/positional_named_format_parameters.stderr

diff --git a/clippy_lints/src/format.rs b/clippy_lints/src/format.rs
index 0c5851cdbed..f10d8256953 100644
--- a/clippy_lints/src/format.rs
+++ b/clippy_lints/src/format.rs
@@ -71,12 +71,12 @@ impl<'tcx> LateLintPass<'tcx> for UselessFormat {
             let value = arg.param.value;
             if_chain! {
                 if format_args.format_string.parts == [kw::Empty];
+                if arg.format.is_default();
                 if match cx.typeck_results().expr_ty(value).peel_refs().kind() {
                     ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(sym::String, adt.did()),
                     ty::Str => true,
                     _ => false,
                 };
-                if !arg.format.has_string_formatting();
                 then {
                     let is_new_string = match value.kind {
                         ExprKind::Binary(..) => true,
diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs
index 97024be16fa..e1c46fd0bfd 100644
--- a/clippy_lints/src/format_args.rs
+++ b/clippy_lints/src/format_args.rs
@@ -77,7 +77,7 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs {
             if let ExpnKind::Macro(_, name) = outermost_expn_data.kind;
             then {
                 for arg in &format_args.args {
-                    if arg.format.has_string_formatting() {
+                    if !arg.format.is_default() {
                         continue;
                     }
                     if is_aliased(&format_args, arg.param.value.hir_id) {
diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs
index 1f85382347a..39ab175341f 100644
--- a/clippy_lints/src/lib.register_all.rs
+++ b/clippy_lints/src/lib.register_all.rs
@@ -352,7 +352,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
     LintId::of(useless_conversion::USELESS_CONVERSION),
     LintId::of(vec::USELESS_VEC),
     LintId::of(vec_init_then_push::VEC_INIT_THEN_PUSH),
-    LintId::of(write::POSITIONAL_NAMED_FORMAT_PARAMETERS),
     LintId::of(write::PRINTLN_EMPTY_STRING),
     LintId::of(write::PRINT_LITERAL),
     LintId::of(write::PRINT_WITH_NEWLINE),
diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs
index 962e6722006..c16f2d86ae0 100644
--- a/clippy_lints/src/lib.register_lints.rs
+++ b/clippy_lints/src/lib.register_lints.rs
@@ -595,7 +595,6 @@ store.register_lints(&[
     vec_init_then_push::VEC_INIT_THEN_PUSH,
     wildcard_imports::ENUM_GLOB_USE,
     wildcard_imports::WILDCARD_IMPORTS,
-    write::POSITIONAL_NAMED_FORMAT_PARAMETERS,
     write::PRINTLN_EMPTY_STRING,
     write::PRINT_LITERAL,
     write::PRINT_STDERR,
diff --git a/clippy_lints/src/lib.register_suspicious.rs b/clippy_lints/src/lib.register_suspicious.rs
index 8f131bbf98b..f463ad9f6d2 100644
--- a/clippy_lints/src/lib.register_suspicious.rs
+++ b/clippy_lints/src/lib.register_suspicious.rs
@@ -36,5 +36,4 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec!
     LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
     LintId::of(swap_ptr_to_ref::SWAP_PTR_TO_REF),
     LintId::of(unused_peekable::UNUSED_PEEKABLE),
-    LintId::of(write::POSITIONAL_NAMED_FORMAT_PARAMETERS),
 ])
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index ec96999896e..83fdc15c9f0 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -40,7 +40,6 @@ extern crate rustc_lint;
 extern crate rustc_middle;
 extern crate rustc_mir_dataflow;
 extern crate rustc_parse;
-extern crate rustc_parse_format;
 extern crate rustc_session;
 extern crate rustc_span;
 extern crate rustc_target;
@@ -425,7 +424,6 @@ pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore, sess: &Se
         })
     });
 
-    store.register_pre_expansion_pass(|| Box::new(write::Write::default()));
     store.register_pre_expansion_pass(move || Box::new(attrs::EarlyAttributes { msrv }));
 }
 
@@ -879,6 +877,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
             ignore_publish: cargo_ignore_publish,
         })
     });
+    store.register_late_pass(|| Box::new(write::Write::default()));
     store.register_early_pass(|| Box::new(crate_in_macro_def::CrateInMacroDef));
     store.register_early_pass(|| Box::new(empty_structs_with_brackets::EmptyStructsWithBrackets));
     store.register_late_pass(|| Box::new(unnecessary_owned_empty_strings::UnnecessaryOwnedEmptyStrings));
diff --git a/clippy_lints/src/renamed_lints.rs b/clippy_lints/src/renamed_lints.rs
index 6bea6dc0773..d320eea1c37 100644
--- a/clippy_lints/src/renamed_lints.rs
+++ b/clippy_lints/src/renamed_lints.rs
@@ -36,6 +36,7 @@ pub static RENAMED_LINTS: &[(&str, &str)] = &[
     ("clippy::invalid_ref", "invalid_value"),
     ("clippy::mem_discriminant_non_enum", "enum_intrinsics_non_enums"),
     ("clippy::panic_params", "non_fmt_panics"),
+    ("clippy::positional_named_format_parameters", "named_arguments_used_positionally"),
     ("clippy::temporary_cstring_as_ptr", "temporary_cstring_as_ptr"),
     ("clippy::unknown_clippy_lints", "unknown_lints"),
     ("clippy::unused_label", "unused_labels"),
diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs
index a8500beb257..2be3fa99c81 100644
--- a/clippy_lints/src/utils/conf.rs
+++ b/clippy_lints/src/utils/conf.rs
@@ -476,7 +476,7 @@ pub fn format_error(error: Box<dyn Error>) -> String {
 
             let mut msg = String::from(prefix);
             for row in 0..rows {
-                write!(msg, "\n").unwrap();
+                writeln!(msg).unwrap();
                 for (column, column_width) in column_widths.iter().copied().enumerate() {
                     let index = column * rows + row;
                     let field = fields.get(index).copied().unwrap_or_default();
diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs
index 640a09a7a91..06e7d701701 100644
--- a/clippy_lints/src/write.rs
+++ b/clippy_lints/src/write.rs
@@ -1,20 +1,12 @@
-use std::borrow::Cow;
-use std::iter;
-use std::ops::{Deref, Range};
-
-use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
-use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability};
-use rustc_ast::ast::{Expr, ExprKind, Impl, Item, ItemKind, MacCall, Path, StrLit, StrStyle};
-use rustc_ast::ptr::P;
-use rustc_ast::token::{self, LitKind};
-use rustc_ast::tokenstream::TokenStream;
-use rustc_errors::{Applicability, DiagnosticBuilder};
-use rustc_lexer::unescape::{self, EscapeError};
-use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
-use rustc_parse::parser;
+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 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::symbol::{kw, Symbol};
-use rustc_span::{sym, BytePos, InnerSpan, Span, DUMMY_SP};
+use rustc_span::{sym, BytePos, Span};
 
 declare_clippy_lint! {
     /// ### What it does
@@ -74,13 +66,7 @@ declare_clippy_lint! {
     /// application and might forget to remove those prints afterward.
     ///
     /// ### Known problems
-    /// * Only catches `print!` and `println!` calls.
-    /// * The lint level is unaffected by crate attributes. The level can still
-    ///   be set for functions, modules and other items. To change the level for
-    ///   the entire crate, please use command line flags. More information and a
-    ///   configuration example can be found in [clippy#6610].
-    ///
-    /// [clippy#6610]: https://github.com/rust-lang/rust-clippy/issues/6610#issuecomment-977120558
+    /// Only catches `print!` and `println!` calls.
     ///
     /// ### Example
     /// ```rust
@@ -102,13 +88,7 @@ declare_clippy_lint! {
     /// application and might forget to remove those prints afterward.
     ///
     /// ### Known problems
-    /// * Only catches `eprint!` and `eprintln!` calls.
-    /// * The lint level is unaffected by crate attributes. The level can still
-    ///   be set for functions, modules and other items. To change the level for
-    ///   the entire crate, please use command line flags. More information and a
-    ///   configuration example can be found in [clippy#6610].
-    ///
-    /// [clippy#6610]: https://github.com/rust-lang/rust-clippy/issues/6610#issuecomment-977120558
+    /// Only catches `eprint!` and `eprintln!` calls.
     ///
     /// ### Example
     /// ```rust
@@ -149,10 +129,6 @@ declare_clippy_lint! {
     /// (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary
     /// (i.e., just put the literal in the format string)
     ///
-    /// ### Known problems
-    /// Will also warn with macro calls as arguments that expand to literals
-    /// -- e.g., `println!("{}", env!("FOO"))`.
-    ///
     /// ### Example
     /// ```rust
     /// println!("{}", "foo");
@@ -234,10 +210,6 @@ declare_clippy_lint! {
     /// (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary
     /// (i.e., just put the literal in the format string)
     ///
-    /// ### Known problems
-    /// Will also warn with macro calls as arguments that expand to literals
-    /// -- e.g., `writeln!(buf, "{}", env!("FOO"))`.
-    ///
     /// ### Example
     /// ```rust
     /// # use std::fmt::Write;
@@ -257,28 +229,6 @@ declare_clippy_lint! {
     "writing a literal with a format string"
 }
 
-declare_clippy_lint! {
-    /// ### What it does
-    /// This lint warns when a named parameter in a format string is used as a positional one.
-    ///
-    /// ### Why is this bad?
-    /// It may be confused for an assignment and obfuscates which parameter is being used.
-    ///
-    /// ### Example
-    /// ```rust
-    /// println!("{}", x = 10);
-    /// ```
-    ///
-    /// Use instead:
-    /// ```rust
-    /// println!("{x}", x = 10);
-    /// ```
-    #[clippy::version = "1.63.0"]
-    pub POSITIONAL_NAMED_FORMAT_PARAMETERS,
-    suspicious,
-    "named parameter in a format string is used positionally"
-}
-
 #[derive(Default)]
 pub struct Write {
     in_debug_impl: bool,
@@ -294,537 +244,308 @@ impl_lint_pass!(Write => [
     WRITE_WITH_NEWLINE,
     WRITELN_EMPTY_STRING,
     WRITE_LITERAL,
-    POSITIONAL_NAMED_FORMAT_PARAMETERS,
 ]);
 
-impl EarlyLintPass for Write {
-    fn check_item(&mut self, _: &EarlyContext<'_>, item: &Item) {
-        if let ItemKind::Impl(box Impl {
-            of_trait: Some(trait_ref),
-            ..
-        }) = &item.kind
-        {
-            let trait_name = trait_ref
-                .path
-                .segments
-                .iter()
-                .last()
-                .expect("path has at least one segment")
-                .ident
-                .name;
-            if trait_name == sym::Debug {
-                self.in_debug_impl = true;
-            }
+impl<'tcx> LateLintPass<'tcx> for Write {
+    fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
+        if is_debug_impl(cx, item) {
+            self.in_debug_impl = true;
         }
     }
 
-    fn check_item_post(&mut self, _: &EarlyContext<'_>, _: &Item) {
-        self.in_debug_impl = false;
+    fn check_item_post(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
+        if is_debug_impl(cx, item) {
+            self.in_debug_impl = false;
+        }
     }
 
-    fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &MacCall) {
-        fn is_build_script(cx: &EarlyContext<'_>) -> bool {
-            // Cargo sets the crate name for build scripts to `build_script_build`
-            cx.sess()
-                .opts
-                .crate_name
-                .as_ref()
-                .map_or(false, |crate_name| crate_name == "build_script_build")
-        }
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+        let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return };
+        let Some(diag_name) = cx.tcx.get_diagnostic_name(macro_call.def_id) else { return };
+        let Some(name) = diag_name.as_str().strip_suffix("_macro") else { return };
 
-        if mac.path == sym!(print) {
-            if !is_build_script(cx) {
-                span_lint(cx, PRINT_STDOUT, mac.span(), "use of `print!`");
-            }
-            self.lint_print_with_newline(cx, mac);
-        } else if mac.path == sym!(println) {
-            if !is_build_script(cx) {
-                span_lint(cx, PRINT_STDOUT, mac.span(), "use of `println!`");
-            }
-            self.lint_println_empty_string(cx, mac);
-        } else if mac.path == sym!(eprint) {
-            span_lint(cx, PRINT_STDERR, mac.span(), "use of `eprint!`");
-            self.lint_print_with_newline(cx, mac);
-        } else if mac.path == sym!(eprintln) {
-            span_lint(cx, PRINT_STDERR, mac.span(), "use of `eprintln!`");
-            self.lint_println_empty_string(cx, mac);
-        } else if mac.path == sym!(write) {
-            if let (Some(fmt_str), dest) = self.check_tts(cx, mac.args.inner_tokens(), true) {
-                if check_newlines(&fmt_str) {
-                    let (nl_span, only_nl) = newline_span(&fmt_str);
-                    let nl_span = match (dest, only_nl) {
-                        // Special case of `write!(buf, "\n")`: Mark everything from the end of
-                        // `buf` for removal so no trailing comma [`writeln!(buf, )`] remains.
-                        (Some(dest_expr), true) => nl_span.with_lo(dest_expr.span.hi()),
-                        _ => nl_span,
-                    };
-                    span_lint_and_then(
-                        cx,
-                        WRITE_WITH_NEWLINE,
-                        mac.span(),
-                        "using `write!()` with a format string that ends in a single newline",
-                        |err| {
-                            err.multipart_suggestion(
-                                "use `writeln!()` instead",
-                                vec![(mac.path.span, String::from("writeln")), (nl_span, String::new())],
-                                Applicability::MachineApplicable,
-                            );
-                        },
-                    );
+        let is_build_script = cx
+            .sess()
+            .opts
+            .crate_name
+            .as_ref()
+            .map_or(false, |crate_name| crate_name == "build_script_build");
+
+        match diag_name {
+            sym::print_macro | sym::println_macro => {
+                if !is_build_script {
+                    span_lint(cx, PRINT_STDOUT, macro_call.span, &format!("use of `{name}!`"));
                 }
-            }
-        } else if mac.path == sym!(writeln) {
-            if let (Some(fmt_str), expr) = self.check_tts(cx, mac.args.inner_tokens(), true) {
-                if fmt_str.symbol == kw::Empty {
-                    let mut applicability = Applicability::MachineApplicable;
-                    let suggestion = if let Some(e) = expr {
-                        snippet_with_applicability(cx, e.span, "v", &mut applicability)
-                    } else {
-                        applicability = Applicability::HasPlaceholders;
-                        Cow::Borrowed("v")
-                    };
+            },
+            sym::eprint_macro | sym::eprintln_macro => {
+                span_lint(cx, PRINT_STDERR, macro_call.span, &format!("use of `{name}!`"));
+            },
+            sym::write_macro | sym::writeln_macro => {},
+            _ => return,
+        }
 
-                    span_lint_and_sugg(
-                        cx,
-                        WRITELN_EMPTY_STRING,
-                        mac.span(),
-                        format!("using `writeln!({}, \"\")`", suggestion).as_str(),
-                        "replace it with",
-                        format!("writeln!({})", suggestion),
-                        applicability,
-                    );
+        let Some(format_args) = FormatArgsExpn::find_nested(cx, expr, macro_call.expn) else { return };
+
+        // ignore `writeln!(w)` and `write!(v, some_macro!())`
+        if format_args.format_string.span.from_expansion() {
+            return;
+        }
+
+        match diag_name {
+            sym::print_macro | sym::eprint_macro | sym::write_macro => {
+                check_newline(cx, &format_args, &macro_call, name);
+            },
+            sym::println_macro | sym::eprintln_macro | sym::writeln_macro => {
+                check_empty_string(cx, &format_args, &macro_call, name);
+            },
+            _ => {},
+        }
+
+        check_literal(cx, &format_args, name);
+
+        if !self.in_debug_impl {
+            for arg in &format_args.args {
+                if arg.format.r#trait == sym::Debug {
+                    span_lint(cx, USE_DEBUG, arg.span, "use of `Debug`-based formatting");
                 }
             }
         }
     }
 }
-
-/// Given a format string that ends in a newline and its span, calculates the span of the
-/// newline, or the format string itself if the format string consists solely of a newline.
-/// Return this and a boolean indicating whether it only consisted of a newline.
-fn newline_span(fmtstr: &StrLit) -> (Span, bool) {
-    let sp = fmtstr.span;
-    let contents = fmtstr.symbol.as_str();
-
-    if contents == r"\n" {
-        return (sp, true);
-    }
-
-    let newline_sp_hi = sp.hi()
-        - match fmtstr.style {
-            StrStyle::Cooked => BytePos(1),
-            StrStyle::Raw(hashes) => BytePos((1 + hashes).into()),
-        };
-
-    let newline_sp_len = if contents.ends_with('\n') {
-        BytePos(1)
-    } else if contents.ends_with(r"\n") {
-        BytePos(2)
+fn is_debug_impl(cx: &LateContext<'_>, item: &Item<'_>) -> bool {
+    if let ItemKind::Impl(Impl { of_trait: Some(trait_ref), .. }) = &item.kind
+        && let Some(trait_id) = trait_ref.trait_def_id()
+    {
+        cx.tcx.is_diagnostic_item(sym::Debug, trait_id)
     } else {
-        panic!("expected format string to contain a newline");
+        false
+    }
+}
+
+fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, macro_call: &MacroCall, name: &str) {
+    let format_string_parts = &format_args.format_string.parts;
+    let mut format_string_span = format_args.format_string.span;
+
+    let Some(last) = format_string_parts.last() else { return };
+
+    let count_vertical_whitespace = || {
+        format_string_parts
+            .iter()
+            .flat_map(|part| part.as_str().chars())
+            .filter(|ch| matches!(ch, '\r' | '\n'))
+            .count()
     };
 
-    (sp.with_lo(newline_sp_hi - newline_sp_len).with_hi(newline_sp_hi), false)
-}
+    if last.as_str().ends_with('\n')
+        // ignore format strings with other internal vertical whitespace
+        && count_vertical_whitespace() == 1
 
-/// Stores a list of replacement spans for each argument, but only if all the replacements used an
-/// empty format string.
-#[derive(Default)]
-struct SimpleFormatArgs {
-    unnamed: Vec<Vec<Span>>,
-    complex_unnamed: Vec<Vec<Span>>,
-    named: Vec<(Symbol, Vec<Span>)>,
-}
-impl SimpleFormatArgs {
-    fn get_unnamed(&self) -> impl Iterator<Item = &[Span]> {
-        self.unnamed.iter().map(|x| match x.as_slice() {
-            // Ignore the dummy span added from out of order format arguments.
-            [DUMMY_SP] => &[],
-            x => x,
-        })
-    }
+        // ignore trailing arguments: `print!("Issue\n{}", 1265);`
+        && format_string_parts.len() > format_args.args.len()
+    {
+        let lint = if name == "write" {
+            format_string_span = expand_past_previous_comma(cx, format_string_span);
 
-    fn get_complex_unnamed(&self) -> impl Iterator<Item = &[Span]> {
-        self.complex_unnamed.iter().map(Vec::as_slice)
-    }
-
-    fn get_named(&self, n: &Path) -> &[Span] {
-        self.named.iter().find(|x| *n == x.0).map_or(&[], |x| x.1.as_slice())
-    }
-
-    fn push(&mut self, arg: rustc_parse_format::Argument<'_>, span: Span) {
-        use rustc_parse_format::{
-            AlignUnknown, ArgumentImplicitlyIs, ArgumentIs, ArgumentNamed, CountImplied, FormatSpec,
+            WRITE_WITH_NEWLINE
+        } else {
+            PRINT_WITH_NEWLINE
         };
 
-        const SIMPLE: FormatSpec<'_> = FormatSpec {
-            fill: None,
-            align: AlignUnknown,
-            flags: 0,
-            precision: CountImplied,
-            precision_span: None,
-            width: CountImplied,
-            width_span: None,
-            ty: "",
-            ty_span: None,
-        };
+        span_lint_and_then(
+            cx,
+            lint,
+            macro_call.span,
+            &format!("using `{name}!()` with a format string that ends in a single newline"),
+            |diag| {
+                let name_span = cx.sess().source_map().span_until_char(macro_call.span, '!');
+                let Some(format_snippet) = snippet_opt(cx, format_string_span) else { return };
 
-        match arg.position {
-            ArgumentIs(n) | ArgumentImplicitlyIs(n) => {
-                if self.unnamed.len() <= n {
-                    // Use a dummy span to mark all unseen arguments.
-                    self.unnamed.resize_with(n, || vec![DUMMY_SP]);
-                    if arg.format == SIMPLE {
-                        self.unnamed.push(vec![span]);
-                    } else {
-                        self.unnamed.push(Vec::new());
-                    }
-                } else {
-                    let args = &mut self.unnamed[n];
-                    match (args.as_mut_slice(), arg.format == SIMPLE) {
-                        // A non-empty format string has been seen already.
-                        ([], _) => (),
-                        // Replace the dummy span, if it exists.
-                        ([dummy @ DUMMY_SP], true) => *dummy = span,
-                        ([_, ..], true) => args.push(span),
-                        ([_, ..], false) => *args = Vec::new(),
-                    }
+                if format_string_parts.len() == 1 && last.as_str() == "\n" {
+                    // print!("\n"), write!(f, "\n")
+
+                    diag.multipart_suggestion(
+                        &format!("use `{name}ln!` instead"),
+                        vec![(name_span, format!("{name}ln")), (format_string_span, String::new())],
+                        Applicability::MachineApplicable,
+                    );
+                } else if format_snippet.ends_with("\\n\"") {
+                    // print!("...\n"), write!(f, "...\n")
+
+                    let hi = format_string_span.hi();
+                    let newline_span = format_string_span.with_lo(hi - BytePos(3)).with_hi(hi - BytePos(1));
+
+                    diag.multipart_suggestion(
+                        &format!("use `{name}ln!` instead"),
+                        vec![(name_span, format!("{name}ln")), (newline_span, String::new())],
+                        Applicability::MachineApplicable,
+                    );
                 }
             },
-            ArgumentNamed(n) => {
-                let n = Symbol::intern(n);
-                if let Some(x) = self.named.iter_mut().find(|x| x.0 == n) {
-                    match x.1.as_slice() {
-                        // A non-empty format string has been seen already.
-                        [] => (),
-                        [_, ..] if arg.format == SIMPLE => x.1.push(span),
-                        [_, ..] => x.1 = Vec::new(),
-                    }
-                } else if arg.format == SIMPLE {
-                    self.named.push((n, vec![span]));
-                } else {
-                    self.named.push((n, Vec::new()));
-                }
-            },
-        };
-    }
-
-    fn push_to_complex(&mut self, span: Span, position: usize) {
-        if self.complex_unnamed.len() <= position {
-            self.complex_unnamed.resize_with(position, Vec::new);
-            self.complex_unnamed.push(vec![span]);
-        } else {
-            let args: &mut Vec<Span> = &mut self.complex_unnamed[position];
-            args.push(span);
-        }
-    }
-
-    fn push_complex(
-        &mut self,
-        cx: &EarlyContext<'_>,
-        arg: rustc_parse_format::Argument<'_>,
-        str_lit_span: Span,
-        fmt_span: Span,
-    ) {
-        use rustc_parse_format::{ArgumentImplicitlyIs, ArgumentIs, CountIsParam, CountIsStar};
-
-        let snippet = snippet_opt(cx, fmt_span);
-
-        let end = snippet
-            .as_ref()
-            .and_then(|s| s.find(':'))
-            .or_else(|| fmt_span.hi().0.checked_sub(fmt_span.lo().0 + 1).map(|u| u as usize));
-
-        if let (ArgumentIs(n) | ArgumentImplicitlyIs(n), Some(end)) = (arg.position, end) {
-            let span = fmt_span.from_inner(InnerSpan::new(1, end));
-            self.push_to_complex(span, n);
-        };
-
-        if let (CountIsParam(n) | CountIsStar(n), Some(span)) = (arg.format.precision, arg.format.precision_span) {
-            // We need to do this hack as precision spans should be converted from .* to .foo$
-            let hack = if snippet.as_ref().and_then(|s| s.find('*')).is_some() {
-                0
-            } else {
-                1
-            };
-
-            let span = str_lit_span.from_inner(InnerSpan {
-                start: span.start + 1,
-                end: span.end - hack,
-            });
-            self.push_to_complex(span, n);
-        };
-
-        if let (CountIsParam(n), Some(span)) = (arg.format.width, arg.format.width_span) {
-            let span = str_lit_span.from_inner(InnerSpan {
-                start: span.start,
-                end: span.end - 1,
-            });
-            self.push_to_complex(span, n);
-        };
+        );
     }
 }
 
-impl Write {
-    /// Parses a format string into a collection of spans for each argument. This only keeps track
-    /// of empty format arguments. Will also lint usages of debug format strings outside of debug
-    /// impls.
-    fn parse_fmt_string(&self, cx: &EarlyContext<'_>, str_lit: &StrLit) -> Option<SimpleFormatArgs> {
-        use rustc_parse_format::{ParseMode, Parser, Piece};
+fn check_empty_string(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, macro_call: &MacroCall, name: &str) {
+    if let [part] = &format_args.format_string.parts[..]
+        && let mut span = format_args.format_string.span
+        && part.as_str() == "\n"
+    {
+        let lint = if name == "writeln" {
+            span = expand_past_previous_comma(cx, span);
 
-        let str_sym = str_lit.symbol_unescaped.as_str();
-        let style = match str_lit.style {
-            StrStyle::Cooked => None,
-            StrStyle::Raw(n) => Some(n as usize),
-        };
-
-        let mut parser = Parser::new(str_sym, style, snippet_opt(cx, str_lit.span), false, ParseMode::Format);
-        let mut args = SimpleFormatArgs::default();
-
-        while let Some(arg) = parser.next() {
-            let arg = match arg {
-                Piece::String(_) => continue,
-                Piece::NextArgument(arg) => arg,
-            };
-            let span = parser
-                .arg_places
-                .last()
-                .map_or(DUMMY_SP, |&x| str_lit.span.from_inner(InnerSpan::new(x.start, x.end)));
-
-            if !self.in_debug_impl && arg.format.ty == "?" {
-                // FIXME: modify rustc's fmt string parser to give us the current span
-                span_lint(cx, USE_DEBUG, span, "use of `Debug`-based formatting");
-            }
-            args.push(arg, span);
-            args.push_complex(cx, arg, str_lit.span, span);
-        }
-
-        parser.errors.is_empty().then_some(args)
-    }
-
-    /// Checks the arguments of `print[ln]!` and `write[ln]!` calls. It will return a tuple of two
-    /// `Option`s. The first `Option` of the tuple is the macro's format string. It includes
-    /// the contents of the string, whether it's a raw string, and the span of the literal in the
-    /// source. The second `Option` in the tuple is, in the `write[ln]!` case, the expression the
-    /// `format_str` should be written to.
-    ///
-    /// Example:
-    ///
-    /// Calling this function on
-    /// ```rust
-    /// # use std::fmt::Write;
-    /// # let mut buf = String::new();
-    /// # let something = "something";
-    /// writeln!(buf, "string to write: {}", something);
-    /// ```
-    /// will return
-    /// ```rust,ignore
-    /// (Some("string to write: {}"), Some(buf))
-    /// ```
-    fn check_tts<'a>(&self, cx: &EarlyContext<'a>, tts: TokenStream, is_write: bool) -> (Option<StrLit>, Option<Expr>) {
-        let mut parser = parser::Parser::new(&cx.sess().parse_sess, tts, false, None);
-        let expr = if is_write {
-            match parser
-                .parse_expr()
-                .map(rustc_ast::ptr::P::into_inner)
-                .map_err(DiagnosticBuilder::cancel)
-            {
-                // write!(e, ...)
-                Ok(p) if parser.eat(&token::Comma) => Some(p),
-                // write!(e) or error
-                e => return (None, e.ok()),
-            }
+            WRITELN_EMPTY_STRING
         } else {
-            None
+            PRINTLN_EMPTY_STRING
         };
 
-        let fmtstr = match parser.parse_str_lit() {
-            Ok(fmtstr) => fmtstr,
-            Err(_) => return (None, expr),
-        };
-
-        let args = match self.parse_fmt_string(cx, &fmtstr) {
-            Some(args) => args,
-            None => return (Some(fmtstr), expr),
-        };
-
-        let lint = if is_write { WRITE_LITERAL } else { PRINT_LITERAL };
-        let mut unnamed_args = args.get_unnamed();
-        let mut complex_unnamed_args = args.get_complex_unnamed();
-        loop {
-            if !parser.eat(&token::Comma) {
-                return (Some(fmtstr), expr);
-            }
-
-            let comma_span = parser.prev_token.span;
-            let token_expr = if let Ok(expr) = parser.parse_expr().map_err(DiagnosticBuilder::cancel) {
-                expr
-            } else {
-                return (Some(fmtstr), None);
-            };
-            let complex_unnamed_arg = complex_unnamed_args.next();
-
-            let (fmt_spans, lit) = match &token_expr.kind {
-                ExprKind::Lit(lit) => (unnamed_args.next().unwrap_or(&[]), lit),
-                ExprKind::Assign(lhs, rhs, _) => {
-                    if let Some(span) = complex_unnamed_arg {
-                        for x in span {
-                            Self::report_positional_named_param(cx, *x, lhs, rhs);
-                        }
-                    }
-                    match (&lhs.kind, &rhs.kind) {
-                        (ExprKind::Path(_, p), ExprKind::Lit(lit)) => (args.get_named(p), lit),
-                        _ => continue,
-                    }
-                },
-                _ => {
-                    unnamed_args.next();
-                    continue;
-                },
-            };
-
-            let replacement: String = match lit.token_lit.kind {
-                LitKind::StrRaw(_) | LitKind::ByteStrRaw(_) if matches!(fmtstr.style, StrStyle::Raw(_)) => {
-                    lit.token_lit.symbol.as_str().replace('{', "{{").replace('}', "}}")
-                },
-                LitKind::Str | LitKind::ByteStr if matches!(fmtstr.style, StrStyle::Cooked) => {
-                    lit.token_lit.symbol.as_str().replace('{', "{{").replace('}', "}}")
-                },
-                LitKind::StrRaw(_)
-                | LitKind::Str
-                | LitKind::ByteStrRaw(_)
-                | LitKind::ByteStr
-                | LitKind::Integer
-                | LitKind::Float
-                | LitKind::Err => continue,
-                LitKind::Byte | LitKind::Char => match lit.token_lit.symbol.as_str() {
-                    "\"" if matches!(fmtstr.style, StrStyle::Cooked) => "\\\"",
-                    "\"" if matches!(fmtstr.style, StrStyle::Raw(0)) => continue,
-                    "\\\\" if matches!(fmtstr.style, StrStyle::Raw(_)) => "\\",
-                    "\\'" => "'",
-                    "{" => "{{",
-                    "}" => "}}",
-                    x if matches!(fmtstr.style, StrStyle::Raw(_)) && x.starts_with('\\') => continue,
-                    x => x,
-                }
-                .into(),
-                LitKind::Bool => lit.token_lit.symbol.as_str().deref().into(),
-            };
-
-            if !fmt_spans.is_empty() {
-                span_lint_and_then(
-                    cx,
-                    lint,
-                    token_expr.span,
-                    "literal with an empty format string",
-                    |diag| {
-                        diag.multipart_suggestion(
-                            "try this",
-                            iter::once((comma_span.to(token_expr.span), String::new()))
-                                .chain(fmt_spans.iter().copied().zip(iter::repeat(replacement)))
-                                .collect(),
-                            Applicability::MachineApplicable,
-                        );
-                    },
-                );
-            }
-        }
-    }
-
-    fn report_positional_named_param(cx: &EarlyContext<'_>, span: Span, lhs: &P<Expr>, _rhs: &P<Expr>) {
-        if let ExprKind::Path(_, _p) = &lhs.kind {
-            let mut applicability = Applicability::MachineApplicable;
-            let name = snippet_with_applicability(cx, lhs.span, "name", &mut applicability);
-            // We need to do this hack as precision spans should be converted from .* to .foo$
-            let hack = snippet(cx, span, "").contains('*');
-
-            span_lint_and_sugg(
-                cx,
-                POSITIONAL_NAMED_FORMAT_PARAMETERS,
-                span,
-                &format!("named parameter {} is used as a positional parameter", name),
-                "replace it with",
-                if hack {
-                    format!("{}$", name)
-                } else {
-                    format!("{}", name)
-                },
-                applicability,
-            );
-        };
-    }
-
-    fn lint_println_empty_string(&self, cx: &EarlyContext<'_>, mac: &MacCall) {
-        if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) {
-            if fmt_str.symbol == kw::Empty {
-                let name = mac.path.segments[0].ident.name;
-                span_lint_and_sugg(
-                    cx,
-                    PRINTLN_EMPTY_STRING,
-                    mac.span(),
-                    &format!("using `{}!(\"\")`", name),
-                    "replace it with",
-                    format!("{}!()", name),
+        span_lint_and_then(
+            cx,
+            lint,
+            macro_call.span,
+            &format!("empty string literal in `{name}!`"),
+            |diag| {
+                diag.span_suggestion(
+                    span,
+                    "remove the empty string",
+                    String::new(),
                     Applicability::MachineApplicable,
                 );
-            }
-        }
+            },
+        );
+    }
+}
+
+fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, name: &str) {
+    let mut counts = HirIdMap::<usize>::default();
+    for param in format_args.params() {
+        *counts.entry(param.value.hir_id).or_default() += 1;
     }
 
-    fn lint_print_with_newline(&self, cx: &EarlyContext<'_>, mac: &MacCall) {
-        if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) {
-            if check_newlines(&fmt_str) {
-                let name = mac.path.segments[0].ident.name;
-                let suggested = format!("{}ln", name);
-                span_lint_and_then(
-                    cx,
-                    PRINT_WITH_NEWLINE,
-                    mac.span(),
-                    &format!("using `{}!()` with a format string that ends in a single newline", name),
-                    |err| {
-                        err.multipart_suggestion(
-                            &format!("use `{}!` instead", suggested),
-                            vec![(mac.path.span, suggested), (newline_span(&fmt_str).0, String::new())],
+    for arg in &format_args.args {
+        let value = arg.param.value;
+
+        if counts[&value.hir_id] == 1
+            && arg.format.is_default()
+            && let ExprKind::Lit(lit) = &value.kind
+            && !value.span.from_expansion()
+            && let Some(value_string) = snippet_opt(cx, value.span)
+        {
+            let (replacement, replace_raw) = match lit.node {
+                LitKind::Str(..) => extract_str_literal(&value_string),
+                LitKind::Char(ch) => (
+                    match ch {
+                        '"' => "\\\"",
+                        '\'' => "'",
+                        _ => &value_string[1..value_string.len() - 1],
+                    }
+                    .to_string(),
+                    false,
+                ),
+                LitKind::Bool(b) => (b.to_string(), false),
+                _ => continue,
+            };
+
+            let lint = if name.starts_with("write") {
+                WRITE_LITERAL
+            } else {
+                PRINT_LITERAL
+            };
+
+            let format_string_is_raw = format_args.format_string.style.is_some();
+            let replacement = match (format_string_is_raw, replace_raw) {
+                (false, false) => Some(replacement),
+                (false, true) => Some(replacement.replace('"', "\\\"").replace('\\', "\\\\")),
+                (true, false) => match conservative_unescape(&replacement) {
+                    Ok(unescaped) => Some(unescaped),
+                    Err(UnescapeErr::Lint) => None,
+                    Err(UnescapeErr::Ignore) => continue,
+                },
+                (true, true) => {
+                    if replacement.contains(['#', '"']) {
+                        None
+                    } else {
+                        Some(replacement)
+                    }
+                },
+            };
+
+            span_lint_and_then(
+                cx,
+                lint,
+                value.span,
+                "literal with an empty format string",
+                |diag| {
+                    if let Some(replacement) = replacement {
+                        // `format!("{}", "a")`, `format!("{named}", named = "b")
+                        //              ~~~~~                      ~~~~~~~~~~~~~
+                        let value_span = expand_past_previous_comma(cx, value.span);
+
+                        let replacement = replacement.replace('{', "{{").replace('}', "}}");
+                        diag.multipart_suggestion(
+                            "try this",
+                            vec![(arg.span, replacement), (value_span, String::new())],
                             Applicability::MachineApplicable,
                         );
-                    },
-                );
-            }
+                    }
+                },
+            );
         }
     }
 }
 
-/// Checks if the format string contains a single newline that terminates it.
+/// Removes the raw marker, `#`s and quotes from a str, and returns if the literal is raw
 ///
-/// Literal and escaped newlines are both checked (only literal for raw strings).
-fn check_newlines(fmtstr: &StrLit) -> bool {
-    let mut has_internal_newline = false;
-    let mut last_was_cr = false;
-    let mut should_lint = false;
-
-    let contents = fmtstr.symbol.as_str();
-
-    let mut cb = |r: Range<usize>, c: Result<char, EscapeError>| {
-        let c = match c {
-            Ok(c) => c,
-            Err(e) if !e.is_fatal() => return,
-            Err(e) => panic!("{:?}", e),
-        };
-
-        if r.end == contents.len() && c == '\n' && !last_was_cr && !has_internal_newline {
-            should_lint = true;
-        } else {
-            last_was_cr = c == '\r';
-            if c == '\n' {
-                has_internal_newline = true;
-            }
-        }
+/// `r#"a"#` -> (`a`, true)
+///
+/// `"b"` -> (`b`, false)
+fn extract_str_literal(literal: &str) -> (String, bool) {
+    let (literal, raw) = match literal.strip_prefix('r') {
+        Some(stripped) => (stripped.trim_matches('#'), true),
+        None => (literal, false),
     };
 
-    match fmtstr.style {
-        StrStyle::Cooked => unescape::unescape_literal(contents, unescape::Mode::Str, &mut cb),
-        StrStyle::Raw(_) => unescape::unescape_literal(contents, unescape::Mode::RawStr, &mut cb),
+    (literal[1..literal.len() - 1].to_string(), raw)
+}
+
+enum UnescapeErr {
+    /// Should still be linted, can be manually resolved by author, e.g.
+    ///
+    /// ```ignore
+    /// print!(r"{}", '"');
+    /// ```
+    Lint,
+    /// Should not be linted, e.g.
+    ///
+    /// ```ignore
+    /// print!(r"{}", '\r');
+    /// ```
+    Ignore,
+}
+
+/// Unescape a normal string into a raw string
+fn conservative_unescape(literal: &str) -> Result<String, UnescapeErr> {
+    let mut unescaped = String::with_capacity(literal.len());
+    let mut chars = literal.chars();
+    let mut err = false;
+
+    while let Some(ch) = chars.next() {
+        match ch {
+            '#' => err = true,
+            '\\' => match chars.next() {
+                Some('\\') => unescaped.push('\\'),
+                Some('"') => err = true,
+                _ => return Err(UnescapeErr::Ignore),
+            },
+            _ => unescaped.push(ch),
+        }
     }
 
-    should_lint
+    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))
 }
diff --git a/clippy_utils/src/macros.rs b/clippy_utils/src/macros.rs
index bd89ff977f8..8855b739480 100644
--- a/clippy_utils/src/macros.rs
+++ b/clippy_utils/src/macros.rs
@@ -711,9 +711,14 @@ impl<'tcx> FormatSpec<'tcx> {
         })
     }
 
-    /// Returns true if this format spec would change the contents of a string when formatted
-    pub fn has_string_formatting(&self) -> bool {
-        self.r#trait != sym::Display || !self.width.is_implied() || !self.precision.is_implied()
+    /// Returns true if this format spec is unchanged from the default. e.g. returns true for `{}`,
+    /// `{foo}` and `{2}`, but false for `{:?}`, `{foo:5}` and `{3:.5}`
+    pub fn is_default(&self) -> bool {
+        self.r#trait == sym::Display
+            && self.width.is_implied()
+            && self.precision.is_implied()
+            && self.align == Alignment::AlignUnknown
+            && self.flags == 0
     }
 }
 
diff --git a/src/docs.rs b/src/docs.rs
index f3a5048e7fa..9f6f1fb4727 100644
--- a/src/docs.rs
+++ b/src/docs.rs
@@ -391,7 +391,6 @@ docs! {
     "partialeq_to_none",
     "path_buf_push_overwrite",
     "pattern_type_mismatch",
-    "positional_named_format_parameters",
     "possible_missing_comma",
     "precedence",
     "print_in_format_impl",
diff --git a/src/docs/positional_named_format_parameters.txt b/src/docs/positional_named_format_parameters.txt
deleted file mode 100644
index e391d240667..00000000000
--- a/src/docs/positional_named_format_parameters.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-### What it does
-This lint warns when a named parameter in a format string is used as a positional one.
-
-### Why is this bad?
-It may be confused for an assignment and obfuscates which parameter is being used.
-
-### Example
-```
-println!("{}", x = 10);
-```
-
-Use instead:
-```
-println!("{x}", x = 10);
-```
\ No newline at end of file
diff --git a/src/docs/print_literal.txt b/src/docs/print_literal.txt
index 160073414f9..a6252a68780 100644
--- a/src/docs/print_literal.txt
+++ b/src/docs/print_literal.txt
@@ -6,10 +6,6 @@ Using literals as `println!` args is inefficient
 (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary
 (i.e., just put the literal in the format string)
 
-### Known problems
-Will also warn with macro calls as arguments that expand to literals
--- e.g., `println!("{}", env!("FOO"))`.
-
 ### Example
 ```
 println!("{}", "foo");
diff --git a/src/docs/print_stderr.txt b/src/docs/print_stderr.txt
index fc14511cd6a..9c6edeeef12 100644
--- a/src/docs/print_stderr.txt
+++ b/src/docs/print_stderr.txt
@@ -7,13 +7,7 @@ People often print on *stderr* while debugging an
 application and might forget to remove those prints afterward.
 
 ### Known problems
-* Only catches `eprint!` and `eprintln!` calls.
-* The lint level is unaffected by crate attributes. The level can still
-  be set for functions, modules and other items. To change the level for
-  the entire crate, please use command line flags. More information and a
-  configuration example can be found in [clippy#6610].
-
-[clippy#6610]: https://github.com/rust-lang/rust-clippy/issues/6610#issuecomment-977120558
+Only catches `eprint!` and `eprintln!` calls.
 
 ### Example
 ```
diff --git a/src/docs/print_stdout.txt b/src/docs/print_stdout.txt
index 6c9a4b98e1e..d2cbd811d1b 100644
--- a/src/docs/print_stdout.txt
+++ b/src/docs/print_stdout.txt
@@ -7,13 +7,7 @@ People often print on *stdout* while debugging an
 application and might forget to remove those prints afterward.
 
 ### Known problems
-* Only catches `print!` and `println!` calls.
-* The lint level is unaffected by crate attributes. The level can still
-  be set for functions, modules and other items. To change the level for
-  the entire crate, please use command line flags. More information and a
-  configuration example can be found in [clippy#6610].
-
-[clippy#6610]: https://github.com/rust-lang/rust-clippy/issues/6610#issuecomment-977120558
+Only catches `print!` and `println!` calls.
 
 ### Example
 ```
diff --git a/src/docs/write_literal.txt b/src/docs/write_literal.txt
index 9c41a48f9f7..a7a884d0871 100644
--- a/src/docs/write_literal.txt
+++ b/src/docs/write_literal.txt
@@ -6,10 +6,6 @@ Using literals as `writeln!` args is inefficient
 (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary
 (i.e., just put the literal in the format string)
 
-### Known problems
-Will also warn with macro calls as arguments that expand to literals
--- e.g., `writeln!(buf, "{}", env!("FOO"))`.
-
 ### Example
 ```
 writeln!(buf, "{}", "foo");
diff --git a/tests/ui/eprint_with_newline.rs b/tests/ui/eprint_with_newline.rs
index 8df32649ad9..de5e121be87 100644
--- a/tests/ui/eprint_with_newline.rs
+++ b/tests/ui/eprint_with_newline.rs
@@ -45,5 +45,13 @@ fn main() {
     eprint!("\r\n");
     eprint!("foo\r\n");
     eprint!("\\r\n"); //~ ERROR
-    eprint!("foo\rbar\n") // ~ ERROR
+    eprint!("foo\rbar\n");
+
+    // Ignore expanded format strings
+    macro_rules! newline {
+        () => {
+            "\n"
+        };
+    }
+    eprint!(newline!());
 }
diff --git a/tests/ui/eprint_with_newline.stderr b/tests/ui/eprint_with_newline.stderr
index f137787bff0..0eefb9f0ca9 100644
--- a/tests/ui/eprint_with_newline.stderr
+++ b/tests/ui/eprint_with_newline.stderr
@@ -83,7 +83,7 @@ LL | |     );
 help: use `eprintln!` instead
    |
 LL ~     eprintln!(
-LL ~         ""
+LL ~         
    |
 
 error: using `eprint!()` with a format string that ends in a single newline
@@ -98,7 +98,7 @@ LL | |     );
 help: use `eprintln!` instead
    |
 LL ~     eprintln!(
-LL ~         r""
+LL ~         
    |
 
 error: using `eprint!()` with a format string that ends in a single newline
@@ -113,17 +113,5 @@ LL -     eprint!("/r/n"); //~ ERROR
 LL +     eprintln!("/r"); //~ ERROR
    |
 
-error: using `eprint!()` with a format string that ends in a single newline
-  --> $DIR/eprint_with_newline.rs:48:5
-   |
-LL |     eprint!("foo/rbar/n") // ~ ERROR
-   |     ^^^^^^^^^^^^^^^^^^^^^
-   |
-help: use `eprintln!` instead
-   |
-LL -     eprint!("foo/rbar/n") // ~ ERROR
-LL +     eprintln!("foo/rbar") // ~ ERROR
-   |
-
-error: aborting due to 10 previous errors
+error: aborting due to 9 previous errors
 
diff --git a/tests/ui/format.fixed b/tests/ui/format.fixed
index b56d6aec508..e0c5f692740 100644
--- a/tests/ui/format.fixed
+++ b/tests/ui/format.fixed
@@ -28,8 +28,6 @@ fn main() {
     format!("{:?}", "foo"); // Don't warn about `Debug`.
     format!("{:8}", "foo");
     format!("{:width$}", "foo", width = 8);
-    "foo".to_string(); // Warn when the format makes no difference.
-    "foo".to_string(); // Warn when the format makes no difference.
     format!("foo {}", "bar");
     format!("{} bar", "foo");
 
@@ -38,8 +36,6 @@ fn main() {
     format!("{:?}", arg); // Don't warn about debug.
     format!("{:8}", arg);
     format!("{:width$}", arg, width = 8);
-    arg.to_string(); // Warn when the format makes no difference.
-    arg.to_string(); // Warn when the format makes no difference.
     format!("foo {}", arg);
     format!("{} bar", arg);
 
diff --git a/tests/ui/format.rs b/tests/ui/format.rs
index 4c1a3a840ed..ff83cd64bf0 100644
--- a/tests/ui/format.rs
+++ b/tests/ui/format.rs
@@ -30,8 +30,6 @@ fn main() {
     format!("{:?}", "foo"); // Don't warn about `Debug`.
     format!("{:8}", "foo");
     format!("{:width$}", "foo", width = 8);
-    format!("{:+}", "foo"); // Warn when the format makes no difference.
-    format!("{:<}", "foo"); // Warn when the format makes no difference.
     format!("foo {}", "bar");
     format!("{} bar", "foo");
 
@@ -40,8 +38,6 @@ fn main() {
     format!("{:?}", arg); // Don't warn about debug.
     format!("{:8}", arg);
     format!("{:width$}", arg, width = 8);
-    format!("{:+}", arg); // Warn when the format makes no difference.
-    format!("{:<}", arg); // Warn when the format makes no difference.
     format!("foo {}", arg);
     format!("{} bar", arg);
 
diff --git a/tests/ui/format.stderr b/tests/ui/format.stderr
index 6c35caeb034..0ef0ac655d3 100644
--- a/tests/ui/format.stderr
+++ b/tests/ui/format.stderr
@@ -46,82 +46,58 @@ LL |     format!("{}", "foo");
    |     ^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string()`
 
 error: useless use of `format!`
-  --> $DIR/format.rs:33:5
-   |
-LL |     format!("{:+}", "foo"); // Warn when the format makes no difference.
-   |     ^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string()`
-
-error: useless use of `format!`
-  --> $DIR/format.rs:34:5
-   |
-LL |     format!("{:<}", "foo"); // Warn when the format makes no difference.
-   |     ^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string()`
-
-error: useless use of `format!`
-  --> $DIR/format.rs:39:5
+  --> $DIR/format.rs:37:5
    |
 LL |     format!("{}", arg);
    |     ^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `arg.to_string()`
 
 error: useless use of `format!`
-  --> $DIR/format.rs:43:5
-   |
-LL |     format!("{:+}", arg); // Warn when the format makes no difference.
-   |     ^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `arg.to_string()`
-
-error: useless use of `format!`
-  --> $DIR/format.rs:44:5
-   |
-LL |     format!("{:<}", arg); // Warn when the format makes no difference.
-   |     ^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `arg.to_string()`
-
-error: useless use of `format!`
-  --> $DIR/format.rs:71:5
+  --> $DIR/format.rs:67:5
    |
 LL |     format!("{}", 42.to_string());
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `42.to_string()`
 
 error: useless use of `format!`
-  --> $DIR/format.rs:73:5
+  --> $DIR/format.rs:69:5
    |
 LL |     format!("{}", x.display().to_string());
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.display().to_string()`
 
 error: useless use of `format!`
-  --> $DIR/format.rs:77:18
+  --> $DIR/format.rs:73:18
    |
 LL |     let _ = Some(format!("{}", a + "bar"));
    |                  ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `a + "bar"`
 
 error: useless use of `format!`
-  --> $DIR/format.rs:81:22
+  --> $DIR/format.rs:77:22
    |
 LL |     let _s: String = format!("{}", &*v.join("/n"));
    |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `(&*v.join("/n")).to_string()`
 
 error: useless use of `format!`
-  --> $DIR/format.rs:87:13
+  --> $DIR/format.rs:83:13
    |
 LL |     let _ = format!("{x}");
    |             ^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.to_string()`
 
 error: useless use of `format!`
-  --> $DIR/format.rs:89:13
+  --> $DIR/format.rs:85:13
    |
 LL |     let _ = format!("{y}", y = x);
    |             ^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.to_string()`
 
 error: useless use of `format!`
-  --> $DIR/format.rs:93:13
+  --> $DIR/format.rs:89:13
    |
 LL |     let _ = format!("{abc}");
    |             ^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `abc.to_string()`
 
 error: useless use of `format!`
-  --> $DIR/format.rs:95:13
+  --> $DIR/format.rs:91:13
    |
 LL |     let _ = format!("{xx}");
    |             ^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `xx.to_string()`
 
-error: aborting due to 19 previous errors
+error: aborting due to 15 previous errors
 
diff --git a/tests/ui/positional_named_format_parameters.fixed b/tests/ui/positional_named_format_parameters.fixed
deleted file mode 100644
index 4170e109820..00000000000
--- a/tests/ui/positional_named_format_parameters.fixed
+++ /dev/null
@@ -1,56 +0,0 @@
-// run-rustfix
-#![allow(unused_must_use)]
-#![allow(named_arguments_used_positionally)] // Unstable at time of writing.
-#![warn(clippy::positional_named_format_parameters)]
-
-use std::io::Write;
-
-fn main() {
-    let mut v = Vec::new();
-    let hello = "Hello";
-
-    println!("{hello:.foo$}", foo = 2);
-    writeln!(v, "{hello:.foo$}", foo = 2);
-
-    // Warnings
-    println!("{zero} {one:?}", zero = 0, one = 1);
-    println!("This is a test {zero} {one:?}", zero = 0, one = 1);
-    println!("Hello {one} is {two:.zero$}", zero = 5, one = hello, two = 0.01);
-    println!("Hello {one:zero$}!", zero = 5, one = 1);
-    println!("Hello {zero:one$}!", zero = 4, one = 1);
-    println!("Hello {zero:0one$}!", zero = 4, one = 1);
-    println!("Hello is {one:.zero$}", zero = 5, one = 0.01);
-    println!("Hello is {one:<6.zero$}", zero = 5, one = 0.01);
-    println!("{zero}, `{two:>8.one$}` has 3", zero = hello, one = 3, two = hello);
-    println!("Hello {one} is {two:.zero$}", zero = 5, one = hello, two = 0.01);
-    println!("Hello {world} {world}!", world = 5);
-
-    writeln!(v, "{zero} {one:?}", zero = 0, one = 1);
-    writeln!(v, "This is a test {zero} {one:?}", zero = 0, one = 1);
-    writeln!(v, "Hello {one} is {two:.zero$}", zero = 5, one = hello, two = 0.01);
-    writeln!(v, "Hello {one:zero$}!", zero = 4, one = 1);
-    writeln!(v, "Hello {zero:one$}!", zero = 4, one = 1);
-    writeln!(v, "Hello {zero:0one$}!", zero = 4, one = 1);
-    writeln!(v, "Hello is {one:.zero$}", zero = 3, one = 0.01);
-    writeln!(v, "Hello is {one:<6.zero$}", zero = 2, one = 0.01);
-    writeln!(v, "{zero}, `{two:>8.one$}` has 3", zero = hello, one = 3, two = hello);
-    writeln!(v, "Hello {one} is {two:.zero$}", zero = 1, one = hello, two = 0.01);
-    writeln!(v, "Hello {world} {world}!", world = 0);
-
-    // Tests from other files
-    println!("{w:w$}", w = 1);
-    println!("{p:.p$}", p = 1);
-    println!("{v}", v = 1);
-    println!("{v:v$}", v = 1);
-    println!("{v:v$}", v = 1);
-    println!("{v:v$.v$}", v = 1);
-    println!("{v:v$.v$}", v = 1);
-    println!("{v:v$.v$}", v = 1);
-    println!("{v:v$.v$}", v = 1);
-    println!("{v:v$.v$}", v = 1);
-    println!("{v:v$.v$}", v = 1);
-    println!("{v:v$.v$}", v = 1);
-    println!("{w:w$}", w = 1);
-    println!("{p:.p$}", p = 1);
-    println!("{:p$.w$}", 1, w = 1, p = 1);
-}
diff --git a/tests/ui/positional_named_format_parameters.rs b/tests/ui/positional_named_format_parameters.rs
deleted file mode 100644
index 553d8494ecc..00000000000
--- a/tests/ui/positional_named_format_parameters.rs
+++ /dev/null
@@ -1,56 +0,0 @@
-// run-rustfix
-#![allow(unused_must_use)]
-#![allow(named_arguments_used_positionally)] // Unstable at time of writing.
-#![warn(clippy::positional_named_format_parameters)]
-
-use std::io::Write;
-
-fn main() {
-    let mut v = Vec::new();
-    let hello = "Hello";
-
-    println!("{hello:.foo$}", foo = 2);
-    writeln!(v, "{hello:.foo$}", foo = 2);
-
-    // Warnings
-    println!("{} {1:?}", zero = 0, one = 1);
-    println!("This is a test { } {000001:?}", zero = 0, one = 1);
-    println!("Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01);
-    println!("Hello {1:0$}!", zero = 5, one = 1);
-    println!("Hello {0:1$}!", zero = 4, one = 1);
-    println!("Hello {0:01$}!", zero = 4, one = 1);
-    println!("Hello is {1:.*}", zero = 5, one = 0.01);
-    println!("Hello is {:<6.*}", zero = 5, one = 0.01);
-    println!("{}, `{two:>8.*}` has 3", zero = hello, one = 3, two = hello);
-    println!("Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01);
-    println!("Hello {world} {}!", world = 5);
-
-    writeln!(v, "{} {1:?}", zero = 0, one = 1);
-    writeln!(v, "This is a test { } {000001:?}", zero = 0, one = 1);
-    writeln!(v, "Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01);
-    writeln!(v, "Hello {1:0$}!", zero = 4, one = 1);
-    writeln!(v, "Hello {0:1$}!", zero = 4, one = 1);
-    writeln!(v, "Hello {0:01$}!", zero = 4, one = 1);
-    writeln!(v, "Hello is {1:.*}", zero = 3, one = 0.01);
-    writeln!(v, "Hello is {:<6.*}", zero = 2, one = 0.01);
-    writeln!(v, "{}, `{two:>8.*}` has 3", zero = hello, one = 3, two = hello);
-    writeln!(v, "Hello {1} is {2:.0$}", zero = 1, one = hello, two = 0.01);
-    writeln!(v, "Hello {world} {}!", world = 0);
-
-    // Tests from other files
-    println!("{:w$}", w = 1);
-    println!("{:.p$}", p = 1);
-    println!("{}", v = 1);
-    println!("{:0$}", v = 1);
-    println!("{0:0$}", v = 1);
-    println!("{:0$.0$}", v = 1);
-    println!("{0:0$.0$}", v = 1);
-    println!("{0:0$.v$}", v = 1);
-    println!("{0:v$.0$}", v = 1);
-    println!("{v:0$.0$}", v = 1);
-    println!("{v:v$.0$}", v = 1);
-    println!("{v:0$.v$}", v = 1);
-    println!("{:w$}", w = 1);
-    println!("{:.p$}", p = 1);
-    println!("{:p$.w$}", 1, w = 1, p = 1);
-}
diff --git a/tests/ui/positional_named_format_parameters.stderr b/tests/ui/positional_named_format_parameters.stderr
deleted file mode 100644
index 48ddb6d67ad..00000000000
--- a/tests/ui/positional_named_format_parameters.stderr
+++ /dev/null
@@ -1,418 +0,0 @@
-error: named parameter zero is used as a positional parameter
-  --> $DIR/positional_named_format_parameters.rs:16:16
-   |
-LL |     println!("{} {1:?}", zero = 0, one = 1);
-   |                ^ help: replace it with: `zero`
-   |
-   = note: `-D clippy::positional-named-format-parameters` implied by `-D warnings`
-
-error: named parameter one is used as a positional parameter
-  --> $DIR/positional_named_format_parameters.rs:16:19
-   |
-LL |     println!("{} {1:?}", zero = 0, one = 1);
-   |                   ^ help: replace it with: `one`
-
-error: named parameter zero is used as a positional parameter
-  --> $DIR/positional_named_format_parameters.rs:17:31
-   |
-LL |     println!("This is a test { } {000001:?}", zero = 0, one = 1);
-   |                               ^ help: replace it with: `zero`
-
-error: named parameter one is used as a positional parameter
-  --> $DIR/positional_named_format_parameters.rs:17:35
-   |
-LL |     println!("This is a test { } {000001:?}", zero = 0, one = 1);
-   |                                   ^^^^^^ help: replace it with: `one`
-
-error: named parameter zero is used as a positional parameter
-  --> $DIR/positional_named_format_parameters.rs:18:32
-   |
-LL |     println!("Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01);
-   |                                ^ help: replace it with: `zero`
-
-error: named parameter one is used as a positional parameter
-  --> $DIR/positional_named_format_parameters.rs:18:22
-   |
-LL |     println!("Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01);
-   |                      ^ help: replace it with: `one`
-
-error: named parameter two is used as a positional parameter
-  --> $DIR/positional_named_format_parameters.rs:18:29
-   |
-LL |     println!("Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01);
-   |                             ^ help: replace it with: `two`
-
-error: named parameter zero is used as a positional parameter
-  --> $DIR/positional_named_format_parameters.rs:19:24
-   |
-LL |     println!("Hello {1:0$}!", zero = 5, one = 1);
-   |                        ^ help: replace it with: `zero`
-
-error: named parameter one is used as a positional parameter
-  --> $DIR/positional_named_format_parameters.rs:19:22
-   |
-LL |     println!("Hello {1:0$}!", zero = 5, one = 1);
-   |                      ^ help: replace it with: `one`
-
-error: named parameter zero is used as a positional parameter
-  --> $DIR/positional_named_format_parameters.rs:20:22
-   |
-LL |     println!("Hello {0:1$}!", zero = 4, one = 1);
-   |                      ^ help: replace it with: `zero`
-
-error: named parameter one is used as a positional parameter
-  --> $DIR/positional_named_format_parameters.rs:20:24
-   |
-LL |     println!("Hello {0:1$}!", zero = 4, one = 1);
-   |                        ^ help: replace it with: `one`
-
-error: named parameter zero is used as a positional parameter
-  --> $DIR/positional_named_format_parameters.rs:21:22
-   |
-LL |     println!("Hello {0:01$}!", zero = 4, one = 1);
-   |                      ^ help: replace it with: `zero`
-
-error: named parameter one is used as a positional parameter
-  --> $DIR/positional_named_format_parameters.rs:21:25
-   |
-LL |     println!("Hello {0:01$}!", zero = 4, one = 1);
-   |                         ^ help: replace it with: `one`
-
-error: named parameter zero is used as a positional parameter
-  --> $DIR/positional_named_format_parameters.rs:22:28
-   |
-LL |     println!("Hello is {1:.*}", zero = 5, one = 0.01);
-   |                            ^ help: replace it with: `zero$`
-
-error: named parameter one is used as a positional parameter
-  --> $DIR/positional_named_format_parameters.rs:22:25
-   |
-LL |     println!("Hello is {1:.*}", zero = 5, one = 0.01);
-   |                         ^ help: replace it with: `one`
-
-error: named parameter zero is used as a positional parameter
-  --> $DIR/positional_named_format_parameters.rs:23:29
-   |
-LL |     println!("Hello is {:<6.*}", zero = 5, one = 0.01);
-   |                             ^ help: replace it with: `zero$`
-
-error: named parameter one is used as a positional parameter
-  --> $DIR/positional_named_format_parameters.rs:23:25
-   |
-LL |     println!("Hello is {:<6.*}", zero = 5, one = 0.01);
-   |                         ^ help: replace it with: `one`
-
-error: named parameter zero is used as a positional parameter
-  --> $DIR/positional_named_format_parameters.rs:24:16
-   |
-LL |     println!("{}, `{two:>8.*}` has 3", zero = hello, one = 3, two = hello);
-   |                ^ help: replace it with: `zero`
-
-error: named parameter one is used as a positional parameter
-  --> $DIR/positional_named_format_parameters.rs:24:28
-   |
-LL |     println!("{}, `{two:>8.*}` has 3", zero = hello, one = 3, two = hello);
-   |                            ^ help: replace it with: `one$`
-
-error: named parameter zero is used as a positional parameter
-  --> $DIR/positional_named_format_parameters.rs:25:32
-   |
-LL |     println!("Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01);
-   |                                ^ help: replace it with: `zero`
-
-error: named parameter one is used as a positional parameter
-  --> $DIR/positional_named_format_parameters.rs:25:22
-   |
-LL |     println!("Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01);
-   |                      ^ help: replace it with: `one`
-
-error: named parameter two is used as a positional parameter
-  --> $DIR/positional_named_format_parameters.rs:25:29
-   |
-LL |     println!("Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01);
-   |                             ^ help: replace it with: `two`
-
-error: named parameter world is used as a positional parameter
-  --> $DIR/positional_named_format_parameters.rs:26:30
-   |
-LL |     println!("Hello {world} {}!", world = 5);
-   |                              ^ help: replace it with: `world`
-
-error: named parameter zero is used as a positional parameter
-  --> $DIR/positional_named_format_parameters.rs:28:19
-   |
-LL |     writeln!(v, "{} {1:?}", zero = 0, one = 1);
-   |                   ^ help: replace it with: `zero`
-
-error: named parameter one is used as a positional parameter
-  --> $DIR/positional_named_format_parameters.rs:28:22
-   |
-LL |     writeln!(v, "{} {1:?}", zero = 0, one = 1);
-   |                      ^ help: replace it with: `one`
-
-error: named parameter zero is used as a positional parameter
-  --> $DIR/positional_named_format_parameters.rs:29:34
-   |
-LL |     writeln!(v, "This is a test { } {000001:?}", zero = 0, one = 1);
-   |                                  ^ help: replace it with: `zero`
-
-error: named parameter one is used as a positional parameter
-  --> $DIR/positional_named_format_parameters.rs:29:38
-   |
-LL |     writeln!(v, "This is a test { } {000001:?}", zero = 0, one = 1);
-   |                                      ^^^^^^ help: replace it with: `one`
-
-error: named parameter zero is used as a positional parameter
-  --> $DIR/positional_named_format_parameters.rs:30:35
-   |
-LL |     writeln!(v, "Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01);
-   |                                   ^ help: replace it with: `zero`
-
-error: named parameter one is used as a positional parameter
-  --> $DIR/positional_named_format_parameters.rs:30:25
-   |
-LL |     writeln!(v, "Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01);
-   |                         ^ help: replace it with: `one`
-
-error: named parameter two is used as a positional parameter
-  --> $DIR/positional_named_format_parameters.rs:30:32
-   |
-LL |     writeln!(v, "Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01);
-   |                                ^ help: replace it with: `two`
-
-error: named parameter zero is used as a positional parameter
-  --> $DIR/positional_named_format_parameters.rs:31:27
-   |
-LL |     writeln!(v, "Hello {1:0$}!", zero = 4, one = 1);
-   |                           ^ help: replace it with: `zero`
-
-error: named parameter one is used as a positional parameter
-  --> $DIR/positional_named_format_parameters.rs:31:25
-   |
-LL |     writeln!(v, "Hello {1:0$}!", zero = 4, one = 1);
-   |                         ^ help: replace it with: `one`
-
-error: named parameter zero is used as a positional parameter
-  --> $DIR/positional_named_format_parameters.rs:32:25
-   |
-LL |     writeln!(v, "Hello {0:1$}!", zero = 4, one = 1);
-   |                         ^ help: replace it with: `zero`
-
-error: named parameter one is used as a positional parameter
-  --> $DIR/positional_named_format_parameters.rs:32:27
-   |
-LL |     writeln!(v, "Hello {0:1$}!", zero = 4, one = 1);
-   |                           ^ help: replace it with: `one`
-
-error: named parameter zero is used as a positional parameter
-  --> $DIR/positional_named_format_parameters.rs:33:25
-   |
-LL |     writeln!(v, "Hello {0:01$}!", zero = 4, one = 1);
-   |                         ^ help: replace it with: `zero`
-
-error: named parameter one is used as a positional parameter
-  --> $DIR/positional_named_format_parameters.rs:33:28
-   |
-LL |     writeln!(v, "Hello {0:01$}!", zero = 4, one = 1);
-   |                            ^ help: replace it with: `one`
-
-error: named parameter zero is used as a positional parameter
-  --> $DIR/positional_named_format_parameters.rs:34:31
-   |
-LL |     writeln!(v, "Hello is {1:.*}", zero = 3, one = 0.01);
-   |                               ^ help: replace it with: `zero$`
-
-error: named parameter one is used as a positional parameter
-  --> $DIR/positional_named_format_parameters.rs:34:28
-   |
-LL |     writeln!(v, "Hello is {1:.*}", zero = 3, one = 0.01);
-   |                            ^ help: replace it with: `one`
-
-error: named parameter zero is used as a positional parameter
-  --> $DIR/positional_named_format_parameters.rs:35:32
-   |
-LL |     writeln!(v, "Hello is {:<6.*}", zero = 2, one = 0.01);
-   |                                ^ help: replace it with: `zero$`
-
-error: named parameter one is used as a positional parameter
-  --> $DIR/positional_named_format_parameters.rs:35:28
-   |
-LL |     writeln!(v, "Hello is {:<6.*}", zero = 2, one = 0.01);
-   |                            ^ help: replace it with: `one`
-
-error: named parameter zero is used as a positional parameter
-  --> $DIR/positional_named_format_parameters.rs:36:19
-   |
-LL |     writeln!(v, "{}, `{two:>8.*}` has 3", zero = hello, one = 3, two = hello);
-   |                   ^ help: replace it with: `zero`
-
-error: named parameter one is used as a positional parameter
-  --> $DIR/positional_named_format_parameters.rs:36:31
-   |
-LL |     writeln!(v, "{}, `{two:>8.*}` has 3", zero = hello, one = 3, two = hello);
-   |                               ^ help: replace it with: `one$`
-
-error: named parameter zero is used as a positional parameter
-  --> $DIR/positional_named_format_parameters.rs:37:35
-   |
-LL |     writeln!(v, "Hello {1} is {2:.0$}", zero = 1, one = hello, two = 0.01);
-   |                                   ^ help: replace it with: `zero`
-
-error: named parameter one is used as a positional parameter
-  --> $DIR/positional_named_format_parameters.rs:37:25
-   |
-LL |     writeln!(v, "Hello {1} is {2:.0$}", zero = 1, one = hello, two = 0.01);
-   |                         ^ help: replace it with: `one`
-
-error: named parameter two is used as a positional parameter
-  --> $DIR/positional_named_format_parameters.rs:37:32
-   |
-LL |     writeln!(v, "Hello {1} is {2:.0$}", zero = 1, one = hello, two = 0.01);
-   |                                ^ help: replace it with: `two`
-
-error: named parameter world is used as a positional parameter
-  --> $DIR/positional_named_format_parameters.rs:38:33
-   |
-LL |     writeln!(v, "Hello {world} {}!", world = 0);
-   |                                 ^ help: replace it with: `world`
-
-error: named parameter w is used as a positional parameter
-  --> $DIR/positional_named_format_parameters.rs:41:16
-   |
-LL |     println!("{:w$}", w = 1);
-   |                ^ help: replace it with: `w`
-
-error: named parameter p is used as a positional parameter
-  --> $DIR/positional_named_format_parameters.rs:42:16
-   |
-LL |     println!("{:.p$}", p = 1);
-   |                ^ help: replace it with: `p`
-
-error: named parameter v is used as a positional parameter
-  --> $DIR/positional_named_format_parameters.rs:43:16
-   |
-LL |     println!("{}", v = 1);
-   |                ^ help: replace it with: `v`
-
-error: named parameter v is used as a positional parameter
-  --> $DIR/positional_named_format_parameters.rs:44:16
-   |
-LL |     println!("{:0$}", v = 1);
-   |                ^ help: replace it with: `v`
-
-error: named parameter v is used as a positional parameter
-  --> $DIR/positional_named_format_parameters.rs:44:17
-   |
-LL |     println!("{:0$}", v = 1);
-   |                 ^ help: replace it with: `v`
-
-error: named parameter v is used as a positional parameter
-  --> $DIR/positional_named_format_parameters.rs:45:16
-   |
-LL |     println!("{0:0$}", v = 1);
-   |                ^ help: replace it with: `v`
-
-error: named parameter v is used as a positional parameter
-  --> $DIR/positional_named_format_parameters.rs:45:18
-   |
-LL |     println!("{0:0$}", v = 1);
-   |                  ^ help: replace it with: `v`
-
-error: named parameter v is used as a positional parameter
-  --> $DIR/positional_named_format_parameters.rs:46:16
-   |
-LL |     println!("{:0$.0$}", v = 1);
-   |                ^ help: replace it with: `v`
-
-error: named parameter v is used as a positional parameter
-  --> $DIR/positional_named_format_parameters.rs:46:20
-   |
-LL |     println!("{:0$.0$}", v = 1);
-   |                    ^ help: replace it with: `v`
-
-error: named parameter v is used as a positional parameter
-  --> $DIR/positional_named_format_parameters.rs:46:17
-   |
-LL |     println!("{:0$.0$}", v = 1);
-   |                 ^ help: replace it with: `v`
-
-error: named parameter v is used as a positional parameter
-  --> $DIR/positional_named_format_parameters.rs:47:16
-   |
-LL |     println!("{0:0$.0$}", v = 1);
-   |                ^ help: replace it with: `v`
-
-error: named parameter v is used as a positional parameter
-  --> $DIR/positional_named_format_parameters.rs:47:21
-   |
-LL |     println!("{0:0$.0$}", v = 1);
-   |                     ^ help: replace it with: `v`
-
-error: named parameter v is used as a positional parameter
-  --> $DIR/positional_named_format_parameters.rs:47:18
-   |
-LL |     println!("{0:0$.0$}", v = 1);
-   |                  ^ help: replace it with: `v`
-
-error: named parameter v is used as a positional parameter
-  --> $DIR/positional_named_format_parameters.rs:48:16
-   |
-LL |     println!("{0:0$.v$}", v = 1);
-   |                ^ help: replace it with: `v`
-
-error: named parameter v is used as a positional parameter
-  --> $DIR/positional_named_format_parameters.rs:48:18
-   |
-LL |     println!("{0:0$.v$}", v = 1);
-   |                  ^ help: replace it with: `v`
-
-error: named parameter v is used as a positional parameter
-  --> $DIR/positional_named_format_parameters.rs:49:16
-   |
-LL |     println!("{0:v$.0$}", v = 1);
-   |                ^ help: replace it with: `v`
-
-error: named parameter v is used as a positional parameter
-  --> $DIR/positional_named_format_parameters.rs:49:21
-   |
-LL |     println!("{0:v$.0$}", v = 1);
-   |                     ^ help: replace it with: `v`
-
-error: named parameter v is used as a positional parameter
-  --> $DIR/positional_named_format_parameters.rs:50:21
-   |
-LL |     println!("{v:0$.0$}", v = 1);
-   |                     ^ help: replace it with: `v`
-
-error: named parameter v is used as a positional parameter
-  --> $DIR/positional_named_format_parameters.rs:50:18
-   |
-LL |     println!("{v:0$.0$}", v = 1);
-   |                  ^ help: replace it with: `v`
-
-error: named parameter v is used as a positional parameter
-  --> $DIR/positional_named_format_parameters.rs:51:21
-   |
-LL |     println!("{v:v$.0$}", v = 1);
-   |                     ^ help: replace it with: `v`
-
-error: named parameter v is used as a positional parameter
-  --> $DIR/positional_named_format_parameters.rs:52:18
-   |
-LL |     println!("{v:0$.v$}", v = 1);
-   |                  ^ help: replace it with: `v`
-
-error: named parameter w is used as a positional parameter
-  --> $DIR/positional_named_format_parameters.rs:53:16
-   |
-LL |     println!("{:w$}", w = 1);
-   |                ^ help: replace it with: `w`
-
-error: named parameter p is used as a positional parameter
-  --> $DIR/positional_named_format_parameters.rs:54:16
-   |
-LL |     println!("{:.p$}", p = 1);
-   |                ^ help: replace it with: `p`
-
-error: aborting due to 69 previous errors
-
diff --git a/tests/ui/print_literal.rs b/tests/ui/print_literal.rs
index 8665a3bb28a..3f6639c1458 100644
--- a/tests/ui/print_literal.rs
+++ b/tests/ui/print_literal.rs
@@ -20,11 +20,13 @@ fn main() {
     println!("{} of {:b} people know binary, the other half doesn't", 1, 2);
     println!("10 / 4 is {}", 2.5);
     println!("2 + 1 = {}", 3);
+    println!("From expansion {}", stringify!(not a string literal));
 
     // these should throw warnings
     print!("Hello {}", "world");
     println!("Hello {} {}", world, "world");
     println!("Hello {}", "world");
+    println!("{} {:.4}", "a literal", 5);
 
     // positional args don't change the fact
     // that we're using a literal -- this should
diff --git a/tests/ui/print_literal.stderr b/tests/ui/print_literal.stderr
index 72aae075603..23e6dbc3e34 100644
--- a/tests/ui/print_literal.stderr
+++ b/tests/ui/print_literal.stderr
@@ -1,5 +1,5 @@
 error: literal with an empty format string
-  --> $DIR/print_literal.rs:25:24
+  --> $DIR/print_literal.rs:26:24
    |
 LL |     print!("Hello {}", "world");
    |                        ^^^^^^^
@@ -12,7 +12,7 @@ LL +     print!("Hello world");
    |
 
 error: literal with an empty format string
-  --> $DIR/print_literal.rs:26:36
+  --> $DIR/print_literal.rs:27:36
    |
 LL |     println!("Hello {} {}", world, "world");
    |                                    ^^^^^^^
@@ -24,7 +24,7 @@ LL +     println!("Hello {} world", world);
    |
 
 error: literal with an empty format string
-  --> $DIR/print_literal.rs:27:26
+  --> $DIR/print_literal.rs:28:26
    |
 LL |     println!("Hello {}", "world");
    |                          ^^^^^^^
@@ -36,7 +36,19 @@ LL +     println!("Hello world");
    |
 
 error: literal with an empty format string
-  --> $DIR/print_literal.rs:32:25
+  --> $DIR/print_literal.rs:29:26
+   |
+LL |     println!("{} {:.4}", "a literal", 5);
+   |                          ^^^^^^^^^^^
+   |
+help: try this
+   |
+LL -     println!("{} {:.4}", "a literal", 5);
+LL +     println!("a literal {:.4}", 5);
+   |
+
+error: literal with an empty format string
+  --> $DIR/print_literal.rs:34:25
    |
 LL |     println!("{0} {1}", "hello", "world");
    |                         ^^^^^^^
@@ -48,7 +60,7 @@ LL +     println!("hello {1}", "world");
    |
 
 error: literal with an empty format string
-  --> $DIR/print_literal.rs:32:34
+  --> $DIR/print_literal.rs:34:34
    |
 LL |     println!("{0} {1}", "hello", "world");
    |                                  ^^^^^^^
@@ -60,19 +72,7 @@ LL +     println!("{0} world", "hello");
    |
 
 error: literal with an empty format string
-  --> $DIR/print_literal.rs:33:25
-   |
-LL |     println!("{1} {0}", "hello", "world");
-   |                         ^^^^^^^
-   |
-help: try this
-   |
-LL -     println!("{1} {0}", "hello", "world");
-LL +     println!("{1} hello", "world");
-   |
-
-error: literal with an empty format string
-  --> $DIR/print_literal.rs:33:34
+  --> $DIR/print_literal.rs:35:34
    |
 LL |     println!("{1} {0}", "hello", "world");
    |                                  ^^^^^^^
@@ -84,10 +84,22 @@ LL +     println!("world {0}", "hello");
    |
 
 error: literal with an empty format string
-  --> $DIR/print_literal.rs:36:29
+  --> $DIR/print_literal.rs:35:25
+   |
+LL |     println!("{1} {0}", "hello", "world");
+   |                         ^^^^^^^
+   |
+help: try this
+   |
+LL -     println!("{1} {0}", "hello", "world");
+LL +     println!("{1} hello", "world");
+   |
+
+error: literal with an empty format string
+  --> $DIR/print_literal.rs:38:35
    |
 LL |     println!("{foo} {bar}", foo = "hello", bar = "world");
-   |                             ^^^^^^^^^^^^^
+   |                                   ^^^^^^^
    |
 help: try this
    |
@@ -96,10 +108,10 @@ LL +     println!("hello {bar}", bar = "world");
    |
 
 error: literal with an empty format string
-  --> $DIR/print_literal.rs:36:44
+  --> $DIR/print_literal.rs:38:50
    |
 LL |     println!("{foo} {bar}", foo = "hello", bar = "world");
-   |                                            ^^^^^^^^^^^^^
+   |                                                  ^^^^^^^
    |
 help: try this
    |
@@ -108,22 +120,10 @@ LL +     println!("{foo} world", foo = "hello");
    |
 
 error: literal with an empty format string
-  --> $DIR/print_literal.rs:37:29
+  --> $DIR/print_literal.rs:39:50
    |
 LL |     println!("{bar} {foo}", foo = "hello", bar = "world");
-   |                             ^^^^^^^^^^^^^
-   |
-help: try this
-   |
-LL -     println!("{bar} {foo}", foo = "hello", bar = "world");
-LL +     println!("{bar} hello", bar = "world");
-   |
-
-error: literal with an empty format string
-  --> $DIR/print_literal.rs:37:44
-   |
-LL |     println!("{bar} {foo}", foo = "hello", bar = "world");
-   |                                            ^^^^^^^^^^^^^
+   |                                                  ^^^^^^^
    |
 help: try this
    |
@@ -131,5 +131,17 @@ LL -     println!("{bar} {foo}", foo = "hello", bar = "world");
 LL +     println!("world {foo}", foo = "hello");
    |
 
-error: aborting due to 11 previous errors
+error: literal with an empty format string
+  --> $DIR/print_literal.rs:39:35
+   |
+LL |     println!("{bar} {foo}", foo = "hello", bar = "world");
+   |                                   ^^^^^^^
+   |
+help: try this
+   |
+LL -     println!("{bar} {foo}", foo = "hello", bar = "world");
+LL +     println!("{bar} hello", bar = "world");
+   |
+
+error: aborting due to 12 previous errors
 
diff --git a/tests/ui/print_with_newline.rs b/tests/ui/print_with_newline.rs
index a43a1fc4f52..b8c29d207ad 100644
--- a/tests/ui/print_with_newline.rs
+++ b/tests/ui/print_with_newline.rs
@@ -48,5 +48,13 @@ fn main() {
     print!("\r\n");
     print!("foo\r\n");
     print!("\\r\n"); //~ ERROR
-    print!("foo\rbar\n") // ~ ERROR
+    print!("foo\rbar\n");
+
+    // Ignore expanded format strings
+    macro_rules! newline {
+        () => {
+            "\n"
+        };
+    }
+    print!(newline!());
 }
diff --git a/tests/ui/print_with_newline.stderr b/tests/ui/print_with_newline.stderr
index edbaa1cdf97..b9f5675faec 100644
--- a/tests/ui/print_with_newline.stderr
+++ b/tests/ui/print_with_newline.stderr
@@ -83,7 +83,7 @@ LL | |     );
 help: use `println!` instead
    |
 LL ~     println!(
-LL ~         ""
+LL ~         
    |
 
 error: using `print!()` with a format string that ends in a single newline
@@ -98,7 +98,7 @@ LL | |     );
 help: use `println!` instead
    |
 LL ~     println!(
-LL ~         r""
+LL ~         
    |
 
 error: using `print!()` with a format string that ends in a single newline
@@ -113,17 +113,5 @@ LL -     print!("/r/n"); //~ ERROR
 LL +     println!("/r"); //~ ERROR
    |
 
-error: using `print!()` with a format string that ends in a single newline
-  --> $DIR/print_with_newline.rs:51:5
-   |
-LL |     print!("foo/rbar/n") // ~ ERROR
-   |     ^^^^^^^^^^^^^^^^^^^^
-   |
-help: use `println!` instead
-   |
-LL -     print!("foo/rbar/n") // ~ ERROR
-LL +     println!("foo/rbar") // ~ ERROR
-   |
-
-error: aborting due to 10 previous errors
+error: aborting due to 9 previous errors
 
diff --git a/tests/ui/println_empty_string.stderr b/tests/ui/println_empty_string.stderr
index 17fe4ea7479..3cc8bb947bd 100644
--- a/tests/ui/println_empty_string.stderr
+++ b/tests/ui/println_empty_string.stderr
@@ -1,28 +1,36 @@
-error: using `println!("")`
+error: empty string literal in `println!`
   --> $DIR/println_empty_string.rs:6:5
    |
 LL |     println!("");
-   |     ^^^^^^^^^^^^ help: replace it with: `println!()`
+   |     ^^^^^^^^^--^
+   |              |
+   |              help: remove the empty string
    |
    = note: `-D clippy::println-empty-string` implied by `-D warnings`
 
-error: using `println!("")`
+error: empty string literal in `println!`
   --> $DIR/println_empty_string.rs:9:14
    |
 LL |         _ => println!(""),
-   |              ^^^^^^^^^^^^ help: replace it with: `println!()`
+   |              ^^^^^^^^^--^
+   |                       |
+   |                       help: remove the empty string
 
-error: using `eprintln!("")`
+error: empty string literal in `eprintln!`
   --> $DIR/println_empty_string.rs:13:5
    |
 LL |     eprintln!("");
-   |     ^^^^^^^^^^^^^ help: replace it with: `eprintln!()`
+   |     ^^^^^^^^^^--^
+   |               |
+   |               help: remove the empty string
 
-error: using `eprintln!("")`
+error: empty string literal in `eprintln!`
   --> $DIR/println_empty_string.rs:16:14
    |
 LL |         _ => eprintln!(""),
-   |              ^^^^^^^^^^^^^ help: replace it with: `eprintln!()`
+   |              ^^^^^^^^^^--^
+   |                        |
+   |                        help: remove the empty string
 
 error: aborting due to 4 previous errors
 
diff --git a/tests/ui/rename.fixed b/tests/ui/rename.fixed
index 9cbad2269a0..a6e7bdba77c 100644
--- a/tests/ui/rename.fixed
+++ b/tests/ui/rename.fixed
@@ -32,6 +32,7 @@
 #![allow(invalid_value)]
 #![allow(enum_intrinsics_non_enums)]
 #![allow(non_fmt_panics)]
+#![allow(named_arguments_used_positionally)]
 #![allow(temporary_cstring_as_ptr)]
 #![allow(unknown_lints)]
 #![allow(unused_labels)]
@@ -69,6 +70,7 @@
 #![warn(invalid_value)]
 #![warn(enum_intrinsics_non_enums)]
 #![warn(non_fmt_panics)]
+#![warn(named_arguments_used_positionally)]
 #![warn(temporary_cstring_as_ptr)]
 #![warn(unknown_lints)]
 #![warn(unused_labels)]
diff --git a/tests/ui/rename.rs b/tests/ui/rename.rs
index 9153c0dab02..e8f57597d02 100644
--- a/tests/ui/rename.rs
+++ b/tests/ui/rename.rs
@@ -32,6 +32,7 @@
 #![allow(invalid_value)]
 #![allow(enum_intrinsics_non_enums)]
 #![allow(non_fmt_panics)]
+#![allow(named_arguments_used_positionally)]
 #![allow(temporary_cstring_as_ptr)]
 #![allow(unknown_lints)]
 #![allow(unused_labels)]
@@ -69,6 +70,7 @@
 #![warn(clippy::invalid_ref)]
 #![warn(clippy::mem_discriminant_non_enum)]
 #![warn(clippy::panic_params)]
+#![warn(clippy::positional_named_format_parameters)]
 #![warn(clippy::temporary_cstring_as_ptr)]
 #![warn(clippy::unknown_clippy_lints)]
 #![warn(clippy::unused_label)]
diff --git a/tests/ui/rename.stderr b/tests/ui/rename.stderr
index 9c03ea914bb..31865a7f66d 100644
--- a/tests/ui/rename.stderr
+++ b/tests/ui/rename.stderr
@@ -1,5 +1,5 @@
 error: lint `clippy::blacklisted_name` has been renamed to `clippy::disallowed_names`
-  --> $DIR/rename.rs:38:9
+  --> $DIR/rename.rs:39:9
    |
 LL | #![warn(clippy::blacklisted_name)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_names`
@@ -7,220 +7,226 @@ LL | #![warn(clippy::blacklisted_name)]
    = note: `-D renamed-and-removed-lints` implied by `-D warnings`
 
 error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_if_conditions`
-  --> $DIR/rename.rs:39:9
+  --> $DIR/rename.rs:40:9
    |
 LL | #![warn(clippy::block_in_if_condition_expr)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions`
 
 error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_if_conditions`
-  --> $DIR/rename.rs:40:9
+  --> $DIR/rename.rs:41:9
    |
 LL | #![warn(clippy::block_in_if_condition_stmt)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions`
 
 error: lint `clippy::box_vec` has been renamed to `clippy::box_collection`
-  --> $DIR/rename.rs:41:9
+  --> $DIR/rename.rs:42:9
    |
 LL | #![warn(clippy::box_vec)]
    |         ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection`
 
 error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes`
-  --> $DIR/rename.rs:42:9
+  --> $DIR/rename.rs:43:9
    |
 LL | #![warn(clippy::const_static_lifetime)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes`
 
 error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity`
-  --> $DIR/rename.rs:43:9
+  --> $DIR/rename.rs:44:9
    |
 LL | #![warn(clippy::cyclomatic_complexity)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity`
 
 error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods`
-  --> $DIR/rename.rs:44:9
+  --> $DIR/rename.rs:45:9
    |
 LL | #![warn(clippy::disallowed_method)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods`
 
 error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types`
-  --> $DIR/rename.rs:45:9
+  --> $DIR/rename.rs:46:9
    |
 LL | #![warn(clippy::disallowed_type)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types`
 
 error: lint `clippy::eval_order_dependence` has been renamed to `clippy::mixed_read_write_in_expression`
-  --> $DIR/rename.rs:46:9
+  --> $DIR/rename.rs:47:9
    |
 LL | #![warn(clippy::eval_order_dependence)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::mixed_read_write_in_expression`
 
 error: lint `clippy::for_loop_over_option` has been renamed to `clippy::for_loops_over_fallibles`
-  --> $DIR/rename.rs:47:9
+  --> $DIR/rename.rs:48:9
    |
 LL | #![warn(clippy::for_loop_over_option)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::for_loops_over_fallibles`
 
 error: lint `clippy::for_loop_over_result` has been renamed to `clippy::for_loops_over_fallibles`
-  --> $DIR/rename.rs:48:9
+  --> $DIR/rename.rs:49:9
    |
 LL | #![warn(clippy::for_loop_over_result)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::for_loops_over_fallibles`
 
 error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion`
-  --> $DIR/rename.rs:49:9
+  --> $DIR/rename.rs:50:9
    |
 LL | #![warn(clippy::identity_conversion)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion`
 
 error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok`
-  --> $DIR/rename.rs:50:9
+  --> $DIR/rename.rs:51:9
    |
 LL | #![warn(clippy::if_let_some_result)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok`
 
 error: lint `clippy::logic_bug` has been renamed to `clippy::overly_complex_bool_expr`
-  --> $DIR/rename.rs:51:9
+  --> $DIR/rename.rs:52:9
    |
 LL | #![warn(clippy::logic_bug)]
    |         ^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::overly_complex_bool_expr`
 
 error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default`
-  --> $DIR/rename.rs:52:9
+  --> $DIR/rename.rs:53:9
    |
 LL | #![warn(clippy::new_without_default_derive)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default`
 
 error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map`
-  --> $DIR/rename.rs:53:9
+  --> $DIR/rename.rs:54:9
    |
 LL | #![warn(clippy::option_and_then_some)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map`
 
 error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_used`
-  --> $DIR/rename.rs:54:9
+  --> $DIR/rename.rs:55:9
    |
 LL | #![warn(clippy::option_expect_used)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used`
 
 error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or`
-  --> $DIR/rename.rs:55:9
+  --> $DIR/rename.rs:56:9
    |
 LL | #![warn(clippy::option_map_unwrap_or)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
 
 error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or`
-  --> $DIR/rename.rs:56:9
+  --> $DIR/rename.rs:57:9
    |
 LL | #![warn(clippy::option_map_unwrap_or_else)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
 
 error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used`
-  --> $DIR/rename.rs:57:9
+  --> $DIR/rename.rs:58:9
    |
 LL | #![warn(clippy::option_unwrap_used)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used`
 
 error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow`
-  --> $DIR/rename.rs:58:9
+  --> $DIR/rename.rs:59:9
    |
 LL | #![warn(clippy::ref_in_deref)]
    |         ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow`
 
 error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used`
-  --> $DIR/rename.rs:59:9
+  --> $DIR/rename.rs:60:9
    |
 LL | #![warn(clippy::result_expect_used)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used`
 
 error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or`
-  --> $DIR/rename.rs:60:9
+  --> $DIR/rename.rs:61:9
    |
 LL | #![warn(clippy::result_map_unwrap_or_else)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
 
 error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used`
-  --> $DIR/rename.rs:61:9
+  --> $DIR/rename.rs:62:9
    |
 LL | #![warn(clippy::result_unwrap_used)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used`
 
 error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str`
-  --> $DIR/rename.rs:62:9
+  --> $DIR/rename.rs:63:9
    |
 LL | #![warn(clippy::single_char_push_str)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str`
 
 error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions`
-  --> $DIR/rename.rs:63:9
+  --> $DIR/rename.rs:64:9
    |
 LL | #![warn(clippy::stutter)]
    |         ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions`
 
 error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl`
-  --> $DIR/rename.rs:64:9
+  --> $DIR/rename.rs:65:9
    |
 LL | #![warn(clippy::to_string_in_display)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl`
 
 error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters`
-  --> $DIR/rename.rs:65:9
+  --> $DIR/rename.rs:66:9
    |
 LL | #![warn(clippy::zero_width_space)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters`
 
 error: lint `clippy::drop_bounds` has been renamed to `drop_bounds`
-  --> $DIR/rename.rs:66:9
+  --> $DIR/rename.rs:67:9
    |
 LL | #![warn(clippy::drop_bounds)]
    |         ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds`
 
 error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter`
-  --> $DIR/rename.rs:67:9
+  --> $DIR/rename.rs:68:9
    |
 LL | #![warn(clippy::into_iter_on_array)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter`
 
 error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering`
-  --> $DIR/rename.rs:68:9
+  --> $DIR/rename.rs:69:9
    |
 LL | #![warn(clippy::invalid_atomic_ordering)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering`
 
 error: lint `clippy::invalid_ref` has been renamed to `invalid_value`
-  --> $DIR/rename.rs:69:9
+  --> $DIR/rename.rs:70:9
    |
 LL | #![warn(clippy::invalid_ref)]
    |         ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value`
 
 error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums`
-  --> $DIR/rename.rs:70:9
+  --> $DIR/rename.rs:71:9
    |
 LL | #![warn(clippy::mem_discriminant_non_enum)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums`
 
 error: lint `clippy::panic_params` has been renamed to `non_fmt_panics`
-  --> $DIR/rename.rs:71:9
+  --> $DIR/rename.rs:72:9
    |
 LL | #![warn(clippy::panic_params)]
    |         ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics`
 
+error: lint `clippy::positional_named_format_parameters` has been renamed to `named_arguments_used_positionally`
+  --> $DIR/rename.rs:73:9
+   |
+LL | #![warn(clippy::positional_named_format_parameters)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `named_arguments_used_positionally`
+
 error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr`
-  --> $DIR/rename.rs:72:9
+  --> $DIR/rename.rs:74:9
    |
 LL | #![warn(clippy::temporary_cstring_as_ptr)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr`
 
 error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints`
-  --> $DIR/rename.rs:73:9
+  --> $DIR/rename.rs:75:9
    |
 LL | #![warn(clippy::unknown_clippy_lints)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints`
 
 error: lint `clippy::unused_label` has been renamed to `unused_labels`
-  --> $DIR/rename.rs:74:9
+  --> $DIR/rename.rs:76:9
    |
 LL | #![warn(clippy::unused_label)]
    |         ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels`
 
-error: aborting due to 37 previous errors
+error: aborting due to 38 previous errors
 
diff --git a/tests/ui/write_literal.rs b/tests/ui/write_literal.rs
index 44669174411..5892818aa9a 100644
--- a/tests/ui/write_literal.rs
+++ b/tests/ui/write_literal.rs
@@ -25,11 +25,13 @@ fn main() {
     writeln!(v, "{} of {:b} people know binary, the other half doesn't", 1, 2);
     writeln!(v, "10 / 4 is {}", 2.5);
     writeln!(v, "2 + 1 = {}", 3);
+    writeln!(v, "From expansion {}", stringify!(not a string literal));
 
     // these should throw warnings
     write!(v, "Hello {}", "world");
     writeln!(v, "Hello {} {}", world, "world");
     writeln!(v, "Hello {}", "world");
+    writeln!(v, "{} {:.4}", "a literal", 5);
 
     // positional args don't change the fact
     // that we're using a literal -- this should
diff --git a/tests/ui/write_literal.stderr b/tests/ui/write_literal.stderr
index 3c5ec91d3e0..1e306ae28a2 100644
--- a/tests/ui/write_literal.stderr
+++ b/tests/ui/write_literal.stderr
@@ -1,5 +1,5 @@
 error: literal with an empty format string
-  --> $DIR/write_literal.rs:30:27
+  --> $DIR/write_literal.rs:31:27
    |
 LL |     write!(v, "Hello {}", "world");
    |                           ^^^^^^^
@@ -12,7 +12,7 @@ LL +     write!(v, "Hello world");
    |
 
 error: literal with an empty format string
-  --> $DIR/write_literal.rs:31:39
+  --> $DIR/write_literal.rs:32:39
    |
 LL |     writeln!(v, "Hello {} {}", world, "world");
    |                                       ^^^^^^^
@@ -24,7 +24,7 @@ LL +     writeln!(v, "Hello {} world", world);
    |
 
 error: literal with an empty format string
-  --> $DIR/write_literal.rs:32:29
+  --> $DIR/write_literal.rs:33:29
    |
 LL |     writeln!(v, "Hello {}", "world");
    |                             ^^^^^^^
@@ -36,7 +36,19 @@ LL +     writeln!(v, "Hello world");
    |
 
 error: literal with an empty format string
-  --> $DIR/write_literal.rs:37:28
+  --> $DIR/write_literal.rs:34:29
+   |
+LL |     writeln!(v, "{} {:.4}", "a literal", 5);
+   |                             ^^^^^^^^^^^
+   |
+help: try this
+   |
+LL -     writeln!(v, "{} {:.4}", "a literal", 5);
+LL +     writeln!(v, "a literal {:.4}", 5);
+   |
+
+error: literal with an empty format string
+  --> $DIR/write_literal.rs:39:28
    |
 LL |     writeln!(v, "{0} {1}", "hello", "world");
    |                            ^^^^^^^
@@ -48,7 +60,7 @@ LL +     writeln!(v, "hello {1}", "world");
    |
 
 error: literal with an empty format string
-  --> $DIR/write_literal.rs:37:37
+  --> $DIR/write_literal.rs:39:37
    |
 LL |     writeln!(v, "{0} {1}", "hello", "world");
    |                                     ^^^^^^^
@@ -60,19 +72,7 @@ LL +     writeln!(v, "{0} world", "hello");
    |
 
 error: literal with an empty format string
-  --> $DIR/write_literal.rs:38:28
-   |
-LL |     writeln!(v, "{1} {0}", "hello", "world");
-   |                            ^^^^^^^
-   |
-help: try this
-   |
-LL -     writeln!(v, "{1} {0}", "hello", "world");
-LL +     writeln!(v, "{1} hello", "world");
-   |
-
-error: literal with an empty format string
-  --> $DIR/write_literal.rs:38:37
+  --> $DIR/write_literal.rs:40:37
    |
 LL |     writeln!(v, "{1} {0}", "hello", "world");
    |                                     ^^^^^^^
@@ -84,10 +84,22 @@ LL +     writeln!(v, "world {0}", "hello");
    |
 
 error: literal with an empty format string
-  --> $DIR/write_literal.rs:41:32
+  --> $DIR/write_literal.rs:40:28
+   |
+LL |     writeln!(v, "{1} {0}", "hello", "world");
+   |                            ^^^^^^^
+   |
+help: try this
+   |
+LL -     writeln!(v, "{1} {0}", "hello", "world");
+LL +     writeln!(v, "{1} hello", "world");
+   |
+
+error: literal with an empty format string
+  --> $DIR/write_literal.rs:43:38
    |
 LL |     writeln!(v, "{foo} {bar}", foo = "hello", bar = "world");
-   |                                ^^^^^^^^^^^^^
+   |                                      ^^^^^^^
    |
 help: try this
    |
@@ -96,10 +108,10 @@ LL +     writeln!(v, "hello {bar}", bar = "world");
    |
 
 error: literal with an empty format string
-  --> $DIR/write_literal.rs:41:47
+  --> $DIR/write_literal.rs:43:53
    |
 LL |     writeln!(v, "{foo} {bar}", foo = "hello", bar = "world");
-   |                                               ^^^^^^^^^^^^^
+   |                                                     ^^^^^^^
    |
 help: try this
    |
@@ -108,22 +120,10 @@ LL +     writeln!(v, "{foo} world", foo = "hello");
    |
 
 error: literal with an empty format string
-  --> $DIR/write_literal.rs:42:32
+  --> $DIR/write_literal.rs:44:53
    |
 LL |     writeln!(v, "{bar} {foo}", foo = "hello", bar = "world");
-   |                                ^^^^^^^^^^^^^
-   |
-help: try this
-   |
-LL -     writeln!(v, "{bar} {foo}", foo = "hello", bar = "world");
-LL +     writeln!(v, "{bar} hello", bar = "world");
-   |
-
-error: literal with an empty format string
-  --> $DIR/write_literal.rs:42:47
-   |
-LL |     writeln!(v, "{bar} {foo}", foo = "hello", bar = "world");
-   |                                               ^^^^^^^^^^^^^
+   |                                                     ^^^^^^^
    |
 help: try this
    |
@@ -131,5 +131,17 @@ LL -     writeln!(v, "{bar} {foo}", foo = "hello", bar = "world");
 LL +     writeln!(v, "world {foo}", foo = "hello");
    |
 
-error: aborting due to 11 previous errors
+error: literal with an empty format string
+  --> $DIR/write_literal.rs:44:38
+   |
+LL |     writeln!(v, "{bar} {foo}", foo = "hello", bar = "world");
+   |                                      ^^^^^^^
+   |
+help: try this
+   |
+LL -     writeln!(v, "{bar} {foo}", foo = "hello", bar = "world");
+LL +     writeln!(v, "{bar} hello", bar = "world");
+   |
+
+error: aborting due to 12 previous errors
 
diff --git a/tests/ui/write_literal_2.rs b/tests/ui/write_literal_2.rs
index ba0d7be5eaa..55a11daa1d3 100644
--- a/tests/ui/write_literal_2.rs
+++ b/tests/ui/write_literal_2.rs
@@ -10,7 +10,7 @@ fn main() {
     writeln!(v, r"{}", r"{hello}");
     writeln!(v, "{}", '\'');
     writeln!(v, "{}", '"');
-    writeln!(v, r"{}", '"'); // don't lint
+    writeln!(v, r"{}", '"');
     writeln!(v, r"{}", '\'');
     writeln!(
         v,
@@ -24,4 +24,11 @@ fn main() {
         {} \\ {}",
         "1", "2", "3",
     );
+    writeln!(v, "{}", "\\");
+    writeln!(v, r"{}", "\\");
+    writeln!(v, r#"{}"#, "\\");
+    writeln!(v, "{}", r"\");
+    writeln!(v, "{}", "\r");
+    writeln!(v, r#"{}{}"#, '#', '"'); // hard mode
+    writeln!(v, r"{}", "\r"); // should not lint
 }
diff --git a/tests/ui/write_literal_2.stderr b/tests/ui/write_literal_2.stderr
index 9ff297069c4..d5956db9ff0 100644
--- a/tests/ui/write_literal_2.stderr
+++ b/tests/ui/write_literal_2.stderr
@@ -47,6 +47,12 @@ LL -     writeln!(v, "{}", '"');
 LL +     writeln!(v, "/"");
    |
 
+error: literal with an empty format string
+  --> $DIR/write_literal_2.rs:13:24
+   |
+LL |     writeln!(v, r"{}", '"');
+   |                        ^^^
+
 error: literal with an empty format string
   --> $DIR/write_literal_2.rs:14:24
    |
@@ -108,5 +114,77 @@ LL ~         {} / 3",
 LL ~         "1", "2",
    |
 
-error: aborting due to 9 previous errors
+error: literal with an empty format string
+  --> $DIR/write_literal_2.rs:27:23
+   |
+LL |     writeln!(v, "{}", "/");
+   |                       ^^^^
+   |
+help: try this
+   |
+LL -     writeln!(v, "{}", "/");
+LL +     writeln!(v, "/");
+   |
+
+error: literal with an empty format string
+  --> $DIR/write_literal_2.rs:28:24
+   |
+LL |     writeln!(v, r"{}", "/");
+   |                        ^^^^
+   |
+help: try this
+   |
+LL -     writeln!(v, r"{}", "/");
+LL +     writeln!(v, r"/");
+   |
+
+error: literal with an empty format string
+  --> $DIR/write_literal_2.rs:29:26
+   |
+LL |     writeln!(v, r#"{}"#, "/");
+   |                          ^^^^
+   |
+help: try this
+   |
+LL -     writeln!(v, r#"{}"#, "/");
+LL +     writeln!(v, r#"/"#);
+   |
+
+error: literal with an empty format string
+  --> $DIR/write_literal_2.rs:30:23
+   |
+LL |     writeln!(v, "{}", r"/");
+   |                       ^^^^
+   |
+help: try this
+   |
+LL -     writeln!(v, "{}", r"/");
+LL +     writeln!(v, "/");
+   |
+
+error: literal with an empty format string
+  --> $DIR/write_literal_2.rs:31:23
+   |
+LL |     writeln!(v, "{}", "/r");
+   |                       ^^^^
+   |
+help: try this
+   |
+LL -     writeln!(v, "{}", "/r");
+LL +     writeln!(v, "/r");
+   |
+
+error: literal with an empty format string
+  --> $DIR/write_literal_2.rs:32:28
+   |
+LL |     writeln!(v, r#"{}{}"#, '#', '"'); // hard mode
+   |                            ^^^
+
+error: literal with an empty format string
+  --> $DIR/write_literal_2.rs:32:33
+   |
+LL |     writeln!(v, r#"{}{}"#, '#', '"'); // hard mode
+   |                                 ^^^
+
+error: aborting due to 17 previous errors
 
diff --git a/tests/ui/write_with_newline.rs b/tests/ui/write_with_newline.rs
index 446d6914d34..b79364c8758 100644
--- a/tests/ui/write_with_newline.rs
+++ b/tests/ui/write_with_newline.rs
@@ -56,4 +56,12 @@ fn main() {
     write!(v, "foo\r\n");
     write!(v, "\\r\n"); //~ ERROR
     write!(v, "foo\rbar\n");
+
+    // Ignore expanded format strings
+    macro_rules! newline {
+        () => {
+            "\n"
+        };
+    }
+    write!(v, newline!());
 }
diff --git a/tests/ui/write_with_newline.stderr b/tests/ui/write_with_newline.stderr
index 5f55431be0b..2baaea166d8 100644
--- a/tests/ui/write_with_newline.stderr
+++ b/tests/ui/write_with_newline.stderr
@@ -5,7 +5,7 @@ LL |     write!(v, "Hello/n");
    |     ^^^^^^^^^^^^^^^^^^^^
    |
    = note: `-D clippy::write-with-newline` implied by `-D warnings`
-help: use `writeln!()` instead
+help: use `writeln!` instead
    |
 LL -     write!(v, "Hello/n");
 LL +     writeln!(v, "Hello");
@@ -17,7 +17,7 @@ error: using `write!()` with a format string that ends in a single newline
 LL |     write!(v, "Hello {}/n", "world");
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-help: use `writeln!()` instead
+help: use `writeln!` instead
    |
 LL -     write!(v, "Hello {}/n", "world");
 LL +     writeln!(v, "Hello {}", "world");
@@ -29,7 +29,7 @@ error: using `write!()` with a format string that ends in a single newline
 LL |     write!(v, "Hello {} {}/n", "world", "#2");
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-help: use `writeln!()` instead
+help: use `writeln!` instead
    |
 LL -     write!(v, "Hello {} {}/n", "world", "#2");
 LL +     writeln!(v, "Hello {} {}", "world", "#2");
@@ -41,7 +41,7 @@ error: using `write!()` with a format string that ends in a single newline
 LL |     write!(v, "{}/n", 1265);
    |     ^^^^^^^^^^^^^^^^^^^^^^^
    |
-help: use `writeln!()` instead
+help: use `writeln!` instead
    |
 LL -     write!(v, "{}/n", 1265);
 LL +     writeln!(v, "{}", 1265);
@@ -53,7 +53,7 @@ error: using `write!()` with a format string that ends in a single newline
 LL |     write!(v, "/n");
    |     ^^^^^^^^^^^^^^^
    |
-help: use `writeln!()` instead
+help: use `writeln!` instead
    |
 LL -     write!(v, "/n");
 LL +     writeln!(v);
@@ -65,7 +65,7 @@ error: using `write!()` with a format string that ends in a single newline
 LL |     write!(v, "//n"); // should fail
    |     ^^^^^^^^^^^^^^^^^
    |
-help: use `writeln!()` instead
+help: use `writeln!` instead
    |
 LL -     write!(v, "//n"); // should fail
 LL +     writeln!(v, "/"); // should fail
@@ -81,11 +81,10 @@ LL | | "
 LL | |     );
    | |_____^
    |
-help: use `writeln!()` instead
+help: use `writeln!` instead
    |
 LL ~     writeln!(
-LL |         v,
-LL ~         ""
+LL ~         v
    |
 
 error: using `write!()` with a format string that ends in a single newline
@@ -98,11 +97,10 @@ LL | | "
 LL | |     );
    | |_____^
    |
-help: use `writeln!()` instead
+help: use `writeln!` instead
    |
 LL ~     writeln!(
-LL |         v,
-LL ~         r""
+LL ~         v
    |
 
 error: using `write!()` with a format string that ends in a single newline
@@ -111,23 +109,11 @@ error: using `write!()` with a format string that ends in a single newline
 LL |     write!(v, "/r/n"); //~ ERROR
    |     ^^^^^^^^^^^^^^^^^^
    |
-help: use `writeln!()` instead
+help: use `writeln!` instead
    |
 LL -     write!(v, "/r/n"); //~ ERROR
 LL +     writeln!(v, "/r"); //~ ERROR
    |
 
-error: using `write!()` with a format string that ends in a single newline
-  --> $DIR/write_with_newline.rs:58:5
-   |
-LL |     write!(v, "foo/rbar/n");
-   |     ^^^^^^^^^^^^^^^^^^^^^^^
-   |
-help: use `writeln!()` instead
-   |
-LL -     write!(v, "foo/rbar/n");
-LL +     writeln!(v, "foo/rbar");
-   |
-
-error: aborting due to 10 previous errors
+error: aborting due to 9 previous errors
 
diff --git a/tests/ui/writeln_empty_string.stderr b/tests/ui/writeln_empty_string.stderr
index ac65aadfc0e..25e69ec48e7 100644
--- a/tests/ui/writeln_empty_string.stderr
+++ b/tests/ui/writeln_empty_string.stderr
@@ -1,16 +1,20 @@
-error: using `writeln!(v, "")`
+error: empty string literal in `writeln!`
   --> $DIR/writeln_empty_string.rs:11:5
    |
 LL |     writeln!(v, "");
-   |     ^^^^^^^^^^^^^^^ help: replace it with: `writeln!(v)`
+   |     ^^^^^^^^^^----^
+   |               |
+   |               help: remove the empty string
    |
    = note: `-D clippy::writeln-empty-string` implied by `-D warnings`
 
-error: using `writeln!(suggestion, "")`
+error: empty string literal in `writeln!`
   --> $DIR/writeln_empty_string.rs:14:5
    |
 LL |     writeln!(suggestion, "");
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `writeln!(suggestion)`
+   |     ^^^^^^^^^^^^^^^^^^^----^
+   |                        |
+   |                        help: remove the empty string
 
 error: aborting due to 2 previous errors