@@ -16,6 +16,7 @@ use smallvec::SmallVec;
16
16
17
17
use rustc_lint_defs::builtin::NAMED_ARGUMENTS_USED_POSITIONALLY;
18
18
use rustc_lint_defs::{BufferedEarlyLint, BuiltinLintDiagnostics, LintId};
19
+ use rustc_parse_format::Count;
19
20
use std::borrow::Cow;
20
21
use std::collections::hash_map::Entry;
21
22
@@ -57,26 +58,47 @@ struct PositionalNamedArg {
57
58
replacement: Symbol,
58
59
/// The span for the positional named argument (so the lint can point a message to it)
59
60
positional_named_arg_span: Span,
61
+ has_formatting: bool,
60
62
}
61
63
62
64
impl PositionalNamedArg {
63
- /// Determines what span to replace with the name of the named argument
64
- fn get_span_to_replace(&self, cx: &Context<'_, '_>) -> Option<Span> {
65
+ /// Determines:
66
+ /// 1) span to be replaced with the name of the named argument and
67
+ /// 2) span to be underlined for error messages
68
+ fn get_positional_arg_spans(&self, cx: &Context<'_, '_>) -> (Option<Span>, Option<Span>) {
65
69
if let Some(inner_span) = &self.inner_span_to_replace {
66
- return Some(
67
- cx.fmtsp.from_inner(InnerSpan { start: inner_span.start, end: inner_span.end }),
68
- );
70
+ let span =
71
+ cx.fmtsp.from_inner(InnerSpan { start: inner_span.start, end: inner_span.end });
72
+ (Some(span), Some(span))
69
73
} else if self.ty == PositionalNamedArgType::Arg {
70
- // In the case of a named argument whose position is implicit, there will not be a span
71
- // to replace. Instead, we insert the name after the `{`, which is the first character
72
- // of arg_span.
73
- return cx
74
- .arg_spans
75
- .get(self.cur_piece)
76
- .map(|arg_span| arg_span.with_lo(arg_span.lo() + BytePos(1)).shrink_to_lo());
74
+ // In the case of a named argument whose position is implicit, if the argument *has*
75
+ // formatting, there will not be a span to replace. Instead, we insert the name after
76
+ // the `{`, which will be the first character of arg_span. If the argument does *not*
77
+ // have formatting, there may or may not be a span to replace. This is because
78
+ // whitespace is allowed in arguments without formatting (such as `format!("{ }", 1);`)
79
+ // but is not allowed in arguments with formatting (an error will be generated in cases
80
+ // like `format!("{ :1.1}", 1.0f32);`.
81
+ // For the message span, if there is formatting, we want to use the opening `{` and the
82
+ // next character, which will the `:` indicating the start of formatting. If there is
83
+ // not any formatting, we want to underline the entire span.
84
+ if self.has_formatting {
85
+ cx.arg_spans.get(self.cur_piece).map_or((None, None), |arg_span| {
86
+ (
87
+ Some(arg_span.with_lo(arg_span.lo() + BytePos(1)).shrink_to_lo()),
88
+ Some(arg_span.with_hi(arg_span.lo() + BytePos(2))),
89
+ )
90
+ })
91
+ } else {
92
+ cx.arg_spans.get(self.cur_piece).map_or((None, None), |arg_span| {
93
+ let replace_start = arg_span.lo() + BytePos(1);
94
+ let replace_end = arg_span.hi() - BytePos(1);
95
+ let to_replace = arg_span.with_lo(replace_start).with_hi(replace_end);
96
+ (Some(to_replace), Some(*arg_span))
97
+ })
98
+ }
99
+ } else {
100
+ (None, None)
77
101
}
78
-
79
- None
80
102
}
81
103
}
82
104
@@ -117,10 +139,18 @@ impl PositionalNamedArgsLint {
117
139
cur_piece: usize,
118
140
inner_span_to_replace: Option<rustc_parse_format::InnerSpan>,
119
141
names: &FxHashMap<Symbol, (usize, Span)>,
142
+ has_formatting: bool,
120
143
) {
121
144
let start_of_named_args = total_args_length - names.len();
122
145
if current_positional_arg >= start_of_named_args {
123
- self.maybe_push(format_argument_index, ty, cur_piece, inner_span_to_replace, names)
146
+ self.maybe_push(
147
+ format_argument_index,
148
+ ty,
149
+ cur_piece,
150
+ inner_span_to_replace,
151
+ names,
152
+ has_formatting,
153
+ )
124
154
}
125
155
}
126
156
@@ -134,6 +164,7 @@ impl PositionalNamedArgsLint {
134
164
cur_piece: usize,
135
165
inner_span_to_replace: Option<rustc_parse_format::InnerSpan>,
136
166
names: &FxHashMap<Symbol, (usize, Span)>,
167
+ has_formatting: bool,
137
168
) {
138
169
let named_arg = names
139
170
.iter()
@@ -156,6 +187,7 @@ impl PositionalNamedArgsLint {
156
187
inner_span_to_replace,
157
188
replacement,
158
189
positional_named_arg_span,
190
+ has_formatting,
159
191
});
160
192
}
161
193
}
@@ -414,6 +446,9 @@ impl<'a, 'b> Context<'a, 'b> {
414
446
PositionalNamedArgType::Precision,
415
447
);
416
448
449
+ let has_precision = arg.format.precision != Count::CountImplied;
450
+ let has_width = arg.format.width != Count::CountImplied;
451
+
417
452
// argument second, if it's an implicit positional parameter
418
453
// it's written second, so it should come after width/precision.
419
454
let pos = match arg.position {
@@ -426,6 +461,7 @@ impl<'a, 'b> Context<'a, 'b> {
426
461
self.curpiece,
427
462
arg_end,
428
463
&self.names,
464
+ has_precision || has_width,
429
465
);
430
466
431
467
Exact(i)
@@ -439,6 +475,7 @@ impl<'a, 'b> Context<'a, 'b> {
439
475
self.curpiece,
440
476
None,
441
477
&self.names,
478
+ has_precision || has_width,
442
479
);
443
480
Exact(i)
444
481
}
@@ -529,6 +566,7 @@ impl<'a, 'b> Context<'a, 'b> {
529
566
self.curpiece,
530
567
*inner_span,
531
568
&self.names,
569
+ true,
532
570
);
533
571
self.verify_arg_type(Exact(i), Count);
534
572
}
@@ -1150,24 +1188,22 @@ pub fn expand_format_args_nl<'cx>(
1150
1188
1151
1189
fn create_lints_for_named_arguments_used_positionally(cx: &mut Context<'_, '_>) {
1152
1190
for named_arg in &cx.unused_names_lint.positional_named_args {
1153
- let arg_span = named_arg.get_span_to_replace (cx);
1191
+ let (position_sp_to_replace, position_sp_for_msg) = named_arg.get_positional_arg_spans (cx);
1154
1192
1155
1193
let msg = format!("named argument `{}` is not used by name", named_arg.replacement);
1156
- let replacement = match named_arg.ty {
1157
- PositionalNamedArgType::Arg => named_arg.replacement.to_string(),
1158
- _ => named_arg.replacement.to_string() + "$",
1159
- };
1160
1194
1161
1195
cx.ecx.buffered_early_lint.push(BufferedEarlyLint {
1162
1196
span: MultiSpan::from_span(named_arg.positional_named_arg_span),
1163
1197
msg: msg.clone(),
1164
1198
node_id: ast::CRATE_NODE_ID,
1165
1199
lint_id: LintId::of(&NAMED_ARGUMENTS_USED_POSITIONALLY),
1166
- diagnostic: BuiltinLintDiagnostics::NamedArgumentUsedPositionally(
1167
- arg_span,
1168
- named_arg.positional_named_arg_span,
1169
- replacement,
1170
- ),
1200
+ diagnostic: BuiltinLintDiagnostics::NamedArgumentUsedPositionally {
1201
+ position_sp_to_replace,
1202
+ position_sp_for_msg,
1203
+ named_arg_sp: named_arg.positional_named_arg_span,
1204
+ named_arg_name: named_arg.replacement.to_string(),
1205
+ is_formatting_arg: named_arg.ty != PositionalNamedArgType::Arg,
1206
+ },
1171
1207
});
1172
1208
}
1173
1209
}
0 commit comments