-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Migrate format_args.rs
to rustc_ast::FormatArgs
#10484
Conversation
} | ||
|
||
false | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a slightly simplified version of
rust-clippy/clippy_utils/src/macros.rs
Lines 932 to 992 in f19db28
/// Gets the spans of the commas inbetween the format string and explicit args, not including | |
/// any trailing comma | |
/// | |
/// ```ignore | |
/// format!("{} {}", a, b) | |
/// // ^ ^ | |
/// ``` | |
/// | |
/// Ensures that the format string and values aren't coming from a proc macro that sets the | |
/// output span to that of its input | |
fn comma_spans(cx: &LateContext<'_>, explicit_values: &[&Expr<'_>], fmt_span: Span) -> Option<Vec<Span>> { | |
// `format!("{} {} {c}", "one", "two", c = "three")` | |
// ^^^^^ ^^^^^ ^^^^^^^ | |
let value_spans = explicit_values | |
.iter() | |
.map(|val| hygiene::walk_chain(val.span, fmt_span.ctxt())); | |
// `format!("{} {} {c}", "one", "two", c = "three")` | |
// ^^ ^^ ^^^^^^ | |
let between_spans = once(fmt_span) | |
.chain(value_spans) | |
.tuple_windows() | |
.map(|(start, end)| start.between(end)); | |
let mut comma_spans = Vec::new(); | |
for between_span in between_spans { | |
let mut offset = 0; | |
let mut seen_comma = false; | |
for token in tokenize(&snippet_opt(cx, between_span)?) { | |
match token.kind { | |
TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace => {}, | |
TokenKind::Comma if !seen_comma => { | |
seen_comma = true; | |
let base = between_span.data(); | |
comma_spans.push(Span::new( | |
base.lo + BytePos(offset), | |
base.lo + BytePos(offset + 1), | |
base.ctxt, | |
base.parent, | |
)); | |
}, | |
// named arguments, `start_val, name = end_val` | |
// ^^^^^^^^^ between_span | |
TokenKind::Ident | TokenKind::Eq if seen_comma => {}, | |
// An unexpected token usually indicates the format string or a value came from a proc macro output | |
// that sets the span of its output to an input, e.g. `println!(some_proc_macro!("input"), ..)` that | |
// emits a string literal with the span set to that of `"input"` | |
_ => return None, | |
} | |
offset += token.len; | |
} | |
if !seen_comma { | |
return None; | |
} | |
} | |
Some(comma_spans) | |
} |
error: variables can be used directly in the `format!` string | ||
--> $DIR/uninlined_format_args.rs:125:20 | ||
| | ||
LL | println!("{}", format!("{}", local_i32)); | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^ | ||
| | ||
help: change this to | ||
| | ||
LL - println!("{}", format!("{}", local_i32)); | ||
LL + println!("{}", format!("{local_i32}")); | ||
| | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a bug with root_macro_call_first_node
, not sure why but it doesn't pick up that format!()
as a macro call
@@ -9,6 +9,7 @@ keywords = ["clippy", "lint", "plugin"] | |||
edition = "2021" | |||
|
|||
[dependencies] | |||
arrayvec = { version = "0.7", default-features = false } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
clippy_utils
depends on this already so it's not a new dependency
We should probably just move that warning to rustc, since an empty |
I tried to apply a similar change to explicit_write.rs , and if let ExprKind::MethodCall(unwrap_fun, write_call, [], _) = expr.kind
&& unwrap_fun.ident.name == sym::unwrap
// match call to write_fmt
&& let ExprKind::MethodCall(write_fun, write_recv, [write_arg], _) = look_in_block(cx, &write_call.kind)
&& let ExprKind::Call(_, write_arg2) = look_in_block(cx, &write_arg.kind)
&& write_fun.ident.name == sym!(write_fmt)
// match calls to std::io::stdout() / std::io::stderr ()
&& let Some(dest_name) = if match_function_call(cx, write_recv, &paths::STDOUT).is_some() {
Some("stdout")
} else if match_function_call(cx, write_recv, &paths::STDERR).is_some() {
Some("stderr")
} else {
None
}
&& let Some(format_args) = root_macro_call_first_node(cx, write_call)
&& is_format_macro(cx, format_args.def_id)
// && let Some(format_args2) = FormatArgsExpn::find_nested(cx, expr, format_args.expn) |
The replacement would be You wouldn't need |
7b94c41
to
066949c
Compare
No longer lints empty precisions `{:.}` as the spans aren't available
066949c
to
3259b48
Compare
collect_ast_format_args(expr.span, args); | ||
} | ||
} | ||
} | ||
|
||
/// Detects if the format string or an argument has its span set by a proc macro to something inside |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
praise: this is neat
@bors r+ |
☀️ Test successful - checks-action_dev_test, checks-action_remark_test, checks-action_test |
changelog: none
Part of #10233
Empty precision specifiers are no longer linted as the span for that isn't present in
FormatOptions
That could be fixed later with some hackery or a change upstream
r? @flip1995