From 6fc6d87fd069470893b6a539a46fa1e2f10ae906 Mon Sep 17 00:00:00 2001 From: Alex Macleod 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 | 787 ++++++------------ 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 | 56 +- 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 | 56 +- 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, 554 insertions(+), 1308 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 0c5851cdbed2..f10d82569536 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 97024be16fa4..e1c46fd0bfd4 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 1f85382347aa..39ab175341f4 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 962e67220069..c16f2d86ae0a 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 8f131bbf98be..f463ad9f6d2e 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 ec96999896e2..83fdc15c9f02 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 6bea6dc0773d..d320eea1c377 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 a8500beb2574..2be3fa99c811 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) -> 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 640a09a7a912..06e7d7017017 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, - ); - }, - ); - } - } - } 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") - }; - - span_lint_and_sugg( - cx, - WRITELN_EMPTY_STRING, - mac.span(), - format!("using `writeln!({}, \"\")`", suggestion).as_str(), - "replace it with", - format!("writeln!({})", suggestion), - applicability, - ); + 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}!`")); } - } + }, + 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, } - } -} -/// 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(); + let Some(format_args) = FormatArgsExpn::find_nested(cx, expr, macro_call.expn) else { return }; - 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()), - }; + // ignore `writeln!(w)` and `write!(v, some_macro!())` + if format_args.format_string.span.from_expansion() { + return; + } - let newline_sp_len = if contents.ends_with('\n') { - BytePos(1) - } else if contents.ends_with(r"\n") { - BytePos(2) - } else { - panic!("expected format string to contain a newline"); - }; + match diag_name { + sym::print_macro | sym::eprint_macro | sym::write_macro => { + check_newline(cx, &format_args, ¯o_call, name); + }, + sym::println_macro | sym::eprintln_macro | sym::writeln_macro => { + check_empty_string(cx, &format_args, ¯o_call, name); + }, + _ => {}, + } - (sp.with_lo(newline_sp_hi - newline_sp_len).with_hi(newline_sp_hi), false) -} + check_literal(cx, &format_args, name); -/// 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>, - complex_unnamed: Vec>, - named: Vec<(Symbol, Vec)>, + 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"); + } + } + } + } } -impl SimpleFormatArgs { - fn get_unnamed(&self) -> impl Iterator { - self.unnamed.iter().map(|x| match x.as_slice() { - // Ignore the dummy span added from out of order format arguments. - [DUMMY_SP] => &[], - x => x, - }) +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 { + false } +} - fn get_complex_unnamed(&self) -> impl Iterator { - self.complex_unnamed.iter().map(Vec::as_slice) - } +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; - fn get_named(&self, n: &Path) -> &[Span] { - self.named.iter().find(|x| *n == x.0).map_or(&[], |x| x.1.as_slice()) - } + let Some(last) = format_string_parts.last() else { return }; - fn push(&mut self, arg: rustc_parse_format::Argument<'_>, span: Span) { - use rustc_parse_format::{ - AlignUnknown, ArgumentImplicitlyIs, ArgumentIs, ArgumentNamed, CountImplied, FormatSpec, - }; + let count_vertical_whitespace = || { + format_string_parts + .iter() + .flat_map(|part| part.as_str().chars()) + .filter(|ch| matches!(ch, '\r' | '\n')) + .count() + }; - const SIMPLE: FormatSpec<'_> = FormatSpec { - fill: None, - align: AlignUnknown, - flags: 0, - precision: CountImplied, - precision_span: None, - width: CountImplied, - width_span: None, - ty: "", - ty_span: None, - }; + if last.as_str().ends_with('\n') + // ignore format strings with other internal vertical whitespace + && count_vertical_whitespace() == 1 - 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(), - } - } - }, - 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())); - } - }, - }; - } + // 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 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]); + WRITE_WITH_NEWLINE } else { - let args: &mut Vec = &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); + PRINT_WITH_NEWLINE }; - 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 - }; + 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 }; + + 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 span = str_lit_span.from_inner(InnerSpan { - start: span.start + 1, - end: span.end - hack, - }); - self.push_to_complex(span, n); - }; + let hi = format_string_span.hi(); + let newline_span = format_string_span.with_lo(hi - BytePos(3)).with_hi(hi - BytePos(1)); - 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); - }; + diag.multipart_suggestion( + &format!("use `{name}ln!` instead"), + vec![(name_span, format!("{name}ln")), (newline_span, String::new())], + Applicability::MachineApplicable, + ); + } + }, + ); } } -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 { - use rustc_parse_format::{ParseMode, Parser, Piece}; - - 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(); +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); - 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, Option) { - 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), - }; + 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, + ); + }, + ); + } +} - let args = match self.parse_fmt_string(cx, &fmtstr) { - Some(args) => args, - None => return (Some(fmtstr), expr), - }; +fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, name: &str) { + let mut counts = HirIdMap::::default(); + for param in format_args.params() { + *counts.entry(param.value.hir_id).or_default() += 1; + } - 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); - } + for arg in &format_args.args { + let value = arg.param.value; - 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, + 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], } - }, - _ => { - unnamed_args.next(); - continue; - }, + .to_string(), + false, + ), + LitKind::Bool(b) => (b.to_string(), false), + _ => continue, + }; + + let lint = if name.starts_with("write") { + WRITE_LITERAL + } else { + PRINT_LITERAL }; - 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('}', "}}") + 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, }, - LitKind::Str | LitKind::ByteStr if matches!(fmtstr.style, StrStyle::Cooked) => { - lit.token_lit.symbol.as_str().replace('{', "{{").replace('}', "}}") + (true, true) => { + if replacement.contains(['#', '"']) { + None + } else { + Some(replacement) + } }, - 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| { + 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", - iter::once((comma_span.to(token_expr.span), String::new())) - .chain(fmt_spans.iter().copied().zip(iter::repeat(replacement))) - .collect(), + vec![(arg.span, replacement), (value_span, String::new())], Applicability::MachineApplicable, ); - }, - ); - } - } - } - - fn report_positional_named_param(cx: &EarlyContext<'_>, span: Span, lhs: &P, _rhs: &P) { - 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), - Applicability::MachineApplicable, - ); - } - } - } - - 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())], - 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, c: Result| { - 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 { + 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 bd89ff977f87..8855b7394801 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 f3a5048e7fa8..9f6f1fb47276 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 e391d2406677..000000000000 --- 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 160073414f9a..a6252a68780b 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 fc14511cd6a6..9c6edeeef125 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 6c9a4b98e1e6..d2cbd811d1b2 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 9c41a48f9f73..a7a884d08711 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 8df32649ad94..de5e121be877 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 f137787bff0c..0eefb9f0ca97 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 b56d6aec508d..e0c5f692740a 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 4c1a3a840ed9..ff83cd64bf09 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 6c35caeb034d..0ef0ac655d39 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 4170e1098204..000000000000 --- 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 553d8494ecc0..000000000000 --- 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 48ddb6d67ad2..000000000000 --- 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 8665a3bb28ae..3f6639c14585 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 72aae0756033..23e6dbc3e341 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,34 +72,34 @@ LL + println!("{0} world", "hello"); | error: literal with an empty format string - --> $DIR/print_literal.rs:33:25 + --> $DIR/print_literal.rs:35:34 | LL | println!("{1} {0}", "hello", "world"); - | ^^^^^^^ + | ^^^^^^^ | help: try this | LL - println!("{1} {0}", "hello", "world"); -LL + println!("{1} hello", "world"); +LL + println!("world {0}", "hello"); | error: literal with an empty format string - --> $DIR/print_literal.rs:33:34 + --> $DIR/print_literal.rs:35:25 | LL | println!("{1} {0}", "hello", "world"); - | ^^^^^^^ + | ^^^^^^^ | help: try this | LL - println!("{1} {0}", "hello", "world"); -LL + println!("world {0}", "hello"); +LL + println!("{1} hello", "world"); | error: literal with an empty format string - --> $DIR/print_literal.rs:36:29 + --> $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,28 +120,28 @@ 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"); +LL + println!("world {foo}", foo = "hello"); | error: literal with an empty format string - --> $DIR/print_literal.rs:37:44 + --> $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!("world {foo}", foo = "hello"); +LL + println!("{bar} hello", bar = "world"); | -error: aborting due to 11 previous errors +error: aborting due to 12 previous errors diff --git a/tests/ui/print_with_newline.rs b/tests/ui/print_with_newline.rs index a43a1fc4f524..b8c29d207ada 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 edbaa1cdf979..b9f5675faec7 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 17fe4ea74790..3cc8bb947bd3 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 9cbad2269a09..a6e7bdba77c6 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 9153c0dab029..e8f57597d02b 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 9c03ea914bb6..31865a7f66d6 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 446691744116..5892818aa9a6 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 3c5ec91d3e0f..1e306ae28a26 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,34 +72,34 @@ LL + writeln!(v, "{0} world", "hello"); | error: literal with an empty format string - --> $DIR/write_literal.rs:38:28 + --> $DIR/write_literal.rs:40:37 | LL | writeln!(v, "{1} {0}", "hello", "world"); - | ^^^^^^^ + | ^^^^^^^ | help: try this | LL - writeln!(v, "{1} {0}", "hello", "world"); -LL + writeln!(v, "{1} hello", "world"); +LL + writeln!(v, "world {0}", "hello"); | error: literal with an empty format string - --> $DIR/write_literal.rs:38:37 + --> $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, "world {0}", "hello"); +LL + writeln!(v, "{1} hello", "world"); | error: literal with an empty format string - --> $DIR/write_literal.rs:41:32 + --> $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,28 +120,28 @@ 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"); +LL + writeln!(v, "world {foo}", foo = "hello"); | error: literal with an empty format string - --> $DIR/write_literal.rs:42:47 + --> $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, "world {foo}", foo = "hello"); +LL + writeln!(v, "{bar} hello", bar = "world"); | -error: aborting due to 11 previous errors +error: aborting due to 12 previous errors diff --git a/tests/ui/write_literal_2.rs b/tests/ui/write_literal_2.rs index ba0d7be5eaa6..55a11daa1d34 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 9ff297069c40..d5956db9ff0b 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 446d6914d346..b79364c8758c 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 5f55431be0bd..2baaea166d8e 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 ac65aadfc0e8..25e69ec48e7e 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