2023-08-28 18:37:03 +02:00
|
|
|
use parse::Position::ArgumentNamed;
|
2020-02-29 20:37:32 +03:00
|
|
|
use rustc_ast::ptr::P;
|
|
|
|
use rustc_ast::tokenstream::TokenStream;
|
2023-01-11 21:41:13 +01:00
|
|
|
use rustc_ast::{
|
|
|
|
Expr, ExprKind, FormatAlignment, FormatArgPosition, FormatArgPositionKind, FormatArgs,
|
|
|
|
FormatArgsPiece, FormatArgument, FormatArgumentKind, FormatArguments, FormatCount,
|
2024-05-09 18:44:40 +10:00
|
|
|
FormatDebugHex, FormatOptions, FormatPlaceholder, FormatSign, FormatTrait, Recovered, StmtKind,
|
2024-09-22 19:05:04 -04:00
|
|
|
token,
|
2023-01-11 21:41:13 +01:00
|
|
|
};
|
2022-08-08 16:17:08 +02:00
|
|
|
use rustc_data_structures::fx::FxHashSet;
|
2024-02-23 10:20:45 +11:00
|
|
|
use rustc_errors::{Applicability, Diag, MultiSpan, PResult, SingleLabelManySpans};
|
2024-03-12 10:55:17 +08:00
|
|
|
use rustc_expand::base::*;
|
2024-04-26 08:44:23 +10:00
|
|
|
use rustc_lint_defs::builtin::NAMED_ARGUMENTS_USED_POSITIONALLY;
|
|
|
|
use rustc_lint_defs::{BufferedEarlyLint, BuiltinLintDiag, LintId};
|
2020-06-02 20:19:49 +03:00
|
|
|
use rustc_parse_format as parse;
|
2022-08-08 16:17:08 +02:00
|
|
|
use rustc_span::symbol::{Ident, Symbol};
|
2024-02-25 22:22:11 +01:00
|
|
|
use rustc_span::{BytePos, ErrorGuaranteed, InnerSpan, Span};
|
2014-02-19 19:29:58 -08:00
|
|
|
|
2024-04-26 08:44:23 +10:00
|
|
|
use crate::errors;
|
|
|
|
use crate::util::expr_to_spanned_string;
|
2024-07-29 08:13:50 +10:00
|
|
|
|
2022-08-08 16:17:08 +02:00
|
|
|
// The format_args!() macro is expanded in three steps:
|
|
|
|
// 1. First, `parse_args` will parse the `(literal, arg, arg, name=arg, name=arg)` syntax,
|
|
|
|
// but doesn't parse the template (the literal) itself.
|
|
|
|
// 2. Second, `make_format_args` will parse the template, the format options, resolve argument references,
|
2023-01-12 00:45:29 +01:00
|
|
|
// produce diagnostics, and turn the whole thing into a `FormatArgs` AST node.
|
|
|
|
// 3. Much later, in AST lowering (rustc_ast_lowering), that `FormatArgs` structure will be turned
|
|
|
|
// into the expression of type `core::fmt::Arguments`.
|
2022-08-08 16:17:08 +02:00
|
|
|
|
2023-01-12 00:45:29 +01:00
|
|
|
// See rustc_ast/src/format.rs for the FormatArgs structure and glossary.
|
2022-08-08 16:17:08 +02:00
|
|
|
|
|
|
|
// Only used in parse_args and report_invalid_references,
|
|
|
|
// to indicate how a referred argument was used.
|
|
|
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
|
|
enum PositionUsedAs {
|
|
|
|
Placeholder(Option<Span>),
|
|
|
|
Precision,
|
|
|
|
Width,
|
2022-08-02 20:33:40 +09:00
|
|
|
}
|
2022-08-08 16:17:08 +02:00
|
|
|
use PositionUsedAs::*;
|
2022-08-02 20:33:40 +09:00
|
|
|
|
2024-03-12 10:55:17 +08:00
|
|
|
#[derive(Debug)]
|
2023-01-05 20:17:30 +01:00
|
|
|
struct MacroInput {
|
|
|
|
fmtstr: P<Expr>,
|
|
|
|
args: FormatArguments,
|
|
|
|
/// Whether the first argument was a string literal or a result from eager macro expansion.
|
2023-04-10 22:02:52 +02:00
|
|
|
/// If it's not a string literal, we disallow implicit argument capturing.
|
2023-01-05 20:17:30 +01:00
|
|
|
///
|
|
|
|
/// This does not correspond to whether we can treat spans to the literal normally, as the whole
|
|
|
|
/// invocation might be the result of another macro expansion, in which case this flag may still be true.
|
|
|
|
///
|
|
|
|
/// See [RFC 2795] for more information.
|
|
|
|
///
|
|
|
|
/// [RFC 2795]: https://rust-lang.github.io/rfcs/2795-format-args-implicit-identifiers.html#macro-hygiene
|
|
|
|
is_direct_literal: bool,
|
|
|
|
}
|
|
|
|
|
2018-12-04 14:10:32 -05:00
|
|
|
/// Parses the arguments from the given list of tokens, returning the diagnostic
|
2014-02-06 08:50:36 +11:00
|
|
|
/// if there's a parse error so we can continue parsing other format!
|
|
|
|
/// expressions.
|
|
|
|
///
|
2014-12-21 11:28:18 +02:00
|
|
|
/// If parsing succeeds, the return value is:
|
2017-06-20 15:15:16 +08:00
|
|
|
///
|
|
|
|
/// ```text
|
2022-09-06 23:15:13 +02:00
|
|
|
/// Ok((fmtstr, parsed arguments))
|
2015-11-03 16:34:11 +00:00
|
|
|
/// ```
|
compiler: fix few needless_pass_by_ref_mut clippy lints
warning: this argument is a mutable reference, but not used mutably
--> compiler\rustc_builtin_macros\src\asm.rs:306:28
|
306 | fn err_duplicate_option(p: &mut Parser<'_>, symbol: Symbol, span: Span) {
| ^^^^^^^^^^^^^^^ help: consider changing to: `&Parser<'_>`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut
warning: this argument is a mutable reference, but not used mutably
--> compiler\rustc_builtin_macros\src\asm.rs:318:8
|
318 | p: &mut Parser<'a>,
| ^^^^^^^^^^^^^^^ help: consider changing to: `&Parser<'a>`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut
warning: this argument is a mutable reference, but not used mutably
--> compiler\rustc_builtin_macros\src\assert.rs:114:25
|
114 | fn parse_assert<'a>(cx: &mut ExtCtxt<'a>, sp: Span, stream: TokenStream) -> PResult<'a, Assert> {
| ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'a>`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut
warning: this argument is a mutable reference, but not used mutably
--> compiler\rustc_builtin_macros\src\asm.rs:32:10
|
32 | ecx: &mut ExtCtxt<'a>,
| ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'a>`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut
warning: this argument is a mutable reference, but not used mutably
--> compiler\rustc_builtin_macros\src\test.rs:99:9
|
99 | cx: &mut ExtCtxt<'_>,
| ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'_>`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut
warning: this argument is a mutable reference, but not used mutably
--> compiler\rustc_builtin_macros\src\source_util.rs:237:9
|
237 | cx: &mut ExtCtxt<'_>,
| ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'_>`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut
warning: this argument is a mutable reference, but not used mutably
--> compiler\rustc_builtin_macros\src\format.rs:809:10
|
809 | ecx: &mut ExtCtxt<'_>,
| ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'_>`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut
warning: this argument is a mutable reference, but not used mutably
--> compiler\rustc_builtin_macros\src\format.rs:737:10
|
737 | ecx: &mut ExtCtxt<'a>,
| ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'a>`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut
warning: this argument is a mutable reference, but not used mutably
--> compiler\rustc_builtin_macros\src\format.rs:68:24
|
68 | fn parse_args<'a>(ecx: &mut ExtCtxt<'a>, sp: Span, tts: TokenStream) -> PResult<'a, MacroInput> {
| ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'a>`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut
warning: this argument is a mutable reference, but not used mutably
--> compiler\rustc_builtin_macros\src\format.rs:607:10
|
607 | ecx: &mut ExtCtxt<'_>,
| ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'_>`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut
warning: this argument is a mutable reference, but not used mutably
--> compiler\rustc_builtin_macros\src\edition_panic.rs:43:9
|
43 | cx: &'cx mut ExtCtxt<'_>,
| ^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'_>`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut
warning: this argument is a mutable reference, but not used mutably
--> compiler\rustc_builtin_macros\src\concat_bytes.rs:11:9
|
11 | cx: &mut ExtCtxt<'_>,
| ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'_>`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut
warning: this argument is a mutable reference, but not used mutably
--> compiler\rustc_builtin_macros\src\cfg.rs:38:22
|
38 | fn parse_cfg<'a>(cx: &mut ExtCtxt<'a>, span: Span, tts: TokenStream) -> PResult<'a, ast::MetaItem> {
| ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'a>`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut
warning: this argument is a mutable reference, but not used mutably
--> compiler\rustc_builtin_macros\src\cfg_accessible.rs:13:28
|
13 | fn validate_input<'a>(ecx: &mut ExtCtxt<'_>, mi: &'a ast::MetaItem) -> Option<&'a ast::Path> {
| ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'_>`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut
2024-03-28 12:04:00 +03:00
|
|
|
fn parse_args<'a>(ecx: &ExtCtxt<'a>, sp: Span, tts: TokenStream) -> PResult<'a, MacroInput> {
|
2022-09-06 23:15:13 +02:00
|
|
|
let mut args = FormatArguments::new();
|
2014-02-06 08:50:36 +11:00
|
|
|
|
2014-07-03 11:42:24 +02:00
|
|
|
let mut p = ecx.new_parser_from_tts(tts);
|
2013-08-20 00:40:27 -07:00
|
|
|
|
2014-10-27 19:22:52 +11:00
|
|
|
if p.token == token::Eof {
|
2023-12-18 20:54:03 +11:00
|
|
|
return Err(ecx.dcx().create_err(errors::FormatRequiresString { span: sp }));
|
2014-02-06 08:50:36 +11:00
|
|
|
}
|
2018-08-09 09:58:36 +02:00
|
|
|
|
2020-08-28 23:04:42 +02:00
|
|
|
let first_token = &p.token;
|
2023-01-05 20:17:30 +01:00
|
|
|
|
|
|
|
let fmtstr = if let token::Literal(lit) = first_token.kind
|
|
|
|
&& matches!(lit.kind, token::Str | token::StrRaw(_))
|
|
|
|
{
|
|
|
|
// This allows us to properly handle cases when the first comma
|
|
|
|
// after the format string is mistakenly replaced with any operator,
|
|
|
|
// which cause the expression parser to eat too much tokens.
|
|
|
|
p.parse_literal_maybe_minus()?
|
|
|
|
} else {
|
|
|
|
// Otherwise, we fall back to the expression parser.
|
|
|
|
p.parse_expr()?
|
2020-08-28 23:04:42 +02:00
|
|
|
};
|
|
|
|
|
2023-01-05 20:17:30 +01:00
|
|
|
// Only allow implicit captures to be used when the argument is a direct literal
|
|
|
|
// instead of a macro expanding to one.
|
|
|
|
let is_direct_literal = matches!(fmtstr.kind, ExprKind::Lit(_));
|
|
|
|
|
2019-09-01 15:09:05 -07:00
|
|
|
let mut first = true;
|
2018-08-09 09:58:36 +02:00
|
|
|
|
2014-10-27 19:22:52 +11:00
|
|
|
while p.token != token::Eof {
|
2015-12-31 12:11:53 +13:00
|
|
|
if !p.eat(&token::Comma) {
|
2019-09-01 15:09:05 -07:00
|
|
|
if first {
|
2020-08-31 11:45:50 +02:00
|
|
|
p.clear_expected_tokens();
|
|
|
|
}
|
|
|
|
|
2021-09-10 22:33:44 +02:00
|
|
|
match p.expect(&token::Comma) {
|
Make `DiagnosticBuilder::emit` consuming.
This works for most of its call sites. This is nice, because `emit` very
much makes sense as a consuming operation -- indeed,
`DiagnosticBuilderState` exists to ensure no diagnostic is emitted
twice, but it uses runtime checks.
For the small number of call sites where a consuming emit doesn't work,
the commit adds `DiagnosticBuilder::emit_without_consuming`. (This will
be removed in subsequent commits.)
Likewise, `emit_unless` becomes consuming. And `delay_as_bug` becomes
consuming, while `delay_as_bug_without_consuming` is added (which will
also be removed in subsequent commits.)
All this requires significant changes to `DiagnosticBuilder`'s chaining
methods. Currently `DiagnosticBuilder` method chaining uses a
non-consuming `&mut self -> &mut Self` style, which allows chaining to
be used when the chain ends in `emit()`, like so:
```
struct_err(msg).span(span).emit();
```
But it doesn't work when producing a `DiagnosticBuilder` value,
requiring this:
```
let mut err = self.struct_err(msg);
err.span(span);
err
```
This style of chaining won't work with consuming `emit` though. For
that, we need to use to a `self -> Self` style. That also would allow
`DiagnosticBuilder` production to be chained, e.g.:
```
self.struct_err(msg).span(span)
```
However, removing the `&mut self -> &mut Self` style would require that
individual modifications of a `DiagnosticBuilder` go from this:
```
err.span(span);
```
to this:
```
err = err.span(span);
```
There are *many* such places. I have a high tolerance for tedious
refactorings, but even I gave up after a long time trying to convert
them all.
Instead, this commit has it both ways: the existing `&mut self -> Self`
chaining methods are kept, and new `self -> Self` chaining methods are
added, all of which have a `_mv` suffix (short for "move"). Changes to
the existing `forward!` macro lets this happen with very little
additional boilerplate code. I chose to add the suffix to the new
chaining methods rather than the existing ones, because the number of
changes required is much smaller that way.
This doubled chainging is a bit clumsy, but I think it is worthwhile
because it allows a *lot* of good things to subsequently happen. In this
commit, there are many `mut` qualifiers removed in places where
diagnostics are emitted without being modified. In subsequent commits:
- chaining can be used more, making the code more concise;
- more use of chaining also permits the removal of redundant diagnostic
APIs like `struct_err_with_code`, which can be replaced easily with
`struct_err` + `code_mv`;
- `emit_without_diagnostic` can be removed, which simplifies a lot of
machinery, removing the need for `DiagnosticBuilderState`.
2024-01-03 12:17:35 +11:00
|
|
|
Err(err) => {
|
2021-09-10 22:33:44 +02:00
|
|
|
match token::TokenKind::Comma.similar_tokens() {
|
|
|
|
Some(tks) if tks.contains(&p.token.kind) => {
|
|
|
|
// If a similar token is found, then it may be a typo. We
|
|
|
|
// consider it as a comma, and continue parsing.
|
|
|
|
err.emit();
|
|
|
|
p.bump();
|
|
|
|
}
|
|
|
|
// Otherwise stop the parsing and return the error.
|
|
|
|
_ => return Err(err),
|
|
|
|
}
|
|
|
|
}
|
2024-05-09 18:44:40 +10:00
|
|
|
Ok(Recovered::Yes(_)) => (),
|
2024-02-13 23:44:33 +00:00
|
|
|
Ok(Recovered::No) => unreachable!(),
|
2019-09-01 15:09:05 -07:00
|
|
|
}
|
2013-07-29 01:12:41 -07:00
|
|
|
}
|
2019-09-01 15:09:05 -07:00
|
|
|
first = false;
|
2016-06-06 20:22:48 +05:30
|
|
|
if p.token == token::Eof {
|
|
|
|
break;
|
|
|
|
} // accept trailing commas
|
2020-03-04 23:37:52 +03:00
|
|
|
match p.token.ident() {
|
|
|
|
Some((ident, _)) if p.look_ahead(1, |t| *t == token::Eq) => {
|
2018-12-14 10:43:10 +01:00
|
|
|
p.bump();
|
2020-03-04 23:37:52 +03:00
|
|
|
p.expect(&token::Eq)?;
|
2022-09-06 23:15:13 +02:00
|
|
|
let expr = p.parse_expr()?;
|
|
|
|
if let Some((_, prev)) = args.by_name(ident.name) {
|
2023-12-18 20:54:03 +11:00
|
|
|
ecx.dcx().emit_err(errors::FormatDuplicateArg {
|
2023-04-08 20:37:41 +01:00
|
|
|
span: ident.span,
|
|
|
|
prev: prev.kind.ident().unwrap().span,
|
|
|
|
duplicate: ident.span,
|
|
|
|
ident,
|
|
|
|
});
|
2020-03-04 23:37:52 +03:00
|
|
|
continue;
|
|
|
|
}
|
2022-09-06 23:15:13 +02:00
|
|
|
args.add(FormatArgument { kind: FormatArgumentKind::Named(ident), expr });
|
2020-03-04 23:37:52 +03:00
|
|
|
}
|
|
|
|
_ => {
|
2022-09-06 23:15:13 +02:00
|
|
|
let expr = p.parse_expr()?;
|
|
|
|
if !args.named_args().is_empty() {
|
2024-01-25 17:12:09 +00:00
|
|
|
return Err(ecx.dcx().create_err(errors::PositionalAfterNamed {
|
2023-04-08 20:37:41 +01:00
|
|
|
span: expr.span,
|
|
|
|
args: args
|
|
|
|
.named_args()
|
|
|
|
.iter()
|
2023-04-15 20:18:30 +02:00
|
|
|
.filter_map(|a| a.kind.ident().map(|ident| (a, ident)))
|
2023-04-08 20:37:41 +01:00
|
|
|
.map(|(arg, n)| n.span.to(arg.expr.span))
|
|
|
|
.collect(),
|
2024-01-25 17:12:09 +00:00
|
|
|
}));
|
2019-07-15 20:51:32 -07:00
|
|
|
}
|
2022-09-06 23:15:13 +02:00
|
|
|
args.add(FormatArgument { kind: FormatArgumentKind::Normal, expr });
|
2019-07-15 20:51:32 -07:00
|
|
|
}
|
2013-07-29 01:12:41 -07:00
|
|
|
}
|
|
|
|
}
|
2023-01-05 20:17:30 +01:00
|
|
|
Ok(MacroInput { fmtstr, args, is_direct_literal })
|
2014-02-06 08:50:36 +11:00
|
|
|
}
|
2013-07-29 01:12:41 -07:00
|
|
|
|
2023-01-05 20:17:30 +01:00
|
|
|
fn make_format_args(
|
2019-07-15 20:51:32 -07:00
|
|
|
ecx: &mut ExtCtxt<'_>,
|
2023-01-05 20:17:30 +01:00
|
|
|
input: MacroInput,
|
2019-07-15 20:51:32 -07:00
|
|
|
append_newline: bool,
|
2024-03-12 10:55:17 +08:00
|
|
|
) -> ExpandResult<Result<FormatArgs, ErrorGuaranteed>, ()> {
|
2018-07-14 20:50:30 -07:00
|
|
|
let msg = "format argument must be a string literal";
|
2023-01-05 20:17:30 +01:00
|
|
|
let unexpanded_fmt_span = input.fmtstr.span;
|
|
|
|
|
|
|
|
let MacroInput { fmtstr: efmt, mut args, is_direct_literal } = input;
|
|
|
|
|
2024-03-12 10:55:17 +08:00
|
|
|
let (fmt_str, fmt_style, fmt_span) = {
|
|
|
|
let ExpandResult::Ready(mac) = expr_to_spanned_string(ecx, efmt.clone(), msg) else {
|
|
|
|
return ExpandResult::Retry(());
|
|
|
|
};
|
|
|
|
match mac {
|
|
|
|
Ok(mut fmt) if append_newline => {
|
|
|
|
fmt.0 = Symbol::intern(&format!("{}\n", fmt.0));
|
|
|
|
fmt
|
|
|
|
}
|
|
|
|
Ok(fmt) => fmt,
|
|
|
|
Err(err) => {
|
|
|
|
let guar = match err {
|
|
|
|
Ok((mut err, suggested)) => {
|
|
|
|
if !suggested {
|
|
|
|
if let ExprKind::Block(block, None) = &efmt.kind
|
2024-08-07 12:41:49 +02:00
|
|
|
&& let [stmt] = block.stmts.as_slice()
|
|
|
|
&& let StmtKind::Expr(expr) = &stmt.kind
|
2024-03-12 10:55:17 +08:00
|
|
|
&& let ExprKind::Path(None, path) = &expr.kind
|
|
|
|
&& path.is_potential_trivial_const_arg()
|
|
|
|
{
|
|
|
|
err.multipart_suggestion(
|
|
|
|
"quote your inlined format argument to use as string literal",
|
|
|
|
vec![
|
|
|
|
(unexpanded_fmt_span.shrink_to_hi(), "\"".to_string()),
|
|
|
|
(unexpanded_fmt_span.shrink_to_lo(), "\"".to_string()),
|
|
|
|
],
|
|
|
|
Applicability::MaybeIncorrect,
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
let sugg_fmt = match args.explicit_args().len() {
|
|
|
|
0 => "{}".to_string(),
|
2024-08-07 12:41:49 +02:00
|
|
|
count => {
|
|
|
|
format!("{}{{}}", "{} ".repeat(count))
|
2024-03-12 10:55:17 +08:00
|
|
|
}
|
|
|
|
};
|
|
|
|
err.span_suggestion(
|
|
|
|
unexpanded_fmt_span.shrink_to_lo(),
|
|
|
|
"you might be missing a string literal to format with",
|
|
|
|
format!("\"{sugg_fmt}\", "),
|
|
|
|
Applicability::MaybeIncorrect,
|
|
|
|
);
|
|
|
|
}
|
2024-02-25 22:22:11 +01:00
|
|
|
}
|
2024-03-12 10:55:17 +08:00
|
|
|
err.emit()
|
2023-08-05 17:23:33 +08:00
|
|
|
}
|
2024-03-12 10:55:17 +08:00
|
|
|
Err(guar) => guar,
|
|
|
|
};
|
|
|
|
return ExpandResult::Ready(Err(guar));
|
|
|
|
}
|
2018-07-23 15:09:00 -07:00
|
|
|
}
|
2016-09-02 22:01:35 +00:00
|
|
|
};
|
2018-08-08 18:59:59 +02:00
|
|
|
|
2019-08-15 01:56:44 +03:00
|
|
|
let str_style = match fmt_style {
|
2022-08-26 19:13:17 +02:00
|
|
|
rustc_ast::StrStyle::Cooked => None,
|
|
|
|
rustc_ast::StrStyle::Raw(raw) => Some(raw as usize),
|
2018-08-08 18:59:59 +02:00
|
|
|
};
|
|
|
|
|
2021-12-15 14:39:23 +11:00
|
|
|
let fmt_str = fmt_str.as_str(); // for the suggestions below
|
2022-09-30 16:44:39 +01:00
|
|
|
let fmt_snippet = ecx.source_map().span_to_snippet(unexpanded_fmt_span).ok();
|
2020-02-12 15:47:43 +00:00
|
|
|
let mut parser = parse::Parser::new(
|
|
|
|
fmt_str,
|
|
|
|
str_style,
|
|
|
|
fmt_snippet,
|
|
|
|
append_newline,
|
|
|
|
parse::ParseMode::Format,
|
|
|
|
);
|
2018-08-08 18:59:59 +02:00
|
|
|
|
2022-08-08 16:17:08 +02:00
|
|
|
let mut pieces = Vec::new();
|
2018-08-08 18:59:59 +02:00
|
|
|
while let Some(piece) = parser.next() {
|
|
|
|
if !parser.errors.is_empty() {
|
|
|
|
break;
|
|
|
|
} else {
|
2022-08-08 16:17:08 +02:00
|
|
|
pieces.push(piece);
|
2018-08-08 18:59:59 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-05 20:17:30 +01:00
|
|
|
let is_source_literal = parser.is_source_literal;
|
2022-08-08 16:17:08 +02:00
|
|
|
|
2018-08-08 18:59:59 +02:00
|
|
|
if !parser.errors.is_empty() {
|
|
|
|
let err = parser.errors.remove(0);
|
2023-01-05 20:17:30 +01:00
|
|
|
let sp = if is_source_literal {
|
2022-04-29 18:48:58 +02:00
|
|
|
fmt_span.from_inner(InnerSpan::new(err.span.start, err.span.end))
|
2021-06-07 17:42:42 +02:00
|
|
|
} else {
|
|
|
|
// The format string could be another macro invocation, e.g.:
|
|
|
|
// format!(concat!("abc", "{}"), 4);
|
|
|
|
// However, `err.span` is an inner span relative to the *result* of
|
|
|
|
// the macro invocation, which is why we would get a nonsensical
|
|
|
|
// result calling `fmt_span.from_inner(err.span)` as above, and
|
|
|
|
// might even end up inside a multibyte character (issue #86085).
|
|
|
|
// Therefore, we conservatively report the error for the entire
|
|
|
|
// argument span here.
|
|
|
|
fmt_span
|
|
|
|
};
|
2023-04-08 20:37:41 +01:00
|
|
|
let mut e = errors::InvalidFormatString {
|
|
|
|
span: sp,
|
|
|
|
note_: None,
|
|
|
|
label_: None,
|
|
|
|
sugg_: None,
|
|
|
|
desc: err.description,
|
|
|
|
label1: err.label,
|
|
|
|
};
|
2018-08-08 18:59:59 +02:00
|
|
|
if let Some(note) = err.note {
|
2023-04-08 20:37:41 +01:00
|
|
|
e.note_ = Some(errors::InvalidFormatStringNote { note });
|
2018-08-08 18:59:59 +02:00
|
|
|
}
|
2023-01-05 20:17:30 +01:00
|
|
|
if let Some((label, span)) = err.secondary_label
|
|
|
|
&& is_source_literal
|
|
|
|
{
|
2023-04-08 20:37:41 +01:00
|
|
|
e.label_ = Some(errors::InvalidFormatStringLabel {
|
|
|
|
span: fmt_span.from_inner(InnerSpan::new(span.start, span.end)),
|
|
|
|
label,
|
|
|
|
});
|
2018-12-20 22:33:16 -08:00
|
|
|
}
|
2023-09-06 16:32:06 +02:00
|
|
|
match err.suggestion {
|
|
|
|
parse::Suggestion::None => {}
|
|
|
|
parse::Suggestion::UsePositional => {
|
|
|
|
let captured_arg_span =
|
|
|
|
fmt_span.from_inner(InnerSpan::new(err.span.start, err.span.end));
|
|
|
|
if let Ok(arg) = ecx.source_map().span_to_snippet(captured_arg_span) {
|
|
|
|
let span = match args.unnamed_args().last() {
|
|
|
|
Some(arg) => arg.expr.span,
|
|
|
|
None => fmt_span,
|
|
|
|
};
|
|
|
|
e.sugg_ = Some(errors::InvalidFormatStringSuggestion::UsePositional {
|
|
|
|
captured: captured_arg_span,
|
|
|
|
len: args.unnamed_args().len().to_string(),
|
|
|
|
span: span.shrink_to_hi(),
|
|
|
|
arg,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
parse::Suggestion::RemoveRawIdent(span) => {
|
|
|
|
if is_source_literal {
|
|
|
|
let span = fmt_span.from_inner(InnerSpan::new(span.start, span.end));
|
|
|
|
e.sugg_ = Some(errors::InvalidFormatStringSuggestion::RemoveRawIdent { span })
|
|
|
|
}
|
2022-08-02 20:33:40 +09:00
|
|
|
}
|
|
|
|
}
|
2024-02-25 22:22:11 +01:00
|
|
|
let guar = ecx.dcx().emit_err(e);
|
2024-03-12 10:55:17 +08:00
|
|
|
return ExpandResult::Ready(Err(guar));
|
2018-08-08 18:59:59 +02:00
|
|
|
}
|
|
|
|
|
2024-05-03 15:52:26 +10:00
|
|
|
let to_span = |inner_span: parse::InnerSpan| {
|
2023-01-05 20:17:30 +01:00
|
|
|
is_source_literal.then(|| {
|
2022-08-08 16:17:08 +02:00
|
|
|
fmt_span.from_inner(InnerSpan { start: inner_span.start, end: inner_span.end })
|
|
|
|
})
|
2014-01-18 01:53:10 +11:00
|
|
|
};
|
2013-07-29 01:12:41 -07:00
|
|
|
|
2022-09-06 23:15:13 +02:00
|
|
|
let mut used = vec![false; args.explicit_args().len()];
|
2022-08-30 17:18:09 +02:00
|
|
|
let mut invalid_refs = Vec::new();
|
2024-04-19 12:19:45 +02:00
|
|
|
let mut numeric_references_to_named_arg = Vec::new();
|
2022-08-30 17:18:09 +02:00
|
|
|
|
2022-08-08 16:17:08 +02:00
|
|
|
enum ArgRef<'a> {
|
|
|
|
Index(usize),
|
|
|
|
Name(&'a str, Option<Span>),
|
|
|
|
}
|
|
|
|
use ArgRef::*;
|
|
|
|
|
2024-01-25 17:12:09 +00:00
|
|
|
let mut unnamed_arg_after_named_arg = false;
|
|
|
|
|
2022-08-08 16:17:08 +02:00
|
|
|
let mut lookup_arg = |arg: ArgRef<'_>,
|
|
|
|
span: Option<Span>,
|
|
|
|
used_as: PositionUsedAs,
|
|
|
|
kind: FormatArgPositionKind|
|
|
|
|
-> FormatArgPosition {
|
|
|
|
let index = match arg {
|
|
|
|
Index(index) => {
|
2022-09-06 23:15:13 +02:00
|
|
|
if let Some(arg) = args.by_index(index) {
|
|
|
|
used[index] = true;
|
|
|
|
if arg.kind.ident().is_some() {
|
|
|
|
// This was a named argument, but it was used as a positional argument.
|
2024-04-19 12:19:45 +02:00
|
|
|
numeric_references_to_named_arg.push((index, span, used_as));
|
2022-08-08 16:17:08 +02:00
|
|
|
}
|
2022-09-06 23:15:13 +02:00
|
|
|
Ok(index)
|
|
|
|
} else {
|
|
|
|
// Doesn't exist as an explicit argument.
|
|
|
|
invalid_refs.push((index, span, used_as, kind));
|
|
|
|
Err(index)
|
2022-08-08 16:17:08 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
Name(name, span) => {
|
|
|
|
let name = Symbol::intern(name);
|
2022-09-06 23:15:13 +02:00
|
|
|
if let Some((index, _)) = args.by_name(name) {
|
|
|
|
// Name found in `args`, so we resolve it to its index.
|
|
|
|
if index < args.explicit_args().len() {
|
2022-08-30 17:26:50 +02:00
|
|
|
// Mark it as used, if it was an explicit argument.
|
2022-08-08 16:17:08 +02:00
|
|
|
used[index] = true;
|
|
|
|
}
|
|
|
|
Ok(index)
|
|
|
|
} else {
|
2022-08-30 17:26:50 +02:00
|
|
|
// Name not found in `args`, so we add it as an implicitly captured argument.
|
2022-08-08 16:17:08 +02:00
|
|
|
let span = span.unwrap_or(fmt_span);
|
|
|
|
let ident = Ident::new(name, span);
|
2023-01-05 20:17:30 +01:00
|
|
|
let expr = if is_direct_literal {
|
2022-08-08 16:17:08 +02:00
|
|
|
ecx.expr_ident(span, ident)
|
|
|
|
} else {
|
|
|
|
// For the moment capturing variables from format strings expanded from macros is
|
|
|
|
// disabled (see RFC #2795)
|
2024-02-25 22:22:11 +01:00
|
|
|
let guar = ecx.dcx().emit_err(errors::FormatNoArgNamed { span, name });
|
2024-01-25 17:12:09 +00:00
|
|
|
unnamed_arg_after_named_arg = true;
|
2024-02-25 22:22:11 +01:00
|
|
|
DummyResult::raw_expr(span, Some(guar))
|
2022-08-08 16:17:08 +02:00
|
|
|
};
|
2022-09-06 23:15:13 +02:00
|
|
|
Ok(args.add(FormatArgument { kind: FormatArgumentKind::Captured(ident), expr }))
|
2022-08-08 16:17:08 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
FormatArgPosition { index, kind, span }
|
|
|
|
};
|
|
|
|
|
|
|
|
let mut template = Vec::new();
|
|
|
|
let mut unfinished_literal = String::new();
|
|
|
|
let mut placeholder_index = 0;
|
2019-12-22 17:42:04 -05:00
|
|
|
|
2023-08-28 18:37:03 +02:00
|
|
|
for piece in &pieces {
|
|
|
|
match *piece {
|
2022-08-08 16:17:08 +02:00
|
|
|
parse::Piece::String(s) => {
|
|
|
|
unfinished_literal.push_str(s);
|
|
|
|
}
|
2022-12-06 12:02:56 +00:00
|
|
|
parse::Piece::NextArgument(box parse::Argument { position, position_span, format }) => {
|
2022-08-08 16:17:08 +02:00
|
|
|
if !unfinished_literal.is_empty() {
|
|
|
|
template.push(FormatArgsPiece::Literal(Symbol::intern(&unfinished_literal)));
|
|
|
|
unfinished_literal.clear();
|
|
|
|
}
|
2017-11-09 17:16:25 +00:00
|
|
|
|
2022-08-08 16:17:08 +02:00
|
|
|
let span = parser.arg_places.get(placeholder_index).and_then(|&s| to_span(s));
|
|
|
|
placeholder_index += 1;
|
|
|
|
|
|
|
|
let position_span = to_span(position_span);
|
|
|
|
let argument = match position {
|
|
|
|
parse::ArgumentImplicitlyIs(i) => lookup_arg(
|
|
|
|
Index(i),
|
|
|
|
position_span,
|
|
|
|
Placeholder(span),
|
|
|
|
FormatArgPositionKind::Implicit,
|
|
|
|
),
|
|
|
|
parse::ArgumentIs(i) => lookup_arg(
|
|
|
|
Index(i),
|
|
|
|
position_span,
|
|
|
|
Placeholder(span),
|
|
|
|
FormatArgPositionKind::Number,
|
|
|
|
),
|
|
|
|
parse::ArgumentNamed(name) => lookup_arg(
|
|
|
|
Name(name, position_span),
|
|
|
|
position_span,
|
|
|
|
Placeholder(span),
|
|
|
|
FormatArgPositionKind::Named,
|
|
|
|
),
|
|
|
|
};
|
2016-05-14 20:42:47 +08:00
|
|
|
|
2022-08-08 16:17:08 +02:00
|
|
|
let alignment = match format.align {
|
|
|
|
parse::AlignUnknown => None,
|
|
|
|
parse::AlignLeft => Some(FormatAlignment::Left),
|
|
|
|
parse::AlignRight => Some(FormatAlignment::Right),
|
|
|
|
parse::AlignCenter => Some(FormatAlignment::Center),
|
|
|
|
};
|
2018-08-08 18:59:59 +02:00
|
|
|
|
2022-08-08 16:17:08 +02:00
|
|
|
let format_trait = match format.ty {
|
|
|
|
"" => FormatTrait::Display,
|
|
|
|
"?" => FormatTrait::Debug,
|
|
|
|
"e" => FormatTrait::LowerExp,
|
|
|
|
"E" => FormatTrait::UpperExp,
|
|
|
|
"o" => FormatTrait::Octal,
|
|
|
|
"p" => FormatTrait::Pointer,
|
|
|
|
"b" => FormatTrait::Binary,
|
|
|
|
"x" => FormatTrait::LowerHex,
|
|
|
|
"X" => FormatTrait::UpperHex,
|
|
|
|
_ => {
|
|
|
|
invalid_placeholder_type_error(ecx, format.ty, format.ty_span, fmt_span);
|
|
|
|
FormatTrait::Display
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
let precision_span = format.precision_span.and_then(to_span);
|
|
|
|
let precision = match format.precision {
|
|
|
|
parse::CountIs(n) => Some(FormatCount::Literal(n)),
|
|
|
|
parse::CountIsName(name, name_span) => Some(FormatCount::Argument(lookup_arg(
|
|
|
|
Name(name, to_span(name_span)),
|
|
|
|
precision_span,
|
|
|
|
Precision,
|
|
|
|
FormatArgPositionKind::Named,
|
|
|
|
))),
|
|
|
|
parse::CountIsParam(i) => Some(FormatCount::Argument(lookup_arg(
|
|
|
|
Index(i),
|
|
|
|
precision_span,
|
|
|
|
Precision,
|
|
|
|
FormatArgPositionKind::Number,
|
|
|
|
))),
|
|
|
|
parse::CountIsStar(i) => Some(FormatCount::Argument(lookup_arg(
|
|
|
|
Index(i),
|
|
|
|
precision_span,
|
|
|
|
Precision,
|
|
|
|
FormatArgPositionKind::Implicit,
|
|
|
|
))),
|
|
|
|
parse::CountImplied => None,
|
|
|
|
};
|
|
|
|
|
|
|
|
let width_span = format.width_span.and_then(to_span);
|
|
|
|
let width = match format.width {
|
|
|
|
parse::CountIs(n) => Some(FormatCount::Literal(n)),
|
|
|
|
parse::CountIsName(name, name_span) => Some(FormatCount::Argument(lookup_arg(
|
|
|
|
Name(name, to_span(name_span)),
|
|
|
|
width_span,
|
|
|
|
Width,
|
|
|
|
FormatArgPositionKind::Named,
|
|
|
|
))),
|
|
|
|
parse::CountIsParam(i) => Some(FormatCount::Argument(lookup_arg(
|
|
|
|
Index(i),
|
|
|
|
width_span,
|
|
|
|
Width,
|
|
|
|
FormatArgPositionKind::Number,
|
|
|
|
))),
|
|
|
|
parse::CountIsStar(_) => unreachable!(),
|
|
|
|
parse::CountImplied => None,
|
|
|
|
};
|
|
|
|
|
|
|
|
template.push(FormatArgsPiece::Placeholder(FormatPlaceholder {
|
|
|
|
argument,
|
|
|
|
span,
|
|
|
|
format_trait,
|
|
|
|
format_options: FormatOptions {
|
|
|
|
fill: format.fill,
|
|
|
|
alignment,
|
2023-01-13 13:32:49 +01:00
|
|
|
sign: format.sign.map(|s| match s {
|
|
|
|
parse::Sign::Plus => FormatSign::Plus,
|
|
|
|
parse::Sign::Minus => FormatSign::Minus,
|
|
|
|
}),
|
|
|
|
alternate: format.alternate,
|
|
|
|
zero_pad: format.zero_pad,
|
|
|
|
debug_hex: format.debug_hex.map(|s| match s {
|
|
|
|
parse::DebugHex::Lower => FormatDebugHex::Lower,
|
|
|
|
parse::DebugHex::Upper => FormatDebugHex::Upper,
|
|
|
|
}),
|
2022-08-08 16:17:08 +02:00
|
|
|
precision,
|
|
|
|
width,
|
|
|
|
},
|
|
|
|
}));
|
|
|
|
}
|
2016-06-05 18:01:37 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-08 16:17:08 +02:00
|
|
|
if !unfinished_literal.is_empty() {
|
|
|
|
template.push(FormatArgsPiece::Literal(Symbol::intern(&unfinished_literal)));
|
2014-08-21 14:34:00 +01:00
|
|
|
}
|
2013-07-29 01:12:41 -07:00
|
|
|
|
2022-08-08 16:17:08 +02:00
|
|
|
if !invalid_refs.is_empty() {
|
2022-09-06 23:15:13 +02:00
|
|
|
report_invalid_references(ecx, &invalid_refs, &template, fmt_span, &args, parser);
|
2017-11-06 08:49:47 +00:00
|
|
|
}
|
|
|
|
|
2022-08-08 16:17:08 +02:00
|
|
|
let unused = used
|
2018-08-08 18:59:59 +02:00
|
|
|
.iter()
|
|
|
|
.enumerate()
|
2022-08-08 16:17:08 +02:00
|
|
|
.filter(|&(_, used)| !used)
|
2018-08-08 18:59:59 +02:00
|
|
|
.map(|(i, _)| {
|
2023-04-08 20:37:41 +01:00
|
|
|
let named = matches!(args.explicit_args()[i].kind, FormatArgumentKind::Named(_));
|
|
|
|
(args.explicit_args()[i].expr.span, named)
|
2018-08-08 18:59:59 +02:00
|
|
|
})
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
|
2024-01-25 17:12:09 +00:00
|
|
|
let has_unused = !unused.is_empty();
|
|
|
|
if has_unused {
|
2022-08-08 16:17:08 +02:00
|
|
|
// If there's a lot of unused arguments,
|
|
|
|
// let's check if this format arguments looks like another syntax (printf / shell).
|
2022-09-06 23:15:13 +02:00
|
|
|
let detect_foreign_fmt = unused.len() > args.explicit_args().len() / 2;
|
2023-08-28 18:37:03 +02:00
|
|
|
report_missing_placeholders(
|
|
|
|
ecx,
|
|
|
|
unused,
|
|
|
|
&used,
|
|
|
|
&args,
|
|
|
|
&pieces,
|
|
|
|
detect_foreign_fmt,
|
|
|
|
str_style,
|
|
|
|
fmt_str,
|
|
|
|
fmt_span,
|
|
|
|
);
|
2022-08-08 16:17:08 +02:00
|
|
|
}
|
2016-11-11 15:23:15 +11:00
|
|
|
|
2022-08-08 16:17:08 +02:00
|
|
|
// Only check for unused named argument names if there are no other errors to avoid causing
|
|
|
|
// too much noise in output errors, such as when a named argument is entirely unused.
|
2024-01-25 17:12:09 +00:00
|
|
|
if invalid_refs.is_empty() && !has_unused && !unnamed_arg_after_named_arg {
|
2024-04-19 12:19:45 +02:00
|
|
|
for &(index, span, used_as) in &numeric_references_to_named_arg {
|
2022-08-08 16:17:08 +02:00
|
|
|
let (position_sp_to_replace, position_sp_for_msg) = match used_as {
|
|
|
|
Placeholder(pspan) => (span, pspan),
|
|
|
|
Precision => {
|
|
|
|
// Strip the leading `.` for precision.
|
|
|
|
let span = span.map(|span| span.with_lo(span.lo() + BytePos(1)));
|
|
|
|
(span, span)
|
2018-12-26 20:18:41 -08:00
|
|
|
}
|
2022-08-08 16:17:08 +02:00
|
|
|
Width => (span, span),
|
|
|
|
};
|
2022-09-06 23:15:13 +02:00
|
|
|
let arg_name = args.explicit_args()[index].kind.ident().unwrap();
|
2022-08-08 16:17:08 +02:00
|
|
|
ecx.buffered_early_lint.push(BufferedEarlyLint {
|
2024-06-22 12:38:10 +02:00
|
|
|
span: Some(arg_name.span.into()),
|
2022-08-26 19:13:17 +02:00
|
|
|
node_id: rustc_ast::CRATE_NODE_ID,
|
2023-11-21 20:07:32 +01:00
|
|
|
lint_id: LintId::of(NAMED_ARGUMENTS_USED_POSITIONALLY),
|
2024-02-29 16:40:44 +11:00
|
|
|
diagnostic: BuiltinLintDiag::NamedArgumentUsedPositionally {
|
2022-08-08 16:17:08 +02:00
|
|
|
position_sp_to_replace,
|
|
|
|
position_sp_for_msg,
|
|
|
|
named_arg_sp: arg_name.span,
|
|
|
|
named_arg_name: arg_name.name.to_string(),
|
|
|
|
is_formatting_arg: matches!(used_as, Width | Precision),
|
|
|
|
},
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
2016-11-11 15:23:15 +11:00
|
|
|
|
2024-03-12 10:55:17 +08:00
|
|
|
ExpandResult::Ready(Ok(FormatArgs { span: fmt_span, template, arguments: args }))
|
2022-08-08 16:17:08 +02:00
|
|
|
}
|
2016-11-11 15:23:15 +11:00
|
|
|
|
2022-08-08 16:17:08 +02:00
|
|
|
fn invalid_placeholder_type_error(
|
|
|
|
ecx: &ExtCtxt<'_>,
|
|
|
|
ty: &str,
|
2024-05-03 15:52:26 +10:00
|
|
|
ty_span: Option<parse::InnerSpan>,
|
2022-08-08 16:17:08 +02:00
|
|
|
fmt_span: Span,
|
|
|
|
) {
|
|
|
|
let sp = ty_span.map(|sp| fmt_span.from_inner(InnerSpan::new(sp.start, sp.end)));
|
2023-04-08 20:37:41 +01:00
|
|
|
let suggs = if let Some(sp) = sp {
|
|
|
|
[
|
2022-08-08 16:17:08 +02:00
|
|
|
("", "Display"),
|
|
|
|
("?", "Debug"),
|
|
|
|
("e", "LowerExp"),
|
|
|
|
("E", "UpperExp"),
|
|
|
|
("o", "Octal"),
|
|
|
|
("p", "Pointer"),
|
|
|
|
("b", "Binary"),
|
|
|
|
("x", "LowerHex"),
|
|
|
|
("X", "UpperHex"),
|
2023-04-08 20:37:41 +01:00
|
|
|
]
|
|
|
|
.into_iter()
|
|
|
|
.map(|(fmt, trait_name)| errors::FormatUnknownTraitSugg { span: sp, fmt, trait_name })
|
|
|
|
.collect()
|
|
|
|
} else {
|
|
|
|
vec![]
|
|
|
|
};
|
2023-12-18 20:54:03 +11:00
|
|
|
ecx.dcx().emit_err(errors::FormatUnknownTrait { span: sp.unwrap_or(fmt_span), ty, suggs });
|
2022-08-08 16:17:08 +02:00
|
|
|
}
|
2016-11-11 15:23:15 +11:00
|
|
|
|
2022-08-08 16:17:08 +02:00
|
|
|
fn report_missing_placeholders(
|
compiler: fix few needless_pass_by_ref_mut clippy lints
warning: this argument is a mutable reference, but not used mutably
--> compiler\rustc_builtin_macros\src\asm.rs:306:28
|
306 | fn err_duplicate_option(p: &mut Parser<'_>, symbol: Symbol, span: Span) {
| ^^^^^^^^^^^^^^^ help: consider changing to: `&Parser<'_>`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut
warning: this argument is a mutable reference, but not used mutably
--> compiler\rustc_builtin_macros\src\asm.rs:318:8
|
318 | p: &mut Parser<'a>,
| ^^^^^^^^^^^^^^^ help: consider changing to: `&Parser<'a>`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut
warning: this argument is a mutable reference, but not used mutably
--> compiler\rustc_builtin_macros\src\assert.rs:114:25
|
114 | fn parse_assert<'a>(cx: &mut ExtCtxt<'a>, sp: Span, stream: TokenStream) -> PResult<'a, Assert> {
| ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'a>`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut
warning: this argument is a mutable reference, but not used mutably
--> compiler\rustc_builtin_macros\src\asm.rs:32:10
|
32 | ecx: &mut ExtCtxt<'a>,
| ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'a>`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut
warning: this argument is a mutable reference, but not used mutably
--> compiler\rustc_builtin_macros\src\test.rs:99:9
|
99 | cx: &mut ExtCtxt<'_>,
| ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'_>`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut
warning: this argument is a mutable reference, but not used mutably
--> compiler\rustc_builtin_macros\src\source_util.rs:237:9
|
237 | cx: &mut ExtCtxt<'_>,
| ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'_>`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut
warning: this argument is a mutable reference, but not used mutably
--> compiler\rustc_builtin_macros\src\format.rs:809:10
|
809 | ecx: &mut ExtCtxt<'_>,
| ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'_>`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut
warning: this argument is a mutable reference, but not used mutably
--> compiler\rustc_builtin_macros\src\format.rs:737:10
|
737 | ecx: &mut ExtCtxt<'a>,
| ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'a>`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut
warning: this argument is a mutable reference, but not used mutably
--> compiler\rustc_builtin_macros\src\format.rs:68:24
|
68 | fn parse_args<'a>(ecx: &mut ExtCtxt<'a>, sp: Span, tts: TokenStream) -> PResult<'a, MacroInput> {
| ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'a>`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut
warning: this argument is a mutable reference, but not used mutably
--> compiler\rustc_builtin_macros\src\format.rs:607:10
|
607 | ecx: &mut ExtCtxt<'_>,
| ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'_>`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut
warning: this argument is a mutable reference, but not used mutably
--> compiler\rustc_builtin_macros\src\edition_panic.rs:43:9
|
43 | cx: &'cx mut ExtCtxt<'_>,
| ^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'_>`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut
warning: this argument is a mutable reference, but not used mutably
--> compiler\rustc_builtin_macros\src\concat_bytes.rs:11:9
|
11 | cx: &mut ExtCtxt<'_>,
| ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'_>`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut
warning: this argument is a mutable reference, but not used mutably
--> compiler\rustc_builtin_macros\src\cfg.rs:38:22
|
38 | fn parse_cfg<'a>(cx: &mut ExtCtxt<'a>, span: Span, tts: TokenStream) -> PResult<'a, ast::MetaItem> {
| ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'a>`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut
warning: this argument is a mutable reference, but not used mutably
--> compiler\rustc_builtin_macros\src\cfg_accessible.rs:13:28
|
13 | fn validate_input<'a>(ecx: &mut ExtCtxt<'_>, mi: &'a ast::MetaItem) -> Option<&'a ast::Path> {
| ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'_>`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut
2024-03-28 12:04:00 +03:00
|
|
|
ecx: &ExtCtxt<'_>,
|
2023-04-08 20:37:41 +01:00
|
|
|
unused: Vec<(Span, bool)>,
|
2023-08-28 18:37:03 +02:00
|
|
|
used: &[bool],
|
|
|
|
args: &FormatArguments,
|
|
|
|
pieces: &[parse::Piece<'_>],
|
2022-08-08 16:17:08 +02:00
|
|
|
detect_foreign_fmt: bool,
|
|
|
|
str_style: Option<usize>,
|
|
|
|
fmt_str: &str,
|
|
|
|
fmt_span: Span,
|
|
|
|
) {
|
2023-04-08 20:37:41 +01:00
|
|
|
let mut diag = if let &[(span, named)] = &unused[..] {
|
2023-12-18 20:54:03 +11:00
|
|
|
ecx.dcx().create_err(errors::FormatUnusedArg { span, named })
|
2022-08-08 16:17:08 +02:00
|
|
|
} else {
|
2023-04-08 20:37:41 +01:00
|
|
|
let unused_labels =
|
|
|
|
unused.iter().map(|&(span, named)| errors::FormatUnusedArg { span, named }).collect();
|
|
|
|
let unused_spans = unused.iter().map(|&(span, _)| span).collect();
|
2023-12-18 20:54:03 +11:00
|
|
|
ecx.dcx().create_err(errors::FormatUnusedArgs {
|
2023-04-08 20:37:41 +01:00
|
|
|
fmt: fmt_span,
|
|
|
|
unused: unused_spans,
|
|
|
|
unused_labels,
|
|
|
|
})
|
2022-08-08 16:17:08 +02:00
|
|
|
};
|
|
|
|
|
2023-08-28 18:37:03 +02:00
|
|
|
let placeholders = pieces
|
|
|
|
.iter()
|
|
|
|
.filter_map(|piece| {
|
2023-11-13 08:24:55 -05:00
|
|
|
if let parse::Piece::NextArgument(argument) = piece
|
|
|
|
&& let ArgumentNamed(binding) = argument.position
|
|
|
|
{
|
|
|
|
let span = fmt_span.from_inner(InnerSpan::new(
|
|
|
|
argument.position_span.start,
|
|
|
|
argument.position_span.end,
|
|
|
|
));
|
2023-08-28 18:37:03 +02:00
|
|
|
Some((span, binding))
|
2023-11-13 08:24:55 -05:00
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
2023-08-28 18:37:03 +02:00
|
|
|
})
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
|
2023-09-25 16:50:17 +02:00
|
|
|
if !placeholders.is_empty() {
|
Make `DiagnosticBuilder::emit` consuming.
This works for most of its call sites. This is nice, because `emit` very
much makes sense as a consuming operation -- indeed,
`DiagnosticBuilderState` exists to ensure no diagnostic is emitted
twice, but it uses runtime checks.
For the small number of call sites where a consuming emit doesn't work,
the commit adds `DiagnosticBuilder::emit_without_consuming`. (This will
be removed in subsequent commits.)
Likewise, `emit_unless` becomes consuming. And `delay_as_bug` becomes
consuming, while `delay_as_bug_without_consuming` is added (which will
also be removed in subsequent commits.)
All this requires significant changes to `DiagnosticBuilder`'s chaining
methods. Currently `DiagnosticBuilder` method chaining uses a
non-consuming `&mut self -> &mut Self` style, which allows chaining to
be used when the chain ends in `emit()`, like so:
```
struct_err(msg).span(span).emit();
```
But it doesn't work when producing a `DiagnosticBuilder` value,
requiring this:
```
let mut err = self.struct_err(msg);
err.span(span);
err
```
This style of chaining won't work with consuming `emit` though. For
that, we need to use to a `self -> Self` style. That also would allow
`DiagnosticBuilder` production to be chained, e.g.:
```
self.struct_err(msg).span(span)
```
However, removing the `&mut self -> &mut Self` style would require that
individual modifications of a `DiagnosticBuilder` go from this:
```
err.span(span);
```
to this:
```
err = err.span(span);
```
There are *many* such places. I have a high tolerance for tedious
refactorings, but even I gave up after a long time trying to convert
them all.
Instead, this commit has it both ways: the existing `&mut self -> Self`
chaining methods are kept, and new `self -> Self` chaining methods are
added, all of which have a `_mv` suffix (short for "move"). Changes to
the existing `forward!` macro lets this happen with very little
additional boilerplate code. I chose to add the suffix to the new
chaining methods rather than the existing ones, because the number of
changes required is much smaller that way.
This doubled chainging is a bit clumsy, but I think it is worthwhile
because it allows a *lot* of good things to subsequently happen. In this
commit, there are many `mut` qualifiers removed in places where
diagnostics are emitted without being modified. In subsequent commits:
- chaining can be used more, making the code more concise;
- more use of chaining also permits the removal of redundant diagnostic
APIs like `struct_err_with_code`, which can be replaced easily with
`struct_err` + `code_mv`;
- `emit_without_diagnostic` can be removed, which simplifies a lot of
machinery, removing the need for `DiagnosticBuilderState`.
2024-01-03 12:17:35 +11:00
|
|
|
if let Some(new_diag) = report_redundant_format_arguments(ecx, args, used, placeholders) {
|
2023-10-05 14:23:02 +02:00
|
|
|
diag.cancel();
|
|
|
|
new_diag.emit();
|
|
|
|
return;
|
|
|
|
}
|
2023-08-28 18:37:03 +02:00
|
|
|
}
|
|
|
|
|
2022-08-08 16:17:08 +02:00
|
|
|
// Used to ensure we only report translations for *one* kind of foreign format.
|
|
|
|
let mut found_foreign = false;
|
|
|
|
|
|
|
|
// Decide if we want to look for foreign formatting directives.
|
|
|
|
if detect_foreign_fmt {
|
|
|
|
use super::format_foreign as foreign;
|
|
|
|
|
2022-11-16 20:34:16 +00:00
|
|
|
// The set of foreign substitutions we've explained. This prevents spamming the user
|
2022-08-08 16:17:08 +02:00
|
|
|
// with `%d should be written as {}` over and over again.
|
|
|
|
let mut explained = FxHashSet::default();
|
|
|
|
|
|
|
|
macro_rules! check_foreign {
|
|
|
|
($kind:ident) => {{
|
|
|
|
let mut show_doc_note = false;
|
|
|
|
|
|
|
|
let mut suggestions = vec![];
|
|
|
|
// account for `"` and account for raw strings `r#`
|
|
|
|
let padding = str_style.map(|i| i + 2).unwrap_or(1);
|
|
|
|
for sub in foreign::$kind::iter_subs(fmt_str, padding) {
|
|
|
|
let (trn, success) = match sub.translate() {
|
|
|
|
Ok(trn) => (trn, true),
|
|
|
|
Err(Some(msg)) => (msg, false),
|
|
|
|
|
|
|
|
// If it has no translation, don't call it out specifically.
|
|
|
|
_ => continue,
|
|
|
|
};
|
|
|
|
|
|
|
|
let pos = sub.position();
|
|
|
|
let sub = String::from(sub.as_str());
|
|
|
|
if explained.contains(&sub) {
|
|
|
|
continue;
|
2016-11-11 15:23:15 +11:00
|
|
|
}
|
2023-11-28 14:40:24 +03:00
|
|
|
explained.insert(sub);
|
2016-11-11 15:23:15 +11:00
|
|
|
|
2022-08-08 16:17:08 +02:00
|
|
|
if !found_foreign {
|
|
|
|
found_foreign = true;
|
|
|
|
show_doc_note = true;
|
2016-11-11 15:23:15 +11:00
|
|
|
}
|
2022-08-08 16:17:08 +02:00
|
|
|
|
2023-11-28 14:40:24 +03:00
|
|
|
let sp = fmt_span.from_inner(pos);
|
2022-08-08 16:17:08 +02:00
|
|
|
|
2023-11-28 14:40:24 +03:00
|
|
|
if success {
|
|
|
|
suggestions.push((sp, trn));
|
2022-08-08 16:17:08 +02:00
|
|
|
} else {
|
2023-11-28 14:40:24 +03:00
|
|
|
diag.span_note(
|
|
|
|
sp,
|
|
|
|
format!("format specifiers use curly braces, and {}", trn),
|
|
|
|
);
|
2018-07-24 16:01:38 -07:00
|
|
|
}
|
2022-08-08 16:17:08 +02:00
|
|
|
}
|
2016-11-11 15:23:15 +11:00
|
|
|
|
2022-08-08 16:17:08 +02:00
|
|
|
if show_doc_note {
|
|
|
|
diag.note(concat!(
|
|
|
|
stringify!($kind),
|
2023-01-02 15:51:54 +05:30
|
|
|
" formatting is not supported; see the documentation for `std::fmt`",
|
2022-08-08 16:17:08 +02:00
|
|
|
));
|
|
|
|
}
|
|
|
|
if suggestions.len() > 0 {
|
|
|
|
diag.multipart_suggestion(
|
|
|
|
"format specifiers use curly braces",
|
|
|
|
suggestions,
|
|
|
|
Applicability::MachineApplicable,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}};
|
2018-07-24 20:46:22 -07:00
|
|
|
}
|
2016-11-11 15:23:15 +11:00
|
|
|
|
2022-08-08 16:17:08 +02:00
|
|
|
check_foreign!(printf);
|
|
|
|
if !found_foreign {
|
|
|
|
check_foreign!(shell);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !found_foreign && unused.len() == 1 {
|
|
|
|
diag.span_label(fmt_span, "formatting specifier missing");
|
2016-11-11 15:23:15 +11:00
|
|
|
}
|
2013-07-29 01:12:41 -07:00
|
|
|
|
2022-08-08 16:17:08 +02:00
|
|
|
diag.emit();
|
2013-07-29 01:12:41 -07:00
|
|
|
}
|
2022-01-29 12:05:19 -08:00
|
|
|
|
2023-10-04 17:38:46 +02:00
|
|
|
/// This function detects and reports unused format!() arguments that are
|
|
|
|
/// redundant due to implicit captures (e.g. `format!("{x}", x)`).
|
2023-10-05 14:23:02 +02:00
|
|
|
fn report_redundant_format_arguments<'a>(
|
compiler: fix few needless_pass_by_ref_mut clippy lints
warning: this argument is a mutable reference, but not used mutably
--> compiler\rustc_builtin_macros\src\asm.rs:306:28
|
306 | fn err_duplicate_option(p: &mut Parser<'_>, symbol: Symbol, span: Span) {
| ^^^^^^^^^^^^^^^ help: consider changing to: `&Parser<'_>`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut
warning: this argument is a mutable reference, but not used mutably
--> compiler\rustc_builtin_macros\src\asm.rs:318:8
|
318 | p: &mut Parser<'a>,
| ^^^^^^^^^^^^^^^ help: consider changing to: `&Parser<'a>`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut
warning: this argument is a mutable reference, but not used mutably
--> compiler\rustc_builtin_macros\src\assert.rs:114:25
|
114 | fn parse_assert<'a>(cx: &mut ExtCtxt<'a>, sp: Span, stream: TokenStream) -> PResult<'a, Assert> {
| ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'a>`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut
warning: this argument is a mutable reference, but not used mutably
--> compiler\rustc_builtin_macros\src\asm.rs:32:10
|
32 | ecx: &mut ExtCtxt<'a>,
| ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'a>`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut
warning: this argument is a mutable reference, but not used mutably
--> compiler\rustc_builtin_macros\src\test.rs:99:9
|
99 | cx: &mut ExtCtxt<'_>,
| ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'_>`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut
warning: this argument is a mutable reference, but not used mutably
--> compiler\rustc_builtin_macros\src\source_util.rs:237:9
|
237 | cx: &mut ExtCtxt<'_>,
| ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'_>`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut
warning: this argument is a mutable reference, but not used mutably
--> compiler\rustc_builtin_macros\src\format.rs:809:10
|
809 | ecx: &mut ExtCtxt<'_>,
| ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'_>`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut
warning: this argument is a mutable reference, but not used mutably
--> compiler\rustc_builtin_macros\src\format.rs:737:10
|
737 | ecx: &mut ExtCtxt<'a>,
| ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'a>`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut
warning: this argument is a mutable reference, but not used mutably
--> compiler\rustc_builtin_macros\src\format.rs:68:24
|
68 | fn parse_args<'a>(ecx: &mut ExtCtxt<'a>, sp: Span, tts: TokenStream) -> PResult<'a, MacroInput> {
| ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'a>`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut
warning: this argument is a mutable reference, but not used mutably
--> compiler\rustc_builtin_macros\src\format.rs:607:10
|
607 | ecx: &mut ExtCtxt<'_>,
| ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'_>`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut
warning: this argument is a mutable reference, but not used mutably
--> compiler\rustc_builtin_macros\src\edition_panic.rs:43:9
|
43 | cx: &'cx mut ExtCtxt<'_>,
| ^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'_>`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut
warning: this argument is a mutable reference, but not used mutably
--> compiler\rustc_builtin_macros\src\concat_bytes.rs:11:9
|
11 | cx: &mut ExtCtxt<'_>,
| ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'_>`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut
warning: this argument is a mutable reference, but not used mutably
--> compiler\rustc_builtin_macros\src\cfg.rs:38:22
|
38 | fn parse_cfg<'a>(cx: &mut ExtCtxt<'a>, span: Span, tts: TokenStream) -> PResult<'a, ast::MetaItem> {
| ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'a>`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut
warning: this argument is a mutable reference, but not used mutably
--> compiler\rustc_builtin_macros\src\cfg_accessible.rs:13:28
|
13 | fn validate_input<'a>(ecx: &mut ExtCtxt<'_>, mi: &'a ast::MetaItem) -> Option<&'a ast::Path> {
| ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'_>`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut
2024-03-28 12:04:00 +03:00
|
|
|
ecx: &ExtCtxt<'a>,
|
2023-09-25 16:50:17 +02:00
|
|
|
args: &FormatArguments,
|
|
|
|
used: &[bool],
|
|
|
|
placeholders: Vec<(Span, &str)>,
|
2024-02-23 10:20:45 +11:00
|
|
|
) -> Option<Diag<'a>> {
|
2023-10-04 15:47:53 +02:00
|
|
|
let mut fmt_arg_indices = vec![];
|
2023-09-25 16:50:17 +02:00
|
|
|
let mut args_spans = vec![];
|
2023-10-04 15:47:53 +02:00
|
|
|
let mut fmt_spans = vec![];
|
2023-09-25 16:50:17 +02:00
|
|
|
|
|
|
|
for (i, unnamed_arg) in args.unnamed_args().iter().enumerate().rev() {
|
|
|
|
let Some(ty) = unnamed_arg.expr.to_ty() else { continue };
|
|
|
|
let Some(argument_binding) = ty.kind.is_simple_path() else { continue };
|
|
|
|
let argument_binding = argument_binding.as_str();
|
|
|
|
|
|
|
|
if used[i] {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
let matching_placeholders = placeholders
|
|
|
|
.iter()
|
|
|
|
.filter(|(_, inline_binding)| argument_binding == *inline_binding)
|
2023-10-04 17:46:48 +02:00
|
|
|
.map(|(span, _)| span)
|
2023-09-25 16:50:17 +02:00
|
|
|
.collect::<Vec<_>>();
|
|
|
|
|
|
|
|
if !matching_placeholders.is_empty() {
|
2023-10-04 15:47:53 +02:00
|
|
|
fmt_arg_indices.push(i);
|
2023-09-25 16:50:17 +02:00
|
|
|
args_spans.push(unnamed_arg.expr.span);
|
2023-10-04 17:46:48 +02:00
|
|
|
for span in &matching_placeholders {
|
|
|
|
if fmt_spans.contains(*span) {
|
2023-10-04 15:47:53 +02:00
|
|
|
continue;
|
|
|
|
}
|
2023-10-04 17:46:48 +02:00
|
|
|
fmt_spans.push(**span);
|
2023-09-25 16:50:17 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if !args_spans.is_empty() {
|
2023-10-04 15:47:53 +02:00
|
|
|
let multispan = MultiSpan::from(fmt_spans);
|
|
|
|
let mut suggestion_spans = vec![];
|
|
|
|
|
|
|
|
for (arg_span, fmt_arg_idx) in args_spans.iter().zip(fmt_arg_indices.iter()) {
|
|
|
|
let span = if fmt_arg_idx + 1 == args.explicit_args().len() {
|
|
|
|
*arg_span
|
|
|
|
} else {
|
|
|
|
arg_span.until(args.explicit_args()[*fmt_arg_idx + 1].expr.span)
|
|
|
|
};
|
2023-09-25 16:50:17 +02:00
|
|
|
|
2023-10-04 15:47:53 +02:00
|
|
|
suggestion_spans.push(span);
|
2023-09-25 16:50:17 +02:00
|
|
|
}
|
|
|
|
|
2023-10-05 15:46:34 +02:00
|
|
|
let sugg = if args.named_args().len() == 0 {
|
|
|
|
Some(errors::FormatRedundantArgsSugg { spans: suggestion_spans })
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
};
|
|
|
|
|
2023-12-18 20:54:03 +11:00
|
|
|
return Some(ecx.dcx().create_err(errors::FormatRedundantArgs {
|
2023-10-04 15:47:53 +02:00
|
|
|
n: args_spans.len(),
|
2023-10-05 14:36:50 +02:00
|
|
|
span: MultiSpan::from(args_spans),
|
|
|
|
note: multispan,
|
2023-10-05 15:46:34 +02:00
|
|
|
sugg,
|
2023-10-05 14:23:02 +02:00
|
|
|
}));
|
2023-09-25 16:50:17 +02:00
|
|
|
}
|
2023-10-05 14:23:02 +02:00
|
|
|
|
|
|
|
None
|
2023-09-25 16:50:17 +02:00
|
|
|
}
|
|
|
|
|
2022-08-08 16:17:08 +02:00
|
|
|
/// Handle invalid references to positional arguments. Output different
|
|
|
|
/// errors for the case where all arguments are positional and for when
|
|
|
|
/// there are named arguments or numbered positional arguments in the
|
|
|
|
/// format string.
|
|
|
|
fn report_invalid_references(
|
compiler: fix few needless_pass_by_ref_mut clippy lints
warning: this argument is a mutable reference, but not used mutably
--> compiler\rustc_builtin_macros\src\asm.rs:306:28
|
306 | fn err_duplicate_option(p: &mut Parser<'_>, symbol: Symbol, span: Span) {
| ^^^^^^^^^^^^^^^ help: consider changing to: `&Parser<'_>`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut
warning: this argument is a mutable reference, but not used mutably
--> compiler\rustc_builtin_macros\src\asm.rs:318:8
|
318 | p: &mut Parser<'a>,
| ^^^^^^^^^^^^^^^ help: consider changing to: `&Parser<'a>`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut
warning: this argument is a mutable reference, but not used mutably
--> compiler\rustc_builtin_macros\src\assert.rs:114:25
|
114 | fn parse_assert<'a>(cx: &mut ExtCtxt<'a>, sp: Span, stream: TokenStream) -> PResult<'a, Assert> {
| ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'a>`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut
warning: this argument is a mutable reference, but not used mutably
--> compiler\rustc_builtin_macros\src\asm.rs:32:10
|
32 | ecx: &mut ExtCtxt<'a>,
| ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'a>`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut
warning: this argument is a mutable reference, but not used mutably
--> compiler\rustc_builtin_macros\src\test.rs:99:9
|
99 | cx: &mut ExtCtxt<'_>,
| ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'_>`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut
warning: this argument is a mutable reference, but not used mutably
--> compiler\rustc_builtin_macros\src\source_util.rs:237:9
|
237 | cx: &mut ExtCtxt<'_>,
| ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'_>`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut
warning: this argument is a mutable reference, but not used mutably
--> compiler\rustc_builtin_macros\src\format.rs:809:10
|
809 | ecx: &mut ExtCtxt<'_>,
| ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'_>`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut
warning: this argument is a mutable reference, but not used mutably
--> compiler\rustc_builtin_macros\src\format.rs:737:10
|
737 | ecx: &mut ExtCtxt<'a>,
| ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'a>`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut
warning: this argument is a mutable reference, but not used mutably
--> compiler\rustc_builtin_macros\src\format.rs:68:24
|
68 | fn parse_args<'a>(ecx: &mut ExtCtxt<'a>, sp: Span, tts: TokenStream) -> PResult<'a, MacroInput> {
| ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'a>`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut
warning: this argument is a mutable reference, but not used mutably
--> compiler\rustc_builtin_macros\src\format.rs:607:10
|
607 | ecx: &mut ExtCtxt<'_>,
| ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'_>`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut
warning: this argument is a mutable reference, but not used mutably
--> compiler\rustc_builtin_macros\src\edition_panic.rs:43:9
|
43 | cx: &'cx mut ExtCtxt<'_>,
| ^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'_>`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut
warning: this argument is a mutable reference, but not used mutably
--> compiler\rustc_builtin_macros\src\concat_bytes.rs:11:9
|
11 | cx: &mut ExtCtxt<'_>,
| ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'_>`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut
warning: this argument is a mutable reference, but not used mutably
--> compiler\rustc_builtin_macros\src\cfg.rs:38:22
|
38 | fn parse_cfg<'a>(cx: &mut ExtCtxt<'a>, span: Span, tts: TokenStream) -> PResult<'a, ast::MetaItem> {
| ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'a>`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut
warning: this argument is a mutable reference, but not used mutably
--> compiler\rustc_builtin_macros\src\cfg_accessible.rs:13:28
|
13 | fn validate_input<'a>(ecx: &mut ExtCtxt<'_>, mi: &'a ast::MetaItem) -> Option<&'a ast::Path> {
| ^^^^^^^^^^^^^^^^ help: consider changing to: `&ExtCtxt<'_>`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut
2024-03-28 12:04:00 +03:00
|
|
|
ecx: &ExtCtxt<'_>,
|
2022-08-08 16:17:08 +02:00
|
|
|
invalid_refs: &[(usize, Option<Span>, PositionUsedAs, FormatArgPositionKind)],
|
|
|
|
template: &[FormatArgsPiece],
|
|
|
|
fmt_span: Span,
|
2022-09-06 23:15:13 +02:00
|
|
|
args: &FormatArguments,
|
2022-08-08 16:17:08 +02:00
|
|
|
parser: parse::Parser<'_>,
|
|
|
|
) {
|
2022-09-06 23:15:13 +02:00
|
|
|
let num_args_desc = match args.explicit_args().len() {
|
2022-08-08 16:17:08 +02:00
|
|
|
0 => "no arguments were given".to_string(),
|
|
|
|
1 => "there is 1 argument".to_string(),
|
2023-07-25 22:00:13 +02:00
|
|
|
n => format!("there are {n} arguments"),
|
2022-08-08 16:17:08 +02:00
|
|
|
};
|
2022-01-29 12:05:19 -08:00
|
|
|
|
2022-08-08 16:17:08 +02:00
|
|
|
let mut e;
|
|
|
|
|
|
|
|
if template.iter().all(|piece| match piece {
|
|
|
|
FormatArgsPiece::Placeholder(FormatPlaceholder {
|
|
|
|
argument: FormatArgPosition { kind: FormatArgPositionKind::Number, .. },
|
|
|
|
..
|
|
|
|
}) => false,
|
|
|
|
FormatArgsPiece::Placeholder(FormatPlaceholder {
|
|
|
|
format_options:
|
|
|
|
FormatOptions {
|
|
|
|
precision:
|
|
|
|
Some(FormatCount::Argument(FormatArgPosition {
|
|
|
|
kind: FormatArgPositionKind::Number,
|
|
|
|
..
|
|
|
|
})),
|
|
|
|
..
|
|
|
|
}
|
|
|
|
| FormatOptions {
|
|
|
|
width:
|
|
|
|
Some(FormatCount::Argument(FormatArgPosition {
|
|
|
|
kind: FormatArgPositionKind::Number,
|
|
|
|
..
|
|
|
|
})),
|
|
|
|
..
|
|
|
|
},
|
|
|
|
..
|
|
|
|
}) => false,
|
|
|
|
_ => true,
|
|
|
|
}) {
|
|
|
|
// There are no numeric positions.
|
|
|
|
// Collect all the implicit positions:
|
|
|
|
let mut spans = Vec::new();
|
|
|
|
let mut num_placeholders = 0;
|
|
|
|
for piece in template {
|
|
|
|
let mut placeholder = None;
|
|
|
|
// `{arg:.*}`
|
|
|
|
if let FormatArgsPiece::Placeholder(FormatPlaceholder {
|
|
|
|
format_options:
|
|
|
|
FormatOptions {
|
|
|
|
precision:
|
|
|
|
Some(FormatCount::Argument(FormatArgPosition {
|
|
|
|
span,
|
|
|
|
kind: FormatArgPositionKind::Implicit,
|
|
|
|
..
|
|
|
|
})),
|
|
|
|
..
|
|
|
|
},
|
|
|
|
..
|
|
|
|
}) = piece
|
|
|
|
{
|
|
|
|
placeholder = *span;
|
|
|
|
num_placeholders += 1;
|
|
|
|
}
|
|
|
|
// `{}`
|
|
|
|
if let FormatArgsPiece::Placeholder(FormatPlaceholder {
|
|
|
|
argument: FormatArgPosition { kind: FormatArgPositionKind::Implicit, .. },
|
|
|
|
span,
|
|
|
|
..
|
|
|
|
}) = piece
|
|
|
|
{
|
|
|
|
placeholder = *span;
|
|
|
|
num_placeholders += 1;
|
2022-01-29 12:05:19 -08:00
|
|
|
}
|
2022-08-08 16:17:08 +02:00
|
|
|
// For `{:.*}`, we only push one span.
|
|
|
|
spans.extend(placeholder);
|
2022-01-29 12:05:19 -08:00
|
|
|
}
|
2022-08-08 16:17:08 +02:00
|
|
|
let span = if spans.is_empty() {
|
|
|
|
MultiSpan::from_span(fmt_span)
|
|
|
|
} else {
|
|
|
|
MultiSpan::from_spans(spans)
|
|
|
|
};
|
2023-12-18 20:54:03 +11:00
|
|
|
e = ecx.dcx().create_err(errors::FormatPositionalMismatch {
|
2022-08-08 16:17:08 +02:00
|
|
|
span,
|
2023-04-08 20:37:41 +01:00
|
|
|
n: num_placeholders,
|
|
|
|
desc: num_args_desc,
|
|
|
|
highlight: SingleLabelManySpans {
|
|
|
|
spans: args.explicit_args().iter().map(|arg| arg.expr.span).collect(),
|
|
|
|
label: "",
|
|
|
|
},
|
|
|
|
});
|
2022-08-08 16:17:08 +02:00
|
|
|
// Point out `{:.*}` placeholders: those take an extra argument.
|
|
|
|
let mut has_precision_star = false;
|
|
|
|
for piece in template {
|
|
|
|
if let FormatArgsPiece::Placeholder(FormatPlaceholder {
|
|
|
|
format_options:
|
|
|
|
FormatOptions {
|
|
|
|
precision:
|
|
|
|
Some(FormatCount::Argument(FormatArgPosition {
|
|
|
|
index,
|
|
|
|
span: Some(span),
|
|
|
|
kind: FormatArgPositionKind::Implicit,
|
|
|
|
..
|
|
|
|
})),
|
|
|
|
..
|
|
|
|
},
|
|
|
|
..
|
|
|
|
}) = piece
|
|
|
|
{
|
|
|
|
let (Ok(index) | Err(index)) = index;
|
|
|
|
has_precision_star = true;
|
|
|
|
e.span_label(
|
|
|
|
*span,
|
Restrict `From<S>` for `{D,Subd}iagnosticMessage`.
Currently a `{D,Subd}iagnosticMessage` can be created from any type that
impls `Into<String>`. That includes `&str`, `String`, and `Cow<'static,
str>`, which are reasonable. It also includes `&String`, which is pretty
weird, and results in many places making unnecessary allocations for
patterns like this:
```
self.fatal(&format!(...))
```
This creates a string with `format!`, takes a reference, passes the
reference to `fatal`, which does an `into()`, which clones the
reference, doing a second allocation. Two allocations for a single
string, bleh.
This commit changes the `From` impls so that you can only create a
`{D,Subd}iagnosticMessage` from `&str`, `String`, or `Cow<'static,
str>`. This requires changing all the places that currently create one
from a `&String`. Most of these are of the `&format!(...)` form
described above; each one removes an unnecessary static `&`, plus an
allocation when executed. There are also a few places where the existing
use of `&String` was more reasonable; these now just use `clone()` at
the call site.
As well as making the code nicer and more efficient, this is a step
towards possibly using `Cow<'static, str>` in
`{D,Subd}iagnosticMessage::{Str,Eager}`. That would require changing
the `From<&'a str>` impls to `From<&'static str>`, which is doable, but
I'm not yet sure if it's worthwhile.
2023-04-20 13:26:58 +10:00
|
|
|
format!(
|
2022-08-08 16:17:08 +02:00
|
|
|
"this precision flag adds an extra required argument at position {}, which is why there {} expected",
|
|
|
|
index,
|
|
|
|
if num_placeholders == 1 {
|
|
|
|
"is 1 argument".to_string()
|
|
|
|
} else {
|
2023-07-25 22:00:13 +02:00
|
|
|
format!("are {num_placeholders} arguments")
|
2022-08-08 16:17:08 +02:00
|
|
|
},
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if has_precision_star {
|
|
|
|
e.note("positional arguments are zero-based");
|
2022-01-29 22:16:35 -08:00
|
|
|
}
|
2022-08-08 16:17:08 +02:00
|
|
|
} else {
|
|
|
|
let mut indexes: Vec<_> = invalid_refs.iter().map(|&(index, _, _, _)| index).collect();
|
|
|
|
// Avoid `invalid reference to positional arguments 7 and 7 (there is 1 argument)`
|
|
|
|
// for `println!("{7:7$}", 1);`
|
|
|
|
indexes.sort();
|
|
|
|
indexes.dedup();
|
2023-01-05 20:17:30 +01:00
|
|
|
let span: MultiSpan = if !parser.is_source_literal || parser.arg_places.is_empty() {
|
2022-08-08 16:17:08 +02:00
|
|
|
MultiSpan::from_span(fmt_span)
|
|
|
|
} else {
|
|
|
|
MultiSpan::from_spans(invalid_refs.iter().filter_map(|&(_, span, _, _)| span).collect())
|
|
|
|
};
|
|
|
|
let arg_list = if let &[index] = &indexes[..] {
|
|
|
|
format!("argument {index}")
|
|
|
|
} else {
|
|
|
|
let tail = indexes.pop().unwrap();
|
|
|
|
format!(
|
|
|
|
"arguments {head} and {tail}",
|
|
|
|
head = indexes.into_iter().map(|i| i.to_string()).collect::<Vec<_>>().join(", ")
|
|
|
|
)
|
|
|
|
};
|
2023-12-18 20:54:03 +11:00
|
|
|
e = ecx.dcx().struct_span_err(
|
2022-08-08 16:17:08 +02:00
|
|
|
span,
|
2023-07-25 22:00:13 +02:00
|
|
|
format!("invalid reference to positional {arg_list} ({num_args_desc})"),
|
2022-08-08 16:17:08 +02:00
|
|
|
);
|
|
|
|
e.note("positional arguments are zero-based");
|
|
|
|
}
|
2022-01-29 22:16:35 -08:00
|
|
|
|
2022-08-08 16:17:08 +02:00
|
|
|
if template.iter().any(|piece| match piece {
|
|
|
|
FormatArgsPiece::Placeholder(FormatPlaceholder { format_options: f, .. }) => {
|
|
|
|
*f != FormatOptions::default()
|
2022-01-29 12:05:19 -08:00
|
|
|
}
|
2022-08-08 16:17:08 +02:00
|
|
|
_ => false,
|
|
|
|
}) {
|
|
|
|
e.note("for information about formatting flags, visit https://doc.rust-lang.org/std/fmt/index.html");
|
|
|
|
}
|
|
|
|
|
|
|
|
e.emit();
|
|
|
|
}
|
2022-01-29 12:05:19 -08:00
|
|
|
|
2022-08-08 16:17:08 +02:00
|
|
|
fn expand_format_args_impl<'cx>(
|
|
|
|
ecx: &'cx mut ExtCtxt<'_>,
|
|
|
|
mut sp: Span,
|
|
|
|
tts: TokenStream,
|
|
|
|
nl: bool,
|
2024-03-12 10:55:17 +08:00
|
|
|
) -> MacroExpanderResult<'cx> {
|
2022-08-08 16:17:08 +02:00
|
|
|
sp = ecx.with_def_site_ctxt(sp);
|
2024-03-12 10:55:17 +08:00
|
|
|
ExpandResult::Ready(match parse_args(ecx, sp, tts) {
|
|
|
|
Ok(input) => {
|
|
|
|
let ExpandResult::Ready(mac) = make_format_args(ecx, input, nl) else {
|
|
|
|
return ExpandResult::Retry(());
|
|
|
|
};
|
|
|
|
match mac {
|
|
|
|
Ok(format_args) => {
|
|
|
|
MacEager::expr(ecx.expr(sp, ExprKind::FormatArgs(P(format_args))))
|
|
|
|
}
|
|
|
|
Err(guar) => MacEager::expr(DummyResult::raw_expr(sp, Some(guar))),
|
|
|
|
}
|
|
|
|
}
|
Make `DiagnosticBuilder::emit` consuming.
This works for most of its call sites. This is nice, because `emit` very
much makes sense as a consuming operation -- indeed,
`DiagnosticBuilderState` exists to ensure no diagnostic is emitted
twice, but it uses runtime checks.
For the small number of call sites where a consuming emit doesn't work,
the commit adds `DiagnosticBuilder::emit_without_consuming`. (This will
be removed in subsequent commits.)
Likewise, `emit_unless` becomes consuming. And `delay_as_bug` becomes
consuming, while `delay_as_bug_without_consuming` is added (which will
also be removed in subsequent commits.)
All this requires significant changes to `DiagnosticBuilder`'s chaining
methods. Currently `DiagnosticBuilder` method chaining uses a
non-consuming `&mut self -> &mut Self` style, which allows chaining to
be used when the chain ends in `emit()`, like so:
```
struct_err(msg).span(span).emit();
```
But it doesn't work when producing a `DiagnosticBuilder` value,
requiring this:
```
let mut err = self.struct_err(msg);
err.span(span);
err
```
This style of chaining won't work with consuming `emit` though. For
that, we need to use to a `self -> Self` style. That also would allow
`DiagnosticBuilder` production to be chained, e.g.:
```
self.struct_err(msg).span(span)
```
However, removing the `&mut self -> &mut Self` style would require that
individual modifications of a `DiagnosticBuilder` go from this:
```
err.span(span);
```
to this:
```
err = err.span(span);
```
There are *many* such places. I have a high tolerance for tedious
refactorings, but even I gave up after a long time trying to convert
them all.
Instead, this commit has it both ways: the existing `&mut self -> Self`
chaining methods are kept, and new `self -> Self` chaining methods are
added, all of which have a `_mv` suffix (short for "move"). Changes to
the existing `forward!` macro lets this happen with very little
additional boilerplate code. I chose to add the suffix to the new
chaining methods rather than the existing ones, because the number of
changes required is much smaller that way.
This doubled chainging is a bit clumsy, but I think it is worthwhile
because it allows a *lot* of good things to subsequently happen. In this
commit, there are many `mut` qualifiers removed in places where
diagnostics are emitted without being modified. In subsequent commits:
- chaining can be used more, making the code more concise;
- more use of chaining also permits the removal of redundant diagnostic
APIs like `struct_err_with_code`, which can be replaced easily with
`struct_err` + `code_mv`;
- `emit_without_diagnostic` can be removed, which simplifies a lot of
machinery, removing the need for `DiagnosticBuilderState`.
2024-01-03 12:17:35 +11:00
|
|
|
Err(err) => {
|
2024-02-25 22:22:11 +01:00
|
|
|
let guar = err.emit();
|
|
|
|
DummyResult::any(sp, guar)
|
2022-01-29 12:05:19 -08:00
|
|
|
}
|
2024-03-12 10:55:17 +08:00
|
|
|
})
|
2022-08-08 16:17:08 +02:00
|
|
|
}
|
2022-01-29 12:05:19 -08:00
|
|
|
|
2024-04-26 07:56:48 +10:00
|
|
|
pub(crate) fn expand_format_args<'cx>(
|
2022-08-08 16:17:08 +02:00
|
|
|
ecx: &'cx mut ExtCtxt<'_>,
|
|
|
|
sp: Span,
|
|
|
|
tts: TokenStream,
|
2024-03-12 10:55:17 +08:00
|
|
|
) -> MacroExpanderResult<'cx> {
|
2022-08-08 16:17:08 +02:00
|
|
|
expand_format_args_impl(ecx, sp, tts, false)
|
|
|
|
}
|
|
|
|
|
2024-04-26 07:56:48 +10:00
|
|
|
pub(crate) fn expand_format_args_nl<'cx>(
|
2022-08-08 16:17:08 +02:00
|
|
|
ecx: &'cx mut ExtCtxt<'_>,
|
|
|
|
sp: Span,
|
|
|
|
tts: TokenStream,
|
2024-03-12 10:55:17 +08:00
|
|
|
) -> MacroExpanderResult<'cx> {
|
2022-08-08 16:17:08 +02:00
|
|
|
expand_format_args_impl(ecx, sp, tts, true)
|
2022-01-29 12:05:19 -08:00
|
|
|
}
|