Skip to content

Allow storing format_args!() in a variable #140748

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Jun 19, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions compiler/rustc_ast_lowering/src/expr.rs
Original file line number Diff line number Diff line change
@@ -2289,12 +2289,12 @@ impl<'hir> LoweringContext<'_, 'hir> {
span: Span,
elements: &'hir [hir::Expr<'hir>],
) -> hir::Expr<'hir> {
let addrof = hir::ExprKind::AddrOf(
hir::BorrowKind::Ref,
hir::Mutability::Not,
self.arena.alloc(self.expr(span, hir::ExprKind::Array(elements))),
);
self.expr(span, addrof)
let array = self.arena.alloc(self.expr(span, hir::ExprKind::Array(elements)));
self.expr_ref(span, array)
}

pub(super) fn expr_ref(&mut self, span: Span, expr: &'hir hir::Expr<'hir>) -> hir::Expr<'hir> {
self.expr(span, hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, expr))
}

pub(super) fn expr(&mut self, span: Span, kind: hir::ExprKind<'hir>) -> hir::Expr<'hir> {
206 changes: 77 additions & 129 deletions compiler/rustc_ast_lowering/src/format.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
use core::ops::ControlFlow;
use std::borrow::Cow;

use rustc_ast::visit::Visitor;
use rustc_ast::*;
use rustc_data_structures::fx::FxIndexMap;
use rustc_hir as hir;
@@ -476,77 +474,52 @@ fn expand_format_args<'hir>(
return hir::ExprKind::Call(new, new_args);
}

// If the args array contains exactly all the original arguments once,
// in order, we can use a simple array instead of a `match` construction.
// However, if there's a yield point in any argument except the first one,
// we don't do this, because an Argument cannot be kept across yield points.
//
// This is an optimization, speeding up compilation about 1-2% in some cases.
// See https://github.com/rust-lang/rust/pull/106770#issuecomment-1380790609
let use_simple_array = argmap.len() == arguments.len()
&& argmap.iter().enumerate().all(|(i, (&(j, _), _))| i == j)
&& arguments.iter().skip(1).all(|arg| !may_contain_yield_point(&arg.expr));

let args = if arguments.is_empty() {
let (let_statements, args) = if arguments.is_empty() {
// Generate:
// &<core::fmt::Argument>::none()
// []
(vec![], ctx.arena.alloc(ctx.expr(macsp, hir::ExprKind::Array(&[]))))
} else if argmap.len() == 1 && arguments.len() == 1 {
// Only one argument, so we don't need to make the `args` tuple.
//
// Note:
// `none()` just returns `[]`. We use `none()` rather than `[]` to limit the lifetime.
//
// This makes sure that this still fails to compile, even when the argument is inlined:
//
// ```
// let f = format_args!("{}", "a");
// println!("{f}"); // error E0716
// ```
//
// Cases where keeping the object around is allowed, such as `format_args!("a")`,
// are handled above by the `allow_const` case.
let none_fn = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
macsp,
hir::LangItem::FormatArgument,
sym::none,
));
let none = ctx.expr_call(macsp, none_fn, &[]);
ctx.expr(macsp, hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, none))
} else if use_simple_array {
// Generate:
// &[
// <core::fmt::Argument>::new_display(&arg0),
// <core::fmt::Argument>::new_lower_hex(&arg1),
// <core::fmt::Argument>::new_debug(&arg2),
// …
// ]
let elements = ctx.arena.alloc_from_iter(arguments.iter().zip(argmap).map(
|(arg, ((_, ty), placeholder_span))| {
// super let args = [<core::fmt::Argument>::new_display(&arg)];
let args = ctx.arena.alloc_from_iter(argmap.iter().map(
|(&(arg_index, ty), &placeholder_span)| {
let arg = &arguments[arg_index];
let placeholder_span =
placeholder_span.unwrap_or(arg.expr.span).with_ctxt(macsp.ctxt());
let arg_span = match arg.kind {
FormatArgumentKind::Captured(_) => placeholder_span,
_ => arg.expr.span.with_ctxt(macsp.ctxt()),
};
let arg = ctx.lower_expr(&arg.expr);
let ref_arg = ctx.arena.alloc(ctx.expr(
arg_span,
hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, arg),
));
let ref_arg = ctx.arena.alloc(ctx.expr_ref(arg.span.with_ctxt(macsp.ctxt()), arg));
make_argument(ctx, placeholder_span, ref_arg, ty)
},
));
ctx.expr_array_ref(macsp, elements)
let args = ctx.arena.alloc(ctx.expr(macsp, hir::ExprKind::Array(args)));
let args_ident = Ident::new(sym::args, macsp);
let (args_pat, args_hir_id) = ctx.pat_ident(macsp, args_ident);
let let_statement = ctx.stmt_super_let_pat(macsp, args_pat, Some(args));
(vec![let_statement], ctx.arena.alloc(ctx.expr_ident_mut(macsp, args_ident, args_hir_id)))
} else {
// Generate:
// &match (&arg0, &arg1, &…) {
// args => [
// <core::fmt::Argument>::new_display(args.0),
// <core::fmt::Argument>::new_lower_hex(args.1),
// <core::fmt::Argument>::new_debug(args.0),
// …
// ]
// }
// super let args = (&arg0, &arg1, &…);
let args_ident = Ident::new(sym::args, macsp);
let (args_pat, args_hir_id) = ctx.pat_ident(macsp, args_ident);
let elements = ctx.arena.alloc_from_iter(arguments.iter().map(|arg| {
let arg_expr = ctx.lower_expr(&arg.expr);
ctx.expr(
arg.expr.span.with_ctxt(macsp.ctxt()),
hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, arg_expr),
)
}));
let args_tuple = ctx.arena.alloc(ctx.expr(macsp, hir::ExprKind::Tup(elements)));
let let_statement_1 = ctx.stmt_super_let_pat(macsp, args_pat, Some(args_tuple));

// Generate:
// super let args = [
// <core::fmt::Argument>::new_display(args.0),
// <core::fmt::Argument>::new_lower_hex(args.1),
// <core::fmt::Argument>::new_debug(args.0),
// …
// ];
let args = ctx.arena.alloc_from_iter(argmap.iter().map(
|(&(arg_index, ty), &placeholder_span)| {
let arg = &arguments[arg_index];
@@ -567,58 +540,47 @@ fn expand_format_args<'hir>(
make_argument(ctx, placeholder_span, arg, ty)
},
));
let elements = ctx.arena.alloc_from_iter(arguments.iter().map(|arg| {
let arg_expr = ctx.lower_expr(&arg.expr);
ctx.expr(
arg.expr.span.with_ctxt(macsp.ctxt()),
hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, arg_expr),
)
}));
let args_tuple = ctx.arena.alloc(ctx.expr(macsp, hir::ExprKind::Tup(elements)));
let array = ctx.arena.alloc(ctx.expr(macsp, hir::ExprKind::Array(args)));
let match_arms = ctx.arena.alloc_from_iter([ctx.arm(args_pat, array)]);
let match_expr = ctx.arena.alloc(ctx.expr_match(
macsp,
args_tuple,
match_arms,
hir::MatchSource::FormatArgs,
));
ctx.expr(
macsp,
hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, match_expr),
let args = ctx.arena.alloc(ctx.expr(macsp, hir::ExprKind::Array(args)));
let (args_pat, args_hir_id) = ctx.pat_ident(macsp, args_ident);
let let_statement_2 = ctx.stmt_super_let_pat(macsp, args_pat, Some(args));
(
vec![let_statement_1, let_statement_2],
ctx.arena.alloc(ctx.expr_ident_mut(macsp, args_ident, args_hir_id)),
)
};

