From cd7cec9066f35e2fd661a69830fd57864b41ea44 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 18 Sep 2024 00:09:34 +0200 Subject: [PATCH 1/8] Add new `literal_string_with_formatting_arg` lint --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/lib.rs | 3 + .../src/literal_string_with_formatting_arg.rs | 101 ++++++++++++++++++ 4 files changed, 106 insertions(+) create mode 100644 clippy_lints/src/literal_string_with_formatting_arg.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index dd3124ee9a3b..3a82bfce82bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5639,6 +5639,7 @@ Released 2018-09-13 [`lines_filter_map_ok`]: https://rust-lang.github.io/rust-clippy/master/index.html#lines_filter_map_ok [`linkedlist`]: https://rust-lang.github.io/rust-clippy/master/index.html#linkedlist [`lint_groups_priority`]: https://rust-lang.github.io/rust-clippy/master/index.html#lint_groups_priority +[`literal_string_with_formatting_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#literal_string_with_formatting_arg [`little_endian_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#little_endian_bytes [`logic_bug`]: https://rust-lang.github.io/rust-clippy/master/index.html#logic_bug [`lossy_float_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#lossy_float_literal diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index dff60f76b746..a3f89b3f70c4 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -276,6 +276,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::literal_representation::MISTYPED_LITERAL_SUFFIXES_INFO, crate::literal_representation::UNREADABLE_LITERAL_INFO, crate::literal_representation::UNUSUAL_BYTE_GROUPINGS_INFO, + crate::literal_string_with_formatting_arg::LITERAL_STRING_WITH_FORMATTING_ARG_INFO, crate::loops::EMPTY_LOOP_INFO, crate::loops::EXPLICIT_COUNTER_LOOP_INFO, crate::loops::EXPLICIT_INTO_ITER_LOOP_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index c9064df25ac8..44e36b99b5d9 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -49,6 +49,7 @@ extern crate rustc_lexer; extern crate rustc_lint; extern crate rustc_middle; extern crate rustc_parse; +extern crate rustc_parse_format; extern crate rustc_resolve; extern crate rustc_session; extern crate rustc_span; @@ -196,6 +197,7 @@ mod let_with_type_underscore; mod lifetimes; mod lines_filter_map_ok; mod literal_representation; +mod literal_string_with_formatting_arg; mod loops; mod macro_metavars_in_unsafe; mod macro_use; @@ -959,6 +961,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(move |_| Box::new(manual_div_ceil::ManualDivCeil::new(conf))); store.register_late_pass(|_| Box::new(manual_is_power_of_two::ManualIsPowerOfTwo)); store.register_late_pass(|_| Box::new(non_zero_suggestions::NonZeroSuggestions)); + store.register_early_pass(|| Box::new(literal_string_with_formatting_arg::LiteralStringWithFormattingArg)); store.register_late_pass(move |_| Box::new(unused_trait_names::UnusedTraitNames::new(conf))); store.register_late_pass(|_| Box::new(manual_ignore_case_cmp::ManualIgnoreCaseCmp)); store.register_late_pass(|_| Box::new(unnecessary_literal_bound::UnnecessaryLiteralBound)); diff --git a/clippy_lints/src/literal_string_with_formatting_arg.rs b/clippy_lints/src/literal_string_with_formatting_arg.rs new file mode 100644 index 000000000000..e72f0de90c65 --- /dev/null +++ b/clippy_lints/src/literal_string_with_formatting_arg.rs @@ -0,0 +1,101 @@ +use rustc_ast::ast::{Expr, ExprKind}; +use rustc_ast::token::LitKind; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_parse_format::{ParseMode, Parser, Piece}; +use rustc_session::declare_lint_pass; +use rustc_span::BytePos; + +use clippy_utils::diagnostics::span_lint; + +declare_clippy_lint! { + /// ### What it does + /// Checks if string literals have formatting arguments outside of macros + /// using them (like `format!`). + /// + /// ### Why is this bad? + /// It will likely not generate the expected content. + /// + /// ### Example + /// ```no_run + /// let x: Option = None; + /// let y = "hello"; + /// x.expect("{y:?}"); + /// ``` + /// Use instead: + /// ```no_run + /// let x: Option = None; + /// let y = "hello"; + /// x.expect(&format!("{y:?}")); + /// ``` + #[clippy::version = "1.83.0"] + pub LITERAL_STRING_WITH_FORMATTING_ARG, + suspicious, + "Checks if string literals have formatting arguments" +} + +declare_lint_pass!(LiteralStringWithFormattingArg => [LITERAL_STRING_WITH_FORMATTING_ARG]); + +impl EarlyLintPass for LiteralStringWithFormattingArg { + fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { + if let ExprKind::Lit(lit) = expr.kind { + let add = match lit.kind { + LitKind::Str => 1, + LitKind::StrRaw(nb) => nb as usize + 2, + _ => return, + }; + let fmt_str = lit.symbol.as_str(); + let lo = expr.span.lo(); + let mut current = fmt_str; + let mut diff_len = 0; + + let mut parser = Parser::new(current, None, None, false, ParseMode::Format); + let mut spans = Vec::new(); + while let Some(piece) = parser.next() { + if let Some(error) = parser.errors.last() { + // We simply ignore the errors and move after them. + if error.span.end >= current.len() { + break; + } + current = ¤t[error.span.end + 1..]; + diff_len = fmt_str.len() - current.len(); + parser = Parser::new(current, None, None, false, ParseMode::Format); + } else if let Piece::NextArgument(arg) = piece { + let mut pos = arg.position_span; + pos.start += diff_len; + pos.end += diff_len; + + let start = fmt_str[..pos.start].rfind('{').unwrap_or(pos.start); + // If this is a unicode character escape, we don't want to lint. + if start > 1 && fmt_str[..start].ends_with("\\u") { + continue; + } + + let mut end = fmt_str[pos.end..].find('}').map_or(pos.end, |found| found + pos.end); + if fmt_str[start..end].contains(':') { + end += 1; + } + spans.push( + expr.span + .with_hi(lo + BytePos((start + add) as _)) + .with_lo(lo + BytePos((end + add) as _)), + ); + } + } + if spans.len() == 1 { + span_lint( + cx, + LITERAL_STRING_WITH_FORMATTING_ARG, + spans, + "this looks like a formatting argument but it is not part of a formatting macro", + ); + } else if spans.len() > 1 { + span_lint( + cx, + LITERAL_STRING_WITH_FORMATTING_ARG, + spans, + "these look like formatting arguments but are not part of a formatting macro", + ); + } + } + } +} From 922aabe348757f69b993f5d93a5bfb7fb1db38f8 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 18 Sep 2024 00:10:16 +0200 Subject: [PATCH 2/8] Add regression test for `literal_string_with_formatting_arg` --- .../ui/literal_string_with_formatting_arg.rs | 29 +++++++++ .../literal_string_with_formatting_arg.stderr | 65 +++++++++++++++++++ 2 files changed, 94 insertions(+) create mode 100644 tests/ui/literal_string_with_formatting_arg.rs create mode 100644 tests/ui/literal_string_with_formatting_arg.stderr diff --git a/tests/ui/literal_string_with_formatting_arg.rs b/tests/ui/literal_string_with_formatting_arg.rs new file mode 100644 index 000000000000..539d24efe7c0 --- /dev/null +++ b/tests/ui/literal_string_with_formatting_arg.rs @@ -0,0 +1,29 @@ +#![warn(clippy::literal_string_with_formatting_arg)] +#![allow(clippy::unnecessary_literal_unwrap)] + +fn main() { + let x: Option = None; + let y = "hello"; + x.expect("{y} {}"); //~ literal_string_with_formatting_arg + x.expect("{:?}"); //~ literal_string_with_formatting_arg + x.expect("{y:?}"); //~ literal_string_with_formatting_arg + x.expect(" {y:?} {y:?} "); //~ literal_string_with_formatting_arg + x.expect(" {y:..} {y:?} "); //~ literal_string_with_formatting_arg + x.expect(r"{y:?} {y:?} "); //~ literal_string_with_formatting_arg + x.expect(r"{y:?} y:?}"); //~ literal_string_with_formatting_arg + x.expect(r##" {y:?} {y:?} "##); //~ literal_string_with_formatting_arg + "\\.+*?()|[]{}^$#&-~".chars().any(|x| x == 'a'); //~ literal_string_with_formatting_arg + // Ensure that it doesn't try to go in the middle of a unicode character. + x.expect("———{}"); //~ literal_string_with_formatting_arg + + // Should not lint! + format!("{y:?}"); + println!("{y:?}"); + x.expect("{{y} {x"); + x.expect("{{y:?}"); + x.expect("{y:...}"); + let _ = "fn main {\n\ + }"; + // Unicode characters escape should not lint either. + "\u{0052}".to_string(); +} diff --git a/tests/ui/literal_string_with_formatting_arg.stderr b/tests/ui/literal_string_with_formatting_arg.stderr new file mode 100644 index 000000000000..c05d4af017c2 --- /dev/null +++ b/tests/ui/literal_string_with_formatting_arg.stderr @@ -0,0 +1,65 @@ +error: these look like formatting arguments but are not part of a formatting macro + --> tests/ui/literal_string_with_formatting_arg.rs:7:15 + | +LL | x.expect("{y} {}"); + | ^^^^^^ + | + = note: `-D clippy::literal-string-with-formatting-arg` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::literal_string_with_formatting_arg)]` + +error: this looks like a formatting argument but it is not part of a formatting macro + --> tests/ui/literal_string_with_formatting_arg.rs:8:15 + | +LL | x.expect("{:?}"); + | ^^^^ + +error: this looks like a formatting argument but it is not part of a formatting macro + --> tests/ui/literal_string_with_formatting_arg.rs:9:15 + | +LL | x.expect("{y:?}"); + | ^^^^^ + +error: these look like formatting arguments but are not part of a formatting macro + --> tests/ui/literal_string_with_formatting_arg.rs:10:16 + | +LL | x.expect(" {y:?} {y:?} "); + | ^^^^^ ^^^^^ + +error: this looks like a formatting argument but it is not part of a formatting macro + --> tests/ui/literal_string_with_formatting_arg.rs:11:23 + | +LL | x.expect(" {y:..} {y:?} "); + | ^^^^^ + +error: these look like formatting arguments but are not part of a formatting macro + --> tests/ui/literal_string_with_formatting_arg.rs:12:16 + | +LL | x.expect(r"{y:?} {y:?} "); + | ^^^^^ ^^^^^ + +error: this looks like a formatting argument but it is not part of a formatting macro + --> tests/ui/literal_string_with_formatting_arg.rs:13:16 + | +LL | x.expect(r"{y:?} y:?}"); + | ^^^^^ + +error: these look like formatting arguments but are not part of a formatting macro + --> tests/ui/literal_string_with_formatting_arg.rs:14:19 + | +LL | x.expect(r##" {y:?} {y:?} "##); + | ^^^^^ ^^^^^ + +error: this looks like a formatting argument but it is not part of a formatting macro + --> tests/ui/literal_string_with_formatting_arg.rs:15:17 + | +LL | "\\.+*?()|[]{}^$#&-~".chars().any(|x| x == 'a'); + | ^^ + +error: this looks like a formatting argument but it is not part of a formatting macro + --> tests/ui/literal_string_with_formatting_arg.rs:17:18 + | +LL | x.expect("———{}"); + | ^^ + +error: aborting due to 10 previous errors + From 05705b693866e4221ba9cd46f566cfa7f7f406ae Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 18 Sep 2024 23:19:31 +0200 Subject: [PATCH 3/8] Update new failing tests --- .../large_include_file/large_include_file.rs | 1 + .../large_include_file.stderr | 6 +-- tests/ui/auxiliary/proc_macro_derive.rs | 1 + tests/ui/format.fixed | 3 +- tests/ui/format.rs | 3 +- tests/ui/format.stderr | 30 ++++++------ tests/ui/print_literal.fixed | 2 +- tests/ui/print_literal.rs | 2 +- tests/ui/regex.rs | 3 +- tests/ui/regex.stderr | 48 +++++++++---------- ...nlined_format_args_panic.edition2018.fixed | 1 + ...lined_format_args_panic.edition2018.stderr | 2 +- ...nlined_format_args_panic.edition2021.fixed | 1 + ...lined_format_args_panic.edition2021.stderr | 12 ++--- tests/ui/uninlined_format_args_panic.rs | 1 + 15 files changed, 62 insertions(+), 54 deletions(-) diff --git a/tests/ui-toml/large_include_file/large_include_file.rs b/tests/ui-toml/large_include_file/large_include_file.rs index 8a6dd36501cf..c456b48daca8 100644 --- a/tests/ui-toml/large_include_file/large_include_file.rs +++ b/tests/ui-toml/large_include_file/large_include_file.rs @@ -1,4 +1,5 @@ #![warn(clippy::large_include_file)] +#![allow(clippy::literal_string_with_formatting_arg)] // Good const GOOD_INCLUDE_BYTES: &[u8; 68] = include_bytes!("../../ui/author.rs"); diff --git a/tests/ui-toml/large_include_file/large_include_file.stderr b/tests/ui-toml/large_include_file/large_include_file.stderr index 9e1494a47bba..82b926cc53ba 100644 --- a/tests/ui-toml/large_include_file/large_include_file.stderr +++ b/tests/ui-toml/large_include_file/large_include_file.stderr @@ -1,5 +1,5 @@ error: attempted to include a large file - --> tests/ui-toml/large_include_file/large_include_file.rs:13:43 + --> tests/ui-toml/large_include_file/large_include_file.rs:14:43 | LL | const TOO_BIG_INCLUDE_BYTES: &[u8; 654] = include_bytes!("too_big.txt"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | const TOO_BIG_INCLUDE_BYTES: &[u8; 654] = include_bytes!("too_big.txt"); = help: to override `-D warnings` add `#[allow(clippy::large_include_file)]` error: attempted to include a large file - --> tests/ui-toml/large_include_file/large_include_file.rs:15:35 + --> tests/ui-toml/large_include_file/large_include_file.rs:16:35 | LL | const TOO_BIG_INCLUDE_STR: &str = include_str!("too_big.txt"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -17,7 +17,7 @@ LL | const TOO_BIG_INCLUDE_STR: &str = include_str!("too_big.txt"); = note: the configuration allows a maximum size of 600 bytes error: attempted to include a large file - --> tests/ui-toml/large_include_file/large_include_file.rs:18:1 + --> tests/ui-toml/large_include_file/large_include_file.rs:19:1 | LL | #[doc = include_str!("too_big.txt")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/auxiliary/proc_macro_derive.rs b/tests/ui/auxiliary/proc_macro_derive.rs index fbf84337382e..33fbea080032 100644 --- a/tests/ui/auxiliary/proc_macro_derive.rs +++ b/tests/ui/auxiliary/proc_macro_derive.rs @@ -2,6 +2,7 @@ #![allow(incomplete_features)] #![allow(clippy::field_reassign_with_default)] #![allow(clippy::eq_op)] +#![allow(clippy::literal_string_with_formatting_arg)] extern crate proc_macro; diff --git a/tests/ui/format.fixed b/tests/ui/format.fixed index 2b32fdeae2b5..df4ac2d1a229 100644 --- a/tests/ui/format.fixed +++ b/tests/ui/format.fixed @@ -6,7 +6,8 @@ clippy::needless_borrow, clippy::uninlined_format_args, clippy::needless_raw_string_hashes, - clippy::useless_vec + clippy::useless_vec, + clippy::literal_string_with_formatting_arg )] struct Foo(pub String); diff --git a/tests/ui/format.rs b/tests/ui/format.rs index bad192067e93..30d8ff38f267 100644 --- a/tests/ui/format.rs +++ b/tests/ui/format.rs @@ -6,7 +6,8 @@ clippy::needless_borrow, clippy::uninlined_format_args, clippy::needless_raw_string_hashes, - clippy::useless_vec + clippy::useless_vec, + clippy::literal_string_with_formatting_arg )] struct Foo(pub String); diff --git a/tests/ui/format.stderr b/tests/ui/format.stderr index faa80b48000f..1368c8cd77e0 100644 --- a/tests/ui/format.stderr +++ b/tests/ui/format.stderr @@ -1,5 +1,5 @@ error: useless use of `format!` - --> tests/ui/format.rs:19:5 + --> tests/ui/format.rs:20:5 | LL | format!("foo"); | ^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string()` @@ -8,19 +8,19 @@ LL | format!("foo"); = help: to override `-D warnings` add `#[allow(clippy::useless_format)]` error: useless use of `format!` - --> tests/ui/format.rs:20:5 + --> tests/ui/format.rs:21:5 | LL | format!("{{}}"); | ^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"{}".to_string()` error: useless use of `format!` - --> tests/ui/format.rs:21:5 + --> tests/ui/format.rs:22:5 | LL | format!("{{}} abc {{}}"); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"{} abc {}".to_string()` error: useless use of `format!` - --> tests/ui/format.rs:22:5 + --> tests/ui/format.rs:23:5 | LL | / format!( LL | | r##"foo {{}} @@ -35,67 +35,67 @@ LL ~ " bar"##.to_string(); | error: useless use of `format!` - --> tests/ui/format.rs:27:13 + --> tests/ui/format.rs:28:13 | LL | let _ = format!(""); | ^^^^^^^^^^^ help: consider using `String::new()`: `String::new()` error: useless use of `format!` - --> tests/ui/format.rs:29:5 + --> tests/ui/format.rs:30:5 | LL | format!("{}", "foo"); | ^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string()` error: useless use of `format!` - --> tests/ui/format.rs:37:5 + --> tests/ui/format.rs:38:5 | LL | format!("{}", arg); | ^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `arg.to_string()` error: useless use of `format!` - --> tests/ui/format.rs:67:5 + --> tests/ui/format.rs:68:5 | LL | format!("{}", 42.to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `42.to_string()` error: useless use of `format!` - --> tests/ui/format.rs:69:5 + --> tests/ui/format.rs:70:5 | LL | format!("{}", x.display().to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.display().to_string()` error: useless use of `format!` - --> tests/ui/format.rs:73:18 + --> tests/ui/format.rs:74:18 | LL | let _ = Some(format!("{}", a + "bar")); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `a + "bar"` error: useless use of `format!` - --> tests/ui/format.rs:77:22 + --> tests/ui/format.rs:78:22 | LL | let _s: String = format!("{}", &*v.join("\n")); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `(&*v.join("\n")).to_string()` error: useless use of `format!` - --> tests/ui/format.rs:83:13 + --> tests/ui/format.rs:84:13 | LL | let _ = format!("{x}"); | ^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.to_string()` error: useless use of `format!` - --> tests/ui/format.rs:85:13 + --> tests/ui/format.rs:86:13 | LL | let _ = format!("{y}", y = x); | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.to_string()` error: useless use of `format!` - --> tests/ui/format.rs:89:13 + --> tests/ui/format.rs:90:13 | LL | let _ = format!("{abc}"); | ^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `abc.to_string()` error: useless use of `format!` - --> tests/ui/format.rs:91:13 + --> tests/ui/format.rs:92:13 | LL | let _ = format!("{xx}"); | ^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `xx.to_string()` diff --git a/tests/ui/print_literal.fixed b/tests/ui/print_literal.fixed index a7157c07f8a9..8c0cedf6299e 100644 --- a/tests/ui/print_literal.fixed +++ b/tests/ui/print_literal.fixed @@ -1,5 +1,5 @@ #![warn(clippy::print_literal)] -#![allow(clippy::uninlined_format_args)] +#![allow(clippy::uninlined_format_args, clippy::literal_string_with_formatting_arg)] fn main() { // these should be fine diff --git a/tests/ui/print_literal.rs b/tests/ui/print_literal.rs index 4b04b42744cc..1df4f4181e9b 100644 --- a/tests/ui/print_literal.rs +++ b/tests/ui/print_literal.rs @@ -1,5 +1,5 @@ #![warn(clippy::print_literal)] -#![allow(clippy::uninlined_format_args)] +#![allow(clippy::uninlined_format_args, clippy::literal_string_with_formatting_arg)] fn main() { // these should be fine diff --git a/tests/ui/regex.rs b/tests/ui/regex.rs index f607a2d50c6d..ea5c83549286 100644 --- a/tests/ui/regex.rs +++ b/tests/ui/regex.rs @@ -3,7 +3,8 @@ clippy::needless_raw_strings, clippy::needless_raw_string_hashes, clippy::needless_borrow, - clippy::needless_borrows_for_generic_args + clippy::needless_borrows_for_generic_args, + clippy::literal_string_with_formatting_arg )] #![warn(clippy::invalid_regex, clippy::trivial_regex, clippy::regex_creation_in_loops)] diff --git a/tests/ui/regex.stderr b/tests/ui/regex.stderr index 18dd538c68b4..919bac8f52f0 100644 --- a/tests/ui/regex.stderr +++ b/tests/ui/regex.stderr @@ -1,5 +1,5 @@ error: trivial regex - --> tests/ui/regex.rs:19:45 + --> tests/ui/regex.rs:20:45 | LL | let pipe_in_wrong_position = Regex::new("|"); | ^^^ @@ -9,7 +9,7 @@ LL | let pipe_in_wrong_position = Regex::new("|"); = help: to override `-D warnings` add `#[allow(clippy::trivial_regex)]` error: trivial regex - --> tests/ui/regex.rs:21:60 + --> tests/ui/regex.rs:22:60 | LL | let pipe_in_wrong_position_builder = RegexBuilder::new("|"); | ^^^ @@ -17,7 +17,7 @@ LL | let pipe_in_wrong_position_builder = RegexBuilder::new("|"); = help: the regex is unlikely to be useful as it is error: regex syntax error: invalid character class range, the start must be <= the end - --> tests/ui/regex.rs:23:42 + --> tests/ui/regex.rs:24:42 | LL | let wrong_char_ranice = Regex::new("[z-a]"); | ^^^ @@ -26,7 +26,7 @@ LL | let wrong_char_ranice = Regex::new("[z-a]"); = help: to override `-D warnings` add `#[allow(clippy::invalid_regex)]` error: regex syntax error: invalid character class range, the start must be <= the end - --> tests/ui/regex.rs:26:37 + --> tests/ui/regex.rs:27:37 | LL | let some_unicode = Regex::new("[é-è]"); | ^^^ @@ -35,13 +35,13 @@ error: regex parse error: ( ^ error: unclosed group - --> tests/ui/regex.rs:29:33 + --> tests/ui/regex.rs:30:33 | LL | let some_regex = Regex::new(OPENING_PAREN); | ^^^^^^^^^^^^^ error: trivial regex - --> tests/ui/regex.rs:31:53 + --> tests/ui/regex.rs:32:53 | LL | let binary_pipe_in_wrong_position = BRegex::new("|"); | ^^^ @@ -52,7 +52,7 @@ error: regex parse error: ( ^ error: unclosed group - --> tests/ui/regex.rs:33:41 + --> tests/ui/regex.rs:34:41 | LL | let some_binary_regex = BRegex::new(OPENING_PAREN); | ^^^^^^^^^^^^^ @@ -61,7 +61,7 @@ error: regex parse error: ( ^ error: unclosed group - --> tests/ui/regex.rs:34:56 + --> tests/ui/regex.rs:35:56 | LL | let some_binary_regex_builder = BRegexBuilder::new(OPENING_PAREN); | ^^^^^^^^^^^^^ @@ -70,7 +70,7 @@ error: regex parse error: ( ^ error: unclosed group - --> tests/ui/regex.rs:46:37 + --> tests/ui/regex.rs:47:37 | LL | let set_error = RegexSet::new(&[OPENING_PAREN, r"[a-z]+\.(com|org|net)"]); | ^^^^^^^^^^^^^ @@ -79,7 +79,7 @@ error: regex parse error: ( ^ error: unclosed group - --> tests/ui/regex.rs:47:39 + --> tests/ui/regex.rs:48:39 | LL | let bset_error = BRegexSet::new(&[OPENING_PAREN, r"[a-z]+\.(com|org|net)"]); | ^^^^^^^^^^^^^ @@ -88,7 +88,7 @@ error: regex parse error: \b\c ^^ error: unrecognized escape sequence - --> tests/ui/regex.rs:54:42 + --> tests/ui/regex.rs:55:42 | LL | let escaped_string_span = Regex::new("\\b\\c"); | ^^^^^^^^ @@ -96,19 +96,19 @@ LL | let escaped_string_span = Regex::new("\\b\\c"); = help: consider using a raw string literal: `r".."` error: regex syntax error: duplicate flag - --> tests/ui/regex.rs:56:34 + --> tests/ui/regex.rs:57:34 | LL | let aux_span = Regex::new("(?ixi)"); | ^ ^ error: regex syntax error: pattern can match invalid UTF-8 - --> tests/ui/regex.rs:62:53 + --> tests/ui/regex.rs:63:53 | LL | let invalid_utf8_should_lint = Regex::new("(?-u)."); | ^ error: trivial regex - --> tests/ui/regex.rs:67:33 + --> tests/ui/regex.rs:68:33 | LL | let trivial_eq = Regex::new("^foobar$"); | ^^^^^^^^^^ @@ -116,7 +116,7 @@ LL | let trivial_eq = Regex::new("^foobar$"); = help: consider using `==` on `str`s error: trivial regex - --> tests/ui/regex.rs:70:48 + --> tests/ui/regex.rs:71:48 | LL | let trivial_eq_builder = RegexBuilder::new("^foobar$"); | ^^^^^^^^^^ @@ -124,7 +124,7 @@ LL | let trivial_eq_builder = RegexBuilder::new("^foobar$"); = help: consider using `==` on `str`s error: trivial regex - --> tests/ui/regex.rs:73:42 + --> tests/ui/regex.rs:74:42 | LL | let trivial_starts_with = Regex::new("^foobar"); | ^^^^^^^^^ @@ -132,7 +132,7 @@ LL | let trivial_starts_with = Regex::new("^foobar"); = help: consider using `str::starts_with` error: trivial regex - --> tests/ui/regex.rs:76:40 + --> tests/ui/regex.rs:77:40 | LL | let trivial_ends_with = Regex::new("foobar$"); | ^^^^^^^^^ @@ -140,7 +140,7 @@ LL | let trivial_ends_with = Regex::new("foobar$"); = help: consider using `str::ends_with` error: trivial regex - --> tests/ui/regex.rs:79:39 + --> tests/ui/regex.rs:80:39 | LL | let trivial_contains = Regex::new("foobar"); | ^^^^^^^^ @@ -148,7 +148,7 @@ LL | let trivial_contains = Regex::new("foobar"); = help: consider using `str::contains` error: trivial regex - --> tests/ui/regex.rs:82:39 + --> tests/ui/regex.rs:83:39 | LL | let trivial_contains = Regex::new(NOT_A_REAL_REGEX); | ^^^^^^^^^^^^^^^^ @@ -156,7 +156,7 @@ LL | let trivial_contains = Regex::new(NOT_A_REAL_REGEX); = help: consider using `str::contains` error: trivial regex - --> tests/ui/regex.rs:85:40 + --> tests/ui/regex.rs:86:40 | LL | let trivial_backslash = Regex::new("a\\.b"); | ^^^^^^^ @@ -164,7 +164,7 @@ LL | let trivial_backslash = Regex::new("a\\.b"); = help: consider using `str::contains` error: trivial regex - --> tests/ui/regex.rs:89:36 + --> tests/ui/regex.rs:90:36 | LL | let trivial_empty = Regex::new(""); | ^^ @@ -172,7 +172,7 @@ LL | let trivial_empty = Regex::new(""); = help: the regex is unlikely to be useful as it is error: trivial regex - --> tests/ui/regex.rs:92:36 + --> tests/ui/regex.rs:93:36 | LL | let trivial_empty = Regex::new("^"); | ^^^ @@ -180,7 +180,7 @@ LL | let trivial_empty = Regex::new("^"); = help: the regex is unlikely to be useful as it is error: trivial regex - --> tests/ui/regex.rs:95:36 + --> tests/ui/regex.rs:96:36 | LL | let trivial_empty = Regex::new("^$"); | ^^^^ @@ -188,7 +188,7 @@ LL | let trivial_empty = Regex::new("^$"); = help: consider using `str::is_empty` error: trivial regex - --> tests/ui/regex.rs:98:44 + --> tests/ui/regex.rs:99:44 | LL | let binary_trivial_empty = BRegex::new("^$"); | ^^^^ diff --git a/tests/ui/uninlined_format_args_panic.edition2018.fixed b/tests/ui/uninlined_format_args_panic.edition2018.fixed index f0d570efdcee..130187d19d6f 100644 --- a/tests/ui/uninlined_format_args_panic.edition2018.fixed +++ b/tests/ui/uninlined_format_args_panic.edition2018.fixed @@ -3,6 +3,7 @@ //@[edition2021] edition:2021 #![warn(clippy::uninlined_format_args)] +#![allow(clippy::literal_string_with_formatting_arg)] fn main() { let var = 1; diff --git a/tests/ui/uninlined_format_args_panic.edition2018.stderr b/tests/ui/uninlined_format_args_panic.edition2018.stderr index 0541dd9a7d7b..4b154abac5bc 100644 --- a/tests/ui/uninlined_format_args_panic.edition2018.stderr +++ b/tests/ui/uninlined_format_args_panic.edition2018.stderr @@ -1,5 +1,5 @@ error: variables can be used directly in the `format!` string - --> tests/ui/uninlined_format_args_panic.rs:10:5 + --> tests/ui/uninlined_format_args_panic.rs:11:5 | LL | println!("val='{}'", var); | ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/uninlined_format_args_panic.edition2021.fixed b/tests/ui/uninlined_format_args_panic.edition2021.fixed index 7c0f28c45764..63b30c64025d 100644 --- a/tests/ui/uninlined_format_args_panic.edition2021.fixed +++ b/tests/ui/uninlined_format_args_panic.edition2021.fixed @@ -3,6 +3,7 @@ //@[edition2021] edition:2021 #![warn(clippy::uninlined_format_args)] +#![allow(clippy::literal_string_with_formatting_arg)] fn main() { let var = 1; diff --git a/tests/ui/uninlined_format_args_panic.edition2021.stderr b/tests/ui/uninlined_format_args_panic.edition2021.stderr index 3615eaa9dee3..7638d3f8bbad 100644 --- a/tests/ui/uninlined_format_args_panic.edition2021.stderr +++ b/tests/ui/uninlined_format_args_panic.edition2021.stderr @@ -1,5 +1,5 @@ error: variables can be used directly in the `format!` string - --> tests/ui/uninlined_format_args_panic.rs:10:5 + --> tests/ui/uninlined_format_args_panic.rs:11:5 | LL | println!("val='{}'", var); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -13,7 +13,7 @@ LL + println!("val='{var}'"); | error: variables can be used directly in the `format!` string - --> tests/ui/uninlined_format_args_panic.rs:13:9 + --> tests/ui/uninlined_format_args_panic.rs:14:9 | LL | panic!("p1 {}", var); | ^^^^^^^^^^^^^^^^^^^^ @@ -25,7 +25,7 @@ LL + panic!("p1 {var}"); | error: variables can be used directly in the `format!` string - --> tests/ui/uninlined_format_args_panic.rs:16:9 + --> tests/ui/uninlined_format_args_panic.rs:17:9 | LL | panic!("p2 {0}", var); | ^^^^^^^^^^^^^^^^^^^^^ @@ -37,7 +37,7 @@ LL + panic!("p2 {var}"); | error: variables can be used directly in the `format!` string - --> tests/ui/uninlined_format_args_panic.rs:19:9 + --> tests/ui/uninlined_format_args_panic.rs:20:9 | LL | panic!("p3 {var}", var = var); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -49,7 +49,7 @@ LL + panic!("p3 {var}"); | error: variables can be used directly in the `format!` string - --> tests/ui/uninlined_format_args_panic.rs:29:5 + --> tests/ui/uninlined_format_args_panic.rs:30:5 | LL | assert!(var == 1, "p5 {}", var); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -61,7 +61,7 @@ LL + assert!(var == 1, "p5 {var}"); | error: variables can be used directly in the `format!` string - --> tests/ui/uninlined_format_args_panic.rs:30:5 + --> tests/ui/uninlined_format_args_panic.rs:31:5 | LL | debug_assert!(var == 1, "p6 {}", var); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/uninlined_format_args_panic.rs b/tests/ui/uninlined_format_args_panic.rs index fa594d9a96f0..9402cea878df 100644 --- a/tests/ui/uninlined_format_args_panic.rs +++ b/tests/ui/uninlined_format_args_panic.rs @@ -3,6 +3,7 @@ //@[edition2021] edition:2021 #![warn(clippy::uninlined_format_args)] +#![allow(clippy::literal_string_with_formatting_arg)] fn main() { let var = 1; From 8d5a4e95e3381e9680212b145a5d5f5c16e2902e Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 19 Sep 2024 12:32:57 +0200 Subject: [PATCH 4/8] Allow `literal_string_with_formatting_arg` in some clippy crates --- clippy_dev/src/lib.rs | 2 +- clippy_lints/src/lib.rs | 3 ++- lintcheck/src/main.rs | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/clippy_dev/src/lib.rs b/clippy_dev/src/lib.rs index ad385d5fbd29..1f4560eaecf1 100644 --- a/clippy_dev/src/lib.rs +++ b/clippy_dev/src/lib.rs @@ -7,7 +7,7 @@ unused_lifetimes, unused_qualifications )] -#![allow(clippy::missing_panics_doc)] +#![allow(clippy::missing_panics_doc, clippy::literal_string_with_formatting_arg)] // The `rustc_driver` crate seems to be required in order to use the `rust_lexer` crate. #[allow(unused_extern_crates)] diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 44e36b99b5d9..051e7653934b 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -17,7 +17,8 @@ clippy::missing_docs_in_private_items, clippy::must_use_candidate, rustc::diagnostic_outside_of_impl, - rustc::untranslatable_diagnostic + rustc::untranslatable_diagnostic, + clippy::literal_string_with_formatting_arg )] #![warn( trivial_casts, diff --git a/lintcheck/src/main.rs b/lintcheck/src/main.rs index 8c62dd3ed385..db7710a7a381 100644 --- a/lintcheck/src/main.rs +++ b/lintcheck/src/main.rs @@ -17,7 +17,8 @@ #![allow( clippy::collapsible_else_if, clippy::needless_borrows_for_generic_args, - clippy::module_name_repetitions + clippy::module_name_repetitions, + clippy::literal_string_with_formatting_arg )] mod config; From 9ea769a4795bc2b85fa22a97a9c9c2e8ce4d483d Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 19 Sep 2024 17:39:28 +0200 Subject: [PATCH 5/8] Disable checks on `{}` --- .../src/literal_string_with_formatting_arg.rs | 47 +++++++++++-------- .../ui/literal_string_with_formatting_arg.rs | 6 ++- .../literal_string_with_formatting_arg.stderr | 34 +++++++------- 3 files changed, 49 insertions(+), 38 deletions(-) diff --git a/clippy_lints/src/literal_string_with_formatting_arg.rs b/clippy_lints/src/literal_string_with_formatting_arg.rs index e72f0de90c65..2938ff3772fd 100644 --- a/clippy_lints/src/literal_string_with_formatting_arg.rs +++ b/clippy_lints/src/literal_string_with_formatting_arg.rs @@ -70,31 +70,40 @@ impl EarlyLintPass for LiteralStringWithFormattingArg { continue; } - let mut end = fmt_str[pos.end..].find('}').map_or(pos.end, |found| found + pos.end); - if fmt_str[start..end].contains(':') { - end += 1; + if fmt_str[start + 1..].trim_start().starts_with('}') { + // For now, we ignore `{}`. + continue; } + + let end = fmt_str[start + 1..] + .find('}') + .map_or(pos.end, |found| start + 1 + found) + + 1; spans.push( expr.span - .with_hi(lo + BytePos((start + add) as _)) - .with_lo(lo + BytePos((end + add) as _)), + .with_hi(lo + BytePos((start + add).try_into().unwrap())) + .with_lo(lo + BytePos((end + add).try_into().unwrap())), ); } } - if spans.len() == 1 { - span_lint( - cx, - LITERAL_STRING_WITH_FORMATTING_ARG, - spans, - "this looks like a formatting argument but it is not part of a formatting macro", - ); - } else if spans.len() > 1 { - span_lint( - cx, - LITERAL_STRING_WITH_FORMATTING_ARG, - spans, - "these look like formatting arguments but are not part of a formatting macro", - ); + match spans.len() { + 0 => {}, + 1 => { + span_lint( + cx, + LITERAL_STRING_WITH_FORMATTING_ARG, + spans, + "this looks like a formatting argument but it is not part of a formatting macro", + ); + }, + _ => { + span_lint( + cx, + LITERAL_STRING_WITH_FORMATTING_ARG, + spans, + "these look like formatting arguments but are not part of a formatting macro", + ); + }, } } } diff --git a/tests/ui/literal_string_with_formatting_arg.rs b/tests/ui/literal_string_with_formatting_arg.rs index 539d24efe7c0..d623ab524fbc 100644 --- a/tests/ui/literal_string_with_formatting_arg.rs +++ b/tests/ui/literal_string_with_formatting_arg.rs @@ -5,6 +5,7 @@ fn main() { let x: Option = None; let y = "hello"; x.expect("{y} {}"); //~ literal_string_with_formatting_arg + x.expect(" {y} bla"); //~ literal_string_with_formatting_arg x.expect("{:?}"); //~ literal_string_with_formatting_arg x.expect("{y:?}"); //~ literal_string_with_formatting_arg x.expect(" {y:?} {y:?} "); //~ literal_string_with_formatting_arg @@ -12,13 +13,14 @@ fn main() { x.expect(r"{y:?} {y:?} "); //~ literal_string_with_formatting_arg x.expect(r"{y:?} y:?}"); //~ literal_string_with_formatting_arg x.expect(r##" {y:?} {y:?} "##); //~ literal_string_with_formatting_arg - "\\.+*?()|[]{}^$#&-~".chars().any(|x| x == 'a'); //~ literal_string_with_formatting_arg // Ensure that it doesn't try to go in the middle of a unicode character. - x.expect("———{}"); //~ literal_string_with_formatting_arg + x.expect("———{:?}"); //~ literal_string_with_formatting_arg // Should not lint! format!("{y:?}"); println!("{y:?}"); + x.expect(" {} "); // For now we ignore `{}` to limit false positives. + x.expect(" { } "); // For now we ignore `{}` to limit false positives. x.expect("{{y} {x"); x.expect("{{y:?}"); x.expect("{y:...}"); diff --git a/tests/ui/literal_string_with_formatting_arg.stderr b/tests/ui/literal_string_with_formatting_arg.stderr index c05d4af017c2..da7eb71ac878 100644 --- a/tests/ui/literal_string_with_formatting_arg.stderr +++ b/tests/ui/literal_string_with_formatting_arg.stderr @@ -1,65 +1,65 @@ -error: these look like formatting arguments but are not part of a formatting macro +error: this looks like a formatting argument but it is not part of a formatting macro --> tests/ui/literal_string_with_formatting_arg.rs:7:15 | LL | x.expect("{y} {}"); - | ^^^^^^ + | ^^^ | = note: `-D clippy::literal-string-with-formatting-arg` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::literal_string_with_formatting_arg)]` error: this looks like a formatting argument but it is not part of a formatting macro - --> tests/ui/literal_string_with_formatting_arg.rs:8:15 + --> tests/ui/literal_string_with_formatting_arg.rs:8:16 + | +LL | x.expect(" {y} bla"); + | ^^^ + +error: this looks like a formatting argument but it is not part of a formatting macro + --> tests/ui/literal_string_with_formatting_arg.rs:9:15 | LL | x.expect("{:?}"); | ^^^^ error: this looks like a formatting argument but it is not part of a formatting macro - --> tests/ui/literal_string_with_formatting_arg.rs:9:15 + --> tests/ui/literal_string_with_formatting_arg.rs:10:15 | LL | x.expect("{y:?}"); | ^^^^^ error: these look like formatting arguments but are not part of a formatting macro - --> tests/ui/literal_string_with_formatting_arg.rs:10:16 + --> tests/ui/literal_string_with_formatting_arg.rs:11:16 | LL | x.expect(" {y:?} {y:?} "); | ^^^^^ ^^^^^ error: this looks like a formatting argument but it is not part of a formatting macro - --> tests/ui/literal_string_with_formatting_arg.rs:11:23 + --> tests/ui/literal_string_with_formatting_arg.rs:12:23 | LL | x.expect(" {y:..} {y:?} "); | ^^^^^ error: these look like formatting arguments but are not part of a formatting macro - --> tests/ui/literal_string_with_formatting_arg.rs:12:16 + --> tests/ui/literal_string_with_formatting_arg.rs:13:16 | LL | x.expect(r"{y:?} {y:?} "); | ^^^^^ ^^^^^ error: this looks like a formatting argument but it is not part of a formatting macro - --> tests/ui/literal_string_with_formatting_arg.rs:13:16 + --> tests/ui/literal_string_with_formatting_arg.rs:14:16 | LL | x.expect(r"{y:?} y:?}"); | ^^^^^ error: these look like formatting arguments but are not part of a formatting macro - --> tests/ui/literal_string_with_formatting_arg.rs:14:19 + --> tests/ui/literal_string_with_formatting_arg.rs:15:19 | LL | x.expect(r##" {y:?} {y:?} "##); | ^^^^^ ^^^^^ -error: this looks like a formatting argument but it is not part of a formatting macro - --> tests/ui/literal_string_with_formatting_arg.rs:15:17 - | -LL | "\\.+*?()|[]{}^$#&-~".chars().any(|x| x == 'a'); - | ^^ - error: this looks like a formatting argument but it is not part of a formatting macro --> tests/ui/literal_string_with_formatting_arg.rs:17:18 | -LL | x.expect("———{}"); - | ^^ +LL | x.expect("———{:?}"); + | ^^^^ error: aborting due to 10 previous errors From b20b75a904c5fa0e461c88bab64370b1d3bf69b9 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 19 Sep 2024 18:04:39 +0200 Subject: [PATCH 6/8] Don't emit if literal comes from macro expansion --- clippy_lints/src/literal_string_with_formatting_arg.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/clippy_lints/src/literal_string_with_formatting_arg.rs b/clippy_lints/src/literal_string_with_formatting_arg.rs index 2938ff3772fd..a05ca4a2681b 100644 --- a/clippy_lints/src/literal_string_with_formatting_arg.rs +++ b/clippy_lints/src/literal_string_with_formatting_arg.rs @@ -37,6 +37,9 @@ declare_lint_pass!(LiteralStringWithFormattingArg => [LITERAL_STRING_WITH_FORMAT impl EarlyLintPass for LiteralStringWithFormattingArg { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { + if expr.span.from_expansion() { + return; + } if let ExprKind::Lit(lit) = expr.kind { let add = match lit.kind { LitKind::Str => 1, From 607a3f6c08b489f9bd71ce11a00b112c3e5c612d Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 19 Nov 2024 16:01:38 +0100 Subject: [PATCH 7/8] Rename `literal_string_with_formatting_arg` into `literal_string_with_formatting_args` --- CHANGELOG.md | 2 +- clippy_dev/src/lib.rs | 2 +- clippy_lints/src/declared_lints.rs | 2 +- clippy_lints/src/lib.rs | 6 ++--- ...=> literal_string_with_formatting_args.rs} | 8 +++---- .../large_include_file/large_include_file.rs | 2 +- tests/ui/auxiliary/proc_macro_derive.rs | 2 +- tests/ui/format.fixed | 2 +- tests/ui/format.rs | 2 +- .../ui/literal_string_with_formatting_arg.rs | 22 +++++++++---------- .../literal_string_with_formatting_arg.stderr | 22 +++++++++---------- tests/ui/print_literal.fixed | 2 +- tests/ui/print_literal.rs | 2 +- tests/ui/regex.rs | 2 +- ...nlined_format_args_panic.edition2018.fixed | 2 +- ...nlined_format_args_panic.edition2021.fixed | 2 +- tests/ui/uninlined_format_args_panic.rs | 2 +- 17 files changed, 42 insertions(+), 42 deletions(-) rename clippy_lints/src/{literal_string_with_formatting_arg.rs => literal_string_with_formatting_args.rs} (95%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a82bfce82bb..efa6ed9f1bb5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5639,7 +5639,7 @@ Released 2018-09-13 [`lines_filter_map_ok`]: https://rust-lang.github.io/rust-clippy/master/index.html#lines_filter_map_ok [`linkedlist`]: https://rust-lang.github.io/rust-clippy/master/index.html#linkedlist [`lint_groups_priority`]: https://rust-lang.github.io/rust-clippy/master/index.html#lint_groups_priority -[`literal_string_with_formatting_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#literal_string_with_formatting_arg +[`literal_string_with_formatting_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#literal_string_with_formatting_args [`little_endian_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#little_endian_bytes [`logic_bug`]: https://rust-lang.github.io/rust-clippy/master/index.html#logic_bug [`lossy_float_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#lossy_float_literal diff --git a/clippy_dev/src/lib.rs b/clippy_dev/src/lib.rs index 1f4560eaecf1..ad385d5fbd29 100644 --- a/clippy_dev/src/lib.rs +++ b/clippy_dev/src/lib.rs @@ -7,7 +7,7 @@ unused_lifetimes, unused_qualifications )] -#![allow(clippy::missing_panics_doc, clippy::literal_string_with_formatting_arg)] +#![allow(clippy::missing_panics_doc)] // The `rustc_driver` crate seems to be required in order to use the `rust_lexer` crate. #[allow(unused_extern_crates)] diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index a3f89b3f70c4..1143c7e10b27 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -276,7 +276,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::literal_representation::MISTYPED_LITERAL_SUFFIXES_INFO, crate::literal_representation::UNREADABLE_LITERAL_INFO, crate::literal_representation::UNUSUAL_BYTE_GROUPINGS_INFO, - crate::literal_string_with_formatting_arg::LITERAL_STRING_WITH_FORMATTING_ARG_INFO, + crate::literal_string_with_formatting_args::LITERAL_STRING_WITH_FORMATTING_ARGS_INFO, crate::loops::EMPTY_LOOP_INFO, crate::loops::EXPLICIT_COUNTER_LOOP_INFO, crate::loops::EXPLICIT_INTO_ITER_LOOP_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 051e7653934b..4f003377a5e5 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -18,7 +18,7 @@ clippy::must_use_candidate, rustc::diagnostic_outside_of_impl, rustc::untranslatable_diagnostic, - clippy::literal_string_with_formatting_arg + clippy::literal_string_with_formatting_args )] #![warn( trivial_casts, @@ -198,7 +198,7 @@ mod let_with_type_underscore; mod lifetimes; mod lines_filter_map_ok; mod literal_representation; -mod literal_string_with_formatting_arg; +mod literal_string_with_formatting_args; mod loops; mod macro_metavars_in_unsafe; mod macro_use; @@ -962,7 +962,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(move |_| Box::new(manual_div_ceil::ManualDivCeil::new(conf))); store.register_late_pass(|_| Box::new(manual_is_power_of_two::ManualIsPowerOfTwo)); store.register_late_pass(|_| Box::new(non_zero_suggestions::NonZeroSuggestions)); - store.register_early_pass(|| Box::new(literal_string_with_formatting_arg::LiteralStringWithFormattingArg)); + store.register_early_pass(|| Box::new(literal_string_with_formatting_args::LiteralStringWithFormattingArg)); store.register_late_pass(move |_| Box::new(unused_trait_names::UnusedTraitNames::new(conf))); store.register_late_pass(|_| Box::new(manual_ignore_case_cmp::ManualIgnoreCaseCmp)); store.register_late_pass(|_| Box::new(unnecessary_literal_bound::UnnecessaryLiteralBound)); diff --git a/clippy_lints/src/literal_string_with_formatting_arg.rs b/clippy_lints/src/literal_string_with_formatting_args.rs similarity index 95% rename from clippy_lints/src/literal_string_with_formatting_arg.rs rename to clippy_lints/src/literal_string_with_formatting_args.rs index a05ca4a2681b..6514a72a44da 100644 --- a/clippy_lints/src/literal_string_with_formatting_arg.rs +++ b/clippy_lints/src/literal_string_with_formatting_args.rs @@ -28,12 +28,12 @@ declare_clippy_lint! { /// x.expect(&format!("{y:?}")); /// ``` #[clippy::version = "1.83.0"] - pub LITERAL_STRING_WITH_FORMATTING_ARG, + pub LITERAL_STRING_WITH_FORMATTING_ARGS, suspicious, "Checks if string literals have formatting arguments" } -declare_lint_pass!(LiteralStringWithFormattingArg => [LITERAL_STRING_WITH_FORMATTING_ARG]); +declare_lint_pass!(LiteralStringWithFormattingArg => [LITERAL_STRING_WITH_FORMATTING_ARGS]); impl EarlyLintPass for LiteralStringWithFormattingArg { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { @@ -94,7 +94,7 @@ impl EarlyLintPass for LiteralStringWithFormattingArg { 1 => { span_lint( cx, - LITERAL_STRING_WITH_FORMATTING_ARG, + LITERAL_STRING_WITH_FORMATTING_ARGS, spans, "this looks like a formatting argument but it is not part of a formatting macro", ); @@ -102,7 +102,7 @@ impl EarlyLintPass for LiteralStringWithFormattingArg { _ => { span_lint( cx, - LITERAL_STRING_WITH_FORMATTING_ARG, + LITERAL_STRING_WITH_FORMATTING_ARGS, spans, "these look like formatting arguments but are not part of a formatting macro", ); diff --git a/tests/ui-toml/large_include_file/large_include_file.rs b/tests/ui-toml/large_include_file/large_include_file.rs index c456b48daca8..184c6d17ba41 100644 --- a/tests/ui-toml/large_include_file/large_include_file.rs +++ b/tests/ui-toml/large_include_file/large_include_file.rs @@ -1,5 +1,5 @@ #![warn(clippy::large_include_file)] -#![allow(clippy::literal_string_with_formatting_arg)] +#![allow(clippy::literal_string_with_formatting_args)] // Good const GOOD_INCLUDE_BYTES: &[u8; 68] = include_bytes!("../../ui/author.rs"); diff --git a/tests/ui/auxiliary/proc_macro_derive.rs b/tests/ui/auxiliary/proc_macro_derive.rs index 33fbea080032..1815dd58f510 100644 --- a/tests/ui/auxiliary/proc_macro_derive.rs +++ b/tests/ui/auxiliary/proc_macro_derive.rs @@ -2,7 +2,7 @@ #![allow(incomplete_features)] #![allow(clippy::field_reassign_with_default)] #![allow(clippy::eq_op)] -#![allow(clippy::literal_string_with_formatting_arg)] +#![allow(clippy::literal_string_with_formatting_args)] extern crate proc_macro; diff --git a/tests/ui/format.fixed b/tests/ui/format.fixed index df4ac2d1a229..3dc8eb79ba28 100644 --- a/tests/ui/format.fixed +++ b/tests/ui/format.fixed @@ -7,7 +7,7 @@ clippy::uninlined_format_args, clippy::needless_raw_string_hashes, clippy::useless_vec, - clippy::literal_string_with_formatting_arg + clippy::literal_string_with_formatting_args )] struct Foo(pub String); diff --git a/tests/ui/format.rs b/tests/ui/format.rs index 30d8ff38f267..eaf33c2a6c92 100644 --- a/tests/ui/format.rs +++ b/tests/ui/format.rs @@ -7,7 +7,7 @@ clippy::uninlined_format_args, clippy::needless_raw_string_hashes, clippy::useless_vec, - clippy::literal_string_with_formatting_arg + clippy::literal_string_with_formatting_args )] struct Foo(pub String); diff --git a/tests/ui/literal_string_with_formatting_arg.rs b/tests/ui/literal_string_with_formatting_arg.rs index d623ab524fbc..fdb8ba606210 100644 --- a/tests/ui/literal_string_with_formatting_arg.rs +++ b/tests/ui/literal_string_with_formatting_arg.rs @@ -1,20 +1,20 @@ -#![warn(clippy::literal_string_with_formatting_arg)] +#![warn(clippy::literal_string_with_formatting_args)] #![allow(clippy::unnecessary_literal_unwrap)] fn main() { let x: Option = None; let y = "hello"; - x.expect("{y} {}"); //~ literal_string_with_formatting_arg - x.expect(" {y} bla"); //~ literal_string_with_formatting_arg - x.expect("{:?}"); //~ literal_string_with_formatting_arg - x.expect("{y:?}"); //~ literal_string_with_formatting_arg - x.expect(" {y:?} {y:?} "); //~ literal_string_with_formatting_arg - x.expect(" {y:..} {y:?} "); //~ literal_string_with_formatting_arg - x.expect(r"{y:?} {y:?} "); //~ literal_string_with_formatting_arg - x.expect(r"{y:?} y:?}"); //~ literal_string_with_formatting_arg - x.expect(r##" {y:?} {y:?} "##); //~ literal_string_with_formatting_arg + x.expect("{y} {}"); //~ literal_string_with_formatting_args + x.expect(" {y} bla"); //~ literal_string_with_formatting_args + x.expect("{:?}"); //~ literal_string_with_formatting_args + x.expect("{y:?}"); //~ literal_string_with_formatting_args + x.expect(" {y:?} {y:?} "); //~ literal_string_with_formatting_args + x.expect(" {y:..} {y:?} "); //~ literal_string_with_formatting_args + x.expect(r"{y:?} {y:?} "); //~ literal_string_with_formatting_args + x.expect(r"{y:?} y:?}"); //~ literal_string_with_formatting_args + x.expect(r##" {y:?} {y:?} "##); //~ literal_string_with_formatting_args // Ensure that it doesn't try to go in the middle of a unicode character. - x.expect("———{:?}"); //~ literal_string_with_formatting_arg + x.expect("———{:?}"); //~ literal_string_with_formatting_args // Should not lint! format!("{y:?}"); diff --git a/tests/ui/literal_string_with_formatting_arg.stderr b/tests/ui/literal_string_with_formatting_arg.stderr index da7eb71ac878..515ae978f7a8 100644 --- a/tests/ui/literal_string_with_formatting_arg.stderr +++ b/tests/ui/literal_string_with_formatting_arg.stderr @@ -1,62 +1,62 @@ error: this looks like a formatting argument but it is not part of a formatting macro - --> tests/ui/literal_string_with_formatting_arg.rs:7:15 + --> tests/ui/literal_string_with_formatting_args.rs:7:15 | LL | x.expect("{y} {}"); | ^^^ | = note: `-D clippy::literal-string-with-formatting-arg` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::literal_string_with_formatting_arg)]` + = help: to override `-D warnings` add `#[allow(clippy::literal_string_with_formatting_args)]` error: this looks like a formatting argument but it is not part of a formatting macro - --> tests/ui/literal_string_with_formatting_arg.rs:8:16 + --> tests/ui/literal_string_with_formatting_args.rs:8:16 | LL | x.expect(" {y} bla"); | ^^^ error: this looks like a formatting argument but it is not part of a formatting macro - --> tests/ui/literal_string_with_formatting_arg.rs:9:15 + --> tests/ui/literal_string_with_formatting_args.rs:9:15 | LL | x.expect("{:?}"); | ^^^^ error: this looks like a formatting argument but it is not part of a formatting macro - --> tests/ui/literal_string_with_formatting_arg.rs:10:15 + --> tests/ui/literal_string_with_formatting_args.rs:10:15 | LL | x.expect("{y:?}"); | ^^^^^ error: these look like formatting arguments but are not part of a formatting macro - --> tests/ui/literal_string_with_formatting_arg.rs:11:16 + --> tests/ui/literal_string_with_formatting_args.rs:11:16 | LL | x.expect(" {y:?} {y:?} "); | ^^^^^ ^^^^^ error: this looks like a formatting argument but it is not part of a formatting macro - --> tests/ui/literal_string_with_formatting_arg.rs:12:23 + --> tests/ui/literal_string_with_formatting_args.rs:12:23 | LL | x.expect(" {y:..} {y:?} "); | ^^^^^ error: these look like formatting arguments but are not part of a formatting macro - --> tests/ui/literal_string_with_formatting_arg.rs:13:16 + --> tests/ui/literal_string_with_formatting_args.rs:13:16 | LL | x.expect(r"{y:?} {y:?} "); | ^^^^^ ^^^^^ error: this looks like a formatting argument but it is not part of a formatting macro - --> tests/ui/literal_string_with_formatting_arg.rs:14:16 + --> tests/ui/literal_string_with_formatting_args.rs:14:16 | LL | x.expect(r"{y:?} y:?}"); | ^^^^^ error: these look like formatting arguments but are not part of a formatting macro - --> tests/ui/literal_string_with_formatting_arg.rs:15:19 + --> tests/ui/literal_string_with_formatting_args.rs:15:19 | LL | x.expect(r##" {y:?} {y:?} "##); | ^^^^^ ^^^^^ error: this looks like a formatting argument but it is not part of a formatting macro - --> tests/ui/literal_string_with_formatting_arg.rs:17:18 + --> tests/ui/literal_string_with_formatting_args.rs:17:18 | LL | x.expect("———{:?}"); | ^^^^ diff --git a/tests/ui/print_literal.fixed b/tests/ui/print_literal.fixed index 8c0cedf6299e..1705a7ff01bb 100644 --- a/tests/ui/print_literal.fixed +++ b/tests/ui/print_literal.fixed @@ -1,5 +1,5 @@ #![warn(clippy::print_literal)] -#![allow(clippy::uninlined_format_args, clippy::literal_string_with_formatting_arg)] +#![allow(clippy::uninlined_format_args, clippy::literal_string_with_formatting_args)] fn main() { // these should be fine diff --git a/tests/ui/print_literal.rs b/tests/ui/print_literal.rs index 1df4f4181e9b..d10b26b5887c 100644 --- a/tests/ui/print_literal.rs +++ b/tests/ui/print_literal.rs @@ -1,5 +1,5 @@ #![warn(clippy::print_literal)] -#![allow(clippy::uninlined_format_args, clippy::literal_string_with_formatting_arg)] +#![allow(clippy::uninlined_format_args, clippy::literal_string_with_formatting_args)] fn main() { // these should be fine diff --git a/tests/ui/regex.rs b/tests/ui/regex.rs index ea5c83549286..adead5ef33ce 100644 --- a/tests/ui/regex.rs +++ b/tests/ui/regex.rs @@ -4,7 +4,7 @@ clippy::needless_raw_string_hashes, clippy::needless_borrow, clippy::needless_borrows_for_generic_args, - clippy::literal_string_with_formatting_arg + clippy::literal_string_with_formatting_args )] #![warn(clippy::invalid_regex, clippy::trivial_regex, clippy::regex_creation_in_loops)] diff --git a/tests/ui/uninlined_format_args_panic.edition2018.fixed b/tests/ui/uninlined_format_args_panic.edition2018.fixed index 130187d19d6f..9911d1317070 100644 --- a/tests/ui/uninlined_format_args_panic.edition2018.fixed +++ b/tests/ui/uninlined_format_args_panic.edition2018.fixed @@ -3,7 +3,7 @@ //@[edition2021] edition:2021 #![warn(clippy::uninlined_format_args)] -#![allow(clippy::literal_string_with_formatting_arg)] +#![allow(clippy::literal_string_with_formatting_args)] fn main() { let var = 1; diff --git a/tests/ui/uninlined_format_args_panic.edition2021.fixed b/tests/ui/uninlined_format_args_panic.edition2021.fixed index 63b30c64025d..87b74670565f 100644 --- a/tests/ui/uninlined_format_args_panic.edition2021.fixed +++ b/tests/ui/uninlined_format_args_panic.edition2021.fixed @@ -3,7 +3,7 @@ //@[edition2021] edition:2021 #![warn(clippy::uninlined_format_args)] -#![allow(clippy::literal_string_with_formatting_arg)] +#![allow(clippy::literal_string_with_formatting_args)] fn main() { let var = 1; diff --git a/tests/ui/uninlined_format_args_panic.rs b/tests/ui/uninlined_format_args_panic.rs index 9402cea878df..647c69bc5c41 100644 --- a/tests/ui/uninlined_format_args_panic.rs +++ b/tests/ui/uninlined_format_args_panic.rs @@ -3,7 +3,7 @@ //@[edition2021] edition:2021 #![warn(clippy::uninlined_format_args)] -#![allow(clippy::literal_string_with_formatting_arg)] +#![allow(clippy::literal_string_with_formatting_args)] fn main() { let var = 1; From cfc6444f84fb80f4cb44be0116d35976c142aeb2 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 19 Nov 2024 18:25:12 +0100 Subject: [PATCH 8/8] Limit `literal_string_with_formatting_args` to known variables if no formatting argument is passed --- clippy_lints/src/lib.rs | 2 +- .../literal_string_with_formatting_args.rs | 110 +++++++++++++----- lintcheck/src/main.rs | 2 +- .../ui/literal_string_with_formatting_arg.rs | 6 +- .../literal_string_with_formatting_arg.stderr | 30 +++-- tests/ui/regex.rs | 3 +- tests/ui/regex.stderr | 48 ++++---- 7 files changed, 127 insertions(+), 74 deletions(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 4f003377a5e5..017e6e2a1423 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -962,7 +962,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(move |_| Box::new(manual_div_ceil::ManualDivCeil::new(conf))); store.register_late_pass(|_| Box::new(manual_is_power_of_two::ManualIsPowerOfTwo)); store.register_late_pass(|_| Box::new(non_zero_suggestions::NonZeroSuggestions)); - store.register_early_pass(|| Box::new(literal_string_with_formatting_args::LiteralStringWithFormattingArg)); + store.register_late_pass(|_| Box::new(literal_string_with_formatting_args::LiteralStringWithFormattingArg)); store.register_late_pass(move |_| Box::new(unused_trait_names::UnusedTraitNames::new(conf))); store.register_late_pass(|_| Box::new(manual_ignore_case_cmp::ManualIgnoreCaseCmp)); store.register_late_pass(|_| Box::new(unnecessary_literal_bound::UnnecessaryLiteralBound)); diff --git a/clippy_lints/src/literal_string_with_formatting_args.rs b/clippy_lints/src/literal_string_with_formatting_args.rs index 6514a72a44da..378be37fbac5 100644 --- a/clippy_lints/src/literal_string_with_formatting_args.rs +++ b/clippy_lints/src/literal_string_with_formatting_args.rs @@ -1,11 +1,13 @@ -use rustc_ast::ast::{Expr, ExprKind}; -use rustc_ast::token::LitKind; -use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_ast::{LitKind, StrStyle}; +use rustc_hir::{Expr, ExprKind}; +use rustc_lexer::is_ident; +use rustc_lint::{LateContext, LateLintPass}; use rustc_parse_format::{ParseMode, Parser, Piece}; use rustc_session::declare_lint_pass; -use rustc_span::BytePos; +use rustc_span::{BytePos, Span}; use clippy_utils::diagnostics::span_lint; +use clippy_utils::mir::enclosing_mir; declare_clippy_lint! { /// ### What it does @@ -35,18 +37,65 @@ declare_clippy_lint! { declare_lint_pass!(LiteralStringWithFormattingArg => [LITERAL_STRING_WITH_FORMATTING_ARGS]); -impl EarlyLintPass for LiteralStringWithFormattingArg { - fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { +fn emit_lint(cx: &LateContext<'_>, expr: &Expr<'_>, spans: &[(Span, Option)]) { + if !spans.is_empty() + && let Some(mir) = enclosing_mir(cx.tcx, expr.hir_id) + { + let spans = spans + .iter() + .filter_map(|(span, name)| { + if let Some(name) = name { + // We need to check that the name is a local. + if !mir + .var_debug_info + .iter() + .any(|local| !local.source_info.span.from_expansion() && local.name.as_str() == name) + { + return None; + } + } + Some(*span) + }) + .collect::>(); + match spans.len() { + 0 => {}, + 1 => { + span_lint( + cx, + LITERAL_STRING_WITH_FORMATTING_ARGS, + spans, + "this looks like a formatting argument but it is not part of a formatting macro", + ); + }, + _ => { + span_lint( + cx, + LITERAL_STRING_WITH_FORMATTING_ARGS, + spans, + "these look like formatting arguments but are not part of a formatting macro", + ); + }, + } + } +} + +impl LateLintPass<'_> for LiteralStringWithFormattingArg { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { if expr.span.from_expansion() { return; } if let ExprKind::Lit(lit) = expr.kind { - let add = match lit.kind { - LitKind::Str => 1, - LitKind::StrRaw(nb) => nb as usize + 2, + let (add, symbol) = match lit.node { + LitKind::Str(symbol, style) => { + let add = match style { + StrStyle::Cooked => 1, + StrStyle::Raw(nb) => nb as usize + 2, + }; + (add, symbol) + }, _ => return, }; - let fmt_str = lit.symbol.as_str(); + let fmt_str = symbol.as_str(); let lo = expr.span.lo(); let mut current = fmt_str; let mut diff_len = 0; @@ -74,7 +123,7 @@ impl EarlyLintPass for LiteralStringWithFormattingArg { } if fmt_str[start + 1..].trim_start().starts_with('}') { - // For now, we ignore `{}`. + // We ignore `{}`. continue; } @@ -82,32 +131,29 @@ impl EarlyLintPass for LiteralStringWithFormattingArg { .find('}') .map_or(pos.end, |found| start + 1 + found) + 1; - spans.push( + let ident_start = start + 1; + let colon_pos = fmt_str[ident_start..end].find(':'); + let ident_end = colon_pos.unwrap_or(end - 1); + let mut name = None; + if ident_start < ident_end + && let arg = &fmt_str[ident_start..ident_end] + && !arg.is_empty() + && is_ident(arg) + { + name = Some(arg.to_string()); + } else if colon_pos.is_none() { + // Not a `{:?}`. + continue; + } + spans.push(( expr.span .with_hi(lo + BytePos((start + add).try_into().unwrap())) .with_lo(lo + BytePos((end + add).try_into().unwrap())), - ); + name, + )); } } - match spans.len() { - 0 => {}, - 1 => { - span_lint( - cx, - LITERAL_STRING_WITH_FORMATTING_ARGS, - spans, - "this looks like a formatting argument but it is not part of a formatting macro", - ); - }, - _ => { - span_lint( - cx, - LITERAL_STRING_WITH_FORMATTING_ARGS, - spans, - "these look like formatting arguments but are not part of a formatting macro", - ); - }, - } + emit_lint(cx, expr, &spans); } } } diff --git a/lintcheck/src/main.rs b/lintcheck/src/main.rs index db7710a7a381..03e2a24f6f98 100644 --- a/lintcheck/src/main.rs +++ b/lintcheck/src/main.rs @@ -18,7 +18,7 @@ clippy::collapsible_else_if, clippy::needless_borrows_for_generic_args, clippy::module_name_repetitions, - clippy::literal_string_with_formatting_arg + clippy::literal_string_with_formatting_args )] mod config; diff --git a/tests/ui/literal_string_with_formatting_arg.rs b/tests/ui/literal_string_with_formatting_arg.rs index fdb8ba606210..20bd243aa300 100644 --- a/tests/ui/literal_string_with_formatting_arg.rs +++ b/tests/ui/literal_string_with_formatting_arg.rs @@ -19,10 +19,12 @@ fn main() { // Should not lint! format!("{y:?}"); println!("{y:?}"); - x.expect(" {} "); // For now we ignore `{}` to limit false positives. - x.expect(" { } "); // For now we ignore `{}` to limit false positives. + x.expect(" {} "); // We ignore `{}` to limit false positives. + x.expect(" { } "); // We ignore `{}` to limit false positives. x.expect("{{y} {x"); x.expect("{{y:?}"); + x.expect(" {0}"); // If it only contains an integer, we ignore it. + x.expect(r##" {x:?} "##); // `x` doesn't exist so we shoud not lint x.expect("{y:...}"); let _ = "fn main {\n\ }"; diff --git a/tests/ui/literal_string_with_formatting_arg.stderr b/tests/ui/literal_string_with_formatting_arg.stderr index 515ae978f7a8..32a84f600da7 100644 --- a/tests/ui/literal_string_with_formatting_arg.stderr +++ b/tests/ui/literal_string_with_formatting_arg.stderr @@ -1,65 +1,71 @@ error: this looks like a formatting argument but it is not part of a formatting macro - --> tests/ui/literal_string_with_formatting_args.rs:7:15 + --> tests/ui/literal_string_with_formatting_arg.rs:7:15 | LL | x.expect("{y} {}"); | ^^^ | - = note: `-D clippy::literal-string-with-formatting-arg` implied by `-D warnings` + = note: `-D clippy::literal-string-with-formatting-args` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::literal_string_with_formatting_args)]` error: this looks like a formatting argument but it is not part of a formatting macro - --> tests/ui/literal_string_with_formatting_args.rs:8:16 + --> tests/ui/literal_string_with_formatting_arg.rs:8:16 | LL | x.expect(" {y} bla"); | ^^^ error: this looks like a formatting argument but it is not part of a formatting macro - --> tests/ui/literal_string_with_formatting_args.rs:9:15 + --> tests/ui/literal_string_with_formatting_arg.rs:9:15 | LL | x.expect("{:?}"); | ^^^^ error: this looks like a formatting argument but it is not part of a formatting macro - --> tests/ui/literal_string_with_formatting_args.rs:10:15 + --> tests/ui/literal_string_with_formatting_arg.rs:10:15 | LL | x.expect("{y:?}"); | ^^^^^ error: these look like formatting arguments but are not part of a formatting macro - --> tests/ui/literal_string_with_formatting_args.rs:11:16 + --> tests/ui/literal_string_with_formatting_arg.rs:11:16 | LL | x.expect(" {y:?} {y:?} "); | ^^^^^ ^^^^^ error: this looks like a formatting argument but it is not part of a formatting macro - --> tests/ui/literal_string_with_formatting_args.rs:12:23 + --> tests/ui/literal_string_with_formatting_arg.rs:12:23 | LL | x.expect(" {y:..} {y:?} "); | ^^^^^ error: these look like formatting arguments but are not part of a formatting macro - --> tests/ui/literal_string_with_formatting_args.rs:13:16 + --> tests/ui/literal_string_with_formatting_arg.rs:13:16 | LL | x.expect(r"{y:?} {y:?} "); | ^^^^^ ^^^^^ error: this looks like a formatting argument but it is not part of a formatting macro - --> tests/ui/literal_string_with_formatting_args.rs:14:16 + --> tests/ui/literal_string_with_formatting_arg.rs:14:16 | LL | x.expect(r"{y:?} y:?}"); | ^^^^^ error: these look like formatting arguments but are not part of a formatting macro - --> tests/ui/literal_string_with_formatting_args.rs:15:19 + --> tests/ui/literal_string_with_formatting_arg.rs:15:19 | LL | x.expect(r##" {y:?} {y:?} "##); | ^^^^^ ^^^^^ error: this looks like a formatting argument but it is not part of a formatting macro - --> tests/ui/literal_string_with_formatting_args.rs:17:18 + --> tests/ui/literal_string_with_formatting_arg.rs:17:18 | LL | x.expect("———{:?}"); | ^^^^ -error: aborting due to 10 previous errors +error: this looks like a formatting argument but it is not part of a formatting macro + --> tests/ui/literal_string_with_formatting_arg.rs:27:19 + | +LL | x.expect(r##" {x:?} "##); // `x` doesn't exist so we shoud not lint + | ^^^^^ + +error: aborting due to 11 previous errors diff --git a/tests/ui/regex.rs b/tests/ui/regex.rs index adead5ef33ce..f607a2d50c6d 100644 --- a/tests/ui/regex.rs +++ b/tests/ui/regex.rs @@ -3,8 +3,7 @@ clippy::needless_raw_strings, clippy::needless_raw_string_hashes, clippy::needless_borrow, - clippy::needless_borrows_for_generic_args, - clippy::literal_string_with_formatting_args + clippy::needless_borrows_for_generic_args )] #![warn(clippy::invalid_regex, clippy::trivial_regex, clippy::regex_creation_in_loops)] diff --git a/tests/ui/regex.stderr b/tests/ui/regex.stderr index 919bac8f52f0..18dd538c68b4 100644 --- a/tests/ui/regex.stderr +++ b/tests/ui/regex.stderr @@ -1,5 +1,5 @@ error: trivial regex - --> tests/ui/regex.rs:20:45 + --> tests/ui/regex.rs:19:45 | LL | let pipe_in_wrong_position = Regex::new("|"); | ^^^ @@ -9,7 +9,7 @@ LL | let pipe_in_wrong_position = Regex::new("|"); = help: to override `-D warnings` add `#[allow(clippy::trivial_regex)]` error: trivial regex - --> tests/ui/regex.rs:22:60 + --> tests/ui/regex.rs:21:60 | LL | let pipe_in_wrong_position_builder = RegexBuilder::new("|"); | ^^^ @@ -17,7 +17,7 @@ LL | let pipe_in_wrong_position_builder = RegexBuilder::new("|"); = help: the regex is unlikely to be useful as it is error: regex syntax error: invalid character class range, the start must be <= the end - --> tests/ui/regex.rs:24:42 + --> tests/ui/regex.rs:23:42 | LL | let wrong_char_ranice = Regex::new("[z-a]"); | ^^^ @@ -26,7 +26,7 @@ LL | let wrong_char_ranice = Regex::new("[z-a]"); = help: to override `-D warnings` add `#[allow(clippy::invalid_regex)]` error: regex syntax error: invalid character class range, the start must be <= the end - --> tests/ui/regex.rs:27:37 + --> tests/ui/regex.rs:26:37 | LL | let some_unicode = Regex::new("[é-è]"); | ^^^ @@ -35,13 +35,13 @@ error: regex parse error: ( ^ error: unclosed group - --> tests/ui/regex.rs:30:33 + --> tests/ui/regex.rs:29:33 | LL | let some_regex = Regex::new(OPENING_PAREN); | ^^^^^^^^^^^^^ error: trivial regex - --> tests/ui/regex.rs:32:53 + --> tests/ui/regex.rs:31:53 | LL | let binary_pipe_in_wrong_position = BRegex::new("|"); | ^^^ @@ -52,7 +52,7 @@ error: regex parse error: ( ^ error: unclosed group - --> tests/ui/regex.rs:34:41 + --> tests/ui/regex.rs:33:41 | LL | let some_binary_regex = BRegex::new(OPENING_PAREN); | ^^^^^^^^^^^^^ @@ -61,7 +61,7 @@ error: regex parse error: ( ^ error: unclosed group - --> tests/ui/regex.rs:35:56 + --> tests/ui/regex.rs:34:56 | LL | let some_binary_regex_builder = BRegexBuilder::new(OPENING_PAREN); | ^^^^^^^^^^^^^ @@ -70,7 +70,7 @@ error: regex parse error: ( ^ error: unclosed group - --> tests/ui/regex.rs:47:37 + --> tests/ui/regex.rs:46:37 | LL | let set_error = RegexSet::new(&[OPENING_PAREN, r"[a-z]+\.(com|org|net)"]); | ^^^^^^^^^^^^^ @@ -79,7 +79,7 @@ error: regex parse error: ( ^ error: unclosed group - --> tests/ui/regex.rs:48:39 + --> tests/ui/regex.rs:47:39 | LL | let bset_error = BRegexSet::new(&[OPENING_PAREN, r"[a-z]+\.(com|org|net)"]); | ^^^^^^^^^^^^^ @@ -88,7 +88,7 @@ error: regex parse error: \b\c ^^ error: unrecognized escape sequence - --> tests/ui/regex.rs:55:42 + --> tests/ui/regex.rs:54:42 | LL | let escaped_string_span = Regex::new("\\b\\c"); | ^^^^^^^^ @@ -96,19 +96,19 @@ LL | let escaped_string_span = Regex::new("\\b\\c"); = help: consider using a raw string literal: `r".."` error: regex syntax error: duplicate flag - --> tests/ui/regex.rs:57:34 + --> tests/ui/regex.rs:56:34 | LL | let aux_span = Regex::new("(?ixi)"); | ^ ^ error: regex syntax error: pattern can match invalid UTF-8 - --> tests/ui/regex.rs:63:53 + --> tests/ui/regex.rs:62:53 | LL | let invalid_utf8_should_lint = Regex::new("(?-u)."); | ^ error: trivial regex - --> tests/ui/regex.rs:68:33 + --> tests/ui/regex.rs:67:33 | LL | let trivial_eq = Regex::new("^foobar$"); | ^^^^^^^^^^ @@ -116,7 +116,7 @@ LL | let trivial_eq = Regex::new("^foobar$"); = help: consider using `==` on `str`s error: trivial regex - --> tests/ui/regex.rs:71:48 + --> tests/ui/regex.rs:70:48 | LL | let trivial_eq_builder = RegexBuilder::new("^foobar$"); | ^^^^^^^^^^ @@ -124,7 +124,7 @@ LL | let trivial_eq_builder = RegexBuilder::new("^foobar$"); = help: consider using `==` on `str`s error: trivial regex - --> tests/ui/regex.rs:74:42 + --> tests/ui/regex.rs:73:42 | LL | let trivial_starts_with = Regex::new("^foobar"); | ^^^^^^^^^ @@ -132,7 +132,7 @@ LL | let trivial_starts_with = Regex::new("^foobar"); = help: consider using `str::starts_with` error: trivial regex - --> tests/ui/regex.rs:77:40 + --> tests/ui/regex.rs:76:40 | LL | let trivial_ends_with = Regex::new("foobar$"); | ^^^^^^^^^ @@ -140,7 +140,7 @@ LL | let trivial_ends_with = Regex::new("foobar$"); = help: consider using `str::ends_with` error: trivial regex - --> tests/ui/regex.rs:80:39 + --> tests/ui/regex.rs:79:39 | LL | let trivial_contains = Regex::new("foobar"); | ^^^^^^^^ @@ -148,7 +148,7 @@ LL | let trivial_contains = Regex::new("foobar"); = help: consider using `str::contains` error: trivial regex - --> tests/ui/regex.rs:83:39 + --> tests/ui/regex.rs:82:39 | LL | let trivial_contains = Regex::new(NOT_A_REAL_REGEX); | ^^^^^^^^^^^^^^^^ @@ -156,7 +156,7 @@ LL | let trivial_contains = Regex::new(NOT_A_REAL_REGEX); = help: consider using `str::contains` error: trivial regex - --> tests/ui/regex.rs:86:40 + --> tests/ui/regex.rs:85:40 | LL | let trivial_backslash = Regex::new("a\\.b"); | ^^^^^^^ @@ -164,7 +164,7 @@ LL | let trivial_backslash = Regex::new("a\\.b"); = help: consider using `str::contains` error: trivial regex - --> tests/ui/regex.rs:90:36 + --> tests/ui/regex.rs:89:36 | LL | let trivial_empty = Regex::new(""); | ^^ @@ -172,7 +172,7 @@ LL | let trivial_empty = Regex::new(""); = help: the regex is unlikely to be useful as it is error: trivial regex - --> tests/ui/regex.rs:93:36 + --> tests/ui/regex.rs:92:36 | LL | let trivial_empty = Regex::new("^"); | ^^^ @@ -180,7 +180,7 @@ LL | let trivial_empty = Regex::new("^"); = help: the regex is unlikely to be useful as it is error: trivial regex - --> tests/ui/regex.rs:96:36 + --> tests/ui/regex.rs:95:36 | LL | let trivial_empty = Regex::new("^$"); | ^^^^ @@ -188,7 +188,7 @@ LL | let trivial_empty = Regex::new("^$"); = help: consider using `str::is_empty` error: trivial regex - --> tests/ui/regex.rs:99:44 + --> tests/ui/regex.rs:98:44 | LL | let binary_trivial_empty = BRegex::new("^$"); | ^^^^