diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 6701bedfe5e..5c6040f63f5 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -9,6 +9,7 @@ keywords = ["clippy", "lint", "plugin"] edition = "2021" [dependencies] +arrayvec = { version = "0.7", default-features = false } cargo_metadata = "0.15.3" clippy_utils = { path = "../clippy_utils" } declare_clippy_lint = { path = "../declare_clippy_lint" } diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs index e7caed65bf2..08e45ed7d0e 100644 --- a/clippy_lints/src/format_args.rs +++ b/clippy_lints/src/format_args.rs @@ -1,27 +1,31 @@ +use arrayvec::ArrayVec; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::is_diag_trait_item; -use clippy_utils::macros::FormatParamKind::{Implicit, Named, NamedInline, Numbered, Starred}; use clippy_utils::macros::{ - is_assert_macro, is_format_macro, is_panic, root_macro_call, Count, FormatArg, FormatArgsExpn, FormatParam, - FormatParamUsage, + find_format_arg_expr, find_format_args, format_arg_removal_span, format_placeholder_format_span, is_assert_macro, + is_format_macro, is_panic, root_macro_call, root_macro_call_first_node, FormatParamUsage, }; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_opt; use clippy_utils::ty::{implements_trait, is_type_lang_item}; use if_chain::if_chain; use itertools::Itertools; +use rustc_ast::{ + FormatArgPosition, FormatArgPositionKind, FormatArgsPiece, FormatArgumentKind, FormatCount, FormatOptions, + FormatPlaceholder, FormatTrait, +}; use rustc_errors::{ Applicability, SuggestionStyle::{CompletelyHidden, ShowCode}, }; -use rustc_hir::{Expr, ExprKind, HirId, LangItem, QPath}; +use rustc_hir::{Expr, ExprKind, LangItem}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty::adjustment::{Adjust, Adjustment}; use rustc_middle::ty::Ty; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::def_id::DefId; use rustc_span::edition::Edition::Edition2021; -use rustc_span::{sym, ExpnData, ExpnKind, Span, Symbol}; +use rustc_span::{sym, Span, Symbol}; declare_clippy_lint! { /// ### What it does @@ -184,72 +188,79 @@ impl FormatArgs { impl<'tcx> LateLintPass<'tcx> for FormatArgs { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if let Some(format_args) = FormatArgsExpn::parse(cx, expr) - && let expr_expn_data = expr.span.ctxt().outer_expn_data() - && let outermost_expn_data = outermost_expn_data(expr_expn_data) - && let Some(macro_def_id) = outermost_expn_data.macro_def_id - && is_format_macro(cx, macro_def_id) - && let ExpnKind::Macro(_, name) = outermost_expn_data.kind - { - for arg in &format_args.args { - check_unused_format_specifier(cx, arg); - if !arg.format.is_default() { - continue; - } - if is_aliased(&format_args, arg.param.value.hir_id) { - continue; - } - check_format_in_format_args(cx, outermost_expn_data.call_site, name, arg.param.value); - check_to_string_in_format_args(cx, name, arg.param.value); - } - if self.msrv.meets(msrvs::FORMAT_ARGS_CAPTURE) { - check_uninlined_args(cx, &format_args, outermost_expn_data.call_site, macro_def_id, self.ignore_mixed); - } + let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return }; + if !is_format_macro(cx, macro_call.def_id) { + return; } + let name = cx.tcx.item_name(macro_call.def_id); + + find_format_args(cx, expr, macro_call.expn, |format_args| { + for piece in &format_args.template { + if let FormatArgsPiece::Placeholder(placeholder) = piece + && let Ok(index) = placeholder.argument.index + && let Some(arg) = format_args.arguments.all_args().get(index) + { + let arg_expr = find_format_arg_expr(expr, arg); + + check_unused_format_specifier(cx, placeholder, arg_expr); + + if placeholder.format_trait != FormatTrait::Display + || placeholder.format_options != FormatOptions::default() + || is_aliased(format_args, index) + { + continue; + } + + if let Ok(arg_hir_expr) = arg_expr { + check_format_in_format_args(cx, macro_call.span, name, arg_hir_expr); + check_to_string_in_format_args(cx, name, arg_hir_expr); + } + } + } + + if self.msrv.meets(msrvs::FORMAT_ARGS_CAPTURE) { + check_uninlined_args(cx, format_args, macro_call.span, macro_call.def_id, self.ignore_mixed); + } + }); } extract_msrv_attr!(LateContext); } -fn check_unused_format_specifier(cx: &LateContext<'_>, arg: &FormatArg<'_>) { - let param_ty = cx.typeck_results().expr_ty(arg.param.value).peel_refs(); +fn check_unused_format_specifier( + cx: &LateContext<'_>, + placeholder: &FormatPlaceholder, + arg_expr: Result<&Expr<'_>, &rustc_ast::Expr>, +) { + let ty_or_ast_expr = arg_expr.map(|expr| cx.typeck_results().expr_ty(expr).peel_refs()); - if let Count::Implied(Some(mut span)) = arg.format.precision - && !span.is_empty() + let is_format_args = match ty_or_ast_expr { + Ok(ty) => is_type_lang_item(cx, ty, LangItem::FormatArguments), + Err(expr) => matches!(expr.peel_parens_and_refs().kind, rustc_ast::ExprKind::FormatArgs(_)), + }; + + let options = &placeholder.format_options; + + let arg_span = match arg_expr { + Ok(expr) => expr.span, + Err(expr) => expr.span, + }; + + if let Some(placeholder_span) = placeholder.span + && is_format_args + && *options != FormatOptions::default() { span_lint_and_then( cx, UNUSED_FORMAT_SPECS, - span, - "empty precision specifier has no effect", - |diag| { - if param_ty.is_floating_point() { - diag.note("a precision specifier is not required to format floats"); - } - - if arg.format.is_default() { - // If there's no other specifiers remove the `:` too - span = arg.format_span(); - } - - diag.span_suggestion_verbose(span, "remove the `.`", "", Applicability::MachineApplicable); - }, - ); - } - - if is_type_lang_item(cx, param_ty, LangItem::FormatArguments) && !arg.format.is_default_for_trait() { - span_lint_and_then( - cx, - UNUSED_FORMAT_SPECS, - arg.span, + placeholder_span, "format specifiers have no effect on `format_args!()`", |diag| { - let mut suggest_format = |spec, span| { + let mut suggest_format = |spec| { let message = format!("for the {spec} to apply consider using `format!()`"); - if let Some(mac_call) = root_macro_call(arg.param.value.span) + if let Some(mac_call) = root_macro_call(arg_span) && cx.tcx.is_diagnostic_item(sym::format_args_macro, mac_call.def_id) - && arg.span.eq_ctxt(mac_call.span) { diag.span_suggestion( cx.sess().source_map().span_until_char(mac_call.span, '!'), @@ -257,25 +268,27 @@ fn check_unused_format_specifier(cx: &LateContext<'_>, arg: &FormatArg<'_>) { "format", Applicability::MaybeIncorrect, ); - } else if let Some(span) = span { - diag.span_help(span, message); + } else { + diag.help(message); } }; - if !arg.format.width.is_implied() { - suggest_format("width", arg.format.width.span()); + if options.width.is_some() { + suggest_format("width"); } - if !arg.format.precision.is_implied() { - suggest_format("precision", arg.format.precision.span()); + if options.precision.is_some() { + suggest_format("precision"); } - diag.span_suggestion_verbose( - arg.format_span(), - "if the current behavior is intentional, remove the format specifiers", - "", - Applicability::MaybeIncorrect, - ); + if let Some(format_span) = format_placeholder_format_span(placeholder) { + diag.span_suggestion_verbose( + format_span, + "if the current behavior is intentional, remove the format specifiers", + "", + Applicability::MaybeIncorrect, + ); + } }, ); } @@ -283,12 +296,12 @@ fn check_unused_format_specifier(cx: &LateContext<'_>, arg: &FormatArg<'_>) { fn check_uninlined_args( cx: &LateContext<'_>, - args: &FormatArgsExpn<'_>, + args: &rustc_ast::FormatArgs, call_site: Span, def_id: DefId, ignore_mixed: bool, ) { - if args.format_string.span.from_expansion() { + if args.span.from_expansion() { return; } if call_site.edition() < Edition2021 && (is_panic(cx, def_id) || is_assert_macro(cx, def_id)) { @@ -303,7 +316,13 @@ fn check_uninlined_args( // we cannot remove any other arguments in the format string, // because the index numbers might be wrong after inlining. // Example of an un-inlinable format: print!("{}{1}", foo, 2) - if !args.params().all(|p| check_one_arg(args, &p, &mut fixes, ignore_mixed)) || fixes.is_empty() { + for (pos, usage) in format_arg_positions(args) { + if !check_one_arg(args, pos, usage, &mut fixes, ignore_mixed) { + return; + } + } + + if fixes.is_empty() { return; } @@ -332,38 +351,36 @@ fn check_uninlined_args( } fn check_one_arg( - args: &FormatArgsExpn<'_>, - param: &FormatParam<'_>, + args: &rustc_ast::FormatArgs, + pos: &FormatArgPosition, + usage: FormatParamUsage, fixes: &mut Vec<(Span, String)>, ignore_mixed: bool, ) -> bool { - if matches!(param.kind, Implicit | Starred | Named(_) | Numbered) - && let ExprKind::Path(QPath::Resolved(None, path)) = param.value.kind - && let [segment] = path.segments + let index = pos.index.unwrap(); + let arg = &args.arguments.all_args()[index]; + + if !matches!(arg.kind, FormatArgumentKind::Captured(_)) + && let rustc_ast::ExprKind::Path(None, path) = &arg.expr.kind + && let [segment] = path.segments.as_slice() && segment.args.is_none() - && let Some(arg_span) = args.value_with_prev_comma_span(param.value.hir_id) + && let Some(arg_span) = format_arg_removal_span(args, index) + && let Some(pos_span) = pos.span { - let replacement = match param.usage { + let replacement = match usage { FormatParamUsage::Argument => segment.ident.name.to_string(), FormatParamUsage::Width => format!("{}$", segment.ident.name), FormatParamUsage::Precision => format!(".{}$", segment.ident.name), }; - fixes.push((param.span, replacement)); + fixes.push((pos_span, replacement)); fixes.push((arg_span, String::new())); true // successful inlining, continue checking } else { // Do not continue inlining (return false) in case // * if we can't inline a numbered argument, e.g. `print!("{0} ...", foo.bar, ...)` // * if allow_mixed_uninlined_format_args is false and this arg hasn't been inlined already - param.kind != Numbered && (!ignore_mixed || matches!(param.kind, NamedInline(_))) - } -} - -fn outermost_expn_data(expn_data: ExpnData) -> ExpnData { - if expn_data.call_site.from_expansion() { - outermost_expn_data(expn_data.call_site.ctxt().outer_expn_data()) - } else { - expn_data + pos.kind != FormatArgPositionKind::Number + && (!ignore_mixed || matches!(arg.kind, FormatArgumentKind::Captured(_))) } } @@ -438,10 +455,31 @@ fn check_to_string_in_format_args(cx: &LateContext<'_>, name: Symbol, value: &Ex } } -/// Returns true if `hir_id` is referred to by multiple format params -fn is_aliased(args: &FormatArgsExpn<'_>, hir_id: HirId) -> bool { - args.params() - .filter(|param| param.value.hir_id == hir_id) +fn format_arg_positions( + format_args: &rustc_ast::FormatArgs, +) -> impl Iterator { + format_args.template.iter().flat_map(|piece| match piece { + FormatArgsPiece::Placeholder(placeholder) => { + let mut positions = ArrayVec::<_, 3>::new(); + + positions.push((&placeholder.argument, FormatParamUsage::Argument)); + if let Some(FormatCount::Argument(position)) = &placeholder.format_options.width { + positions.push((position, FormatParamUsage::Width)); + } + if let Some(FormatCount::Argument(position)) = &placeholder.format_options.precision { + positions.push((position, FormatParamUsage::Precision)); + } + + positions + }, + FormatArgsPiece::Literal(_) => ArrayVec::new(), + }) +} + +/// Returns true if the format argument at `index` is referred to by multiple format params +fn is_aliased(format_args: &rustc_ast::FormatArgs, index: usize) -> bool { + format_arg_positions(format_args) + .filter(|(position, _)| position.index == Ok(index)) .at_most_one() .is_err() } diff --git a/clippy_lints/src/utils/format_args_collector.rs b/clippy_lints/src/utils/format_args_collector.rs index be56b842b98..09fcb82c37c 100644 --- a/clippy_lints/src/utils/format_args_collector.rs +++ b/clippy_lints/src/utils/format_args_collector.rs @@ -1,7 +1,12 @@ use clippy_utils::macros::collect_ast_format_args; -use rustc_ast::{Expr, ExprKind}; +use clippy_utils::source::snippet_opt; +use itertools::Itertools; +use rustc_ast::{Expr, ExprKind, FormatArgs}; +use rustc_lexer::{tokenize, TokenKind}; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::hygiene; +use std::iter::once; declare_clippy_lint! { /// ### What it does @@ -15,9 +20,79 @@ declare_clippy_lint! { declare_lint_pass!(FormatArgsCollector => [FORMAT_ARGS_COLLECTOR]); impl EarlyLintPass for FormatArgsCollector { - fn check_expr(&mut self, _: &EarlyContext<'_>, expr: &Expr) { + fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { if let ExprKind::FormatArgs(args) = &expr.kind { + if has_span_from_proc_macro(cx, args) { + return; + } + collect_ast_format_args(expr.span, args); } } } + +/// Detects if the format string or an argument has its span set by a proc macro to something inside +/// a macro callsite, e.g. +/// +/// ```ignore +/// println!(some_proc_macro!("input {}"), a); +/// ``` +/// +/// Where `some_proc_macro` expands to +/// +/// ```ignore +/// println!("output {}", a); +/// ``` +/// +/// But with the span of `"output {}"` set to the macro input +/// +/// ```ignore +/// println!(some_proc_macro!("input {}"), a); +/// // ^^^^^^^^^^ +/// ``` +fn has_span_from_proc_macro(cx: &EarlyContext<'_>, args: &FormatArgs) -> bool { + let ctxt = args.span.ctxt(); + + // `format!("{} {} {c}", "one", "two", c = "three")` + // ^^^^^ ^^^^^ ^^^^^^^ + let argument_span = args + .arguments + .explicit_args() + .iter() + .map(|argument| hygiene::walk_chain(argument.expr.span, ctxt)); + + // `format!("{} {} {c}", "one", "two", c = "three")` + // ^^ ^^ ^^^^^^ + let between_spans = once(args.span) + .chain(argument_span) + .tuple_windows() + .map(|(start, end)| start.between(end)); + + for between_span in between_spans { + let mut seen_comma = false; + + let Some(snippet) = snippet_opt(cx, between_span) else { return true }; + for token in tokenize(&snippet) { + match token.kind { + TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace => {}, + TokenKind::Comma if !seen_comma => seen_comma = true, + // named arguments, `start_val, name = end_val` + // ^^^^^^^^^ between_span + TokenKind::Ident | TokenKind::Eq if seen_comma => {}, + // An unexpected token usually indicates that we crossed a macro boundary + // + // `println!(some_proc_macro!("input {}"), a)` + // ^^^ between_span + // `println!("{}", val!(x))` + // ^^^^^^^ between_span + _ => return true, + } + } + + if !seen_comma { + return true; + } + } + + false +} diff --git a/clippy_utils/src/macros.rs b/clippy_utils/src/macros.rs index c0e32068eca..b03e0d4a1ca 100644 --- a/clippy_utils/src/macros.rs +++ b/clippy_utils/src/macros.rs @@ -6,7 +6,7 @@ use crate::visitors::{for_each_expr, Descend}; use arrayvec::ArrayVec; use itertools::{izip, Either, Itertools}; use rustc_ast::ast::LitKind; -use rustc_ast::FormatArgs; +use rustc_ast::{FormatArgs, FormatArgument, FormatPlaceholder}; use rustc_data_structures::fx::FxHashMap; use rustc_hir::intravisit::{walk_expr, Visitor}; use rustc_hir::{self as hir, Expr, ExprField, ExprKind, HirId, LangItem, Node, QPath, TyKind}; @@ -391,30 +391,67 @@ pub fn collect_ast_format_args(span: Span, format_args: &FormatArgs) { }); } -/// Calls `callback` with an AST [`FormatArgs`] node if one is found +/// Calls `callback` with an AST [`FormatArgs`] node if a `format_args` expansion is found as a +/// descendant of `expn_id` pub fn find_format_args(cx: &LateContext<'_>, start: &Expr<'_>, expn_id: ExpnId, callback: impl FnOnce(&FormatArgs)) { let format_args_expr = for_each_expr(start, |expr| { let ctxt = expr.span.ctxt(); - if ctxt == start.span.ctxt() { - ControlFlow::Continue(Descend::Yes) - } else if ctxt.outer_expn().is_descendant_of(expn_id) - && macro_backtrace(expr.span) + if ctxt.outer_expn().is_descendant_of(expn_id) { + if macro_backtrace(expr.span) .map(|macro_call| cx.tcx.item_name(macro_call.def_id)) .any(|name| matches!(name, sym::const_format_args | sym::format_args | sym::format_args_nl)) - { - ControlFlow::Break(expr) + { + ControlFlow::Break(expr) + } else { + ControlFlow::Continue(Descend::Yes) + } } else { ControlFlow::Continue(Descend::No) } }); - if let Some(format_args_expr) = format_args_expr { + if let Some(expr) = format_args_expr { AST_FORMAT_ARGS.with(|ast_format_args| { - ast_format_args.borrow().get(&format_args_expr.span).map(callback); + ast_format_args.borrow().get(&expr.span).map(callback); }); } } +/// Attempt to find the [`rustc_hir::Expr`] that corresponds to the [`FormatArgument`]'s value, if +/// it cannot be found it will return the [`rustc_ast::Expr`]. +pub fn find_format_arg_expr<'hir, 'ast>( + start: &'hir Expr<'hir>, + target: &'ast FormatArgument, +) -> Result<&'hir rustc_hir::Expr<'hir>, &'ast rustc_ast::Expr> { + for_each_expr(start, |expr| { + if expr.span == target.expr.span { + ControlFlow::Break(expr) + } else { + ControlFlow::Continue(()) + } + }) + .ok_or(&target.expr) +} + +/// Span of the `:` and format specifiers +/// +/// ```ignore +/// format!("{:.}"), format!("{foo:.}") +/// ^^ ^^ +/// ``` +pub fn format_placeholder_format_span(placeholder: &FormatPlaceholder) -> Option { + let base = placeholder.span?.data(); + + // `base.hi` is `{...}|`, subtract 1 byte (the length of '}') so that it points before the closing + // brace `{...|}` + Some(Span::new( + placeholder.argument.span?.hi(), + base.hi - BytePos(1), + base.ctxt, + base.parent, + )) +} + /// Returns the [`Span`] of the value at `index` extended to the previous comma, e.g. for the value /// `10` /// @@ -897,22 +934,6 @@ pub struct FormatArg<'tcx> { pub span: Span, } -impl<'tcx> FormatArg<'tcx> { - /// Span of the `:` and format specifiers - /// - /// ```ignore - /// format!("{:.}"), format!("{foo:.}") - /// ^^ ^^ - /// ``` - pub fn format_span(&self) -> Span { - let base = self.span.data(); - - // `base.hi` is `{...}|`, subtract 1 byte (the length of '}') so that it points before the closing - // brace `{...|}` - Span::new(self.param.span.hi(), base.hi - BytePos(1), base.ctxt, base.parent) - } -} - /// A parsed `format_args!` expansion. #[derive(Debug)] pub struct FormatArgsExpn<'tcx> { diff --git a/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.stderr b/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.stderr index ee941762196..1be0cda12fc 100644 --- a/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.stderr +++ b/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.stderr @@ -11,6 +11,18 @@ LL - println!("val='{}'", local_i32); LL + println!("val='{local_i32}'"); | +error: variables can be used directly in the `format!` string + --> $DIR/uninlined_format_args.rs:10:5 + | +LL | println!("Hello {} is {:.*}", "x", local_i32, local_f64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: change this to + | +LL - println!("Hello {} is {:.*}", "x", local_i32, local_f64); +LL + println!("Hello {} is {local_f64:.local_i32$}", "x"); + | + error: literal with an empty format string --> $DIR/uninlined_format_args.rs:10:35 | @@ -24,18 +36,6 @@ LL - println!("Hello {} is {:.*}", "x", local_i32, local_f64); LL + println!("Hello x is {:.*}", local_i32, local_f64); | -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:10:5 - | -LL | println!("Hello {} is {:.*}", "x", local_i32, local_f64); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("Hello {} is {:.*}", "x", local_i32, local_f64); -LL + println!("Hello {} is {local_f64:.local_i32$}", "x"); - | - error: variables can be used directly in the `format!` string --> $DIR/uninlined_format_args.rs:11:5 | diff --git a/tests/ui/uninlined_format_args.fixed b/tests/ui/uninlined_format_args.fixed index 1475d781c67..5ecb9a5bf9e 100644 --- a/tests/ui/uninlined_format_args.fixed +++ b/tests/ui/uninlined_format_args.fixed @@ -119,7 +119,7 @@ fn tester(fn_arg: i32) { println!("Width = {local_i32}, value with width = {local_f64:local_i32$}"); println!("{local_i32:width$.prec$}"); println!("{width:width$.prec$}"); - println!("{}", format!("{local_i32}")); + println!("{}", format!("{}", local_i32)); my_println!("{}", local_i32); my_println_args!("{}", local_i32); diff --git a/tests/ui/uninlined_format_args.stderr b/tests/ui/uninlined_format_args.stderr index a12abf8bef8..dc4af6ef42e 100644 --- a/tests/ui/uninlined_format_args.stderr +++ b/tests/ui/uninlined_format_args.stderr @@ -774,18 +774,6 @@ LL - println!("{:w$.p$}", w = width, p = prec); LL + println!("{width:width$.prec$}"); | -error: variables can be used directly in the `format!` string - --> $DIR/uninlined_format_args.rs:125:20 - | -LL | println!("{}", format!("{}", local_i32)); - | ^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: change this to - | -LL - println!("{}", format!("{}", local_i32)); -LL + println!("{}", format!("{local_i32}")); - | - error: variables can be used directly in the `format!` string --> $DIR/uninlined_format_args.rs:143:5 | @@ -856,5 +844,5 @@ LL - println!("expand='{}'", local_i32); LL + println!("expand='{local_i32}'"); | -error: aborting due to 72 previous errors +error: aborting due to 71 previous errors diff --git a/tests/ui/unused_format_specs.fixed b/tests/ui/unused_format_specs.fixed deleted file mode 100644 index 2930722b42d..00000000000 --- a/tests/ui/unused_format_specs.fixed +++ /dev/null @@ -1,18 +0,0 @@ -// run-rustfix - -#![warn(clippy::unused_format_specs)] -#![allow(unused)] - -fn main() { - let f = 1.0f64; - println!("{}", 1.0); - println!("{f} {f:?}"); - - println!("{}", 1); -} - -fn should_not_lint() { - let f = 1.0f64; - println!("{:.1}", 1.0); - println!("{f:.w$} {f:.*?}", 3, w = 2); -} diff --git a/tests/ui/unused_format_specs.rs b/tests/ui/unused_format_specs.rs deleted file mode 100644 index ee192a000d4..00000000000 --- a/tests/ui/unused_format_specs.rs +++ /dev/null @@ -1,18 +0,0 @@ -// run-rustfix - -#![warn(clippy::unused_format_specs)] -#![allow(unused)] - -fn main() { - let f = 1.0f64; - println!("{:.}", 1.0); - println!("{f:.} {f:.?}"); - - println!("{:.}", 1); -} - -fn should_not_lint() { - let f = 1.0f64; - println!("{:.1}", 1.0); - println!("{f:.w$} {f:.*?}", 3, w = 2); -} diff --git a/tests/ui/unused_format_specs.stderr b/tests/ui/unused_format_specs.stderr deleted file mode 100644 index 7231c17e74c..00000000000 --- a/tests/ui/unused_format_specs.stderr +++ /dev/null @@ -1,54 +0,0 @@ -error: empty precision specifier has no effect - --> $DIR/unused_format_specs.rs:8:17 - | -LL | println!("{:.}", 1.0); - | ^ - | - = note: a precision specifier is not required to format floats - = note: `-D clippy::unused-format-specs` implied by `-D warnings` -help: remove the `.` - | -LL - println!("{:.}", 1.0); -LL + println!("{}", 1.0); - | - -error: empty precision specifier has no effect - --> $DIR/unused_format_specs.rs:9:18 - | -LL | println!("{f:.} {f:.?}"); - | ^ - | - = note: a precision specifier is not required to format floats -help: remove the `.` - | -LL - println!("{f:.} {f:.?}"); -LL + println!("{f} {f:.?}"); - | - -error: empty precision specifier has no effect - --> $DIR/unused_format_specs.rs:9:24 - | -LL | println!("{f:.} {f:.?}"); - | ^ - | - = note: a precision specifier is not required to format floats -help: remove the `.` - | -LL - println!("{f:.} {f:.?}"); -LL + println!("{f:.} {f:?}"); - | - -error: empty precision specifier has no effect - --> $DIR/unused_format_specs.rs:11:17 - | -LL | println!("{:.}", 1); - | ^ - | -help: remove the `.` - | -LL - println!("{:.}", 1); -LL + println!("{}", 1); - | - -error: aborting due to 4 previous errors - diff --git a/tests/ui/unused_format_specs_unfixable.stderr b/tests/ui/unused_format_specs_unfixable.stderr index 9f1890282e6..cb7156b6baf 100644 --- a/tests/ui/unused_format_specs_unfixable.stderr +++ b/tests/ui/unused_format_specs_unfixable.stderr @@ -37,11 +37,7 @@ error: format specifiers have no effect on `format_args!()` LL | println!("{:5}.", format_args_from_macro!()); | ^^^^ | -help: for the width to apply consider using `format!()` - --> $DIR/unused_format_specs_unfixable.rs:16:17 - | -LL | println!("{:5}.", format_args_from_macro!()); - | ^ + = help: for the width to apply consider using `format!()` help: if the current behavior is intentional, remove the format specifiers | LL - println!("{:5}.", format_args_from_macro!()); @@ -54,11 +50,7 @@ error: format specifiers have no effect on `format_args!()` LL | println!("{args:5}"); | ^^^^^^^^ | -help: for the width to apply consider using `format!()` - --> $DIR/unused_format_specs_unfixable.rs:19:21 - | -LL | println!("{args:5}"); - | ^ + = help: for the width to apply consider using `format!()` help: if the current behavior is intentional, remove the format specifiers | LL - println!("{args:5}");