if let Some(format_options) = format_options {
// Generate:
// &args
let args = ctx.expr_ref(macsp, args);

let call = if let Some(format_options) = format_options {
// Generate:
// <core::fmt::Arguments>::new_v1_formatted(
// lit_pieces,
// args,
// format_options,
// unsafe { ::core::fmt::UnsafeArg::new() }
// )
// unsafe {
// <core::fmt::Arguments>::new_v1_formatted(
// lit_pieces,
// args,
// format_options,
// )
// }
let new_v1_formatted = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
macsp,
hir::LangItem::FormatArguments,
sym::new_v1_formatted,
));
let unsafe_arg_new = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
macsp,
hir::LangItem::FormatUnsafeArg,
sym::new,
));
let unsafe_arg_new_call = ctx.expr_call(macsp, unsafe_arg_new, &[]);
let args = ctx.arena.alloc_from_iter([lit_pieces, args, format_options]);
let call = ctx.expr_call(macsp, new_v1_formatted, args);
let hir_id = ctx.next_id();
let unsafe_arg = ctx.expr_block(ctx.arena.alloc(hir::Block {
stmts: &[],
expr: Some(unsafe_arg_new_call),
hir_id,
rules: hir::BlockCheckMode::UnsafeBlock(hir::UnsafeSource::CompilerGenerated),
span: macsp,
targeted_by_break: false,
}));
let args = ctx.arena.alloc_from_iter([lit_pieces, args, format_options, unsafe_arg]);
hir::ExprKind::Call(new_v1_formatted, args)
hir::ExprKind::Block(
ctx.arena.alloc(hir::Block {
stmts: &[],
expr: Some(call),
hir_id,
rules: hir::BlockCheckMode::UnsafeBlock(hir::UnsafeSource::CompilerGenerated),
span: macsp,
targeted_by_break: false,
}),
None,
)
} else {
// Generate:
// <core::fmt::Arguments>::new_v1(
@@ -632,35 +594,21 @@ fn expand_format_args<'hir>(
));
let new_args = ctx.arena.alloc_from_iter([lit_pieces, args]);
hir::ExprKind::Call(new_v1, new_args)
}
}

fn may_contain_yield_point(e: &ast::Expr) -> bool {
struct MayContainYieldPoint;

impl Visitor<'_> for MayContainYieldPoint {
type Result = ControlFlow<()>;

fn visit_expr(&mut self, e: &ast::Expr) -> ControlFlow<()> {
if let ast::ExprKind::Await(_, _) | ast::ExprKind::Yield(_) = e.kind {
ControlFlow::Break(())
} else {
visit::walk_expr(self, e)
}
}

fn visit_mac_call(&mut self, _: &ast::MacCall) -> ControlFlow<()> {
// Macros should be expanded at this point.
unreachable!("unexpanded macro in ast lowering");
}
};

