Improve position named arguments lint underline and formatting names
For named arguments used as implicit position arguments, underline both the opening curly brace and either: * if there is formatting, the next character (which will either be the closing curl brace or the `:` denoting the start of formatting args) * if there is no formatting, the entire arg span (important if there is whitespace like `{ }`) This should make it more obvious where the named argument should be. Additionally, in the lint message, emit the formatting argument names without a dollar sign to avoid potentially confusion. Fixes #99907
This commit is contained in:
parent
9d5cd21a5d
commit
d0ea440dfe
8 changed files with 305 additions and 136 deletions
|
@ -16,6 +16,7 @@ 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;
|
||||
|
||||
|
@ -57,26 +58,47 @@ struct PositionalNamedArg {
|
|||
replacement: Symbol,
|
||||
/// The span for the positional named argument (so the lint can point a message to it)
|
||||
positional_named_arg_span: Span,
|
||||
has_formatting: bool,
|
||||
}
|
||||
|
||||
impl PositionalNamedArg {
|
||||
/// Determines what span to replace with the name of the named argument
|
||||
fn get_span_to_replace(&self, cx: &Context<'_, '_>) -> Option<Span> {
|
||||
/// Determines:
|
||||
/// 1) span to be replaced with the name of the named argument and
|
||||
/// 2) span to be underlined for error messages
|
||||
fn get_positional_arg_spans(&self, cx: &Context<'_, '_>) -> (Option<Span>, Option<Span>) {
|
||||
if let Some(inner_span) = &self.inner_span_to_replace {
|
||||
return Some(
|
||||
cx.fmtsp.from_inner(InnerSpan { start: inner_span.start, end: inner_span.end }),
|
||||
);
|
||||
let span =
|
||||
cx.fmtsp.from_inner(InnerSpan { start: inner_span.start, end: inner_span.end });
|
||||
(Some(span), Some(span))
|
||||
} 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.
|
||||
return cx
|
||||
.arg_spans
|
||||
.get(self.cur_piece)
|
||||
.map(|arg_span| arg_span.with_lo(arg_span.lo() + BytePos(1)).shrink_to_lo());
|
||||
// In the case of a named argument whose position is implicit, if the argument *has*
|
||||
// formatting, there will not be a span to replace. Instead, we insert the name after
|
||||
// the `{`, which will be the first character of arg_span. If the argument does *not*
|
||||
// have formatting, there may or may not be a span to replace. This is because
|
||||
// whitespace is allowed in arguments without formatting (such as `format!("{ }", 1);`)
|
||||
// but is not allowed in arguments with formatting (an error will be generated in cases
|
||||
// like `format!("{ :1.1}", 1.0f32);`.
|
||||
// For the message span, if there is formatting, we want to use the opening `{` and the
|
||||
// next character, which will the `:` indicating the start of formatting. If there is
|
||||
// not any formatting, we want to underline the entire span.
|
||||
if self.has_formatting {
|
||||
cx.arg_spans.get(self.cur_piece).map_or((None, None), |arg_span| {
|
||||
(
|
||||
Some(arg_span.with_lo(arg_span.lo() + BytePos(1)).shrink_to_lo()),
|
||||
Some(arg_span.with_hi(arg_span.lo() + BytePos(2))),
|
||||
)
|
||||
})
|
||||
} else {
|
||||
cx.arg_spans.get(self.cur_piece).map_or((None, None), |arg_span| {
|
||||
let replace_start = arg_span.lo() + BytePos(1);
|
||||
let replace_end = arg_span.hi() - BytePos(1);
|
||||
let to_replace = arg_span.with_lo(replace_start).with_hi(replace_end);
|
||||
(Some(to_replace), Some(*arg_span))
|
||||
})
|
||||
}
|
||||
} else {
|
||||
(None, None)
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -117,10 +139,18 @@ impl PositionalNamedArgsLint {
|
|||
cur_piece: usize,
|
||||
inner_span_to_replace: Option<rustc_parse_format::InnerSpan>,
|
||||
names: &FxHashMap<Symbol, (usize, Span)>,
|
||||
has_formatting: bool,
|
||||
) {
|
||||
let start_of_named_args = total_args_length - names.len();
|
||||
if current_positional_arg >= start_of_named_args {
|
||||
self.maybe_push(format_argument_index, ty, cur_piece, inner_span_to_replace, names)
|
||||
self.maybe_push(
|
||||
format_argument_index,
|
||||
ty,
|
||||
cur_piece,
|
||||
inner_span_to_replace,
|
||||
names,
|
||||
has_formatting,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -134,6 +164,7 @@ impl PositionalNamedArgsLint {
|
|||
cur_piece: usize,
|
||||
inner_span_to_replace: Option<rustc_parse_format::InnerSpan>,
|
||||
names: &FxHashMap<Symbol, (usize, Span)>,
|
||||
has_formatting: bool,
|
||||
) {
|
||||
let named_arg = names
|
||||
.iter()
|
||||
|
@ -156,6 +187,7 @@ impl PositionalNamedArgsLint {
|
|||
inner_span_to_replace,
|
||||
replacement,
|
||||
positional_named_arg_span,
|
||||
has_formatting,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -414,6 +446,9 @@ impl<'a, 'b> Context<'a, 'b> {
|
|||
PositionalNamedArgType::Precision,
|
||||
);
|
||||
|
||||
let has_precision = arg.format.precision != Count::CountImplied;
|
||||
let has_width = arg.format.width != Count::CountImplied;
|
||||
|
||||
// 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 {
|
||||
|
@ -426,6 +461,7 @@ impl<'a, 'b> Context<'a, 'b> {
|
|||
self.curpiece,
|
||||
arg_end,
|
||||
&self.names,
|
||||
has_precision || has_width,
|
||||
);
|
||||
|
||||
Exact(i)
|
||||
|
@ -439,6 +475,7 @@ impl<'a, 'b> Context<'a, 'b> {
|
|||
self.curpiece,
|
||||
None,
|
||||
&self.names,
|
||||
has_precision || has_width,
|
||||
);
|
||||
Exact(i)
|
||||
}
|
||||
|
@ -529,6 +566,7 @@ impl<'a, 'b> Context<'a, 'b> {
|
|||
self.curpiece,
|
||||
*inner_span,
|
||||
&self.names,
|
||||
true,
|
||||
);
|
||||
self.verify_arg_type(Exact(i), Count);
|
||||
}
|
||||
|
@ -1150,24 +1188,22 @@ pub fn expand_format_args_nl<'cx>(
|
|||
|
||||
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);
|
||||
let (position_sp_to_replace, position_sp_for_msg) = named_arg.get_positional_arg_spans(cx);
|
||||
|
||||
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,
|
||||
),
|
||||
diagnostic: BuiltinLintDiagnostics::NamedArgumentUsedPositionally {
|
||||
position_sp_to_replace,
|
||||
position_sp_for_msg,
|
||||
named_arg_sp: named_arg.positional_named_arg_span,
|
||||
named_arg_name: named_arg.replacement.to_string(),
|
||||
is_formatting_arg: named_arg.ty != PositionalNamedArgType::Arg,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue