about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_builtin_macros/src/asm.rs2
-rw-r--r--compiler/rustc_builtin_macros/src/format.rs247
-rw-r--r--compiler/rustc_lint/src/context.rs4
-rw-r--r--compiler/rustc_lint_defs/src/lib.rs2
-rw-r--r--compiler/rustc_parse_format/src/lib.rs20
-rw-r--r--compiler/rustc_parse_format/src/tests.rs28
-rw-r--r--compiler/rustc_trait_selection/src/traits/on_unimplemented.rs2
-rw-r--r--src/test/ui/macros/issue-98466.stderr48
-rw-r--r--src/test/ui/macros/issue-99265.fixed139
-rw-r--r--src/test/ui/macros/issue-99265.rs139
-rw-r--r--src/test/ui/macros/issue-99265.stderr562
-rw-r--r--src/tools/clippy/clippy_lints/src/write.rs2
12 files changed, 1093 insertions, 102 deletions
diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs
index 829eaa305e8..a2205c3613d 100644
--- a/compiler/rustc_builtin_macros/src/asm.rs
+++ b/compiler/rustc_builtin_macros/src/asm.rs
@@ -656,7 +656,7 @@ fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, args: AsmArgs) -> Option<ast::Inl
                     let span = arg_spans.next().unwrap_or(template_sp);
 
                     let operand_idx = match arg.position {
-                        parse::ArgumentIs(idx) | parse::ArgumentImplicitlyIs(idx) => {
+                        parse::ArgumentIs(idx, _) | parse::ArgumentImplicitlyIs(idx) => {
                             if idx >= args.operands.len()
                                 || named_pos.contains_key(&idx)
                                 || args.reg_args.contains(&idx)
diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs
index ce897abb766..f5eb984c138 100644
--- a/compiler/rustc_builtin_macros/src/format.rs
+++ b/compiler/rustc_builtin_macros/src/format.rs
@@ -11,14 +11,14 @@ use rustc_errors::{pluralize, Applicability, MultiSpan, PResult};
 use rustc_expand::base::{self, *};
 use rustc_parse_format as parse;
 use rustc_span::symbol::{sym, Ident, Symbol};
-use rustc_span::{InnerSpan, Span};
+use rustc_span::{BytePos, InnerSpan, Span};
 use smallvec::SmallVec;
 
 use rustc_lint_defs::builtin::NAMED_ARGUMENTS_USED_POSITIONALLY;
 use rustc_lint_defs::{BufferedEarlyLint, BuiltinLintDiagnostics, LintId};
-use rustc_parse_format::Count;
 use std::borrow::Cow;
 use std::collections::hash_map::Entry;
+use std::ops::Deref;
 
 #[derive(PartialEq)]
 enum ArgumentType {
@@ -32,6 +32,105 @@ enum Position {
     Named(Symbol, InnerSpan),
 }
 
+/// Indicates how positional named argument (i.e. an named argument which is used by position
+/// instead of by name) is used in format string
+/// * `Arg` is the actual argument to print
+/// * `Width` is width format argument
+/// * `Precision` is precion format argument
+/// Example: `{Arg:Width$.Precision$}
+#[derive(Debug, Eq, PartialEq)]
+enum PositionalNamedArgType {
+    Arg,
+    Width,
+    Precision,
+}
+
+/// Contains information necessary to create a lint for a positional named argument
+#[derive(Debug)]
+struct PositionalNamedArg {
+    ty: PositionalNamedArgType,
+    /// The piece of the using this argument (multiple pieces can use the same argument)
+    cur_piece: usize,
+    /// The InnerSpan for in the string to be replaced with the named argument
+    /// This will be None when the position is implicit
+    inner_span_to_replace: Option<rustc_parse_format::InnerSpan>,
+    /// The name to use instead of the position
+    replacement: Symbol,
+    /// The span for the positional named argument (so the lint can point a message to it)
+    positional_named_arg_span: Span,
+}
+
+impl PositionalNamedArg {
+    /// Determines what span to replace with the name of the named argument
+    fn get_span_to_replace(&self, cx: &Context<'_, '_>) -> Option<Span> {
+        if let Some(inner_span) = &self.inner_span_to_replace {
+            return match self.ty {
+                PositionalNamedArgType::Arg | PositionalNamedArgType::Width => Some(Span::new(
+                    cx.fmtsp.lo() + BytePos(inner_span.start.try_into().unwrap()),
+                    cx.fmtsp.lo() + BytePos(inner_span.end.try_into().unwrap()),
+                    self.positional_named_arg_span.ctxt(),
+                    self.positional_named_arg_span.parent(),
+                )),
+                PositionalNamedArgType::Precision => Some(Span::new(
+                    cx.fmtsp.lo() + BytePos(inner_span.start.try_into().unwrap()) + BytePos(1),
+                    cx.fmtsp.lo() + BytePos(inner_span.end.try_into().unwrap()),
+                    self.positional_named_arg_span.ctxt(),
+                    self.positional_named_arg_span.parent(),
+                )),
+            };
+        } else if self.ty == PositionalNamedArgType::Arg {
+            // In the case of a named argument whose position is implicit, there will not be a span
+            // to replace. Instead, we insert the name after the `{`, which is the first character
+            // of arg_span.
+            if let Some(arg_span) = cx.arg_spans.get(self.cur_piece).copied() {
+                return Some(Span::new(
+                    arg_span.lo() + BytePos(1),
+                    arg_span.lo() + BytePos(1),
+                    self.positional_named_arg_span.ctxt(),
+                    self.positional_named_arg_span.parent(),
+                ));
+            }
+        }
+
+        None
+    }
+}
+
+/// Encapsulates all the named arguments that have been used positionally
+#[derive(Debug)]
+struct PositionalNamedArgsLint {
+    positional_named_args: Vec<PositionalNamedArg>,
+}
+
+impl PositionalNamedArgsLint {
+    /// Try constructing a PositionalNamedArg struct and pushing it into the vec of positional
+    /// named arguments. If a named arg associated with `format_argument_index` cannot be found,
+    /// a new item will not be added as the lint cannot be emitted in this case.
+    fn maybe_push(
+        &mut self,
+        format_argument_index: usize,
+        ty: PositionalNamedArgType,
+        cur_piece: usize,
+        inner_span: Option<rustc_parse_format::InnerSpan>,
+        names: &FxHashMap<Symbol, (usize, Span)>,
+    ) {
+        let named_arg = names
+            .iter()
+            .find(|name| name.deref().1.0 == format_argument_index)
+            .map(|found| found.clone());
+
+        if let Some(named_arg) = named_arg {
+            self.positional_named_args.push(PositionalNamedArg {
+                ty,
+                cur_piece,
+                inner_span_to_replace: inner_span,
+                replacement: named_arg.0.clone(),
+                positional_named_arg_span: named_arg.1.1.clone(),
+            });
+        }
+    }
+}
+
 struct Context<'a, 'b> {
     ecx: &'a mut ExtCtxt<'b>,
     /// The macro's call site. References to unstable formatting internals must
@@ -118,6 +217,7 @@ struct Context<'a, 'b> {
 
     /// Whether this format string came from a string literal, as opposed to a macro.
     is_literal: bool,
+    unused_names_lint: PositionalNamedArgsLint,
 }
 
 /// Parses the arguments from the given list of tokens, returning the diagnostic
@@ -242,7 +342,7 @@ impl<'a, 'b> Context<'a, 'b> {
         self.args.len() - self.num_captured_args
     }
 
-    fn resolve_name_inplace(&self, p: &mut parse::Piece<'_>) {
+    fn resolve_name_inplace(&mut self, p: &mut parse::Piece<'_>) {
         // NOTE: the `unwrap_or` branch is needed in case of invalid format
         // arguments, e.g., `format_args!("{foo}")`.
         let lookup =
@@ -252,7 +352,7 @@ impl<'a, 'b> Context<'a, 'b> {
             parse::String(_) => {}
             parse::NextArgument(ref mut arg) => {
                 if let parse::ArgumentNamed(s, _) = arg.position {
-                    arg.position = parse::ArgumentIs(lookup(s));
+                    arg.position = parse::ArgumentIs(lookup(s), None);
                 }
                 if let parse::CountIsName(s, _) = arg.format.width {
                     arg.format.width = parse::CountIsParam(lookup(s));
@@ -273,15 +373,50 @@ impl<'a, 'b> Context<'a, 'b> {
             parse::NextArgument(ref arg) => {
                 // width/precision first, if they have implicit positional
                 // parameters it makes more sense to consume them first.
-                self.verify_count(arg.format.width);
-                self.verify_count(arg.format.precision);
+                self.verify_count(
+                    arg.format.width,
+                    &arg.format.width_span,
+                    PositionalNamedArgType::Width,
+                );
+                self.verify_count(
+                    arg.format.precision,
+                    &arg.format.precision_span,
+                    PositionalNamedArgType::Precision,
+                );
 
                 // argument second, if it's an implicit positional parameter
                 // it's written second, so it should come after width/precision.
                 let pos = match arg.position {
-                    parse::ArgumentIs(i) | parse::ArgumentImplicitlyIs(i) => Exact(i),
+                    parse::ArgumentIs(i, arg_end) => {
+                        let start_of_named_args = self.args.len() - self.names.len();
+                        if self.curpiece >= start_of_named_args {
+                            self.unused_names_lint.maybe_push(
+                                i,
+                                PositionalNamedArgType::Arg,
+                                self.curpiece,
+                                arg_end,
+                                &self.names,
+                            );
+                        }
+
+                        Exact(i)
+                    }
+                    parse::ArgumentImplicitlyIs(i) => {
+                        let start_of_named_args = self.args.len() - self.names.len();
+                        if self.curpiece >= start_of_named_args {
+                            self.unused_names_lint.maybe_push(
+                                i,
+                                PositionalNamedArgType::Arg,
+                                self.curpiece,
+                                None,
+                                &self.names,
+                            );
+                        }
+                        Exact(i)
+                    }
                     parse::ArgumentNamed(s, span) => {
-                        Named(Symbol::intern(s), InnerSpan::new(span.start, span.end))
+                        let symbol = Symbol::intern(s);
+                        Named(symbol, InnerSpan::new(span.start, span.end))
                     }
                 };
 
@@ -349,10 +484,25 @@ impl<'a, 'b> Context<'a, 'b> {
         }
     }
 
-    fn verify_count(&mut self, c: parse::Count<'_>) {
+    fn verify_count(
+        &mut self,
+        c: parse::Count<'_>,
+        inner_span: &Option<rustc_parse_format::InnerSpan>,
+        named_arg_type: PositionalNamedArgType,
+    ) {
         match c {
             parse::CountImplied | parse::CountIs(..) => {}
             parse::CountIsParam(i) => {
+                let start_of_named_args = self.args.len() - self.names.len();
+                if i >= start_of_named_args {
+                    self.unused_names_lint.maybe_push(
+                        i,
+                        named_arg_type,
+                        self.curpiece,
+                        inner_span.clone(),
+                        &self.names,
+                    );
+                }
                 self.verify_arg_type(Exact(i), Count);
             }
             parse::CountIsName(s, span) => {
@@ -673,7 +823,7 @@ impl<'a, 'b> Context<'a, 'b> {
                 // Build the position
                 let pos = {
                     match arg.position {
-                        parse::ArgumentIs(i) | parse::ArgumentImplicitlyIs(i) => {
+                        parse::ArgumentIs(i, ..) | parse::ArgumentImplicitlyIs(i) => {
                             // Map to index in final generated argument array
                             // in case of multiple types specified
                             let arg_idx = match arg_index_consumed.get_mut(i) {
@@ -701,7 +851,7 @@ impl<'a, 'b> Context<'a, 'b> {
                         // track the current argument ourselves.
                         let i = self.curarg;
                         self.curarg += 1;
-                        parse::ArgumentIs(i)
+                        parse::ArgumentIs(i, None)
                     },
                     format: parse::FormatSpec {
                         fill: arg.format.fill,
@@ -971,43 +1121,27 @@ pub fn expand_format_args_nl<'cx>(
     expand_format_args_impl(ecx, sp, tts, true)
 }
 
-fn lint_named_arguments_used_positionally(
-    names: FxHashMap<Symbol, (usize, Span)>,
-    cx: &mut Context<'_, '_>,
-    unverified_pieces: Vec<parse::Piece<'_>>,
-) {
-    let mut used_argument_names = FxHashSet::<&str>::default();
-    for piece in unverified_pieces {
-        if let rustc_parse_format::Piece::NextArgument(a) = piece {
-            match a.position {
-                rustc_parse_format::Position::ArgumentNamed(arg_name, _) => {
-                    used_argument_names.insert(arg_name);
-                }
-                _ => {}
-            };
-            if let Count::CountIsName(s, _) = a.format.width {
-                used_argument_names.insert(s);
-            }
-            if let Count::CountIsName(s, _) = a.format.precision {
-                used_argument_names.insert(s);
-            }
-        }
-    }
+fn create_lints_for_named_arguments_used_positionally(cx: &mut Context<'_, '_>) {
+    for named_arg in &cx.unused_names_lint.positional_named_args {
+        let arg_span = named_arg.get_span_to_replace(cx);
 
-    for (symbol, (index, span)) in names {
-        if !used_argument_names.contains(symbol.as_str()) {
-            let msg = format!("named argument `{}` is not used by name", symbol.as_str());
-            let arg_span = cx.arg_spans.get(index).copied();
-            cx.ecx.buffered_early_lint.push(BufferedEarlyLint {
-                span: MultiSpan::from_span(span),
-                msg: msg.clone(),
-                node_id: ast::CRATE_NODE_ID,
-                lint_id: LintId::of(&NAMED_ARGUMENTS_USED_POSITIONALLY),
-                diagnostic: BuiltinLintDiagnostics::NamedArgumentUsedPositionally(
-                    arg_span, span, symbol,
-                ),
-            });
-        }
+        let msg = format!("named argument `{}` is not used by name", named_arg.replacement);
+        let replacement = match named_arg.ty {
+            PositionalNamedArgType::Arg => named_arg.replacement.to_string(),
+            _ => named_arg.replacement.to_string() + "$",
+        };
+
+        cx.ecx.buffered_early_lint.push(BufferedEarlyLint {
+            span: MultiSpan::from_span(named_arg.positional_named_arg_span),
+            msg: msg.clone(),
+            node_id: ast::CRATE_NODE_ID,
+            lint_id: LintId::of(&NAMED_ARGUMENTS_USED_POSITIONALLY),
+            diagnostic: BuiltinLintDiagnostics::NamedArgumentUsedPositionally(
+                arg_span,
+                named_arg.positional_named_arg_span,
+                replacement,
+            ),
+        });
     }
 }
 
@@ -1119,11 +1253,6 @@ pub fn expand_preparsed_format_args(
 
     let named_pos: FxHashSet<usize> = names.values().cloned().map(|(i, _)| i).collect();
 
-    // Clone `names` because `names` in Context get updated by verify_piece, which includes usages
-    // of the names of named arguments, resulting in incorrect errors if a name argument is used
-    // but not declared, such as: `println!("x = {x}");`
-    let named_arguments = names.clone();
-
     let mut cx = Context {
         ecx,
         args,
@@ -1148,13 +1277,12 @@ pub fn expand_preparsed_format_args(
         arg_spans,
         arg_with_formatting: Vec::new(),
         is_literal: parser.is_literal,
+        unused_names_lint: PositionalNamedArgsLint { positional_named_args: vec![] },
     };
 
-    // This needs to happen *after* the Parser has consumed all pieces to create all the spans.
-    // unverified_pieces is used later to check named argument names are used, so clone each piece.
+    // This needs to happen *after* the Parser has consumed all pieces to create all the spans
     let pieces = unverified_pieces
-        .iter()
-        .cloned()
+        .into_iter()
         .map(|mut piece| {
             cx.verify_piece(&piece);
             cx.resolve_name_inplace(&mut piece);
@@ -1164,7 +1292,7 @@ pub fn expand_preparsed_format_args(
 
     let numbered_position_args = pieces.iter().any(|arg: &parse::Piece<'_>| match *arg {
         parse::String(_) => false,
-        parse::NextArgument(arg) => matches!(arg.position, parse::Position::ArgumentIs(_)),
+        parse::NextArgument(arg) => matches!(arg.position, parse::Position::ArgumentIs(..)),
     });
 
     cx.build_index_map();
@@ -1316,11 +1444,10 @@ pub fn expand_preparsed_format_args(
         }
 
         diag.emit();
-    } else if cx.invalid_refs.is_empty() && !named_arguments.is_empty() {
+    } else if cx.invalid_refs.is_empty() && cx.ecx.sess.err_count() == 0 {
         // 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.
-        // We also only need to perform this check if there are actually named arguments.
-        lint_named_arguments_used_positionally(named_arguments, &mut cx, unverified_pieces);
+        create_lints_for_named_arguments_used_positionally(&mut cx);
     }
 
     cx.into_expr()
diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs
index b4b472fe2df..04ac50f1d48 100644
--- a/compiler/rustc_lint/src/context.rs
+++ b/compiler/rustc_lint/src/context.rs
@@ -861,10 +861,10 @@ pub trait LintContext: Sized {
                     if let Some(positional_arg) = positional_arg {
                         let msg = format!("this formatting argument uses named argument `{}` by position", name);
                         db.span_label(positional_arg, msg);
-                            db.span_suggestion_verbose(
+                        db.span_suggestion_verbose(
                             positional_arg,
                             "use the named argument by name to avoid ambiguity",
-                            format!("{{{}}}", name),
+                            name,
                             Applicability::MaybeIncorrect,
                         );
                     }
diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs
index 3872d866dee..4fd57ed8533 100644
--- a/compiler/rustc_lint_defs/src/lib.rs
+++ b/compiler/rustc_lint_defs/src/lib.rs
@@ -467,7 +467,7 @@ pub enum BuiltinLintDiagnostics {
         /// If true, the lifetime will be fully elided.
         use_span: Option<(Span, bool)>,
     },
-    NamedArgumentUsedPositionally(Option<Span>, Span, Symbol),
+    NamedArgumentUsedPositionally(Option<Span>, Span, String),
 }
 
 /// Lints that are buffered up early on in the `Session` before the
diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs
index 6e7553f5e49..5deb17b8651 100644
--- a/compiler/rustc_parse_format/src/lib.rs
+++ b/compiler/rustc_parse_format/src/lib.rs
@@ -104,8 +104,8 @@ pub struct FormatSpec<'a> {
 pub enum Position<'a> {
     /// The argument is implied to be located at an index
     ArgumentImplicitlyIs(usize),
-    /// The argument is located at a specific index given in the format
-    ArgumentIs(usize),
+    /// The argument is located at a specific index given in the format,
+    ArgumentIs(usize, Option<InnerSpan>),
     /// The argument has a name.
     ArgumentNamed(&'a str, InnerSpan),
 }
@@ -113,7 +113,7 @@ pub enum Position<'a> {
 impl Position<'_> {
     pub fn index(&self) -> Option<usize> {
         match self {
-            ArgumentIs(i) | ArgumentImplicitlyIs(i) => Some(*i),
+            ArgumentIs(i, ..) | ArgumentImplicitlyIs(i) => Some(*i),
             _ => None,
         }
     }
@@ -502,8 +502,15 @@ impl<'a> Parser<'a> {
     /// Returns `Some(parsed_position)` if the position is not implicitly
     /// consuming a macro argument, `None` if it's the case.
     fn position(&mut self) -> Option<Position<'a>> {
+        let start_position = self.cur.peek().map(|item| item.0);
         if let Some(i) = self.integer() {
-            Some(ArgumentIs(i))
+            let inner_span = start_position.and_then(|start| {
+                self.cur
+                    .peek()
+                    .cloned()
+                    .and_then(|item| Some(self.to_span_index(start).to(self.to_span_index(item.0))))
+            });
+            Some(ArgumentIs(i, inner_span))
         } else {
             match self.cur.peek() {
                 Some(&(start, c)) if rustc_lexer::is_id_start(c) => {
@@ -574,6 +581,10 @@ impl<'a> Parser<'a> {
             // no '0' flag and '0$' as the width instead.
             if let Some(end) = self.consume_pos('$') {
                 spec.width = CountIsParam(0);
+
+                if let Some((pos, _)) = self.cur.peek().cloned() {
+                    spec.width_span = Some(self.to_span_index(pos - 2).to(self.to_span_index(pos)));
+                }
                 havewidth = true;
                 spec.width_span = Some(self.to_span_index(end - 1).to(self.to_span_index(end + 1)));
             } else {
@@ -586,6 +597,7 @@ impl<'a> Parser<'a> {
             spec.width = w;
             spec.width_span = sp;
         }
+
         if let Some(start) = self.consume_pos('.') {
             if let Some(end) = self.consume_pos('*') {
                 // Resolve `CountIsNextParam`.
diff --git a/compiler/rustc_parse_format/src/tests.rs b/compiler/rustc_parse_format/src/tests.rs
index 9c305b4996a..a98f816644b 100644
--- a/compiler/rustc_parse_format/src/tests.rs
+++ b/compiler/rustc_parse_format/src/tests.rs
@@ -62,18 +62,30 @@ fn format_nothing() {
 }
 #[test]
 fn format_position() {
-    same("{3}", &[NextArgument(Argument { position: ArgumentIs(3), format: fmtdflt() })]);
+    same(
+        "{3}",
+        &[NextArgument(Argument {
+            position: ArgumentIs(3, Some(InnerSpan { start: 2, end: 3 })),
+            format: fmtdflt(),
+        })],
+    );
 }
 #[test]
 fn format_position_nothing_else() {
-    same("{3:}", &[NextArgument(Argument { position: ArgumentIs(3), format: fmtdflt() })]);
+    same(
+        "{3:}",
+        &[NextArgument(Argument {
+            position: ArgumentIs(3, Some(InnerSpan { start: 2, end: 3 })),
+            format: fmtdflt(),
+        })],
+    );
 }
 #[test]
 fn format_type() {
     same(
         "{3:x}",
         &[NextArgument(Argument {
-            position: ArgumentIs(3),
+            position: ArgumentIs(3, Some(InnerSpan { start: 2, end: 3 })),
             format: FormatSpec {
                 fill: None,
                 align: AlignUnknown,
@@ -93,7 +105,7 @@ fn format_align_fill() {
     same(
         "{3:>}",
         &[NextArgument(Argument {
-            position: ArgumentIs(3),
+            position: ArgumentIs(3, Some(InnerSpan { start: 2, end: 3 })),
             format: FormatSpec {
                 fill: None,
                 align: AlignRight,
@@ -110,7 +122,7 @@ fn format_align_fill() {
     same(
         "{3:0<}",
         &[NextArgument(Argument {
-            position: ArgumentIs(3),
+            position: ArgumentIs(3, Some(InnerSpan { start: 2, end: 3 })),
             format: FormatSpec {
                 fill: Some('0'),
                 align: AlignLeft,
@@ -127,7 +139,7 @@ fn format_align_fill() {
     same(
         "{3:*<abcd}",
         &[NextArgument(Argument {
-            position: ArgumentIs(3),
+            position: ArgumentIs(3, Some(InnerSpan { start: 2, end: 3 })),
             format: FormatSpec {
                 fill: Some('*'),
                 align: AlignLeft,
@@ -181,7 +193,7 @@ fn format_counts() {
     same(
         "{1:0$.10x}",
         &[NextArgument(Argument {
-            position: ArgumentIs(1),
+            position: ArgumentIs(1, Some(InnerSpan { start: 2, end: 3 })),
             format: FormatSpec {
                 fill: None,
                 align: AlignUnknown,
@@ -291,7 +303,7 @@ fn format_mixture() {
         &[
             String("abcd "),
             NextArgument(Argument {
-                position: ArgumentIs(3),
+                position: ArgumentIs(3, Some(InnerSpan { start: 7, end: 8 })),
                 format: FormatSpec {
                     fill: None,
                     align: AlignUnknown,
diff --git a/compiler/rustc_trait_selection/src/traits/on_unimplemented.rs b/compiler/rustc_trait_selection/src/traits/on_unimplemented.rs
index ed7d16f7a54..4d3b0b4cf07 100644
--- a/compiler/rustc_trait_selection/src/traits/on_unimplemented.rs
+++ b/compiler/rustc_trait_selection/src/traits/on_unimplemented.rs
@@ -337,7 +337,7 @@ impl<'tcx> OnUnimplementedFormatString {
                         }
                     }
                     // `{:1}` and `{}` are not to be used
-                    Position::ArgumentIs(_) | Position::ArgumentImplicitlyIs(_) => {
+                    Position::ArgumentIs(..) | Position::ArgumentImplicitlyIs(_) => {
                         let reported = struct_span_err!(
                             tcx.sess,
                             span,
diff --git a/src/test/ui/macros/issue-98466.stderr b/src/test/ui/macros/issue-98466.stderr
index ad11d181b62..4a39dd1440b 100644
--- a/src/test/ui/macros/issue-98466.stderr
+++ b/src/test/ui/macros/issue-98466.stderr
@@ -2,80 +2,80 @@ warning: named argument `_x` is not used by name
   --> $DIR/issue-98466.rs:7:26
    |
 LL |     println!("_x is {}", _x = 5);
-   |                     --   ^^ this named argument is only referred to by position in formatting string
-   |                     |
-   |                     this formatting argument uses named argument `_x` by position
+   |                      -   ^^ this named argument is only referred to by position in formatting string
+   |                      |
+   |                      this formatting argument uses named argument `_x` by position
    |
    = note: `#[warn(named_arguments_used_positionally)]` on by default
 help: use the named argument by name to avoid ambiguity
    |
 LL |     println!("_x is {_x}", _x = 5);
-   |                     ~~~~
+   |                      ++
 
 warning: named argument `y` is not used by name
   --> $DIR/issue-98466.rs:10:26
    |
 LL |     println!("_x is {}", y = _x);
-   |                     --   ^ this named argument is only referred to by position in formatting string
-   |                     |
-   |                     this formatting argument uses named argument `y` by position
+   |                      -   ^ this named argument is only referred to by position in formatting string
+   |                      |
+   |                      this formatting argument uses named argument `y` by position
    |
 help: use the named argument by name to avoid ambiguity
    |
 LL |     println!("_x is {y}", y = _x);
-   |                     ~~~
+   |                      +
 
 warning: named argument `y` is not used by name
   --> $DIR/issue-98466.rs:13:83
    |
 LL |     println!("first positional arg {}, second positional arg {}, _x is {}", 1, 2, y = _x);
-   |                                                                        --         ^ this named argument is only referred to by position in formatting string
-   |                                                                        |
-   |                                                                        this formatting argument uses named argument `y` by position
+   |                                                                         -         ^ this named argument is only referred to by position in formatting string
+   |                                                                         |
+   |                                                                         this formatting argument uses named argument `y` by position
    |
 help: use the named argument by name to avoid ambiguity
    |
 LL |     println!("first positional arg {}, second positional arg {}, _x is {y}", 1, 2, y = _x);
-   |                                                                        ~~~
+   |                                                                         +
 
 warning: named argument `_x` is not used by name
   --> $DIR/issue-98466.rs:19:34
    |
 LL |     let _f = format!("_x is {}", _x = 5);
-   |                             --   ^^ this named argument is only referred to by position in formatting string
-   |                             |
-   |                             this formatting argument uses named argument `_x` by position
+   |                              -   ^^ this named argument is only referred to by position in formatting string
+   |                              |
+   |                              this formatting argument uses named argument `_x` by position
    |
 help: use the named argument by name to avoid ambiguity
    |
 LL |     let _f = format!("_x is {_x}", _x = 5);
-   |                             ~~~~
+   |                              ++
 
 warning: named argument `y` is not used by name
   --> $DIR/issue-98466.rs:22:34
    |
 LL |     let _f = format!("_x is {}", y = _x);
-   |                             --   ^ this named argument is only referred to by position in formatting string
-   |                             |
-   |                             this formatting argument uses named argument `y` by position
+   |                              -   ^ this named argument is only referred to by position in formatting string
+   |                              |
+   |                              this formatting argument uses named argument `y` by position
    |
 help: use the named argument by name to avoid ambiguity
    |
 LL |     let _f = format!("_x is {y}", y = _x);
-   |                             ~~~
+   |                              +
 
 warning: named argument `y` is not used by name
   --> $DIR/issue-98466.rs:25:91
    |
 LL |     let _f = format!("first positional arg {}, second positional arg {}, _x is {}", 1, 2, y = _x);
-   |                                                                                --         ^ this named argument is only referred to by position in formatting string
-   |                                                                                |
-   |                                                                                this formatting argument uses named argument `y` by position
+   |                                                                                 -         ^ this named argument is only referred to by position in formatting string
+   |                                                                                 |
+   |                                                                                 this formatting argument uses named argument `y` by position
    |
 help: use the named argument by name to avoid ambiguity
    |
 LL |     let _f = format!("first positional arg {}, second positional arg {}, _x is {y}", 1, 2, y = _x);
-   |                                                                                ~~~
+   |                                                                                 +
 
 warning: 6 warnings emitted
 
diff --git a/src/test/ui/macros/issue-99265.fixed b/src/test/ui/macros/issue-99265.fixed
new file mode 100644
index 00000000000..f3be9c6285d
--- /dev/null
+++ b/src/test/ui/macros/issue-99265.fixed
@@ -0,0 +1,139 @@
+// check-pass
+// run-rustfix
+
+fn main() {
+    println!("{b} {a}", a=1, b=2);
+    //~^ WARNING named argument `a` is not used by name [named_arguments_used_positionally]
+    //~| HELP use the named argument by name to avoid ambiguity
+
+    println!("{} {a} {b} {c} {d}", 0, a=1, b=2, c=3, d=4);
+    //~^ WARNING named argument `a` is not used by name [named_arguments_used_positionally]
+    //~| WARNING named argument `b` is not used by name [named_arguments_used_positionally]
+    //~| WARNING named argument `c` is not used by name [named_arguments_used_positionally]
+    //~| WARNING named argument `d` is not used by name [named_arguments_used_positionally]
+    //~| HELP use the named argument by name to avoid ambiguity
+    //~| HELP use the named argument by name to avoid ambiguity
+    //~| HELP use the named argument by name to avoid ambiguity
+    //~| HELP use the named argument by name to avoid ambiguity
+
+    println!("Hello {:width$}!", "x", width = 5);
+    //~^ WARNING named argument `width` is not used by name [named_arguments_used_positionally
+    //~| HELP use the named argument by name to avoid ambiguity
+
+    println!("Hello {f:width$.precision$}!", f = 0.02f32, width = 5, precision = 2);
+    //~^ WARNING named argument `width` is not used by name [named_arguments_used_positionally
+    //~| WARNING named argument `precision` is not used by name [named_arguments_used_positionally]
+    //~| WARNING named argument `f` is not used by name [named_arguments_used_positionally]
+    //~| HELP use the named argument by name to avoid ambiguity
+    //~| HELP use the named argument by name to avoid ambiguity
+    //~| HELP use the named argument by name to avoid ambiguity
+
+    println!("Hello {f:width$.precision$}!", f = 0.02f32, width = 5, precision = 2);
+    //~^ WARNING named argument `width` is not used by name [named_arguments_used_positionally
+    //~| WARNING named argument `precision` is not used by name [named_arguments_used_positionally]
+    //~| WARNING named argument `f` is not used by name [named_arguments_used_positionally]
+    //~| HELP use the named argument by name to avoid ambiguity
+    //~| HELP use the named argument by name to avoid ambiguity
+    //~| HELP use the named argument by name to avoid ambiguity
+
+    println!(
+        "{}, Hello {f:width$.precision$} {g:width2$.precision2$}! {f}",
+        //~^ HELP use the named argument by name to avoid ambiguity
+        //~| HELP use the named argument by name to avoid ambiguity
+        //~| HELP use the named argument by name to avoid ambiguity
+        //~| HELP use the named argument by name to avoid ambiguity
+        //~| HELP use the named argument by name to avoid ambiguity
+        //~| HELP use the named argument by name to avoid ambiguity
+        //~| HELP use the named argument by name to avoid ambiguity
+        1,
+        f = 0.02f32,
+        //~^ WARNING named argument `f` is not used by name [named_arguments_used_positionally]
+        //~| WARNING named argument `f` is not used by name [named_arguments_used_positionally]
+        width = 5,
+        //~^ WARNING named argument `width` is not used by name [named_arguments_used_positionally
+        precision = 2,
+        //~^ WARNING named argument `precision` is not used by name [named_arguments_used_positionally]
+        g = 0.02f32,
+        //~^ WARNING named argument `g` is not used by name [named_arguments_used_positionally]
+        width2 = 5,
+        //~^ WARNING named argument `width2` is not used by name [named_arguments_used_positionally
+        precision2 = 2
+        //~^ WARNING named argument `precision2` is not used by name [named_arguments_used_positionally]
+    );
+
+    println!("Hello {f:0.1}!", f = 0.02f32);
+    //~^ WARNING named argument `f` is not used by name [named_arguments_used_positionally]
+    //~| HELP use the named argument by name to avoid ambiguity
+
+    println!("Hello {f:0.1}!", f = 0.02f32);
+    //~^ WARNING named argument `f` is not used by name [named_arguments_used_positionally]
+    //~| HELP use the named argument by name to avoid ambiguity
+
+    println!("Hello {f:width$.precision$}!", f = 0.02f32, width = 5, precision = 2);
+
+    let width = 5;
+    let precision = 2;
+    println!("Hello {f:width$.precision$}!", f = 0.02f32);
+
+    let val = 5;
+    println!("{v:v$}", v = val);
+    //~^ WARNING named argument `v` is not used by name [named_arguments_used_positionally]
+    //~| WARNING named argument `v` is not used by name [named_arguments_used_positionally]
+    //~| HELP use the named argument by name to avoid ambiguity
+    //~| HELP use the named argument by name to avoid ambiguity
+    println!("{v:v$}", v = val);
+    //~^ WARNING named argument `v` is not used by name [named_arguments_used_positionally]
+    //~| WARNING named argument `v` is not used by name [named_arguments_used_positionally]
+    //~| HELP use the named argument by name to avoid ambiguity
+    //~| HELP use the named argument by name to avoid ambiguity
+    println!("{v:v$.v$}", v = val);
+    //~^ WARNING named argument `v` is not used by name [named_arguments_used_positionally]
+    //~| WARNING named argument `v` is not used by name [named_arguments_used_positionally]
+    //~| WARNING named argument `v` is not used by name [named_arguments_used_positionally]
+    //~| HELP use the named argument by name to avoid ambiguity
+    //~| HELP use the named argument by name to avoid ambiguity
+    //~| HELP use the named argument by name to avoid ambiguity
+    println!("{v:v$.v$}", v = val);
+    //~^ WARNING named argument `v` is not used by name [named_arguments_used_positionally]
+    //~| WARNING named argument `v` is not used by name [named_arguments_used_positionally]
+    //~| WARNING named argument `v` is not used by name [named_arguments_used_positionally]
+    //~| HELP use the named argument by name to avoid ambiguity
+    //~| HELP use the named argument by name to avoid ambiguity
+    //~| HELP use the named argument by name to avoid ambiguity
+
+    println!("{a} {a} {a}", a = 1);
+    //~^ WARNING named argument `a` is not used by name [named_arguments_used_positionally]
+    //~| WARNING named argument `a` is not used by name [named_arguments_used_positionally]
+    //~| HELP use the named argument by name to avoid ambiguity
+    //~| HELP use the named argument by name to avoid ambiguity
+
+    println!("aaaaaaaaaaaaaaa\
+                {a:b$.c$}",
+             //~^ HELP use the named argument by name to avoid ambiguity
+             //~| HELP use the named argument by name to avoid ambiguity
+             //~| HELP use the named argument by name to avoid ambiguity
+             a = 1.0, b = 1, c = 2,
+             //~^ WARNING named argument `a` is not used by name [named_arguments_used_positionally]
+             //~| WARNING named argument `b` is not used by name [named_arguments_used_positionally]
+             //~| WARNING named argument `c` is not used by name [named_arguments_used_positionally]
+    );
+
+    println!("aaaaaaaaaaaaaaa\
+                {a:b$.c$}",
+             //~^ HELP use the named argument by name to avoid ambiguity
+             //~| HELP use the named argument by name to avoid ambiguity
+             //~| HELP use the named argument by name to avoid ambiguity
+             a = 1.0, b = 1, c = 2,
+             //~^ WARNING named argument `a` is not used by name [named_arguments_used_positionally]
+             //~| WARNING named argument `b` is not used by name [named_arguments_used_positionally]
+             //~| WARNING named argument `c` is not used by name [named_arguments_used_positionally]
+    );
+
+    println!("{{{x:width$.precision$}}}", x = 1.0, width = 3, precision = 2);
+    //~^ WARNING named argument `x` is not used by name [named_arguments_used_positionally]
+    //~| WARNING named argument `width` is not used by name [named_arguments_used_positionally]
+    //~| WARNING named argument `precision` is not used by name [named_arguments_used_positionally]
+    //~| HELP use the named argument by name to avoid ambiguity
+    //~| HELP use the named argument by name to avoid ambiguity
+    //~| HELP use the named argument by name to avoid ambiguity
+}
diff --git a/src/test/ui/macros/issue-99265.rs b/src/test/ui/macros/issue-99265.rs
new file mode 100644
index 00000000000..e7cf608765b
--- /dev/null
+++ b/src/test/ui/macros/issue-99265.rs
@@ -0,0 +1,139 @@
+// check-pass
+// run-rustfix
+
+fn main() {
+    println!("{b} {}", a=1, b=2);
+    //~^ WARNING named argument `a` is not used by name [named_arguments_used_positionally]
+    //~| HELP use the named argument by name to avoid ambiguity
+
+    println!("{} {} {} {} {}", 0, a=1, b=2, c=3, d=4);
+    //~^ WARNING named argument `a` is not used by name [named_arguments_used_positionally]
+    //~| WARNING named argument `b` is not used by name [named_arguments_used_positionally]
+    //~| WARNING named argument `c` is not used by name [named_arguments_used_positionally]
+    //~| WARNING named argument `d` is not used by name [named_arguments_used_positionally]
+    //~| HELP use the named argument by name to avoid ambiguity
+    //~| HELP use the named argument by name to avoid ambiguity
+    //~| HELP use the named argument by name to avoid ambiguity
+    //~| HELP use the named argument by name to avoid ambiguity
+
+    println!("Hello {:1$}!", "x", width = 5);
+    //~^ WARNING named argument `width` is not used by name [named_arguments_used_positionally
+    //~| HELP use the named argument by name to avoid ambiguity
+
+    println!("Hello {:1$.2$}!", f = 0.02f32, width = 5, precision = 2);
+    //~^ WARNING named argument `width` is not used by name [named_arguments_used_positionally
+    //~| WARNING named argument `precision` is not used by name [named_arguments_used_positionally]
+    //~| WARNING named argument `f` is not used by name [named_arguments_used_positionally]
+    //~| HELP use the named argument by name to avoid ambiguity
+    //~| HELP use the named argument by name to avoid ambiguity
+    //~| HELP use the named argument by name to avoid ambiguity
+
+    println!("Hello {0:1$.2$}!", f = 0.02f32, width = 5, precision = 2);
+    //~^ WARNING named argument `width` is not used by name [named_arguments_used_positionally
+    //~| WARNING named argument `precision` is not used by name [named_arguments_used_positionally]
+    //~| WARNING named argument `f` is not used by name [named_arguments_used_positionally]
+    //~| HELP use the named argument by name to avoid ambiguity
+    //~| HELP use the named argument by name to avoid ambiguity
+    //~| HELP use the named argument by name to avoid ambiguity
+
+    println!(
+        "{}, Hello {1:2$.3$} {4:5$.6$}! {1}",
+        //~^ HELP use the named argument by name to avoid ambiguity
+        //~| HELP use the named argument by name to avoid ambiguity
+        //~| HELP use the named argument by name to avoid ambiguity
+        //~| HELP use the named argument by name to avoid ambiguity
+        //~| HELP use the named argument by name to avoid ambiguity
+        //~| HELP use the named argument by name to avoid ambiguity
+        //~| HELP use the named argument by name to avoid ambiguity
+        1,
+        f = 0.02f32,
+        //~^ WARNING named argument `f` is not used by name [named_arguments_used_positionally]
+        //~| WARNING named argument `f` is not used by name [named_arguments_used_positionally]
+        width = 5,
+        //~^ WARNING named argument `width` is not used by name [named_arguments_used_positionally
+        precision = 2,
+        //~^ WARNING named argument `precision` is not used by name [named_arguments_used_positionally]
+        g = 0.02f32,
+        //~^ WARNING named argument `g` is not used by name [named_arguments_used_positionally]
+        width2 = 5,
+        //~^ WARNING named argument `width2` is not used by name [named_arguments_used_positionally
+        precision2 = 2
+        //~^ WARNING named argument `precision2` is not used by name [named_arguments_used_positionally]
+    );
+
+    println!("Hello {:0.1}!", f = 0.02f32);
+    //~^ WARNING named argument `f` is not used by name [named_arguments_used_positionally]
+    //~| HELP use the named argument by name to avoid ambiguity
+
+    println!("Hello {0:0.1}!", f = 0.02f32);
+    //~^ WARNING named argument `f` is not used by name [named_arguments_used_positionally]
+    //~| HELP use the named argument by name to avoid ambiguity
+
+    println!("Hello {f:width$.precision$}!", f = 0.02f32, width = 5, precision = 2);
+
+    let width = 5;
+    let precision = 2;
+    println!("Hello {f:width$.precision$}!", f = 0.02f32);
+
+    let val = 5;
+    println!("{:0$}", v = val);
+    //~^ WARNING named argument `v` is not used by name [named_arguments_used_positionally]
+    //~| WARNING named argument `v` is not used by name [named_arguments_used_positionally]
+    //~| HELP use the named argument by name to avoid ambiguity
+    //~| HELP use the named argument by name to avoid ambiguity
+    println!("{0:0$}", v = val);
+    //~^ WARNING named argument `v` is not used by name [named_arguments_used_positionally]
+    //~| WARNING named argument `v` is not used by name [named_arguments_used_positionally]
+    //~| HELP use the named argument by name to avoid ambiguity
+    //~| HELP use the named argument by name to avoid ambiguity
+    println!("{:0$.0$}", v = val);
+    //~^ WARNING named argument `v` is not used by name [named_arguments_used_positionally]
+    //~| WARNING named argument `v` is not used by name [named_arguments_used_positionally]
+    //~| WARNING named argument `v` is not used by name [named_arguments_used_positionally]
+    //~| HELP use the named argument by name to avoid ambiguity
+    //~| HELP use the named argument by name to avoid ambiguity
+    //~| HELP use the named argument by name to avoid ambiguity
+    println!("{0:0$.0$}", v = val);
+    //~^ WARNING named argument `v` is not used by name [named_arguments_used_positionally]
+    //~| WARNING named argument `v` is not used by name [named_arguments_used_positionally]
+    //~| WARNING named argument `v` is not used by name [named_arguments_used_positionally]
+    //~| HELP use the named argument by name to avoid ambiguity
+    //~| HELP use the named argument by name to avoid ambiguity
+    //~| HELP use the named argument by name to avoid ambiguity
+
+    println!("{} {a} {0}", a = 1);
+    //~^ WARNING named argument `a` is not used by name [named_arguments_used_positionally]
+    //~| WARNING named argument `a` is not used by name [named_arguments_used_positionally]
+    //~| HELP use the named argument by name to avoid ambiguity
+    //~| HELP use the named argument by name to avoid ambiguity
+
+    println!("aaaaaaaaaaaaaaa\
+                {:1$.2$}",
+             //~^ HELP use the named argument by name to avoid ambiguity
+             //~| HELP use the named argument by name to avoid ambiguity
+             //~| HELP use the named argument by name to avoid ambiguity
+             a = 1.0, b = 1, c = 2,
+             //~^ WARNING named argument `a` is not used by name [named_arguments_used_positionally]
+             //~| WARNING named argument `b` is not used by name [named_arguments_used_positionally]
+             //~| WARNING named argument `c` is not used by name [named_arguments_used_positionally]
+    );
+
+    println!("aaaaaaaaaaaaaaa\
+                {0:1$.2$}",
+             //~^ HELP use the named argument by name to avoid ambiguity
+             //~| HELP use the named argument by name to avoid ambiguity
+             //~| HELP use the named argument by name to avoid ambiguity
+             a = 1.0, b = 1, c = 2,
+             //~^ WARNING named argument `a` is not used by name [named_arguments_used_positionally]
+             //~| WARNING named argument `b` is not used by name [named_arguments_used_positionally]
+             //~| WARNING named argument `c` is not used by name [named_arguments_used_positionally]
+    );
+
+    println!("{{{:1$.2$}}}", x = 1.0, width = 3, precision = 2);
+    //~^ WARNING named argument `x` is not used by name [named_arguments_used_positionally]
+    //~| WARNING named argument `width` is not used by name [named_arguments_used_positionally]
+    //~| WARNING named argument `precision` is not used by name [named_arguments_used_positionally]
+    //~| HELP use the named argument by name to avoid ambiguity
+    //~| HELP use the named argument by name to avoid ambiguity
+    //~| HELP use the named argument by name to avoid ambiguity
+}
diff --git a/src/test/ui/macros/issue-99265.stderr b/src/test/ui/macros/issue-99265.stderr
new file mode 100644
index 00000000000..0798ad8dc51
--- /dev/null
+++ b/src/test/ui/macros/issue-99265.stderr
@@ -0,0 +1,562 @@
+warning: named argument `a` is not used by name
+  --> $DIR/issue-99265.rs:5:24
+   |
+LL |     println!("{b} {}", a=1, b=2);
+   |                    -   ^ this named argument is only referred to by position in formatting string
+   |                    |
+   |                    this formatting argument uses named argument `a` by position
+   |
+   = note: `#[warn(named_arguments_used_positionally)]` on by default
+help: use the named argument by name to avoid ambiguity
+   |
+LL |     println!("{b} {a}", a=1, b=2);
+   |                    +
+
+warning: named argument `a` is not used by name
+  --> $DIR/issue-99265.rs:9:35
+   |
+LL |     println!("{} {} {} {} {}", 0, a=1, b=2, c=3, d=4);
+   |                   -               ^ this named argument is only referred to by position in formatting string
+   |                   |
+   |                   this formatting argument uses named argument `a` by position
+   |
+help: use the named argument by name to avoid ambiguity
+   |
+LL |     println!("{} {a} {} {} {}", 0, a=1, b=2, c=3, d=4);
+   |                   +
+
+warning: named argument `b` is not used by name
+  --> $DIR/issue-99265.rs:9:40
+   |
+LL |     println!("{} {} {} {} {}", 0, a=1, b=2, c=3, d=4);
+   |                      -                 ^ this named argument is only referred to by position in formatting string
+   |                      |
+   |                      this formatting argument uses named argument `b` by position
+   |
+help: use the named argument by name to avoid ambiguity
+   |
+LL |     println!("{} {} {b} {} {}", 0, a=1, b=2, c=3, d=4);
+   |                      +
+
+warning: named argument `c` is not used by name
+  --> $DIR/issue-99265.rs:9:45
+   |
+LL |     println!("{} {} {} {} {}", 0, a=1, b=2, c=3, d=4);
+   |                         -                   ^ this named argument is only referred to by position in formatting string
+   |                         |
+   |                         this formatting argument uses named argument `c` by position
+   |
+help: use the named argument by name to avoid ambiguity
+   |
+LL |     println!("{} {} {} {c} {}", 0, a=1, b=2, c=3, d=4);
+   |                         +
+
+warning: named argument `d` is not used by name
+  --> $DIR/issue-99265.rs:9:50
+   |
+LL |     println!("{} {} {} {} {}", 0, a=1, b=2, c=3, d=4);
+   |                            -                     ^ this named argument is only referred to by position in formatting string
+   |                            |
+   |                            this formatting argument uses named argument `d` by position
+   |
+help: use the named argument by name to avoid ambiguity
+   |
+LL |     println!("{} {} {} {} {d}", 0, a=1, b=2, c=3, d=4);
+   |                            +
+
+warning: named argument `width` is not used by name
+  --> $DIR/issue-99265.rs:19:35
+   |
+LL |     println!("Hello {:1$}!", "x", width = 5);
+   |                       --          ^^^^^ this named argument is only referred to by position in formatting string
+   |                       |
+   |                       this formatting argument uses named argument `width$` by position
+   |
+help: use the named argument by name to avoid ambiguity
+   |
+LL |     println!("Hello {:width$}!", "x", width = 5);
+   |                       ~~~~~~
+
+warning: named argument `width` is not used by name
+  --> $DIR/issue-99265.rs:23:46
+   |
+LL |     println!("Hello {:1$.2$}!", f = 0.02f32, width = 5, precision = 2);
+   |                       --                     ^^^^^ this named argument is only referred to by position in formatting string
+   |                       |
+   |                       this formatting argument uses named argument `width$` by position
+   |
+help: use the named argument by name to avoid ambiguity
+   |
+LL |     println!("Hello {:width$.2$}!", f = 0.02f32, width = 5, precision = 2);
+   |                       ~~~~~~
+
+warning: named argument `precision` is not used by name
+  --> $DIR/issue-99265.rs:23:57
+   |
+LL |     println!("Hello {:1$.2$}!", f = 0.02f32, width = 5, precision = 2);
+   |                          --                             ^^^^^^^^^ this named argument is only referred to by position in formatting string
+   |                          |
+   |                          this formatting argument uses named argument `precision$` by position
+   |
+help: use the named argument by name to avoid ambiguity
+   |
+LL |     println!("Hello {:1$.precision$}!", f = 0.02f32, width = 5, precision = 2);
+   |                          ~~~~~~~~~~
+
+warning: named argument `f` is not used by name
+  --> $DIR/issue-99265.rs:23:33
+   |
+LL |     println!("Hello {:1$.2$}!", f = 0.02f32, width = 5, precision = 2);
+   |                      -          ^ this named argument is only referred to by position in formatting string
+   |                      |
+   |                      this formatting argument uses named argument `f` by position
+   |
+help: use the named argument by name to avoid ambiguity
+   |
+LL |     println!("Hello {f:1$.2$}!", f = 0.02f32, width = 5, precision = 2);
+   |                      +
+
+warning: named argument `width` is not used by name
+  --> $DIR/issue-99265.rs:31:47
+   |
+LL |     println!("Hello {0:1$.2$}!", f = 0.02f32, width = 5, precision = 2);
+   |                        --                     ^^^^^ this named argument is only referred to by position in formatting string
+   |                        |
+   |                        this formatting argument uses named argument `width$` by position
+   |
+help: use the named argument by name to avoid ambiguity
+   |
+LL |     println!("Hello {0:width$.2$}!", f = 0.02f32, width = 5, precision = 2);
+   |                        ~~~~~~
+
+warning: named argument `precision` is not used by name
+  --> $DIR/issue-99265.rs:31:58
+   |
+LL |     println!("Hello {0:1$.2$}!", f = 0.02f32, width = 5, precision = 2);
+   |                           --                             ^^^^^^^^^ this named argument is only referred to by position in formatting string
+   |                           |
+   |                           this formatting argument uses named argument `precision$` by position
+   |
+help: use the named argument by name to avoid ambiguity
+   |
+LL |     println!("Hello {0:1$.precision$}!", f = 0.02f32, width = 5, precision = 2);
+   |                           ~~~~~~~~~~
+
+warning: named argument `f` is not used by name
+  --> $DIR/issue-99265.rs:31:34
+   |
+LL |     println!("Hello {0:1$.2$}!", f = 0.02f32, width = 5, precision = 2);
+   |                      -           ^ this named argument is only referred to by position in formatting string
+   |                      |
+   |                      this formatting argument uses named argument `f` by position
+   |
+help: use the named argument by name to avoid ambiguity
+   |
+LL |     println!("Hello {f:1$.2$}!", f = 0.02f32, width = 5, precision = 2);
+   |                      ~
+
+warning: named argument `width` is not used by name
+  --> $DIR/issue-99265.rs:52:9
+   |
+LL |         "{}, Hello {1:2$.3$} {4:5$.6$}! {1}",
+   |                       -- this formatting argument uses named argument `width$` by position
+...
+LL |         width = 5,
+   |         ^^^^^ this named argument is only referred to by position in formatting string
+   |
+help: use the named argument by name to avoid ambiguity
+   |
+LL |         "{}, Hello {1:width$.3$} {4:5$.6$}! {1}",
+   |                       ~~~~~~
+
+warning: named argument `precision` is not used by name
+  --> $DIR/issue-99265.rs:54:9
+   |
+LL |         "{}, Hello {1:2$.3$} {4:5$.6$}! {1}",
+   |                          -- this formatting argument uses named argument `precision$` by position
+...
+LL |         precision = 2,
+   |         ^^^^^^^^^ this named argument is only referred to by position in formatting string
+   |
+help: use the named argument by name to avoid ambiguity
+   |
+LL |         "{}, Hello {1:2$.precision$} {4:5$.6$}! {1}",
+   |                          ~~~~~~~~~~
+
+warning: named argument `f` is not used by name
+  --> $DIR/issue-99265.rs:49:9
+   |
+LL |         "{}, Hello {1:2$.3$} {4:5$.6$}! {1}",
+   |                     - this formatting argument uses named argument `f` by position
+...
+LL |         f = 0.02f32,
+   |         ^ this named argument is only referred to by position in formatting string
+   |
+help: use the named argument by name to avoid ambiguity
+   |
+LL |         "{}, Hello {f:2$.3$} {4:5$.6$}! {1}",
+   |                     ~
+
+warning: named argument `width2` is not used by name
+  --> $DIR/issue-99265.rs:58:9
+   |
+LL |         "{}, Hello {1:2$.3$} {4:5$.6$}! {1}",
+   |                                 -- this formatting argument uses named argument `width2$` by position
+...
+LL |         width2 = 5,
+   |         ^^^^^^ this named argument is only referred to by position in formatting string
+   |
+help: use the named argument by name to avoid ambiguity
+   |
+LL |         "{}, Hello {1:2$.3$} {4:width2$.6$}! {1}",
+   |                                 ~~~~~~~
+
+warning: named argument `precision2` is not used by name
+  --> $DIR/issue-99265.rs:60:9
+   |
+LL |         "{}, Hello {1:2$.3$} {4:5$.6$}! {1}",
+   |                                    -- this formatting argument uses named argument `precision2$` by position
+...
+LL |         precision2 = 2
+   |         ^^^^^^^^^^ this named argument is only referred to by position in formatting string
+   |
+help: use the named argument by name to avoid ambiguity
+   |
+LL |         "{}, Hello {1:2$.3$} {4:5$.precision2$}! {1}",
+   |                                    ~~~~~~~~~~~
+
+warning: named argument `g` is not used by name
+  --> $DIR/issue-99265.rs:56:9
+   |
+LL |         "{}, Hello {1:2$.3$} {4:5$.6$}! {1}",
+   |                               - this formatting argument uses named argument `g` by position
+...
+LL |         g = 0.02f32,
+   |         ^ this named argument is only referred to by position in formatting string
+   |
+help: use the named argument by name to avoid ambiguity
+   |
+LL |         "{}, Hello {1:2$.3$} {g:5$.6$}! {1}",
+   |                               ~
+
+warning: named argument `f` is not used by name
+  --> $DIR/issue-99265.rs:49:9
+   |
+LL |         "{}, Hello {1:2$.3$} {4:5$.6$}! {1}",
+   |                                          - this formatting argument uses named argument `f` by position
+...
+LL |         f = 0.02f32,
+   |         ^ this named argument is only referred to by position in formatting string
+   |
+help: use the named argument by name to avoid ambiguity
+   |
+LL |         "{}, Hello {1:2$.3$} {4:5$.6$}! {f}",
+   |                                          ~
+
+warning: named argument `f` is not used by name
+  --> $DIR/issue-99265.rs:64:31
+   |
+LL |     println!("Hello {:0.1}!", f = 0.02f32);
+   |                      -        ^ this named argument is only referred to by position in formatting string
+   |                      |
+   |                      this formatting argument uses named argument `f` by position
+   |
+help: use the named argument by name to avoid ambiguity
+   |
+LL |     println!("Hello {f:0.1}!", f = 0.02f32);
+   |                      +
+
+warning: named argument `f` is not used by name
+  --> $DIR/issue-99265.rs:68:32
+   |
+LL |     println!("Hello {0:0.1}!", f = 0.02f32);
+   |                      -         ^ this named argument is only referred to by position in formatting string
+   |                      |
+   |                      this formatting argument uses named argument `f` by position
+   |
+help: use the named argument by name to avoid ambiguity
+   |
+LL |     println!("Hello {f:0.1}!", f = 0.02f32);
+   |                      ~
+
+warning: named argument `v` is not used by name
+  --> $DIR/issue-99265.rs:79:23
+   |
+LL |     println!("{:0$}", v = val);
+   |                 --    ^ this named argument is only referred to by position in formatting string
+   |                 |
+   |                 this formatting argument uses named argument `v$` by position
+   |
+help: use the named argument by name to avoid ambiguity
+   |
+LL |     println!("{:v$}", v = val);
+   |                 ~~
+
+warning: named argument `v` is not used by name
+  --> $DIR/issue-99265.rs:79:23
+   |
+LL |     println!("{:0$}", v = val);
+   |                -      ^ this named argument is only referred to by position in formatting string
+   |                |
+   |                this formatting argument uses named argument `v` by position
+   |
+help: use the named argument by name to avoid ambiguity
+   |
+LL |     println!("{v:0$}", v = val);
+   |                +
+
+warning: named argument `v` is not used by name
+  --> $DIR/issue-99265.rs:84:24
+   |
+LL |     println!("{0:0$}", v = val);
+   |                  --    ^ this named argument is only referred to by position in formatting string
+   |                  |
+   |                  this formatting argument uses named argument `v$` by position
+   |
+help: use the named argument by name to avoid ambiguity
+   |
+LL |     println!("{0:v$}", v = val);
+   |                  ~~
+
+warning: named argument `v` is not used by name
+  --> $DIR/issue-99265.rs:84:24
+   |
+LL |     println!("{0:0$}", v = val);
+   |                -       ^ this named argument is only referred to by position in formatting string
+   |                |
+   |                this formatting argument uses named argument `v` by position
+   |
+help: use the named argument by name to avoid ambiguity
+   |
+LL |     println!("{v:0$}", v = val);
+   |                ~
+
+warning: named argument `v` is not used by name
+  --> $DIR/issue-99265.rs:89:26
+   |
+LL |     println!("{:0$.0$}", v = val);
+   |                 --       ^ this named argument is only referred to by position in formatting string
+   |                 |
+   |                 this formatting argument uses named argument `v$` by position
+   |
+help: use the named argument by name to avoid ambiguity
+   |
+LL |     println!("{:v$.0$}", v = val);
+   |                 ~~
+
+warning: named argument `v` is not used by name
+  --> $DIR/issue-99265.rs:89:26
+   |
+LL |     println!("{:0$.0$}", v = val);
+   |                    --    ^ this named argument is only referred to by position in formatting string
+   |                    |
+   |                    this formatting argument uses named argument `v$` by position
+   |
+help: use the named argument by name to avoid ambiguity
+   |
+LL |     println!("{:0$.v$}", v = val);
+   |                    ~~
+
+warning: named argument `v` is not used by name
+  --> $DIR/issue-99265.rs:89:26
+   |
+LL |     println!("{:0$.0$}", v = val);
+   |                -         ^ this named argument is only referred to by position in formatting string
+   |                |
+   |                this formatting argument uses named argument `v` by position
+   |
+help: use the named argument by name to avoid ambiguity
+   |
+LL |     println!("{v:0$.0$}", v = val);
+   |                +
+
+warning: named argument `v` is not used by name
+  --> $DIR/issue-99265.rs:96:27
+   |
+LL |     println!("{0:0$.0$}", v = val);
+   |                  --       ^ this named argument is only referred to by position in formatting string
+   |                  |
+   |                  this formatting argument uses named argument `v$` by position
+   |
+help: use the named argument by name to avoid ambiguity
+   |
+LL |     println!("{0:v$.0$}", v = val);
+   |                  ~~
+
+warning: named argument `v` is not used by name
+  --> $DIR/issue-99265.rs:96:27
+   |
+LL |     println!("{0:0$.0$}", v = val);
+   |                     --    ^ this named argument is only referred to by position in formatting string
+   |                     |
+   |                     this formatting argument uses named argument `v$` by position
+   |
+help: use the named argument by name to avoid ambiguity
+   |
+LL |     println!("{0:0$.v$}", v = val);
+   |                     ~~
+
+warning: named argument `v` is not used by name
+  --> $DIR/issue-99265.rs:96:27
+   |
+LL |     println!("{0:0$.0$}", v = val);
+   |                -          ^ this named argument is only referred to by position in formatting string
+   |                |
+   |                this formatting argument uses named argument `v` by position
+   |
+help: use the named argument by name to avoid ambiguity
+   |
+LL |     println!("{v:0$.0$}", v = val);
+   |                ~
+
+warning: named argument `a` is not used by name
+  --> $DIR/issue-99265.rs:104:28
+   |
+LL |     println!("{} {a} {0}", a = 1);
+   |                -           ^ this named argument is only referred to by position in formatting string
+   |                |
+   |                this formatting argument uses named argument `a` by position
+   |
+help: use the named argument by name to avoid ambiguity
+   |
+LL |     println!("{a} {a} {0}", a = 1);
+   |                +
+
+warning: named argument `a` is not used by name
+  --> $DIR/issue-99265.rs:104:28
+   |
+LL |     println!("{} {a} {0}", a = 1);
+   |                       -    ^ this named argument is only referred to by position in formatting string
+   |                       |
+   |                       this formatting argument uses named argument `a` by position
+   |
+help: use the named argument by name to avoid ambiguity
+   |
+LL |     println!("{} {a} {a}", a = 1);
+   |                       ~
+
+warning: named argument `b` is not used by name
+  --> $DIR/issue-99265.rs:115:23
+   |
+LL |                 {:1$.2$}",
+   |                   -- this formatting argument uses named argument `b$` by position
+...
+LL |              a = 1.0, b = 1, c = 2,
+   |                       ^ this named argument is only referred to by position in formatting string
+   |
+help: use the named argument by name to avoid ambiguity
+   |
+LL |                 {:b$.2$}",
+   |                   ~~
+
+warning: named argument `c` is not used by name
+  --> $DIR/issue-99265.rs:115:30
+   |
+LL |                 {:1$.2$}",
+   |                      -- this formatting argument uses named argument `c$` by position
+...
+LL |              a = 1.0, b = 1, c = 2,
+   |                              ^ this named argument is only referred to by position in formatting string
+   |
+help: use the named argument by name to avoid ambiguity
+   |
+LL |                 {:1$.c$}",
+   |                      ~~
+
+warning: named argument `a` is not used by name
+  --> $DIR/issue-99265.rs:115:14
+   |
+LL |                 {:1$.2$}",
+   |                  - this formatting argument uses named argument `a` by position
+...
+LL |              a = 1.0, b = 1, c = 2,
+   |              ^ this named argument is only referred to by position in formatting string
+   |
+help: use the named argument by name to avoid ambiguity
+   |
+LL |                 {a:1$.2$}",
+   |                  +
+
+warning: named argument `b` is not used by name
+  --> $DIR/issue-99265.rs:126:23
+   |
+LL |                 {0:1$.2$}",
+   |                    -- this formatting argument uses named argument `b$` by position
+...
+LL |              a = 1.0, b = 1, c = 2,
+   |                       ^ this named argument is only referred to by position in formatting string
+   |
+help: use the named argument by name to avoid ambiguity
+   |
+LL |                 {0:b$.2$}",
+   |                    ~~
+
+warning: named argument `c` is not used by name
+  --> $DIR/issue-99265.rs:126:30
+   |
+LL |                 {0:1$.2$}",
+   |                       -- this formatting argument uses named argument `c$` by position
+...
+LL |              a = 1.0, b = 1, c = 2,
+   |                              ^ this named argument is only referred to by position in formatting string
+   |
+help: use the named argument by name to avoid ambiguity
+   |
+LL |                 {0:1$.c$}",
+   |                       ~~
+
+warning: named argument `a` is not used by name
+  --> $DIR/issue-99265.rs:126:14
+   |
+LL |                 {0:1$.2$}",
+   |                  - this formatting argument uses named argument `a` by position
+...
+LL |              a = 1.0, b = 1, c = 2,
+   |              ^ this named argument is only referred to by position in formatting string
+   |
+help: use the named argument by name to avoid ambiguity
+   |
+LL |                 {a:1$.2$}",
+   |                  ~
+
+warning: named argument `width` is not used by name
+  --> $DIR/issue-99265.rs:132:39
+   |
+LL |     println!("{{{:1$.2$}}}", x = 1.0, width = 3, precision = 2);
+   |                   --                  ^^^^^ this named argument is only referred to by position in formatting string
+   |                   |
+   |                   this formatting argument uses named argument `width$` by position
+   |
+help: use the named argument by name to avoid ambiguity
+   |
+LL |     println!("{{{:width$.2$}}}", x = 1.0, width = 3, precision = 2);
+   |                   ~~~~~~
+
+warning: named argument `precision` is not used by name
+  --> $DIR/issue-99265.rs:132:50
+   |
+LL |     println!("{{{:1$.2$}}}", x = 1.0, width = 3, precision = 2);
+   |                      --                          ^^^^^^^^^ this named argument is only referred to by position in formatting string
+   |                      |
+   |                      this formatting argument uses named argument `precision$` by position
+   |
+help: use the named argument by name to avoid ambiguity
+   |
+LL |     println!("{{{:1$.precision$}}}", x = 1.0, width = 3, precision = 2);
+   |                      ~~~~~~~~~~
+
+warning: named argument `x` is not used by name
+  --> $DIR/issue-99265.rs:132:30
+   |
+LL |     println!("{{{:1$.2$}}}", x = 1.0, width = 3, precision = 2);
+   |                  -           ^ this named argument is only referred to by position in formatting string
+   |                  |
+   |                  this formatting argument uses named argument `x` by position
+   |
+help: use the named argument by name to avoid ambiguity
+   |
+LL |     println!("{{{x:1$.2$}}}", x = 1.0, width = 3, precision = 2);
+   |                  +
+
+warning: 42 warnings emitted
+
diff --git a/src/tools/clippy/clippy_lints/src/write.rs b/src/tools/clippy/clippy_lints/src/write.rs
index 08b88947520..3a99d1b417f 100644
--- a/src/tools/clippy/clippy_lints/src/write.rs
+++ b/src/tools/clippy/clippy_lints/src/write.rs
@@ -441,7 +441,7 @@ impl SimpleFormatArgs {
         };
 
         match arg.position {
-            ArgumentIs(n) | ArgumentImplicitlyIs(n) => {
+            ArgumentIs(n, _) | ArgumentImplicitlyIs(n) => {
                 if self.unnamed.len() <= n {
                     // Use a dummy span to mark all unseen arguments.
                     self.unnamed.resize_with(n, || vec![DUMMY_SP]);