fn visit_item(&mut self, _: &ast::Item) -> ControlFlow<()> {
// Do not recurse into nested items.
ControlFlow::Continue(())
}
if !let_statements.is_empty() {
// Generate:
// {
// super let …
// super let …
// <core::fmt::Arguments>::new_…(…)
// }
let call = ctx.arena.alloc(ctx.expr(macsp, call));
let block = ctx.block_all(macsp, ctx.arena.alloc_from_iter(let_statements), Some(call));
hir::ExprKind::Block(block, None)
} else {
call
}

MayContainYieldPoint.visit_expr(e).is_break()
}

fn for_all_argument_indexes(template: &mut [FormatArgsPiece], mut f: impl FnMut(&mut usize)) {
20 changes: 20 additions & 0 deletions compiler/rustc_ast_lowering/src/lib.rs
Original file line number Diff line number Diff line change
@@ -2301,6 +2301,26 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
self.stmt(span, hir::StmtKind::Let(self.arena.alloc(local)))
}

fn stmt_super_let_pat(
&mut self,
span: Span,
pat: &'hir hir::Pat<'hir>,
init: Option<&'hir hir::Expr<'hir>>,
) -> hir::Stmt<'hir> {
let hir_id = self.next_id();
let local = hir::LetStmt {
super_: Some(span),
hir_id,
init,
pat,
els: None,
source: hir::LocalSource::Normal,
span: self.lower_span(span),
ty: None,
};
self.stmt(span, hir::StmtKind::Let(self.arena.alloc(local)))
}

fn block_expr(&mut self, expr: &'hir hir::Expr<'hir>) -> &'hir hir::Block<'hir> {
self.block_all(expr.span, &[], Some(expr))
}
38 changes: 11 additions & 27 deletions compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
Original file line number Diff line number Diff line change
@@ -3201,14 +3201,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
let expr_ty: Option<Ty<'_>> =
visitor.prop_expr.map(|expr| typeck_results.expr_ty(expr).peel_refs());

let is_format_arguments_item = if let Some(expr_ty) = expr_ty
&& let ty::Adt(adt, _) = expr_ty.kind()
{
self.infcx.tcx.is_lang_item(adt.did(), LangItem::FormatArguments)
} else {
false
};

if visitor.found == 0
&& stmt.span.contains(proper_span)
&& let Some(p) = sm.span_to_margin(stmt.span)
@@ -3236,25 +3228,17 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
""
};

if !is_format_arguments_item {
let addition = format!(
"let {}binding = {};\n{}",
mutability,
s,
" ".repeat(p)
);
err.multipart_suggestion_verbose(
msg,
vec![
(stmt.span.shrink_to_lo(), addition),
(proper_span, "binding".to_string()),
],
Applicability::MaybeIncorrect,
);
} else {
err.note("the result of `format_args!` can only be assigned directly if no placeholders in its arguments are used");
err.note("to learn more, visit <https://doc.rust-lang.org/std/macro.format_args.html>");
}
let addition =
format!("let {}binding = {};\n{}", mutability, s, " ".repeat(p));
err.multipart_suggestion_verbose(
msg,
vec![
(stmt.span.shrink_to_lo(), addition),
(proper_span, "binding".to_string()),
],
Applicability::MaybeIncorrect,
);

suggested = true;
break;
}
38 changes: 2 additions & 36 deletions library/core/src/fmt/rt.rs
Original file line number Diff line number Diff line change
@@ -183,38 +183,6 @@ impl Argument<'_> {
ArgumentType::Placeholder { .. } => None,
}
}

/// Used by `format_args` when all arguments are gone after inlining,
/// when using `&[]` would incorrectly allow for a bigger lifetime.
///
/// This fails without format argument inlining, and that shouldn't be different
/// when the argument is inlined:
///
/// ```compile_fail,E0716
/// let f = format_args!("{}", "a");
/// println!("{f}");
/// ```
#[inline]
pub const fn none() -> [Self; 0] {
[]
}
}

/// This struct represents the unsafety of constructing an `Arguments`.
/// It exists, rather than an unsafe function, in order to simplify the expansion
/// of `format_args!(..)` and reduce the scope of the `unsafe` block.
#[lang = "format_unsafe_arg"]
pub struct UnsafeArg {
_private: (),
}

impl UnsafeArg {
/// See documentation where `UnsafeArg` is required to know when it is safe to
/// create and use `UnsafeArg`.
#[inline]
pub const unsafe fn new() -> Self {
Self { _private: () }
}
}

