Auto merge of #9637 - Alexendoo:unused-format-specs, r=xFrednet
Add `unused_format_specs` lint Currently catches two cases: An empty precision specifier: ```rust // the same as {} println!("{:.}", x); ``` And using formatting specs on `format_args!()`: ```rust // prints `x.`, not `x .` println("{:5}.", format_args!("x")); ``` changelog: new lint: [`unused_format_specs`]
This commit is contained in:
commit
502e87c379
@ -4316,6 +4316,7 @@ Released 2018-09-13
|
|||||||
[`unstable_as_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#unstable_as_slice
|
[`unstable_as_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#unstable_as_slice
|
||||||
[`unused_async`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_async
|
[`unused_async`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_async
|
||||||
[`unused_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_collect
|
[`unused_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_collect
|
||||||
|
[`unused_format_specs`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_format_specs
|
||||||
[`unused_io_amount`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_io_amount
|
[`unused_io_amount`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_io_amount
|
||||||
[`unused_label`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_label
|
[`unused_label`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_label
|
||||||
[`unused_peekable`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_peekable
|
[`unused_peekable`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_peekable
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
|
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
|
||||||
use clippy_utils::macros::FormatParamKind::{Implicit, Named, Numbered, Starred};
|
use clippy_utils::macros::FormatParamKind::{Implicit, Named, Numbered, Starred};
|
||||||
use clippy_utils::macros::{is_format_macro, is_panic, FormatArgsExpn, FormatParam, FormatParamUsage};
|
use clippy_utils::macros::{
|
||||||
|
is_format_macro, is_panic, root_macro_call, Count, FormatArg, FormatArgsExpn, FormatParam, FormatParamUsage,
|
||||||
|
};
|
||||||
use clippy_utils::source::snippet_opt;
|
use clippy_utils::source::snippet_opt;
|
||||||
use clippy_utils::ty::implements_trait;
|
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
|
||||||
use clippy_utils::{is_diag_trait_item, meets_msrv, msrvs};
|
use clippy_utils::{is_diag_trait_item, meets_msrv, msrvs};
|
||||||
use if_chain::if_chain;
|
use if_chain::if_chain;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
@ -117,7 +119,43 @@ declare_clippy_lint! {
|
|||||||
"using non-inlined variables in `format!` calls"
|
"using non-inlined variables in `format!` calls"
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_lint_pass!(FormatArgs => [FORMAT_IN_FORMAT_ARGS, UNINLINED_FORMAT_ARGS, TO_STRING_IN_FORMAT_ARGS]);
|
declare_clippy_lint! {
|
||||||
|
/// ### What it does
|
||||||
|
/// Detects [formatting parameters] that have no effect on the output of
|
||||||
|
/// `format!()`, `println!()` or similar macros.
|
||||||
|
///
|
||||||
|
/// ### Why is this bad?
|
||||||
|
/// Shorter format specifiers are easier to read, it may also indicate that
|
||||||
|
/// an expected formatting operation such as adding padding isn't happening.
|
||||||
|
///
|
||||||
|
/// ### Example
|
||||||
|
/// ```rust
|
||||||
|
/// println!("{:.}", 1.0);
|
||||||
|
///
|
||||||
|
/// println!("not padded: {:5}", format_args!("..."));
|
||||||
|
/// ```
|
||||||
|
/// Use instead:
|
||||||
|
/// ```rust
|
||||||
|
/// println!("{}", 1.0);
|
||||||
|
///
|
||||||
|
/// println!("not padded: {}", format_args!("..."));
|
||||||
|
/// // OR
|
||||||
|
/// println!("padded: {:5}", format!("..."));
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// [formatting parameters]: https://doc.rust-lang.org/std/fmt/index.html#formatting-parameters
|
||||||
|
#[clippy::version = "1.66.0"]
|
||||||
|
pub UNUSED_FORMAT_SPECS,
|
||||||
|
complexity,
|
||||||
|
"use of a format specifier that has no effect"
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_lint_pass!(FormatArgs => [
|
||||||
|
FORMAT_IN_FORMAT_ARGS,
|
||||||
|
TO_STRING_IN_FORMAT_ARGS,
|
||||||
|
UNINLINED_FORMAT_ARGS,
|
||||||
|
UNUSED_FORMAT_SPECS,
|
||||||
|
]);
|
||||||
|
|
||||||
pub struct FormatArgs {
|
pub struct FormatArgs {
|
||||||
msrv: Option<RustcVersion>,
|
msrv: Option<RustcVersion>,
|
||||||
@ -132,27 +170,26 @@ impl FormatArgs {
|
|||||||
|
|
||||||
impl<'tcx> LateLintPass<'tcx> for FormatArgs {
|
impl<'tcx> LateLintPass<'tcx> for FormatArgs {
|
||||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
||||||
if_chain! {
|
if let Some(format_args) = FormatArgsExpn::parse(cx, expr)
|
||||||
if let Some(format_args) = FormatArgsExpn::parse(cx, expr);
|
&& let expr_expn_data = expr.span.ctxt().outer_expn_data()
|
||||||
let expr_expn_data = expr.span.ctxt().outer_expn_data();
|
&& let outermost_expn_data = outermost_expn_data(expr_expn_data)
|
||||||
let outermost_expn_data = outermost_expn_data(expr_expn_data);
|
&& let Some(macro_def_id) = outermost_expn_data.macro_def_id
|
||||||
if let Some(macro_def_id) = outermost_expn_data.macro_def_id;
|
&& is_format_macro(cx, macro_def_id)
|
||||||
if is_format_macro(cx, macro_def_id);
|
&& let ExpnKind::Macro(_, name) = outermost_expn_data.kind
|
||||||
if let ExpnKind::Macro(_, name) = outermost_expn_data.kind;
|
{
|
||||||
then {
|
for arg in &format_args.args {
|
||||||
for arg in &format_args.args {
|
check_unused_format_specifier(cx, arg);
|
||||||
if !arg.format.is_default() {
|
if !arg.format.is_default() {
|
||||||
continue;
|
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 meets_msrv(self.msrv, msrvs::FORMAT_ARGS_CAPTURE) {
|
if is_aliased(&format_args, arg.param.value.hir_id) {
|
||||||
check_uninlined_args(cx, &format_args, outermost_expn_data.call_site, macro_def_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 meets_msrv(self.msrv, msrvs::FORMAT_ARGS_CAPTURE) {
|
||||||
|
check_uninlined_args(cx, &format_args, outermost_expn_data.call_site, macro_def_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -160,6 +197,76 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs {
|
|||||||
extract_msrv_attr!(LateContext);
|
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();
|
||||||
|
|
||||||
|
if let Count::Implied(Some(mut span)) = arg.format.precision
|
||||||
|
&& !span.is_empty()
|
||||||
|
{
|
||||||
|
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_diagnostic_item(cx, param_ty, sym::Arguments) && !arg.format.is_default_for_trait() {
|
||||||
|
span_lint_and_then(
|
||||||
|
cx,
|
||||||
|
UNUSED_FORMAT_SPECS,
|
||||||
|
arg.span,
|
||||||
|
"format specifiers have no effect on `format_args!()`",
|
||||||
|
|diag| {
|
||||||
|
let mut suggest_format = |spec, span| {
|
||||||
|
let message = format!("for the {spec} to apply consider using `format!()`");
|
||||||
|
|
||||||
|
if let Some(mac_call) = root_macro_call(arg.param.value.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, '!'),
|
||||||
|
message,
|
||||||
|
"format",
|
||||||
|
Applicability::MaybeIncorrect,
|
||||||
|
);
|
||||||
|
} else if let Some(span) = span {
|
||||||
|
diag.span_help(span, message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if !arg.format.width.is_implied() {
|
||||||
|
suggest_format("width", arg.format.width.span());
|
||||||
|
}
|
||||||
|
|
||||||
|
if !arg.format.precision.is_implied() {
|
||||||
|
suggest_format("precision", arg.format.precision.span());
|
||||||
|
}
|
||||||
|
|
||||||
|
diag.span_suggestion_verbose(
|
||||||
|
arg.format_span(),
|
||||||
|
"if the current behavior is intentional, remove the format specifiers",
|
||||||
|
"",
|
||||||
|
Applicability::MaybeIncorrect,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn check_uninlined_args(cx: &LateContext<'_>, args: &FormatArgsExpn<'_>, call_site: Span, def_id: DefId) {
|
fn check_uninlined_args(cx: &LateContext<'_>, args: &FormatArgsExpn<'_>, call_site: Span, def_id: DefId) {
|
||||||
if args.format_string.span.from_expansion() {
|
if args.format_string.span.from_expansion() {
|
||||||
return;
|
return;
|
||||||
|
@ -73,6 +73,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
|||||||
LintId::of(format_args::FORMAT_IN_FORMAT_ARGS),
|
LintId::of(format_args::FORMAT_IN_FORMAT_ARGS),
|
||||||
LintId::of(format_args::TO_STRING_IN_FORMAT_ARGS),
|
LintId::of(format_args::TO_STRING_IN_FORMAT_ARGS),
|
||||||
LintId::of(format_args::UNINLINED_FORMAT_ARGS),
|
LintId::of(format_args::UNINLINED_FORMAT_ARGS),
|
||||||
|
LintId::of(format_args::UNUSED_FORMAT_SPECS),
|
||||||
LintId::of(format_impl::PRINT_IN_FORMAT_IMPL),
|
LintId::of(format_impl::PRINT_IN_FORMAT_IMPL),
|
||||||
LintId::of(format_impl::RECURSIVE_FORMAT_IMPL),
|
LintId::of(format_impl::RECURSIVE_FORMAT_IMPL),
|
||||||
LintId::of(formatting::POSSIBLE_MISSING_COMMA),
|
LintId::of(formatting::POSSIBLE_MISSING_COMMA),
|
||||||
|
@ -13,6 +13,7 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec!
|
|||||||
LintId::of(double_parens::DOUBLE_PARENS),
|
LintId::of(double_parens::DOUBLE_PARENS),
|
||||||
LintId::of(explicit_write::EXPLICIT_WRITE),
|
LintId::of(explicit_write::EXPLICIT_WRITE),
|
||||||
LintId::of(format::USELESS_FORMAT),
|
LintId::of(format::USELESS_FORMAT),
|
||||||
|
LintId::of(format_args::UNUSED_FORMAT_SPECS),
|
||||||
LintId::of(functions::TOO_MANY_ARGUMENTS),
|
LintId::of(functions::TOO_MANY_ARGUMENTS),
|
||||||
LintId::of(int_plus_one::INT_PLUS_ONE),
|
LintId::of(int_plus_one::INT_PLUS_ONE),
|
||||||
LintId::of(lifetimes::EXTRA_UNUSED_LIFETIMES),
|
LintId::of(lifetimes::EXTRA_UNUSED_LIFETIMES),
|
||||||
|
@ -164,6 +164,7 @@ store.register_lints(&[
|
|||||||
format_args::FORMAT_IN_FORMAT_ARGS,
|
format_args::FORMAT_IN_FORMAT_ARGS,
|
||||||
format_args::TO_STRING_IN_FORMAT_ARGS,
|
format_args::TO_STRING_IN_FORMAT_ARGS,
|
||||||
format_args::UNINLINED_FORMAT_ARGS,
|
format_args::UNINLINED_FORMAT_ARGS,
|
||||||
|
format_args::UNUSED_FORMAT_SPECS,
|
||||||
format_impl::PRINT_IN_FORMAT_IMPL,
|
format_impl::PRINT_IN_FORMAT_IMPL,
|
||||||
format_impl::RECURSIVE_FORMAT_IMPL,
|
format_impl::RECURSIVE_FORMAT_IMPL,
|
||||||
format_push_string::FORMAT_PUSH_STRING,
|
format_push_string::FORMAT_PUSH_STRING,
|
||||||
|
@ -627,7 +627,7 @@ pub enum Count<'tcx> {
|
|||||||
/// `FormatParamKind::Numbered`.
|
/// `FormatParamKind::Numbered`.
|
||||||
Param(FormatParam<'tcx>),
|
Param(FormatParam<'tcx>),
|
||||||
/// Not specified.
|
/// Not specified.
|
||||||
Implied,
|
Implied(Option<Span>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> Count<'tcx> {
|
impl<'tcx> Count<'tcx> {
|
||||||
@ -638,8 +638,10 @@ impl<'tcx> Count<'tcx> {
|
|||||||
inner: Option<rpf::InnerSpan>,
|
inner: Option<rpf::InnerSpan>,
|
||||||
values: &FormatArgsValues<'tcx>,
|
values: &FormatArgsValues<'tcx>,
|
||||||
) -> Option<Self> {
|
) -> Option<Self> {
|
||||||
|
let span = inner.map(|inner| span_from_inner(values.format_string_span, inner));
|
||||||
|
|
||||||
Some(match count {
|
Some(match count {
|
||||||
rpf::Count::CountIs(val) => Self::Is(val, span_from_inner(values.format_string_span, inner?)),
|
rpf::Count::CountIs(val) => Self::Is(val, span?),
|
||||||
rpf::Count::CountIsName(name, _) => Self::Param(FormatParam::new(
|
rpf::Count::CountIsName(name, _) => Self::Param(FormatParam::new(
|
||||||
FormatParamKind::Named(Symbol::intern(name)),
|
FormatParamKind::Named(Symbol::intern(name)),
|
||||||
usage,
|
usage,
|
||||||
@ -661,12 +663,12 @@ impl<'tcx> Count<'tcx> {
|
|||||||
inner?,
|
inner?,
|
||||||
values,
|
values,
|
||||||
)?),
|
)?),
|
||||||
rpf::Count::CountImplied => Self::Implied,
|
rpf::Count::CountImplied => Self::Implied(span),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_implied(self) -> bool {
|
pub fn is_implied(self) -> bool {
|
||||||
matches!(self, Count::Implied)
|
matches!(self, Count::Implied(_))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn param(self) -> Option<FormatParam<'tcx>> {
|
pub fn param(self) -> Option<FormatParam<'tcx>> {
|
||||||
@ -675,6 +677,14 @@ impl<'tcx> Count<'tcx> {
|
|||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn span(self) -> Option<Span> {
|
||||||
|
match self {
|
||||||
|
Count::Is(_, span) => Some(span),
|
||||||
|
Count::Param(param) => Some(param.span),
|
||||||
|
Count::Implied(span) => span,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Specification for the formatting of an argument in the format string. See
|
/// Specification for the formatting of an argument in the format string. See
|
||||||
@ -738,8 +748,13 @@ impl<'tcx> FormatSpec<'tcx> {
|
|||||||
/// Returns true if this format spec is unchanged from the default. e.g. returns true for `{}`,
|
/// 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}`
|
/// `{foo}` and `{2}`, but false for `{:?}`, `{foo:5}` and `{3:.5}`
|
||||||
pub fn is_default(&self) -> bool {
|
pub fn is_default(&self) -> bool {
|
||||||
self.r#trait == sym::Display
|
self.r#trait == sym::Display && self.is_default_for_trait()
|
||||||
&& self.width.is_implied()
|
}
|
||||||
|
|
||||||
|
/// Has no other formatting specifiers than setting the format trait. returns true for `{}`,
|
||||||
|
/// `{foo}`, `{:?}`, but false for `{foo:5}`, `{3:.5?}`
|
||||||
|
pub fn is_default_for_trait(&self) -> bool {
|
||||||
|
self.width.is_implied()
|
||||||
&& self.precision.is_implied()
|
&& self.precision.is_implied()
|
||||||
&& self.align == Alignment::AlignUnknown
|
&& self.align == Alignment::AlignUnknown
|
||||||
&& self.flags == 0
|
&& self.flags == 0
|
||||||
@ -757,6 +772,22 @@ pub struct FormatArg<'tcx> {
|
|||||||
pub span: Span,
|
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.
|
/// A parsed `format_args!` expansion.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct FormatArgsExpn<'tcx> {
|
pub struct FormatArgsExpn<'tcx> {
|
||||||
|
@ -558,6 +558,7 @@ docs! {
|
|||||||
"unseparated_literal_suffix",
|
"unseparated_literal_suffix",
|
||||||
"unsound_collection_transmute",
|
"unsound_collection_transmute",
|
||||||
"unused_async",
|
"unused_async",
|
||||||
|
"unused_format_specs",
|
||||||
"unused_io_amount",
|
"unused_io_amount",
|
||||||
"unused_peekable",
|
"unused_peekable",
|
||||||
"unused_rounding",
|
"unused_rounding",
|
||||||
|
24
src/docs/unused_format_specs.txt
Normal file
24
src/docs/unused_format_specs.txt
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
### What it does
|
||||||
|
Detects [formatting parameters] that have no effect on the output of
|
||||||
|
`format!()`, `println!()` or similar macros.
|
||||||
|
|
||||||
|
### Why is this bad?
|
||||||
|
Shorter format specifiers are easier to read, it may also indicate that
|
||||||
|
an expected formatting operation such as adding padding isn't happening.
|
||||||
|
|
||||||
|
### Example
|
||||||
|
```
|
||||||
|
println!("{:.}", 1.0);
|
||||||
|
|
||||||
|
println!("not padded: {:5}", format_args!("..."));
|
||||||
|
```
|
||||||
|
Use instead:
|
||||||
|
```
|
||||||
|
println!("{}", 1.0);
|
||||||
|
|
||||||
|
println!("not padded: {}", format_args!("..."));
|
||||||
|
// OR
|
||||||
|
println!("padded: {:5}", format!("..."));
|
||||||
|
```
|
||||||
|
|
||||||
|
[formatting parameters]: https://doc.rust-lang.org/std/fmt/index.html#formatting-parameters
|
18
tests/ui/unused_format_specs.fixed
Normal file
18
tests/ui/unused_format_specs.fixed
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
// 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);
|
||||||
|
}
|
18
tests/ui/unused_format_specs.rs
Normal file
18
tests/ui/unused_format_specs.rs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
// 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);
|
||||||
|
}
|
54
tests/ui/unused_format_specs.stderr
Normal file
54
tests/ui/unused_format_specs.stderr
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
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
|
||||||
|
|
30
tests/ui/unused_format_specs_unfixable.rs
Normal file
30
tests/ui/unused_format_specs_unfixable.rs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
#![warn(clippy::unused_format_specs)]
|
||||||
|
#![allow(unused)]
|
||||||
|
|
||||||
|
macro_rules! format_args_from_macro {
|
||||||
|
() => {
|
||||||
|
format_args!("from macro")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// prints `.`, not ` .`
|
||||||
|
println!("{:5}.", format_args!(""));
|
||||||
|
//prints `abcde`, not `abc`
|
||||||
|
println!("{:.3}", format_args!("abcde"));
|
||||||
|
|
||||||
|
println!("{:5}.", format_args_from_macro!());
|
||||||
|
|
||||||
|
let args = format_args!("");
|
||||||
|
println!("{args:5}");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn should_not_lint() {
|
||||||
|
println!("{}", format_args!(""));
|
||||||
|
// Technically the same as `{}`, but the `format_args` docs specifically mention that you can use
|
||||||
|
// debug formatting so allow it
|
||||||
|
println!("{:?}", format_args!(""));
|
||||||
|
|
||||||
|
let args = format_args!("");
|
||||||
|
println!("{args}");
|
||||||
|
}
|
69
tests/ui/unused_format_specs_unfixable.stderr
Normal file
69
tests/ui/unused_format_specs_unfixable.stderr
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
error: format specifiers have no effect on `format_args!()`
|
||||||
|
--> $DIR/unused_format_specs_unfixable.rs:12:15
|
||||||
|
|
|
||||||
|
LL | println!("{:5}.", format_args!(""));
|
||||||
|
| ^^^^
|
||||||
|
|
|
||||||
|
= note: `-D clippy::unused-format-specs` implied by `-D warnings`
|
||||||
|
help: for the width to apply consider using `format!()`
|
||||||
|
|
|
||||||
|
LL | println!("{:5}.", format!(""));
|
||||||
|
| ~~~~~~
|
||||||
|
help: if the current behavior is intentional, remove the format specifiers
|
||||||
|
|
|
||||||
|
LL - println!("{:5}.", format_args!(""));
|
||||||
|
LL + println!("{}.", format_args!(""));
|
||||||
|
|
|
||||||
|
|
||||||
|
error: format specifiers have no effect on `format_args!()`
|
||||||
|
--> $DIR/unused_format_specs_unfixable.rs:14:15
|
||||||
|
|
|
||||||
|
LL | println!("{:.3}", format_args!("abcde"));
|
||||||
|
| ^^^^^
|
||||||
|
|
|
||||||
|
help: for the precision to apply consider using `format!()`
|
||||||
|
|
|
||||||
|
LL | println!("{:.3}", format!("abcde"));
|
||||||
|
| ~~~~~~
|
||||||
|
help: if the current behavior is intentional, remove the format specifiers
|
||||||
|
|
|
||||||
|
LL - println!("{:.3}", format_args!("abcde"));
|
||||||
|
LL + println!("{}", format_args!("abcde"));
|
||||||
|
|
|
||||||
|
|
||||||
|
error: format specifiers have no effect on `format_args!()`
|
||||||
|
--> $DIR/unused_format_specs_unfixable.rs:16:15
|
||||||
|
|
|
||||||
|
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: if the current behavior is intentional, remove the format specifiers
|
||||||
|
|
|
||||||
|
LL - println!("{:5}.", format_args_from_macro!());
|
||||||
|
LL + println!("{}.", format_args_from_macro!());
|
||||||
|
|
|
||||||
|
|
||||||
|
error: format specifiers have no effect on `format_args!()`
|
||||||
|
--> $DIR/unused_format_specs_unfixable.rs:19:15
|
||||||
|
|
|
||||||
|
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: if the current behavior is intentional, remove the format specifiers
|
||||||
|
|
|
||||||
|
LL - println!("{args:5}");
|
||||||
|
LL + println!("{args}");
|
||||||
|
|
|
||||||
|
|
||||||
|
error: aborting due to 4 previous errors
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user