Skip to content

Commit b83762c

Browse files
authored
Fix literal_string_with_formatting_args lint emitted when it should not (rust-lang#13953)
Fixes rust-lang#13885. Fixes rust-lang#14007. Problem was that I forgot to check whether or not the `span` was a "real" one. Because if not, then it starts pointing to pretty much only wrong content, hence the problems we saw with clippy linting on `clippy.toml`. changelog: Fix `literal_string_with_formatting_args` lint emitted when it should not r? @samueltardieu
2 parents 943d604 + 277bf08 commit b83762c

4 files changed

+110
-18
lines changed

clippy_lints/src/literal_string_with_formatting_args.rs

+12-4
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use rustc_session::declare_lint_pass;
77
use rustc_span::{BytePos, Span};
88

99
use clippy_utils::diagnostics::span_lint;
10+
use clippy_utils::is_from_proc_macro;
1011
use clippy_utils::mir::enclosing_mir;
1112

1213
declare_clippy_lint! {
@@ -79,9 +80,9 @@ fn emit_lint(cx: &LateContext<'_>, expr: &Expr<'_>, spans: &[(Span, Option<Strin
7980
}
8081
}
8182

82-
impl LateLintPass<'_> for LiteralStringWithFormattingArg {
83-
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
84-
if expr.span.from_expansion() {
83+
impl<'tcx> LateLintPass<'tcx> for LiteralStringWithFormattingArg {
84+
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
85+
if expr.span.from_expansion() || expr.span.is_dummy() {
8586
return;
8687
}
8788
if let ExprKind::Lit(lit) = expr.kind {
@@ -95,6 +96,9 @@ impl LateLintPass<'_> for LiteralStringWithFormattingArg {
9596
},
9697
_ => return,
9798
};
99+
if is_from_proc_macro(cx, expr) {
100+
return;
101+
}
98102
let fmt_str = symbol.as_str();
99103
let lo = expr.span.lo();
100104
let mut current = fmt_str;
@@ -124,7 +128,11 @@ impl LateLintPass<'_> for LiteralStringWithFormattingArg {
124128
pos.start += diff_len;
125129
pos.end += diff_len;
126130

127-
let start = fmt_str[..pos.start].rfind('{').unwrap_or(pos.start);
131+
let mut start = pos.start;
132+
while start < fmt_str.len() && !fmt_str.is_char_boundary(start) {
133+
start += 1;
134+
}
135+
let start = fmt_str[..start].rfind('{').unwrap_or(start);
128136
// If this is a unicode character escape, we don't want to lint.
129137
if start > 1 && fmt_str[..start].ends_with("\\u") {
130138
continue;

tests/ui/literal_string_with_formatting_arg.rs

+26
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,31 @@
11
#![warn(clippy::literal_string_with_formatting_args)]
22
#![allow(clippy::unnecessary_literal_unwrap)]
33

4+
// Regression test for <https://github.com/rust-lang/rust-clippy/issues/13885>.
5+
// It's not supposed to emit the lint in this case (in `assert!` expansion).
6+
fn compiler_macro() {
7+
fn parse(_: &str) -> Result<(), i32> {
8+
unimplemented!()
9+
}
10+
11+
assert!(
12+
parse(
13+
#[allow(clippy::literal_string_with_formatting_args)]
14+
"foo {:}"
15+
)
16+
.is_err()
17+
);
18+
let value = 0;
19+
assert!(format!("{value}").is_ascii());
20+
}
21+
22+
// Regression test for <https://github.com/rust-lang/rust-clippy/issues/14007>.
23+
fn regression_14007() {
24+
let s = "{и}";
25+
let ш = 12;
26+
let s = "{ш}"; //~ literal_string_with_formatting_args
27+
}
28+
429
fn main() {
530
let x: Option<usize> = None;
631
let y = "hello";
@@ -13,6 +38,7 @@ fn main() {
1338
x.expect(r"{y:?} {y:?} "); //~ literal_string_with_formatting_args
1439
x.expect(r"{y:?} y:?}"); //~ literal_string_with_formatting_args
1540
x.expect(r##" {y:?} {y:?} "##); //~ literal_string_with_formatting_args
41+
assert!("{y}".is_ascii()); //~ literal_string_with_formatting_args
1642
// Ensure that it doesn't try to go in the middle of a unicode character.
1743
x.expect("———{:?}"); //~ literal_string_with_formatting_args
1844

Original file line numberDiff line numberDiff line change
@@ -1,71 +1,83 @@
11
error: this looks like a formatting argument but it is not part of a formatting macro
2-
--> tests/ui/literal_string_with_formatting_arg.rs:7:15
2+
--> tests/ui/literal_string_with_formatting_arg.rs:26:14
33
|
4-
LL | x.expect("{y} {}");
5-
| ^^^
4+
LL | let s = "{ш}";
5+
| ^^^
66
|
77
= note: `-D clippy::literal-string-with-formatting-args` implied by `-D warnings`
88
= help: to override `-D warnings` add `#[allow(clippy::literal_string_with_formatting_args)]`
99

1010
error: this looks like a formatting argument but it is not part of a formatting macro
11-
--> tests/ui/literal_string_with_formatting_arg.rs:8:16
11+
--> tests/ui/literal_string_with_formatting_arg.rs:32:15
12+
|
13+
LL | x.expect("{y} {}");
14+
| ^^^
15+
16+
error: this looks like a formatting argument but it is not part of a formatting macro
17+
--> tests/ui/literal_string_with_formatting_arg.rs:33:16
1218
|
1319
LL | x.expect(" {y} bla");
1420
| ^^^
1521

1622
error: this looks like a formatting argument but it is not part of a formatting macro
17-
--> tests/ui/literal_string_with_formatting_arg.rs:9:15
23+
--> tests/ui/literal_string_with_formatting_arg.rs:34:15
1824
|
1925
LL | x.expect("{:?}");
2026
| ^^^^
2127

2228
error: this looks like a formatting argument but it is not part of a formatting macro
23-
--> tests/ui/literal_string_with_formatting_arg.rs:10:15
29+
--> tests/ui/literal_string_with_formatting_arg.rs:35:15
2430
|
2531
LL | x.expect("{y:?}");
2632
| ^^^^^
2733

2834
error: these look like formatting arguments but are not part of a formatting macro
29-
--> tests/ui/literal_string_with_formatting_arg.rs:11:16
35+
--> tests/ui/literal_string_with_formatting_arg.rs:36:16
3036
|
3137
LL | x.expect(" {y:?} {y:?} ");
3238
| ^^^^^ ^^^^^
3339

3440
error: this looks like a formatting argument but it is not part of a formatting macro
35-
--> tests/ui/literal_string_with_formatting_arg.rs:12:23
41+
--> tests/ui/literal_string_with_formatting_arg.rs:37:23
3642
|
3743
LL | x.expect(" {y:..} {y:?} ");
3844
| ^^^^^
3945

4046
error: these look like formatting arguments but are not part of a formatting macro
41-
--> tests/ui/literal_string_with_formatting_arg.rs:13:16
47+
--> tests/ui/literal_string_with_formatting_arg.rs:38:16
4248
|
4349
LL | x.expect(r"{y:?} {y:?} ");
4450
| ^^^^^ ^^^^^
4551

4652
error: this looks like a formatting argument but it is not part of a formatting macro
47-
--> tests/ui/literal_string_with_formatting_arg.rs:14:16
53+
--> tests/ui/literal_string_with_formatting_arg.rs:39:16
4854
|
4955
LL | x.expect(r"{y:?} y:?}");
5056
| ^^^^^
5157

5258
error: these look like formatting arguments but are not part of a formatting macro
53-
--> tests/ui/literal_string_with_formatting_arg.rs:15:19
59+
--> tests/ui/literal_string_with_formatting_arg.rs:40:19
5460
|
5561
LL | x.expect(r##" {y:?} {y:?} "##);
5662
| ^^^^^ ^^^^^
5763

5864
error: this looks like a formatting argument but it is not part of a formatting macro
59-
--> tests/ui/literal_string_with_formatting_arg.rs:17:18
65+
--> tests/ui/literal_string_with_formatting_arg.rs:41:14
66+
|
67+
LL | assert!("{y}".is_ascii());
68+
| ^^^
69+
70+
error: this looks like a formatting argument but it is not part of a formatting macro
71+
--> tests/ui/literal_string_with_formatting_arg.rs:43:18
6072
|
6173
LL | x.expect("———{:?}");
6274
| ^^^^
6375

6476
error: this looks like a formatting argument but it is not part of a formatting macro
65-
--> tests/ui/literal_string_with_formatting_arg.rs:27:19
77+
--> tests/ui/literal_string_with_formatting_arg.rs:53:19
6678
|
6779
LL | x.expect(r##" {x:?} "##); // `x` doesn't exist so we shoud not lint
6880
| ^^^^^
6981

70-
error: aborting due to 11 previous errors
82+
error: aborting due to 13 previous errors
7183

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// Regression test for <https://github.com/rust-lang/rust-clippy/issues/13885>.
2+
// The `dbg` macro generates a literal with the name of the current file, so
3+
// we need to ensure the lint is not emitted in this case.
4+
5+
// Clippy sets `-Zflatten_format_args=no`, which changes the default behavior of how format args
6+
// are lowered and only that one has this non-macro span. Adding the flag makes it repro on
7+
// godbolt and shows a root context span for the file name string.
8+
//
9+
// So instead of having:
10+
//
11+
// ```
12+
// Lit(
13+
// Spanned {
14+
// node: Str(
15+
// "[/app/example.rs:2:5] \"something\" = ",
16+
// Cooked,
17+
// ),
18+
// span: /rustc/eb54a50837ad4bcc9842924f27e7287ca66e294c/library/std/src/macros.rs:365:35: 365:58 (#4),
19+
// },
20+
// ),
21+
// ```
22+
//
23+
// We get:
24+
//
25+
// ```
26+
// Lit(
27+
// Spanned {
28+
// node: Str(
29+
// "/app/example.rs",
30+
// Cooked,
31+
// ),
32+
// span: /app/example.rs:2:5: 2:22 (#0),
33+
// },
34+
// )
35+
// ```
36+
37+
#![crate_name = "foo"]
38+
#![allow(unused)]
39+
#![warn(clippy::literal_string_with_formatting_args)]
40+
41+
fn another_bad() {
42+
let literal_string_with_formatting_args = 0;
43+
dbg!("something");
44+
}
45+
46+
fn main() {}

0 commit comments

Comments
 (0)