/// Used by the format_args!() macro to create a fmt::Arguments object.
@@ -248,8 +216,7 @@ impl<'a> Arguments<'a> {

/// Specifies nonstandard formatting parameters.
///
/// An `rt::UnsafeArg` is required because the following invariants must be held
/// in order for this function to be safe:
/// SAFETY: the following invariants must be held:
/// 1. The `pieces` slice must be at least as long as `fmt`.
/// 2. Every `rt::Placeholder::position` value within `fmt` must be a valid index of `args`.
/// 3. Every `rt::Count::Param` within `fmt` must contain a valid index of `args`.
@@ -261,11 +228,10 @@ impl<'a> Arguments<'a> {
/// const _: () = if false { panic!("a {:1}", "a") };
/// ```
#[inline]
pub fn new_v1_formatted(
pub unsafe fn new_v1_formatted(
pieces: &'a [&'static str],
args: &'a [rt::Argument<'a>],
fmt: &'a [rt::Placeholder],
_unsafe_arg: rt::UnsafeArg,
) -> Arguments<'a> {
Arguments { pieces, fmt: Some(fmt), args }
}
15 changes: 15 additions & 0 deletions library/coretests/tests/fmt/mod.rs
Original file line number Diff line number Diff line change
@@ -2,6 +2,21 @@ mod builders;
mod float;
mod num;

#[test]
fn test_lifetime() {
// Trigger all different forms of expansion,
// and check that each of them can be stored as a variable.
let a = format_args!("hello");
let a = format_args!("hello {a}");
let a = format_args!("hello {a:1}");
let a = format_args!("hello {a} {a:?}");
assert_eq!(a.to_string(), "hello hello hello hello hello hello hello");

// Without arguments, it should also work in consts.
const A: std::fmt::Arguments<'static> = format_args!("hello");
assert_eq!(A.to_string(), "hello");
}

#[test]
fn test_format_flags() {
// No residual flags left by pointer formatting
39 changes: 23 additions & 16 deletions src/tools/clippy/tests/ui/author/macro_in_closure.stdout
Original file line number Diff line number Diff line change
@@ -9,28 +9,35 @@ if let StmtKind::Let(local) = stmt.kind
&& let ExprKind::Call(func, args) = e.kind
&& paths::STD_IO_STDIO__PRINT.matches_path(cx, func) // Add the path to `clippy_utils::paths` if needed
&& args.len() == 1
&& let ExprKind::Call(func1, args1) = args[0].kind
&& paths::CORE_FMT_RT_NEW_V1.matches_path(cx, func1) // Add the path to `clippy_utils::paths` if needed
&& args1.len() == 2
&& let ExprKind::Block(block1, None) = args[0].kind
&& block1.stmts.len() == 1
&& let StmtKind::Let(local1) = block1.stmts[0].kind
&& let Some(init1) = local1.init
&& let ExprKind::Array(elements) = init1.kind
&& elements.len() == 1
&& let ExprKind::Call(func1, args1) = elements[0].kind
&& paths::CORE_FMT_RT_ARGUMENT_NEW_DISPLAY.matches_path(cx, func1) // Add the path to `clippy_utils::paths` if needed
&& args1.len() == 1
&& let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = args1[0].kind
&& let ExprKind::Array(elements) = inner.kind
&& elements.len() == 2
&& let ExprKind::Lit(ref lit) = elements[0].kind
&& let PatKind::Binding(BindingMode::NONE, _, name, None) = local1.pat.kind
&& name.as_str() == "args"
&& let Some(trailing_expr) = block1.expr
&& let ExprKind::Call(func2, args2) = trailing_expr.kind
&& paths::CORE_FMT_RT_NEW_V1.matches_path(cx, func2) // Add the path to `clippy_utils::paths` if needed
&& args2.len() == 2
&& let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner1) = args2[0].kind
&& let ExprKind::Array(elements1) = inner1.kind
&& elements1.len() == 2
&& let ExprKind::Lit(ref lit) = elements1[0].kind
&& let LitKind::Str(s, _) = lit.node
&& s.as_str() == ""
&& let ExprKind::Lit(ref lit1) = elements[1].kind
&& let ExprKind::Lit(ref lit1) = elements1[1].kind
&& let LitKind::Str(s1, _) = lit1.node
&& s1.as_str() == "\n"
&& let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner1) = args1[1].kind
&& let ExprKind::Array(elements1) = inner1.kind
&& elements1.len() == 1
&& let ExprKind::Call(func2, args2) = elements1[0].kind
&& paths::CORE_FMT_RT_ARGUMENT_NEW_DISPLAY.matches_path(cx, func2) // Add the path to `clippy_utils::paths` if needed
&& args2.len() == 1
&& let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner2) = args2[0].kind
&& let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner2) = args2[1].kind
&& block.expr.is_none()
&& let PatKind::Binding(BindingMode::NONE, _, name, None) = local.pat.kind
&& name.as_str() == "print_text"
&& let PatKind::Binding(BindingMode::NONE, _, name1, None) = local.pat.kind
&& name1.as_str() == "print_text"
{
// report your lint here
}
35 changes: 21 additions & 14 deletions src/tools/clippy/tests/ui/author/macro_in_loop.stdout
Original file line number Diff line number Diff line change
@@ -19,25 +19,32 @@ if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::Fo
&& let ExprKind::Call(func, args) = e1.kind
&& paths::STD_IO_STDIO__PRINT.matches_path(cx, func) // Add the path to `clippy_utils::paths` if needed
&& args.len() == 1
&& let ExprKind::Call(func1, args1) = args[0].kind
&& paths::CORE_FMT_RT_NEW_V1.matches_path(cx, func1) // Add the path to `clippy_utils::paths` if needed
&& args1.len() == 2
&& let ExprKind::Block(block2, None) = args[0].kind
&& block2.stmts.len() == 1
&& let StmtKind::Let(local) = block2.stmts[0].kind
&& let Some(init) = local.init
&& let ExprKind::Array(elements) = init.kind
&& elements.len() == 1
&& let ExprKind::Call(func1, args1) = elements[0].kind
&& paths::CORE_FMT_RT_ARGUMENT_NEW_DISPLAY.matches_path(cx, func1) // Add the path to `clippy_utils::paths` if needed
&& args1.len() == 1
&& let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = args1[0].kind
&& let ExprKind::Array(elements) = inner.kind
&& elements.len() == 2
&& let ExprKind::Lit(ref lit2) = elements[0].kind
&& let PatKind::Binding(BindingMode::NONE, _, name1, None) = local.pat.kind
&& name1.as_str() == "args"
&& let Some(trailing_expr) = block2.expr
&& let ExprKind::Call(func2, args2) = trailing_expr.kind
&& paths::CORE_FMT_RT_NEW_V1.matches_path(cx, func2) // Add the path to `clippy_utils::paths` if needed
&& args2.len() == 2
&& let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner1) = args2[0].kind
&& let ExprKind::Array(elements1) = inner1.kind
&& elements1.len() == 2
&& let ExprKind::Lit(ref lit2) = elements1[0].kind
&& let LitKind::Str(s, _) = lit2.node
&& s.as_str() == ""
&& let ExprKind::Lit(ref lit3) = elements[1].kind
&& let ExprKind::Lit(ref lit3) = elements1[1].kind
&& let LitKind::Str(s1, _) = lit3.node
&& s1.as_str() == "\n"
&& let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner1) = args1[1].kind
&& let ExprKind::Array(elements1) = inner1.kind
&& elements1.len() == 1
&& let ExprKind::Call(func2, args2) = elements1[0].kind
&& paths::CORE_FMT_RT_ARGUMENT_NEW_DISPLAY.matches_path(cx, func2) // Add the path to `clippy_utils::paths` if needed
&& args2.len() == 1
&& let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner2) = args2[0].kind
&& let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner2) = args2[1].kind
&& block1.expr.is_none()
&& block.expr.is_none()
{
1 change: 0 additions & 1 deletion src/tools/clippy/tests/ui/manual_inspect.fixed
Original file line number Diff line number Diff line change
@@ -154,7 +154,6 @@ fn main() {
});

