Prevent ICE when formatting item-only vec!{} (#5879)

* Prevent ICE when formatting item-only `vec!{}`

Fixes 5735

Attempting to format invocations of macros which are considered "forced
bracket macros" (currently only `vec!`), but are invoked with braces
instead of brackets, and contain only items in their token trees,
currently triggers an ICE in rustfmt. This is because the function that
handles formatting macro invocations containing only items,
`rewrite_macro_with_items`, assumes that the forced delimiter style of
the macro being formatted is the same as the delimiter style in the
macro's source text when attempting to locate the span after the macro's
opening delimiter. This leads to the construction of an invalid span,
triggering the ICE.

The fix here is to pass the old delimiter style to
`rewrite_macro_with_items` as well, so that it can successfully locate
the span.
This commit is contained in:
tdanniels 2023-08-17 12:21:53 -07:00 committed by GitHub
parent a57d57b16a
commit e480739e56
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 31 additions and 9 deletions

View File

@ -255,6 +255,7 @@ fn rewrite_macro_inner(
&macro_name, &macro_name,
shape, shape,
style, style,
original_style,
position, position,
mac.span(), mac.span(),
); );
@ -295,20 +296,19 @@ fn rewrite_macro_inner(
// If we are rewriting `vec!` macro or other special macros, // If we are rewriting `vec!` macro or other special macros,
// then we can rewrite this as a usual array literal. // then we can rewrite this as a usual array literal.
// Otherwise, we must preserve the original existence of trailing comma. // Otherwise, we must preserve the original existence of trailing comma.
let macro_name = &macro_name.as_str();
let mut force_trailing_comma = if trailing_comma { let mut force_trailing_comma = if trailing_comma {
Some(SeparatorTactic::Always) Some(SeparatorTactic::Always)
} else { } else {
Some(SeparatorTactic::Never) Some(SeparatorTactic::Never)
}; };
if FORCED_BRACKET_MACROS.contains(macro_name) && !is_nested_macro { if is_forced_bracket && !is_nested_macro {
context.leave_macro(); context.leave_macro();
if context.use_block_indent() { if context.use_block_indent() {
force_trailing_comma = Some(SeparatorTactic::Vertical); force_trailing_comma = Some(SeparatorTactic::Vertical);
}; };
} }
let rewrite = rewrite_array( let rewrite = rewrite_array(
macro_name, &macro_name,
arg_vec.iter(), arg_vec.iter(),
mac.span(), mac.span(),
context, context,
@ -1402,15 +1402,19 @@ fn rewrite_macro_with_items(
macro_name: &str, macro_name: &str,
shape: Shape, shape: Shape,
style: Delimiter, style: Delimiter,
original_style: Delimiter,
position: MacroPosition, position: MacroPosition,
span: Span, span: Span,
) -> Option<String> { ) -> Option<String> {
let (opener, closer) = match style { let style_to_delims = |style| match style {
Delimiter::Parenthesis => ("(", ")"), Delimiter::Parenthesis => Some(("(", ")")),
Delimiter::Bracket => ("[", "]"), Delimiter::Bracket => Some(("[", "]")),
Delimiter::Brace => (" {", "}"), Delimiter::Brace => Some((" {", "}")),
_ => return None, _ => None,
}; };
let (opener, closer) = style_to_delims(style)?;
let (original_opener, _) = style_to_delims(original_style)?;
let trailing_semicolon = match style { let trailing_semicolon = match style {
Delimiter::Parenthesis | Delimiter::Bracket if position == MacroPosition::Item => ";", Delimiter::Parenthesis | Delimiter::Bracket if position == MacroPosition::Item => ";",
_ => "", _ => "",
@ -1418,7 +1422,13 @@ fn rewrite_macro_with_items(
let mut visitor = FmtVisitor::from_context(context); let mut visitor = FmtVisitor::from_context(context);
visitor.block_indent = shape.indent.block_indent(context.config); visitor.block_indent = shape.indent.block_indent(context.config);
visitor.last_pos = context.snippet_provider.span_after(span, opener.trim());
// The current opener may be different from the original opener. This can happen
// if our macro is a forced bracket macro originally written with non-bracket
// delimiters. We need to use the original opener to locate the span after it.
visitor.last_pos = context
.snippet_provider
.span_after(span, original_opener.trim());
for item in items { for item in items {
let item = match item { let item = match item {
MacroArg::Item(item) => item, MacroArg::Item(item) => item,

View File

@ -0,0 +1,6 @@
fn find_errors(mut self) {
let errors: Vec<> = vec!{
#[debug_format = "A({})"]
struct A {}
};
}

View File

@ -0,0 +1,6 @@
fn find_errors(mut self) {
let errors: Vec = vec![
#[debug_format = "A({})"]
struct A {}
];
}