Skip to content

Commit ea6ab1b

Browse files
committed
Auto merge of #99660 - PrestonFrom:issue_99265, r=compiler-errors
Generate correct suggestion with named arguments used positionally Address issue #99265 by checking each positionally used argument to see if the argument is named and adding a lint to use the name instead. This way, when named arguments are used positionally in a different order than their argument order, the suggested lint is correct. For example: ``` println!("{b} {}", a=1, b=2); ``` This will now generate the suggestion: ``` println!("{b} {a}", a=1, b=2); ``` Additionally, this check now also correctly replaces or inserts only where the positional argument is (or would be if implicit). Also, width and precision are replaced with their argument names when they exists. Since the issues were so closely related, this fix for issue #99265 also fixes issue #99266. Fixes #99265 Fixes #99266
2 parents 9de7474 + 1b2e05e commit ea6ab1b

File tree

12 files changed

+1120
-102
lines changed

12 files changed

+1120
-102
lines changed

compiler/rustc_builtin_macros/src/asm.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -656,7 +656,7 @@ fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, args: AsmArgs) -> Option<ast::Inl
656656
let span = arg_spans.next().unwrap_or(template_sp);
657657

658658
let operand_idx = match arg.position {
659-
parse::ArgumentIs(idx) | parse::ArgumentImplicitlyIs(idx) => {
659+
parse::ArgumentIs(idx, _) | parse::ArgumentImplicitlyIs(idx) => {
660660
if idx >= args.operands.len()
661661
|| named_pos.contains_key(&idx)
662662
|| args.reg_args.contains(&idx)

compiler/rustc_builtin_macros/src/format.rs

+214-60
Large diffs are not rendered by default.

compiler/rustc_lint/src/context.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -861,10 +861,10 @@ pub trait LintContext: Sized {
861861
if let Some(positional_arg) = positional_arg {
862862
let msg = format!("this formatting argument uses named argument `{}` by position", name);
863863
db.span_label(positional_arg, msg);
864-
db.span_suggestion_verbose(
864+
db.span_suggestion_verbose(
865865
positional_arg,
866866
"use the named argument by name to avoid ambiguity",
867-
format!("{{{}}}", name),
867+
name,
868868
Applicability::MaybeIncorrect,
869869
);
870870
}

compiler/rustc_lint_defs/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -467,7 +467,7 @@ pub enum BuiltinLintDiagnostics {
467467
/// If true, the lifetime will be fully elided.
468468
use_span: Option<(Span, bool)>,
469469
},
470-
NamedArgumentUsedPositionally(Option<Span>, Span, Symbol),
470+
NamedArgumentUsedPositionally(Option<Span>, Span, String),
471471
}
472472

473473
/// Lints that are buffered up early on in the `Session` before the

compiler/rustc_parse_format/src/lib.rs

+16-4
Original file line numberDiff line numberDiff line change
@@ -104,16 +104,16 @@ pub struct FormatSpec<'a> {
104104
pub enum Position<'a> {
105105
/// The argument is implied to be located at an index
106106
ArgumentImplicitlyIs(usize),
107-
/// The argument is located at a specific index given in the format
108-
ArgumentIs(usize),
107+
/// The argument is located at a specific index given in the format,
108+
ArgumentIs(usize, Option<InnerSpan>),
109109
/// The argument has a name.
110110
ArgumentNamed(&'a str, InnerSpan),
111111
}
112112

113113
impl Position<'_> {
114114
pub fn index(&self) -> Option<usize> {
115115
match self {
116-
ArgumentIs(i) | ArgumentImplicitlyIs(i) => Some(*i),
116+
ArgumentIs(i, ..) | ArgumentImplicitlyIs(i) => Some(*i),
117117
_ => None,
118118
}
119119
}
@@ -502,8 +502,15 @@ impl<'a> Parser<'a> {
502502
/// Returns `Some(parsed_position)` if the position is not implicitly
503503
/// consuming a macro argument, `None` if it's the case.
504504
fn position(&mut self) -> Option<Position<'a>> {
505+
let start_position = self.cur.peek().map(|item| item.0);
505506
if let Some(i) = self.integer() {
506-
Some(ArgumentIs(i))
507+
let inner_span = start_position.and_then(|start| {
508+
self.cur
509+
.peek()
510+
.cloned()
511+
.and_then(|item| Some(self.to_span_index(start).to(self.to_span_index(item.0))))
512+
});
513+
Some(ArgumentIs(i, inner_span))
507514
} else {
508515
match self.cur.peek() {
509516
Some(&(start, c)) if rustc_lexer::is_id_start(c) => {
@@ -574,6 +581,10 @@ impl<'a> Parser<'a> {
574581
// no '0' flag and '0$' as the width instead.
575582
if let Some(end) = self.consume_pos('$') {
576583
spec.width = CountIsParam(0);
584+
585+
if let Some((pos, _)) = self.cur.peek().cloned() {
586+
spec.width_span = Some(self.to_span_index(pos - 2).to(self.to_span_index(pos)));
587+
}
577588
havewidth = true;
578589
spec.width_span = Some(self.to_span_index(end - 1).to(self.to_span_index(end + 1)));
579590
} else {
@@ -586,6 +597,7 @@ impl<'a> Parser<'a> {
586597
spec.width = w;
587598
spec.width_span = sp;
588599
}
600+
589601
if let Some(start) = self.consume_pos('.') {
590602
if let Some(end) = self.consume_pos('*') {
591603
// Resolve `CountIsNextParam`.

compiler/rustc_parse_format/src/tests.rs

+20-8
Original file line numberDiff line numberDiff line change
@@ -62,18 +62,30 @@ fn format_nothing() {
6262
}
6363
#[test]
6464
fn format_position() {
65-
same("{3}", &[NextArgument(Argument { position: ArgumentIs(3), format: fmtdflt() })]);
65+
same(
66+
"{3}",
67+
&[NextArgument(Argument {
68+
position: ArgumentIs(3, Some(InnerSpan { start: 2, end: 3 })),
69+
format: fmtdflt(),
70+
})],
71+
);
6672
}
6773
#[test]
6874
fn format_position_nothing_else() {
69-
same("{3:}", &[NextArgument(Argument { position: ArgumentIs(3), format: fmtdflt() })]);
75+
same(
76+
"{3:}",
77+
&[NextArgument(Argument {
78+
position: ArgumentIs(3, Some(InnerSpan { start: 2, end: 3 })),
79+
format: fmtdflt(),
80+
})],
81+
);
7082
}
7183
#[test]
7284
fn format_type() {
7385
same(
7486
"{3:x}",
7587
&[NextArgument(Argument {
76-
position: ArgumentIs(3),
88+
position: ArgumentIs(3, Some(InnerSpan { start: 2, end: 3 })),
7789
format: FormatSpec {
7890
fill: None,
7991
align: AlignUnknown,
@@ -93,7 +105,7 @@ fn format_align_fill() {
93105
same(
94106
"{3:>}",
95107
&[NextArgument(Argument {
96-
position: ArgumentIs(3),
108+
position: ArgumentIs(3, Some(InnerSpan { start: 2, end: 3 })),
97109
format: FormatSpec {
98110
fill: None,
99111
align: AlignRight,
@@ -110,7 +122,7 @@ fn format_align_fill() {
110122
same(
111123
"{3:0<}",
112124
&[NextArgument(Argument {
113-
position: ArgumentIs(3),
125+
position: ArgumentIs(3, Some(InnerSpan { start: 2, end: 3 })),
114126
format: FormatSpec {
115127
fill: Some('0'),
116128
align: AlignLeft,
@@ -127,7 +139,7 @@ fn format_align_fill() {
127139
same(
128140
"{3:*<abcd}",
129141
&[NextArgument(Argument {
130-
position: ArgumentIs(3),
142+
position: ArgumentIs(3, Some(InnerSpan { start: 2, end: 3 })),
131143
format: FormatSpec {
132144
fill: Some('*'),
133145
align: AlignLeft,
@@ -181,7 +193,7 @@ fn format_counts() {
181193
same(
182194
"{1:0$.10x}",
183195
&[NextArgument(Argument {
184-
position: ArgumentIs(1),
196+
position: ArgumentIs(1, Some(InnerSpan { start: 2, end: 3 })),
185197
format: FormatSpec {
186198
fill: None,
187199
align: AlignUnknown,
@@ -291,7 +303,7 @@ fn format_mixture() {
291303
&[
292304
String("abcd "),
293305
NextArgument(Argument {
294-
position: ArgumentIs(3),
306+
position: ArgumentIs(3, Some(InnerSpan { start: 7, end: 8 })),
295307
format: FormatSpec {
296308
fill: None,
297309
align: AlignUnknown,

compiler/rustc_trait_selection/src/traits/on_unimplemented.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,7 @@ impl<'tcx> OnUnimplementedFormatString {
337337
}
338338
}
339339
// `{:1}` and `{}` are not to be used
340-
Position::ArgumentIs(_) | Position::ArgumentImplicitlyIs(_) => {
340+
Position::ArgumentIs(..) | Position::ArgumentImplicitlyIs(_) => {
341341
let reported = struct_span_err!(
342342
tcx.sess,
343343
span,

src/test/ui/macros/issue-98466.stderr

+24-24
Original file line numberDiff line numberDiff line change
@@ -2,80 +2,80 @@ warning: named argument `_x` is not used by name
22
--> $DIR/issue-98466.rs:7:26
33
|
44
LL | println!("_x is {}", _x = 5);
5-
| -- ^^ this named argument is only referred to by position in formatting string
6-
| |
7-
| this formatting argument uses named argument `_x` by position
5+
| - ^^ this named argument is only referred to by position in formatting string
6+
| |
7+
| this formatting argument uses named argument `_x` by position
88
|
99
= note: `#[warn(named_arguments_used_positionally)]` on by default
1010
help: use the named argument by name to avoid ambiguity
1111
|
1212
LL | println!("_x is {_x}", _x = 5);
13-
| ~~~~
13+
| ++
1414

1515
warning: named argument `y` is not used by name
1616
--> $DIR/issue-98466.rs:10:26
1717
|
1818
LL | println!("_x is {}", y = _x);
19-
| -- ^ this named argument is only referred to by position in formatting string
20-
| |
21-
| this formatting argument uses named argument `y` by position
19+
| - ^ this named argument is only referred to by position in formatting string
20+
| |
21+
| this formatting argument uses named argument `y` by position
2222
|
2323
help: use the named argument by name to avoid ambiguity
2424
|
2525
LL | println!("_x is {y}", y = _x);
26-
| ~~~
26+
| +
2727

2828
warning: named argument `y` is not used by name
2929
--> $DIR/issue-98466.rs:13:83
3030
|
3131
LL | println!("first positional arg {}, second positional arg {}, _x is {}", 1, 2, y = _x);
32-
| -- ^ this named argument is only referred to by position in formatting string
33-
| |
34-
| this formatting argument uses named argument `y` by position
32+
| - ^ this named argument is only referred to by position in formatting string
33+
| |
34+
| this formatting argument uses named argument `y` by position
3535
|
3636
help: use the named argument by name to avoid ambiguity
3737
|
3838
LL | println!("first positional arg {}, second positional arg {}, _x is {y}", 1, 2, y = _x);
39-
| ~~~
39+
| +
4040

4141
warning: named argument `_x` is not used by name
4242
--> $DIR/issue-98466.rs:19:34
4343
|
4444
LL | let _f = format!("_x is {}", _x = 5);
45-
| -- ^^ this named argument is only referred to by position in formatting string
46-
| |
47-
| this formatting argument uses named argument `_x` by position
45+
| - ^^ this named argument is only referred to by position in formatting string
46+
| |
47+
| this formatting argument uses named argument `_x` by position
4848
|
4949
help: use the named argument by name to avoid ambiguity
5050
|
5151
LL | let _f = format!("_x is {_x}", _x = 5);
52-
| ~~~~
52+
| ++
5353

5454
warning: named argument `y` is not used by name
5555
--> $DIR/issue-98466.rs:22:34
5656
|
5757
LL | let _f = format!("_x is {}", y = _x);
58-
| -- ^ this named argument is only referred to by position in formatting string
59-
| |
60-
| this formatting argument uses named argument `y` by position
58+
| - ^ this named argument is only referred to by position in formatting string
59+
| |
60+
| this formatting argument uses named argument `y` by position
6161
|
6262
help: use the named argument by name to avoid ambiguity
6363
|
6464
LL | let _f = format!("_x is {y}", y = _x);
65-
| ~~~
65+
| +
6666

6767
warning: named argument `y` is not used by name
6868
--> $DIR/issue-98466.rs:25:91
6969
|
7070
LL | let _f = format!("first positional arg {}, second positional arg {}, _x is {}", 1, 2, y = _x);
71-
| -- ^ this named argument is only referred to by position in formatting string
72-
| |
73-
| this formatting argument uses named argument `y` by position
71+
| - ^ this named argument is only referred to by position in formatting string
72+
| |
73+
| this formatting argument uses named argument `y` by position
7474
|
7575
help: use the named argument by name to avoid ambiguity
7676
|
7777
LL | let _f = format!("first positional arg {}, second positional arg {}, _x is {y}", 1, 2, y = _x);
78-
| ~~~
78+
| +
7979

8080
warning: 6 warnings emitted
8181

0 commit comments

Comments
 (0)