let _ = [0]
//~^ suspicious_map
.into_iter()
.inspect(|&x| {
//~^ manual_inspect
1 change: 0 additions & 1 deletion src/tools/clippy/tests/ui/manual_inspect.rs
Original file line number Diff line number Diff line change
@@ -165,7 +165,6 @@ fn main() {
});

let _ = [0]
//~^ suspicious_map
.into_iter()
.map(|x| {
//~^ manual_inspect
23 changes: 3 additions & 20 deletions src/tools/clippy/tests/ui/manual_inspect.stderr
Original file line number Diff line number Diff line change
@@ -157,25 +157,8 @@ LL |
LL ~ println!("{}", x);
|

error: this call to `map()` won't have an effect on the call to `count()`
--> tests/ui/manual_inspect.rs:167:13
|
LL | let _ = [0]
| _____________^
LL | |
LL | | .into_iter()
LL | | .map(|x| {
... |
LL | | })
LL | | .count();
| |________________^
|
= help: make sure you did not confuse `map` with `filter`, `for_each` or `inspect`
= note: `-D clippy::suspicious-map` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::suspicious_map)]`

error: using `map` over `inspect`
--> tests/ui/manual_inspect.rs:170:10
--> tests/ui/manual_inspect.rs:169:10
|
LL | .map(|x| {
| ^^^
@@ -188,7 +171,7 @@ LL ~ println!("{}", x);
|

error: using `map` over `inspect`
--> tests/ui/manual_inspect.rs:203:30
--> tests/ui/manual_inspect.rs:202:30
|
LL | if let Some(x) = Some(1).map(|x| { println!("{x}");
| ^^^
@@ -200,5 +183,5 @@ LL | // Do not collapse code into this comment
LL ~ }) {
|

error: aborting due to 14 previous errors
error: aborting due to 13 previous errors

1 change: 0 additions & 1 deletion src/tools/tidy/src/issues.txt
Original file line number Diff line number Diff line change
@@ -305,7 +305,6 @@ ui/borrowck/issue-104639-lifetime-order.rs
ui/borrowck/issue-10876.rs
ui/borrowck/issue-109271-pass-self-into-closure.rs
ui/borrowck/issue-111554.rs
ui/borrowck/issue-114374-invalid-help-fmt-args.rs
ui/borrowck/issue-11493.rs
ui/borrowck/issue-115259-suggest-iter-mut.rs
ui/borrowck/issue-119915-bad-clone-suggestion.rs
159 changes: 94 additions & 65 deletions tests/mir-opt/sroa/lifetimes.foo.ScalarReplacementOfAggregates.diff
Original file line number Diff line number Diff line change
@@ -11,28 +11,29 @@
let _9: ();
let _10: ();
let mut _11: std::fmt::Arguments<'_>;
let mut _12: &[&str; 3];
let _13: &[&str; 3];
let _14: [&str; 3];
let mut _15: &[core::fmt::rt::Argument<'_>; 2];
let _16: &[core::fmt::rt::Argument<'_>; 2];
let _17: [core::fmt::rt::Argument<'_>; 2];
let mut _13: &std::boxed::Box<dyn std::fmt::Display>;
let mut _14: &u32;
let mut _16: core::fmt::rt::Argument<'_>;
let mut _17: &std::boxed::Box<dyn std::fmt::Display>;
let mut _18: core::fmt::rt::Argument<'_>;
let mut _19: &std::boxed::Box<dyn std::fmt::Display>;
let _20: &std::boxed::Box<dyn std::fmt::Display>;
let mut _21: core::fmt::rt::Argument<'_>;
let mut _22: &u32;
let _23: &u32;
let mut _25: bool;
let mut _26: isize;
let mut _27: isize;
let mut _28: isize;
+ let _29: std::result::Result<std::boxed::Box<dyn std::fmt::Display>, <T as Err>::Err>;
+ let _30: u32;
let mut _19: &u32;
let mut _20: &[&str; 3];
let _21: &[&str; 3];
let _22: [&str; 3];
let mut _23: &[core::fmt::rt::Argument<'_>; 2];
let _24: &[core::fmt::rt::Argument<'_>; 2];
let mut _26: &std::boxed::Box<dyn std::fmt::Display>;
let mut _27: &u32;
let mut _28: bool;
let mut _29: isize;
let mut _30: isize;
let mut _31: isize;
+ let _32: std::result::Result<std::boxed::Box<dyn std::fmt::Display>, <T as Err>::Err>;
+ let _33: u32;
scope 1 {
- debug foo => _1;
+ debug ((foo: Foo<T>).0: std::result::Result<std::boxed::Box<dyn std::fmt::Display>, <T as Err>::Err>) => _29;
+ debug ((foo: Foo<T>).1: u32) => _30;
+ debug ((foo: Foo<T>).0: std::result::Result<std::boxed::Box<dyn std::fmt::Display>, <T as Err>::Err>) => _32;
+ debug ((foo: Foo<T>).1: u32) => _33;
let _5: std::result::Result<std::boxed::Box<dyn std::fmt::Display>, <T as Err>::Err>;
scope 2 {
debug x => _5;
@@ -42,17 +43,29 @@
scope 4 {
debug x => _8;
let _8: std::boxed::Box<dyn std::fmt::Display>;
let mut _24: &[&str; 3];
let _12: (&std::boxed::Box<dyn std::fmt::Display>, &u32);
+ let _34: &std::boxed::Box<dyn std::fmt::Display>;
+ let _35: &u32;
scope 5 {
- debug args => _12;
+ debug ((args: (&Box<dyn std::fmt::Display>, &u32)).0: &std::boxed::Box<dyn std::fmt::Display>) => _34;
+ debug ((args: (&Box<dyn std::fmt::Display>, &u32)).1: &u32) => _35;
let _15: [core::fmt::rt::Argument<'_>; 2];
scope 6 {
debug args => _15;
let mut _25: &[&str; 3];
}
}
}
}
}
}

bb0: {
_25 = const false;
_28 = const false;
- StorageLive(_1);
+ StorageLive(_29);
+ StorageLive(_30);
+ StorageLive(_32);
+ StorageLive(_33);
+ nop;
StorageLive(_2);
StorageLive(_3);
@@ -66,77 +79,93 @@
_2 = Result::<Box<dyn std::fmt::Display>, <T as Err>::Err>::Ok(move _3);
StorageDead(_3);
- _1 = Foo::<T> { x: move _2, y: const 7_u32 };
+ _29 = move _2;
+ _30 = const 7_u32;
+ _32 = move _2;
+ _33 = const 7_u32;
+ nop;
StorageDead(_2);
StorageLive(_5);
_25 = const true;
_28 = const true;
- _5 = move (_1.0: std::result::Result<std::boxed::Box<dyn std::fmt::Display>, <T as Err>::Err>);
+ _5 = move _29;
+ _5 = move _32;
StorageLive(_6);
- _6 = copy (_1.1: u32);
+ _6 = copy _30;
+ _6 = copy _33;
_7 = discriminant(_5);
switchInt(move _7) -> [0: bb2, otherwise: bb7];
}

bb2: {
StorageLive(_8);
_25 = const false;
_28 = const false;
_8 = move ((_5 as Ok).0: std::boxed::Box<dyn std::fmt::Display>);
StorageLive(_9);
StorageLive(_10);
StorageLive(_11);
StorageLive(_12);
- StorageLive(_12);
+ StorageLive(_34);
+ StorageLive(_35);
+ nop;
StorageLive(_13);
_24 = const foo::<T>::promoted[0];
_13 = &(*_24);
_12 = &(*_13);
_13 = &_8;
StorageLive(_14);
_14 = &_6;
- _12 = (move _13, move _14);
+ _34 = move _13;
+ _35 = move _14;
+ nop;
StorageDead(_14);
StorageDead(_13);
StorageLive(_15);
StorageLive(_16);
StorageLive(_17);
StorageLive(_18);
StorageLive(_19);
StorageLive(_20);
_20 = &_8;
_19 = &(*_20);
_18 = core::fmt::rt::Argument::<'_>::new_display::<Box<dyn std::fmt::Display>>(move _19) -> [return: bb3, unwind unreachable];
- _26 = deref_copy (_12.0: &std::boxed::Box<dyn std::fmt::Display>);
+ _26 = deref_copy _34;
_17 = &(*_26);
_16 = core::fmt::rt::Argument::<'_>::new_display::<Box<dyn std::fmt::Display>>(move _17) -> [return: bb3, unwind unreachable];
}

bb3: {
StorageDead(_19);
StorageLive(_21);
StorageLive(_22);
StorageLive(_23);
_23 = &_6;
_22 = &(*_23);
_21 = core::fmt::rt::Argument::<'_>::new_display::<u32>(move _22) -> [return: bb4, unwind unreachable];
StorageDead(_17);
StorageLive(_18);
StorageLive(_19);
- _27 = deref_copy (_12.1: &u32);
+ _27 = deref_copy _35;
_19 = &(*_27);
_18 = core::fmt::rt::Argument::<'_>::new_display::<u32>(move _19) -> [return: bb4, unwind unreachable];
}

bb4: {
StorageDead(_22);
_17 = [move _18, move _21];
StorageDead(_21);
StorageDead(_19);
_15 = [move _16, move _18];
StorageDead(_18);
_16 = &_17;
_15 = &(*_16);
_11 = core::fmt::rt::<impl Arguments<'_>>::new_v1::<3, 2>(move _12, move _15) -> [return: bb5, unwind unreachable];
StorageDead(_16);
StorageLive(_20);
StorageLive(_21);
_25 = const foo::<T>::promoted[0];
_21 = &(*_25);
_20 = &(*_21);
StorageLive(_23);
StorageLive(_24);
_24 = &_15;
_23 = &(*_24);
_11 = core::fmt::rt::<impl Arguments<'_>>::new_v1::<3, 2>(move _20, move _23) -> [return: bb5, unwind unreachable];
}

bb5: {
StorageDead(_15);
StorageDead(_12);
StorageDead(_24);
StorageDead(_23);
StorageDead(_21);
StorageDead(_20);
_10 = _eprint(move _11) -> [return: bb6, unwind unreachable];
}

bb6: {
StorageDead(_11);
StorageDead(_23);
StorageDead(_20);
StorageDead(_17);
StorageDead(_16);
StorageDead(_13);
StorageDead(_15);
- StorageDead(_12);
+ StorageDead(_34);
+ StorageDead(_35);
+ nop;
StorageDead(_10);
_9 = const ();
StorageDead(_9);
@@ -156,22 +185,22 @@

bb9: {
StorageDead(_6);
_26 = discriminant(_5);
switchInt(move _26) -> [0: bb11, otherwise: bb13];
_29 = discriminant(_5);
switchInt(move _29) -> [0: bb11, otherwise: bb13];
}

bb10: {
_25 = const false;
_28 = const false;
StorageDead(_5);
- StorageDead(_1);
+ StorageDead(_29);
+ StorageDead(_30);
+ StorageDead(_32);
+ StorageDead(_33);
+ nop;
return;
}

bb11: {
switchInt(copy _25) -> [0: bb10, otherwise: bb12];
switchInt(copy _28) -> [0: bb10, otherwise: bb12];
}

bb12: {
4 changes: 2 additions & 2 deletions tests/ui/borrowck/clone-on-ref.stderr
Original file line number Diff line number Diff line change
@@ -30,7 +30,7 @@ LL | drop(x);
| ^ move out of `x` occurs here
LL |
LL | println!("{b}");
| --- borrow later used here
| - borrow later used here
|
help: if `T` implemented `Clone`, you could clone the value
--> $DIR/clone-on-ref.rs:11:8
@@ -57,7 +57,7 @@ LL | drop(x);
| ^ move out of `x` occurs here
LL |
LL | println!("{b:?}");
| ----- borrow later used here
| - borrow later used here
|
note: if `A` implemented `Clone`, you could clone the value
--> $DIR/clone-on-ref.rs:19:1
16 changes: 0 additions & 16 deletions tests/ui/borrowck/issue-114374-invalid-help-fmt-args.rs

This file was deleted.

31 changes: 0 additions & 31 deletions tests/ui/borrowck/issue-114374-invalid-help-fmt-args.stderr

This file was deleted.

5 changes: 3 additions & 2 deletions tests/ui/consts/recursive-const-in-impl.stderr
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
error: queries overflow the depth limit!
--> $DIR/recursive-const-in-impl.rs:11:14
--> $DIR/recursive-const-in-impl.rs:11:20
|
LL | println!("{}", Thing::<i32>::X);
| ^^^^
| ^^^^^^^^^^^^^^^
|
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "14"]` attribute to your crate (`recursive_const_in_impl`)
= note: query depth increased by 9 when simplifying constant for the type system `main::promoted[1]`
= note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)

error: aborting due to 1 previous error

2 changes: 1 addition & 1 deletion tests/ui/impl-trait/precise-capturing/foreign-2021.stderr
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@ LL | x.push(0);
| ^^^^^^^^^ mutable borrow occurs here
...
LL | println!("{h}");
| --- immutable borrow later used here
| - immutable borrow later used here
|
note: this call may capture more lifetimes than intended, because Rust 2024 has adjusted the `impl Trait` lifetime capture rules
--> $DIR/foreign-2021.rs:7:13
10 changes: 5 additions & 5 deletions tests/ui/impl-trait/precise-capturing/migration-note.stderr
Original file line number Diff line number Diff line change
@@ -23,7 +23,7 @@ LL | x.push(1);
| ^^^^^^^^^ mutable borrow occurs here
...
LL | println!("{a}");
| --- immutable borrow later used here
| - immutable borrow later used here
|
note: this call may capture more lifetimes than intended, because Rust 2024 has adjusted the `impl Trait` lifetime capture rules
--> $DIR/migration-note.rs:16:13
@@ -99,7 +99,7 @@ LL | x.push(1);
| ^ second mutable borrow occurs here
...
LL | println!("{a}");
| --- first borrow later used here
| - first borrow later used here
|
note: this call may capture more lifetimes than intended, because Rust 2024 has adjusted the `impl Trait` lifetime capture rules
--> $DIR/migration-note.rs:63:13
@@ -175,7 +175,7 @@ LL | s.f = 1;
| ^^^^^^^ `s.f` is assigned to here but it was already borrowed
...
LL | println!("{a}");
| --- borrow later used here
| - borrow later used here
|
note: this call may capture more lifetimes than intended, because Rust 2024 has adjusted the `impl Trait` lifetime capture rules
--> $DIR/migration-note.rs:112:13
@@ -197,7 +197,7 @@ LL | s.f = 1;
| ^^^^^^^ `s.f` is assigned to here but it was already borrowed
...
LL | println!("{a}");
| --- borrow later used here
| - borrow later used here
|
note: this call may capture more lifetimes than intended, because Rust 2024 has adjusted the `impl Trait` lifetime capture rules
--> $DIR/migration-note.rs:128:13
@@ -219,7 +219,7 @@ LL | s.f;
| ^^^ use of borrowed `s.f`
...
LL | println!("{a}");
| --- borrow later used here
| - borrow later used here
|
note: this call may capture more lifetimes than intended, because Rust 2024 has adjusted the `impl Trait` lifetime capture rules
--> $DIR/migration-note.rs:140:13
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@ LL | let g = some(&temp());
| |
| creates a temporary value which is freed while still in use
LL | println!("{a:?} {b:?} {c:?} {d:?} {e:?} {f:?} {g:?}");
| ----- borrow later used here
| - borrow later used here
|
help: consider using a `let` binding to create a longer lived value
|
10 changes: 0 additions & 10 deletions tests/ui/macros/format-args-temporaries-in-write.stderr
Original file line number Diff line number Diff line change
@@ -13,11 +13,6 @@ LL | };
| -- ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `MutexGuard`
| |
| `mutex` dropped here while still borrowed
|
help: consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped
|
LL | write!(Out, "{}", mutex.lock()); /* no semicolon */
| +

error[E0597]: `mutex` does not live long enough
--> $DIR/format-args-temporaries-in-write.rs:47:29
@@ -34,11 +29,6 @@ LL | };
| -- ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `MutexGuard`
| |
| `mutex` dropped here while still borrowed
|
help: consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped
|
LL | writeln!(Out, "{}", mutex.lock()); /* no semicolon */
| +

error: aborting due to 2 previous errors

4 changes: 2 additions & 2 deletions tests/ui/sized/unsized-binding.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
fn main() {
let x = *""; //~ ERROR E0277
println!("{}", x);
println!("{}", x);
drop(x);
drop(x);
}
4 changes: 2 additions & 2 deletions tests/ui/suggestions/issue-97760.stderr
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
error[E0277]: `<impl IntoIterator as IntoIterator>::Item` doesn't implement `std::fmt::Display`
--> $DIR/issue-97760.rs:4:19
--> $DIR/issue-97760.rs:4:20
|
LL | println!("{x}");
| ^^^ `<impl IntoIterator as IntoIterator>::Item` cannot be formatted with the default formatter
| ^ `<impl IntoIterator as IntoIterator>::Item` cannot be formatted with the default formatter
|
= help: the trait `std::fmt::Display` is not implemented for `<impl IntoIterator as IntoIterator>::Item`
= note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
6 changes: 4 additions & 2 deletions tests/ui/unpretty/exhaustive.hir.stdout
Original file line number Diff line number Diff line change
@@ -405,8 +405,10 @@ mod expressions {
fn expr_format_args() {
let expr;
format_arguments::new_const(&[]);
format_arguments::new_v1(&[""],
&[format_argument::new_display(&expr)]);
{
super let args = [format_argument::new_display(&expr)];
format_arguments::new_v1(&[""], &args)
};
}
}
mod items {
6 changes: 4 additions & 2 deletions tests/ui/unpretty/flattened-format-args.stdout
Original file line number Diff line number Diff line change
@@ -10,7 +10,9 @@ fn main() {
let x = 1;
// Should flatten to println!("a 123 b {x} xyz\n"):
{
::std::io::_print(format_arguments::new_v1(&["a 123 b ", " xyz\n"],
&[format_argument::new_display(&x)]));
::std::io::_print({
super let args = [format_argument::new_display(&x)];
format_arguments::new_v1(&["a 123 b ", " xyz\n"], &args)
});
};
}