about summary refs log tree commit diff
diff options
context:
space:
mode:
authorLukas Wirth <lukastw97@gmail.com>2023-09-06 15:21:41 +0200
committerLukas Wirth <lukastw97@gmail.com>2023-09-06 15:21:41 +0200
commite243a03da1cef50148d1c2da5e64afa1656e444a (patch)
tree1bad94e09538074ce609441c1c898d5449627aee
parentabe8f1ece416e5627fa1e5cd30b86110f3f7d91b (diff)
downloadrust-e243a03da1cef50148d1c2da5e64afa1656e444a.tar.gz
rust-e243a03da1cef50148d1c2da5e64afa1656e444a.zip
Desugar builtin#format_args
-rw-r--r--crates/hir-def/src/body/lower.rs450
-rw-r--r--crates/hir-def/src/body/pretty.rs38
-rw-r--r--crates/hir-def/src/body/tests.rs89
-rw-r--r--crates/hir-def/src/hir.rs10
-rw-r--r--crates/hir-def/src/hir/format_args.rs17
-rw-r--r--crates/hir-def/src/item_tree.rs2
-rw-r--r--crates/hir-def/src/item_tree/pretty.rs64
-rw-r--r--crates/hir-def/src/lang_item.rs13
-rw-r--r--crates/hir-def/src/path.rs31
-rw-r--r--crates/hir-def/src/pretty.rs72
-rw-r--r--crates/hir-def/src/resolver.rs45
-rw-r--r--crates/hir-expand/src/name.rs19
-rw-r--r--crates/hir-ty/src/infer/closure.rs13
-rw-r--r--crates/hir-ty/src/infer/expr.rs22
-rw-r--r--crates/hir-ty/src/infer/mutability.rs12
-rw-r--r--crates/hir-ty/src/infer/path.rs23
-rw-r--r--crates/hir-ty/src/mir/lower.rs3
-rw-r--r--crates/hir-ty/src/tests/simple.rs17
-rw-r--r--crates/test-utils/src/minicore.rs86
19 files changed, 783 insertions, 243 deletions
diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs
index bb0127c9ef6..e6fdbba9cbe 100644
--- a/crates/hir-def/src/body/lower.rs
+++ b/crates/hir-def/src/body/lower.rs
@@ -25,13 +25,16 @@ use triomphe::Arc;
 
 use crate::{
     body::{Body, BodyDiagnostic, BodySourceMap, ExprPtr, LabelPtr, PatPtr},
+    builtin_type::BuiltinUint,
     data::adt::StructKind,
     db::DefDatabase,
     expander::Expander,
     hir::{
         dummy_expr_id,
         format_args::{
-            self, FormatArgs, FormatArgument, FormatArgumentKind, FormatArgumentsCollector,
+            self, FormatAlignment, FormatArgsPiece, FormatArgument, FormatArgumentKind,
+            FormatArgumentsCollector, FormatCount, FormatDebugHex, FormatOptions,
+            FormatPlaceholder, FormatSign, FormatTrait,
         },
         Array, Binding, BindingAnnotation, BindingId, BindingProblems, CaptureBy, ClosureKind,
         Expr, ExprId, InlineAsm, Label, LabelId, Literal, LiteralOrConst, MatchArm, Movability,
@@ -46,6 +49,8 @@ use crate::{
     AdtId, BlockId, BlockLoc, ConstBlockLoc, DefWithBodyId, ModuleDefId, UnresolvedMacro,
 };
 
+type FxIndexSet<K> = indexmap::IndexSet<K, std::hash::BuildHasherDefault<rustc_hash::FxHasher>>;
+
 pub(super) fn lower(
     db: &dyn DefDatabase,
     owner: DefWithBodyId,
@@ -661,50 +666,10 @@ impl ExprCollector<'_> {
                 let fields = e.fields().map(|it| it.as_name()).collect();
                 self.alloc_expr(Expr::OffsetOf(OffsetOf { container, fields }), syntax_ptr)
             }
-            ast::Expr::FormatArgsExpr(f) => {
-                let mut args = FormatArgumentsCollector::new();
-                f.args().for_each(|arg| {
-                    args.add(FormatArgument {
-                        kind: match arg.name() {
-                            Some(name) => FormatArgumentKind::Named(name.as_name()),
-                            None => FormatArgumentKind::Normal,
-                        },
-                        expr: self.collect_expr_opt(arg.expr()),
-                    });
-                });
-                let template = f.template();
-                let fmt_snippet = template.as_ref().map(ToString::to_string);
-                let expr = self.collect_expr_opt(f.template());
-                if let Expr::Literal(Literal::String(_)) = self.body[expr] {
-                    let source = self.source_map.expr_map_back[expr].clone();
-                    let is_direct_literal = source.file_id == self.expander.current_file_id;
-                    if let ast::Expr::Literal(l) =
-                        source.value.to_node(&self.db.parse_or_expand(source.file_id))
-                    {
-                        if let ast::LiteralKind::String(s) = l.kind() {
-                            return Some(self.alloc_expr(
-                                Expr::FormatArgs(format_args::parse(
-                                    expr,
-                                    &s,
-                                    fmt_snippet,
-                                    args,
-                                    is_direct_literal,
-                                )),
-                                syntax_ptr,
-                            ));
-                        }
-                    }
-                }
-
-                self.alloc_expr(
-                    Expr::FormatArgs(FormatArgs {
-                        template_expr: expr,
-                        template: Default::default(),
-                        arguments: args.finish(),
-                    }),
-                    syntax_ptr,
-                )
-            }
+            ast::Expr::FormatArgsExpr(f) => match self.collect_format_args(f, syntax_ptr) {
+                Ok(value) => value,
+                Err(value) => return value,
+            },
         })
     }
 
@@ -1604,6 +1569,395 @@ impl ExprCollector<'_> {
         }
     }
     // endregion: labels
+
+    // region: format
+
+    fn collect_format_args(
+        &mut self,
+        f: ast::FormatArgsExpr,
+        syntax_ptr: AstPtr<ast::Expr>,
+    ) -> Result<la_arena::Idx<Expr>, Option<la_arena::Idx<Expr>>> {
+        let mut args = FormatArgumentsCollector::new();
+        f.args().for_each(|arg| {
+            args.add(FormatArgument {
+                kind: match arg.name() {
+                    Some(name) => FormatArgumentKind::Named(name.as_name()),
+                    None => FormatArgumentKind::Normal,
+                },
+                expr: self.collect_expr_opt(arg.expr()),
+            });
+        });
+        let template = f.template();
+        let fmt_snippet = template.as_ref().map(ToString::to_string);
+
+        // FIXME: We shouldn't allocate this one, just resolve and expand the macros to fetch the
+        // string literal!
+        let expr = self.collect_expr_opt(template);
+
+        let fmt = 'b: {
+            if let Expr::Literal(Literal::String(_)) = self.body[expr] {
+                let source = self.source_map.expr_map_back[expr].clone();
+                let is_direct_literal = source.file_id == self.expander.current_file_id;
+                if let ast::Expr::Literal(l) =
+                    source.value.to_node(&self.db.parse_or_expand(source.file_id))
+                {
+                    if let ast::LiteralKind::String(s) = l.kind() {
+                        break 'b format_args::parse(
+                            expr,
+                            &s,
+                            fmt_snippet,
+                            args,
+                            is_direct_literal,
+                            |name| self.alloc_expr_desugared(Expr::Path(Path::from(name))),
+                        );
+                    }
+                }
+            }
+            todo!();
+        };
+
+        // Create a list of all _unique_ (argument, format trait) combinations.
+        // E.g. "{0} {0:x} {0} {1}" -> [(0, Display), (0, LowerHex), (1, Display)]
+        let mut argmap = FxIndexSet::default();
+        for piece in fmt.template.iter() {
+            let FormatArgsPiece::Placeholder(placeholder) = piece else { continue };
+            if let Ok(index) = placeholder.argument.index {
+                argmap.insert((index, ArgumentType::Format(placeholder.format_trait)));
+            }
+        }
+
+        let lit_pieces =
+            fmt.template
+                .iter()
+                .enumerate()
+                .filter_map(|(i, piece)| {
+                    match piece {
+                        FormatArgsPiece::Literal(s) => Some(
+                            self.alloc_expr_desugared(Expr::Literal(Literal::String(s.clone()))),
+                        ),
+                        &FormatArgsPiece::Placeholder(_) => {
+                            // Inject empty string before placeholders when not already preceded by a literal piece.
+                            if i == 0
+                                || matches!(fmt.template[i - 1], FormatArgsPiece::Placeholder(_))
+                            {
+                                Some(self.alloc_expr_desugared(Expr::Literal(Literal::String(
+                                    "".into(),
+                                ))))
+                            } else {
+                                None
+                            }
+                        }
+                    }
+                })
+                .collect();
+        let lit_pieces = self.alloc_expr_desugared(Expr::Array(Array::ElementList {
+            elements: lit_pieces,
+            is_assignee_expr: false,
+        }));
+        let lit_pieces = self.alloc_expr_desugared(Expr::Ref {
+            expr: lit_pieces,
+            rawness: Rawness::Ref,
+            mutability: Mutability::Shared,
+        });
+        let format_options = {
+            // Generate:
+            //     &[format_spec_0, format_spec_1, format_spec_2]
+            let elements = fmt
+                .template
+                .iter()
+                .filter_map(|piece| {
+                    let FormatArgsPiece::Placeholder(placeholder) = piece else { return None };
+                    Some(self.make_format_spec(placeholder, &mut argmap))
+                })
+                .collect();
+            let array = self.alloc_expr_desugared(Expr::Array(Array::ElementList {
+                elements,
+                is_assignee_expr: false,
+            }));
+            self.alloc_expr_desugared(Expr::Ref {
+                expr: array,
+                rawness: Rawness::Ref,
+                mutability: Mutability::Shared,
+            })
+        };
+        let arguments = &*fmt.arguments.arguments;
+
+        let args = if arguments.is_empty() {
+            let expr = self.alloc_expr_desugared(Expr::Array(Array::ElementList {
+                elements: Box::default(),
+                is_assignee_expr: false,
+            }));
+            self.alloc_expr_desugared(Expr::Ref {
+                expr,
+                rawness: Rawness::Ref,
+                mutability: Mutability::Shared,
+            })
+        } 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),
+            //             …
+            //         ]
+            //     }
+            let args = argmap
+                .iter()
+                .map(|&(arg_index, ty)| {
+                    let arg = self.alloc_expr_desugared(Expr::Ref {
+                        expr: arguments[arg_index].expr,
+                        rawness: Rawness::Ref,
+                        mutability: Mutability::Shared,
+                    });
+                    self.make_argument(arg, ty)
+                })
+                .collect();
+            let array = self.alloc_expr_desugared(Expr::Array(Array::ElementList {
+                elements: args,
+                is_assignee_expr: false,
+            }));
+            self.alloc_expr_desugared(Expr::Ref {
+                expr: array,
+                rawness: Rawness::Ref,
+                mutability: Mutability::Shared,
+            })
+        };
+
+        // Generate:
+        //     <core::fmt::Arguments>::new_v1_formatted(
+        //         lit_pieces,
+        //         args,
+        //         format_options,
+        //         unsafe { ::core::fmt::UnsafeArg::new() }
+        //     )
+
+        let Some(new_v1_formatted) =
+            LangItem::FormatArguments.ty_rel_path(self.db, self.krate, name![new_v1_formatted])
+        else {
+            todo!()
+        };
+        let Some(unsafe_arg_new) =
+            LangItem::FormatUnsafeArg.ty_rel_path(self.db, self.krate, name![new])
+        else {
+            todo!()
+        };
+        let new_v1_formatted = self.alloc_expr_desugared(Expr::Path(new_v1_formatted));
+
+        let unsafe_arg_new = self.alloc_expr_desugared(Expr::Path(unsafe_arg_new));
+        let unsafe_arg_new = self.alloc_expr_desugared(Expr::Call {
+            callee: unsafe_arg_new,
+            args: Box::default(),
+            is_assignee_expr: false,
+        });
+        let unsafe_arg_new = self.alloc_expr_desugared(Expr::Unsafe {
+            id: None,
+            statements: Box::default(),
+            tail: Some(unsafe_arg_new),
+        });
+
+        Ok(self.alloc_expr(
+            Expr::Call {
+                callee: new_v1_formatted,
+                args: Box::new([lit_pieces, args, format_options, unsafe_arg_new]),
+                is_assignee_expr: false,
+            },
+            syntax_ptr,
+        ))
+    }
+
+    /// Generate a hir expression for a format_args placeholder specification.
+    ///
+    /// Generates
+    ///
+    /// ```text
+    ///     <core::fmt::rt::Placeholder::new(
+    ///         …usize, // position
+    ///         '…', // fill
+    ///         <core::fmt::rt::Alignment>::…, // alignment
+    ///         …u32, // flags
+    ///         <core::fmt::rt::Count::…>, // width
+    ///         <core::fmt::rt::Count::…>, // precision
+    ///     )
+    /// ```
+    fn make_format_spec(
+        &mut self,
+        placeholder: &FormatPlaceholder,
+        argmap: &mut FxIndexSet<(usize, ArgumentType)>,
+    ) -> ExprId {
+        let position = match placeholder.argument.index {
+            Ok(arg_index) => {
+                let (i, _) =
+                    argmap.insert_full((arg_index, ArgumentType::Format(placeholder.format_trait)));
+                self.alloc_expr_desugared(Expr::Literal(Literal::Uint(
+                    i as u128,
+                    Some(BuiltinUint::Usize),
+                )))
+            }
+            Err(_) => self.missing_expr(),
+        };
+        let &FormatOptions {
+            ref width,
+            ref precision,
+            alignment,
+            fill,
+            sign,
+            alternate,
+            zero_pad,
+            debug_hex,
+        } = &placeholder.format_options;
+        let fill = self.alloc_expr_desugared(Expr::Literal(Literal::Char(fill.unwrap_or(' '))));
+
+        let Some(align) = LangItem::FormatAlignment.ty_rel_path(
+            self.db,
+            self.krate,
+            match alignment {
+                Some(FormatAlignment::Left) => name![Left],
+                Some(FormatAlignment::Right) => name![Right],
+                Some(FormatAlignment::Center) => name![Center],
+                None => name![Unknown],
+            },
+        ) else {
+            todo!()
+        };
+        let align = self.alloc_expr_desugared(Expr::Path(align));
+        // This needs to match `Flag` in library/core/src/fmt/rt.rs.
+        let flags: u32 = ((sign == Some(FormatSign::Plus)) as u32)
+            | ((sign == Some(FormatSign::Minus)) as u32) << 1
+            | (alternate as u32) << 2
+            | (zero_pad as u32) << 3
+            | ((debug_hex == Some(FormatDebugHex::Lower)) as u32) << 4
+            | ((debug_hex == Some(FormatDebugHex::Upper)) as u32) << 5;
+        let flags = self.alloc_expr_desugared(Expr::Literal(Literal::Uint(
+            flags as u128,
+            Some(BuiltinUint::U32),
+        )));
+        let precision = self.make_count(&precision, argmap);
+        let width = self.make_count(&width, argmap);
+        let Some(format_placeholder_new) =
+            LangItem::FormatPlaceholder.ty_rel_path(self.db, self.krate, name![new])
+        else {
+            todo!()
+        };
+        let format_placeholder_new = self.alloc_expr_desugared(Expr::Path(format_placeholder_new));
+        self.alloc_expr_desugared(Expr::Call {
+            callee: format_placeholder_new,
+            args: Box::new([position, fill, align, flags, precision, width]),
+            is_assignee_expr: false,
+        })
+    }
+
+    /// Generate a hir expression for a format_args Count.
+    ///
+    /// Generates:
+    ///
+    /// ```text
+    ///     <core::fmt::rt::Count>::Is(…)
+    /// ```
+    ///
+    /// or
+    ///
+    /// ```text
+    ///     <core::fmt::rt::Count>::Param(…)
+    /// ```
+    ///
+    /// or
+    ///
+    /// ```text
+    ///     <core::fmt::rt::Count>::Implied
+    /// ```
+    fn make_count(
+        &mut self,
+        count: &Option<FormatCount>,
+        argmap: &mut FxIndexSet<(usize, ArgumentType)>,
+    ) -> ExprId {
+        match count {
+            Some(FormatCount::Literal(n)) => {
+                let Some(count_is) =
+                    LangItem::FormatCount.ty_rel_path(self.db, self.krate, name![Is])
+                else {
+                    todo!()
+                };
+                let count_is = self.alloc_expr_desugared(Expr::Path(count_is));
+                let args = self.alloc_expr_desugared(Expr::Literal(Literal::Uint(
+                    *n as u128,
+                    Some(BuiltinUint::Usize),
+                )));
+                self.alloc_expr_desugared(Expr::Call {
+                    callee: count_is,
+                    args: Box::new([args]),
+                    is_assignee_expr: false,
+                })
+            }
+            Some(FormatCount::Argument(arg)) => {
+                if let Ok(arg_index) = arg.index {
+                    let (i, _) = argmap.insert_full((arg_index, ArgumentType::Usize));
+                    let Some(count_param) =
+                        LangItem::FormatCount.ty_rel_path(self.db, self.krate, name![Param])
+                    else {
+                        todo!()
+                    };
+                    let count_param = self.alloc_expr_desugared(Expr::Path(count_param));
+                    let args = self.alloc_expr_desugared(Expr::Literal(Literal::Uint(
+                        i as u128,
+                        Some(BuiltinUint::Usize),
+                    )));
+                    self.alloc_expr_desugared(Expr::Call {
+                        callee: count_param,
+                        args: Box::new([args]),
+                        is_assignee_expr: false,
+                    })
+                } else {
+                    self.missing_expr()
+                }
+            }
+            None => {
+                let Some(count_param) =
+                    LangItem::FormatCount.ty_rel_path(self.db, self.krate, name![Implied])
+                else {
+                    todo!()
+                };
+                self.alloc_expr_desugared(Expr::Path(count_param))
+            }
+        }
+    }
+
+    /// Generate a hir expression representing an argument to a format_args invocation.
+    ///
+    /// Generates:
+    ///
+    /// ```text
+    ///     <core::fmt::Argument>::new_…(arg)
+    /// ```
+    fn make_argument(&mut self, arg: ExprId, ty: ArgumentType) -> ExprId {
+        use ArgumentType::*;
+        use FormatTrait::*;
+        let Some(new_fn) = LangItem::FormatArgument.ty_rel_path(
+            self.db,
+            self.krate,
+            match ty {
+                Format(Display) => name![new_display],
+                Format(Debug) => name![new_debug],
+                Format(LowerExp) => name![new_lower_exp],
+                Format(UpperExp) => name![new_upper_exp],
+                Format(Octal) => name![new_octal],
+                Format(Pointer) => name![new_pointer],
+                Format(Binary) => name![new_binary],
+                Format(LowerHex) => name![new_lower_hex],
+                Format(UpperHex) => name![new_upper_hex],
+                Usize => name![from_usize],
+            },
+        ) else {
+            todo!()
+        };
+        let new_fn = self.alloc_expr_desugared(Expr::Path(new_fn));
+        self.alloc_expr_desugared(Expr::Call {
+            callee: new_fn,
+            args: Box::new([arg]),
+            is_assignee_expr: false,
+        })
+    }
+    // endregion: format
 }
 
 fn pat_literal_to_hir(lit: &ast::LiteralPat) -> Option<(Literal, ast::Literal)> {
@@ -1679,3 +2033,9 @@ fn comma_follows_token(t: Option<syntax::SyntaxToken>) -> bool {
     (|| syntax::algo::skip_trivia_token(t?.next_token()?, syntax::Direction::Next))()
         .map_or(false, |it| it.kind() == syntax::T![,])
 }
+
+#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
+enum ArgumentType {
+    Format(FormatTrait),
+    Usize,
+}
diff --git a/crates/hir-def/src/body/pretty.rs b/crates/hir-def/src/body/pretty.rs
index b67ed2fb385..31c2e9c1fb9 100644
--- a/crates/hir-def/src/body/pretty.rs
+++ b/crates/hir-def/src/body/pretty.rs
@@ -2,7 +2,6 @@
 
 use std::fmt::{self, Write};
 
-use hir_expand::db::ExpandDatabase;
 use itertools::Itertools;
 use syntax::ast::HasName;
 
@@ -52,8 +51,7 @@ pub(super) fn print_body_hir(db: &dyn DefDatabase, body: &Body, owner: DefWithBo
         }
     };
 
-    let mut p =
-        Printer { db: db.upcast(), body, buf: header, indent_level: 0, needs_indent: false };
+    let mut p = Printer { db, body, buf: header, indent_level: 0, needs_indent: false };
     if let DefWithBodyId::FunctionId(it) = owner {
         p.buf.push('(');
         body.params.iter().zip(&db.function_data(it).params).for_each(|(&param, ty)| {
@@ -77,8 +75,7 @@ pub(super) fn print_expr_hir(
     _owner: DefWithBodyId,
     expr: ExprId,
 ) -> String {
-    let mut p =
-        Printer { db: db.upcast(), body, buf: String::new(), indent_level: 0, needs_indent: false };
+    let mut p = Printer { db, body, buf: String::new(), indent_level: 0, needs_indent: false };
     p.print_expr(expr);
     p.buf
 }
@@ -99,7 +96,7 @@ macro_rules! wln {
 }
 
 struct Printer<'a> {
-    db: &'a dyn ExpandDatabase,
+    db: &'a dyn DefDatabase,
     body: &'a Body,
     buf: String,
     indent_level: usize,
@@ -156,18 +153,16 @@ impl Printer<'_> {
             Expr::Missing => w!(self, "�"),
             Expr::Underscore => w!(self, "_"),
             Expr::InlineAsm(_) => w!(self, "builtin#asm(_)"),
-            Expr::FormatArgs(_fmt_args) => {
-                w!(self, "builtin#format_args(");
-                // FIXME
-                w!(self, ")");
-            }
             Expr::OffsetOf(offset_of) => {
                 w!(self, "builtin#offset_of(");
                 self.print_type_ref(&offset_of.container);
                 w!(
                     self,
                     ", {})",
-                    offset_of.fields.iter().format_with(".", |field, f| f(&field.display(self.db)))
+                    offset_of
+                        .fields
+                        .iter()
+                        .format_with(".", |field, f| f(&field.display(self.db.upcast())))
                 );
             }
             Expr::Path(path) => self.print_path(path),
@@ -189,7 +184,7 @@ impl Printer<'_> {
             }
             Expr::Loop { body, label } => {
                 if let Some(lbl) = label {
-                    w!(self, "{}: ", self.body[*lbl].name.display(self.db));
+                    w!(self, "{}: ", self.body[*lbl].name.display(self.db.upcast()));
                 }
                 w!(self, "loop ");
                 self.print_expr(*body);
@@ -209,7 +204,7 @@ impl Printer<'_> {
             }
             Expr::MethodCall { receiver, method_name, args, generic_args } => {
                 self.print_expr(*receiver);
-                w!(self, ".{}", method_name.display(self.db));
+                w!(self, ".{}", method_name.display(self.db.upcast()));
                 if let Some(args) = generic_args {
                     w!(self, "::<");
                     print_generic_args(self.db, args, self).unwrap();
@@ -247,13 +242,13 @@ impl Printer<'_> {
             Expr::Continue { label } => {
                 w!(self, "continue");
                 if let Some(lbl) = label {
-                    w!(self, " {}", self.body[*lbl].name.display(self.db));
+                    w!(self, " {}", self.body[*lbl].name.display(self.db.upcast()));
                 }
             }
             Expr::Break { expr, label } => {
                 w!(self, "break");
                 if let Some(lbl) = label {
-                    w!(self, " {}", self.body[*lbl].name.display(self.db));
+                    w!(self, " {}", self.body[*lbl].name.display(self.db.upcast()));
                 }
                 if let Some(expr) = expr {
                     self.whitespace();
@@ -292,7 +287,7 @@ impl Printer<'_> {
                 w!(self, "{{");
                 self.indented(|p| {
                     for field in &**fields {
-                        w!(p, "{}: ", field.name.display(self.db));
+                        w!(p, "{}: ", field.name.display(self.db.upcast()));
                         p.print_expr(field.expr);
                         wln!(p, ",");
                     }
@@ -309,7 +304,7 @@ impl Printer<'_> {
             }
             Expr::Field { expr, name } => {
                 self.print_expr(*expr);
-                w!(self, ".{}", name.display(self.db));
+                w!(self, ".{}", name.display(self.db.upcast()));
             }
             Expr::Await { expr } => {
                 self.print_expr(*expr);
@@ -447,7 +442,8 @@ impl Printer<'_> {
             }
             Expr::Literal(lit) => self.print_literal(lit),
             Expr::Block { id: _, statements, tail, label } => {
-                let label = label.map(|lbl| format!("{}: ", self.body[lbl].name.display(self.db)));
+                let label =
+                    label.map(|lbl| format!("{}: ", self.body[lbl].name.display(self.db.upcast())));
                 self.print_block(label.as_deref(), statements, tail);
             }
             Expr::Unsafe { id: _, statements, tail } => {
@@ -523,7 +519,7 @@ impl Printer<'_> {
                 w!(self, " {{");
                 self.indented(|p| {
                     for arg in args.iter() {
-                        w!(p, "{}: ", arg.name.display(self.db));
+                        w!(p, "{}: ", arg.name.display(self.db.upcast()));
                         p.print_pat(arg.pat);
                         wln!(p, ",");
                     }
@@ -682,6 +678,6 @@ impl Printer<'_> {
             BindingAnnotation::Ref => "ref ",
             BindingAnnotation::RefMut => "ref mut ",
         };
-        w!(self, "{}{}", mode, name.display(self.db));
+        w!(self, "{}{}", mode, name.display(self.db.upcast()));
     }
 }
diff --git a/crates/hir-def/src/body/tests.rs b/crates/hir-def/src/body/tests.rs
index d5582011645..76880bf68ed 100644
--- a/crates/hir-def/src/body/tests.rs
+++ b/crates/hir-def/src/body/tests.rs
@@ -1,13 +1,13 @@
 mod block;
 
 use base_db::{fixture::WithFixture, SourceDatabase};
-use expect_test::Expect;
+use expect_test::{expect, Expect};
 
 use crate::{test_db::TestDB, ModuleDefId};
 
 use super::*;
 
-fn lower(ra_fixture: &str) -> Arc<Body> {
+fn lower(ra_fixture: &str) -> (TestDB, Arc<Body>, DefWithBodyId) {
     let db = TestDB::with_files(ra_fixture);
 
     let krate = db.crate_graph().iter().next().unwrap();
@@ -21,8 +21,10 @@ fn lower(ra_fixture: &str) -> Arc<Body> {
             }
         }
     }
+    let fn_def = fn_def.unwrap().into();
 
-    db.body(fn_def.unwrap().into())
+    let body = db.body(fn_def);
+    (db, body, fn_def)
 }
 
 fn def_map_at(ra_fixture: &str) -> String {
@@ -138,3 +140,84 @@ mod m {
 "#,
     );
 }
+
+#[test]
+fn desugar_builtin_format_args() {
+    // Regression test for a path resolution bug introduced with inner item handling.
+    let (db, body, def) = lower(
+        r#"
+//- minicore: fmt
+fn main() {
+    let are = "are";
+    let count = 10;
+    builtin#format_args("hello {count:02} {} friends, we {are:?} {0}{last}", "fancy", last = "!");
+}
+"#,
+    );
+
+    expect![[r#"
+        fn main() {
+            let are = "are";
+            let count = 10;
+            builtin#lang(Arguments::new_v1_formatted)(
+                &[
+                    "\"hello ", " ", " friends, we ", " ", "", "\"", 
+                ],
+                &[
+                    builtin#lang(Argument::new_display)(
+                        &count,
+                    ), builtin#lang(Argument::new_display)(
+                        &"fancy",
+                    ), builtin#lang(Argument::new_debug)(
+                        &are,
+                    ), builtin#lang(Argument::new_display)(
+                        &"!",
+                    ), 
+                ],
+                &[
+                    builtin#lang(Placeholder::new)(
+                        0usize,
+                        ' ',
+                        builtin#lang(Alignment::Unknown),
+                        8u32,
+                        builtin#lang(Count::Implied),
+                        builtin#lang(Count::Is)(
+                            2usize,
+                        ),
+                    ), builtin#lang(Placeholder::new)(
+                        1usize,
+                        ' ',
+                        builtin#lang(Alignment::Unknown),
+                        0u32,
+                        builtin#lang(Count::Implied),
+                        builtin#lang(Count::Implied),
+                    ), builtin#lang(Placeholder::new)(
+                        2usize,
+                        ' ',
+                        builtin#lang(Alignment::Unknown),
+                        0u32,
+                        builtin#lang(Count::Implied),
+                        builtin#lang(Count::Implied),
+                    ), builtin#lang(Placeholder::new)(
+                        1usize,
+                        ' ',
+                        builtin#lang(Alignment::Unknown),
+                        0u32,
+                        builtin#lang(Count::Implied),
+                        builtin#lang(Count::Implied),
+                    ), builtin#lang(Placeholder::new)(
+                        3usize,
+                        ' ',
+                        builtin#lang(Alignment::Unknown),
+                        0u32,
+                        builtin#lang(Count::Implied),
+                        builtin#lang(Count::Implied),
+                    ), 
+                ],
+                unsafe {
+                    builtin#lang(UnsafeArg::new)()
+                },
+            );
+        }"#]]
+    .assert_eq(&body.pretty_print(&db, def))
+}
diff --git a/crates/hir-def/src/hir.rs b/crates/hir-def/src/hir.rs
index 98220de388e..591ee77c70a 100644
--- a/crates/hir-def/src/hir.rs
+++ b/crates/hir-def/src/hir.rs
@@ -25,7 +25,6 @@ use syntax::ast;
 
 use crate::{
     builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint},
-    hir::format_args::{FormatArgs, FormatArgumentKind},
     path::{GenericArgs, Path},
     type_ref::{Mutability, Rawness, TypeRef},
     BlockId, ConstBlockId,
@@ -284,7 +283,6 @@ pub enum Expr {
     Underscore,
     OffsetOf(OffsetOf),
     InlineAsm(InlineAsm),
-    FormatArgs(FormatArgs),
 }
 
 #[derive(Debug, Clone, PartialEq, Eq)]
@@ -358,14 +356,6 @@ impl Expr {
             Expr::Missing => {}
             Expr::Path(_) | Expr::OffsetOf(_) => {}
             Expr::InlineAsm(it) => f(it.e),
-            Expr::FormatArgs(it) => {
-                f(it.template_expr);
-                it.arguments
-                    .arguments
-                    .iter()
-                    .filter(|it| !matches!(it.kind, FormatArgumentKind::Captured(_)))
-                    .for_each(|it| f(it.expr));
-            }
             Expr::If { condition, then_branch, else_branch } => {
                 f(*condition);
                 f(*then_branch);
diff --git a/crates/hir-def/src/hir/format_args.rs b/crates/hir-def/src/hir/format_args.rs
index 8fa8b7246bc..6197ee16494 100644
--- a/crates/hir-def/src/hir/format_args.rs
+++ b/crates/hir-def/src/hir/format_args.rs
@@ -6,7 +6,7 @@ use syntax::{
     AstToken, SmolStr, TextRange,
 };
 
-use crate::hir::{dummy_expr_id, ExprId};
+use crate::hir::ExprId;
 
 mod parse;
 
@@ -31,7 +31,7 @@ pub enum FormatArgsPiece {
     Placeholder(FormatPlaceholder),
 }
 
-#[derive(Clone, Debug, PartialEq, Eq)]
+#[derive(Copy, Debug, Clone, PartialEq, Eq)]
 pub struct FormatPlaceholder {
     /// Index into [`FormatArgs::arguments`].
     pub argument: FormatArgPosition,
@@ -43,7 +43,7 @@ pub struct FormatPlaceholder {
     pub format_options: FormatOptions,
 }
 
-#[derive(Debug, Clone, PartialEq, Eq)]
+#[derive(Copy, Debug, Clone, PartialEq, Eq)]
 pub struct FormatArgPosition {
     /// Which argument this position refers to (Ok),
     /// or would've referred to if it existed (Err).
@@ -64,7 +64,7 @@ pub enum FormatArgPositionKind {
     Named,
 }
 
-#[derive(Debug, Clone, PartialEq, Eq)]
+#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
 pub enum FormatTrait {
     /// `{}`
     Display,
@@ -86,7 +86,7 @@ pub enum FormatTrait {
     UpperHex,
 }
 
-#[derive(Clone, Default, Debug, PartialEq, Eq)]
+#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)]
 pub struct FormatOptions {
     /// The width. E.g. `{:5}` or `{:width$}`.
     pub width: Option<FormatCount>,
@@ -131,7 +131,7 @@ pub enum FormatAlignment {
     Center,
 }
 
-#[derive(Clone, Debug, PartialEq, Eq)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
 pub enum FormatCount {
     /// `{:5}` or `{:.5}`
     Literal(usize),
@@ -171,6 +171,7 @@ pub(crate) fn parse(
     fmt_snippet: Option<String>,
     mut args: FormatArgumentsCollector,
     is_direct_literal: bool,
+    mut synth: impl FnMut(Name) -> ExprId,
 ) -> FormatArgs {
     let text = s.text();
     let str_style = match s.quote_offsets() {
@@ -252,10 +253,10 @@ pub(crate) fn parse(
                         // FIXME: Diagnose
                     }
                     Ok(args.add(FormatArgument {
-                        kind: FormatArgumentKind::Captured(name),
+                        kind: FormatArgumentKind::Captured(name.clone()),
                         // FIXME: This is problematic, we might want to synthesize a dummy
                         // expression proper and/or desugar these.
-                        expr: dummy_expr_id(),
+                        expr: synth(name),
                     }))
                 }
             }
diff --git a/crates/hir-def/src/item_tree.rs b/crates/hir-def/src/item_tree.rs
index 3e1922750b9..4c812b62a46 100644
--- a/crates/hir-def/src/item_tree.rs
+++ b/crates/hir-def/src/item_tree.rs
@@ -177,7 +177,7 @@ impl ItemTree {
     }
 
     pub fn pretty_print(&self, db: &dyn DefDatabase) -> String {
-        pretty::print_item_tree(db.upcast(), self)
+        pretty::print_item_tree(db, self)
     }
 
     fn data(&self) -> &ItemTreeData {
diff --git a/crates/hir-def/src/item_tree/pretty.rs b/crates/hir-def/src/item_tree/pretty.rs
index 4b852dd613e..417bd37c8a9 100644
--- a/crates/hir-def/src/item_tree/pretty.rs
+++ b/crates/hir-def/src/item_tree/pretty.rs
@@ -2,8 +2,6 @@
 
 use std::fmt::{self, Write};
 
-use hir_expand::db::ExpandDatabase;
-
 use crate::{
     generics::{TypeOrConstParamData, WherePredicate, WherePredicateTypeTarget},
     pretty::{print_path, print_type_bounds, print_type_ref},
@@ -12,7 +10,7 @@ use crate::{
 
 use super::*;
 
-pub(super) fn print_item_tree(db: &dyn ExpandDatabase, tree: &ItemTree) -> String {
+pub(super) fn print_item_tree(db: &dyn DefDatabase, tree: &ItemTree) -> String {
     let mut p = Printer { db, tree, buf: String::new(), indent_level: 0, needs_indent: true };
 
     if let Some(attrs) = tree.attrs.get(&AttrOwner::TopLevel) {
@@ -45,7 +43,7 @@ macro_rules! wln {
 }
 
 struct Printer<'a> {
-    db: &'a dyn ExpandDatabase,
+    db: &'a dyn DefDatabase,
     tree: &'a ItemTree,
     buf: String,
     indent_level: usize,
@@ -91,7 +89,7 @@ impl Printer<'_> {
                 self,
                 "#{}[{}{}]{}",
                 inner,
-                attr.path.display(self.db),
+                attr.path.display(self.db.upcast()),
                 attr.input.as_ref().map(|it| it.to_string()).unwrap_or_default(),
                 separated_by,
             );
@@ -106,7 +104,7 @@ impl Printer<'_> {
 
     fn print_visibility(&mut self, vis: RawVisibilityId) {
         match &self.tree[vis] {
-            RawVisibility::Module(path) => w!(self, "pub({}) ", path.display(self.db)),
+            RawVisibility::Module(path) => w!(self, "pub({}) ", path.display(self.db.upcast())),
             RawVisibility::Public => w!(self, "pub "),
         };
     }
@@ -121,7 +119,7 @@ impl Printer<'_> {
                         let Field { visibility, name, type_ref, ast_id: _ } = &this.tree[field];
                         this.print_attrs_of(field, "\n");
                         this.print_visibility(*visibility);
-                        w!(this, "{}: ", name.display(self.db));
+                        w!(this, "{}: ", name.display(self.db.upcast()));
                         this.print_type_ref(type_ref);
                         wln!(this, ",");
                     }
@@ -135,7 +133,7 @@ impl Printer<'_> {
                         let Field { visibility, name, type_ref, ast_id: _ } = &this.tree[field];
                         this.print_attrs_of(field, "\n");
                         this.print_visibility(*visibility);
-                        w!(this, "{}: ", name.display(self.db));
+                        w!(this, "{}: ", name.display(self.db.upcast()));
                         this.print_type_ref(type_ref);
                         wln!(this, ",");
                     }
@@ -168,20 +166,20 @@ impl Printer<'_> {
     fn print_use_tree(&mut self, use_tree: &UseTree) {
         match &use_tree.kind {
             UseTreeKind::Single { path, alias } => {
-                w!(self, "{}", path.display(self.db));
+                w!(self, "{}", path.display(self.db.upcast()));
                 if let Some(alias) = alias {
                     w!(self, " as {}", alias);
                 }
             }
             UseTreeKind::Glob { path } => {
                 if let Some(path) = path {
-                    w!(self, "{}::", path.display(self.db));
+                    w!(self, "{}::", path.display(self.db.upcast()));
                 }
                 w!(self, "*");
             }
             UseTreeKind::Prefixed { prefix, list } => {
                 if let Some(prefix) = prefix {
-                    w!(self, "{}::", prefix.display(self.db));
+                    w!(self, "{}::", prefix.display(self.db.upcast()));
                 }
                 w!(self, "{{");
                 for (i, tree) in list.iter().enumerate() {
@@ -209,7 +207,7 @@ impl Printer<'_> {
             ModItem::ExternCrate(it) => {
                 let ExternCrate { name, alias, visibility, ast_id: _ } = &self.tree[it];
                 self.print_visibility(*visibility);
-                w!(self, "extern crate {}", name.display(self.db));
+                w!(self, "extern crate {}", name.display(self.db.upcast()));
                 if let Some(alias) = alias {
                     w!(self, " as {}", alias);
                 }
@@ -256,7 +254,7 @@ impl Printer<'_> {
                 if let Some(abi) = abi {
                     w!(self, "extern \"{}\" ", abi);
                 }
-                w!(self, "fn {}", name.display(self.db));
+                w!(self, "fn {}", name.display(self.db.upcast()));
                 self.print_generic_params(explicit_generic_params);
                 w!(self, "(");
                 if !params.is_empty() {
@@ -290,7 +288,7 @@ impl Printer<'_> {
             ModItem::Struct(it) => {
                 let Struct { visibility, name, fields, generic_params, ast_id: _ } = &self.tree[it];
                 self.print_visibility(*visibility);
-                w!(self, "struct {}", name.display(self.db));
+                w!(self, "struct {}", name.display(self.db.upcast()));
                 self.print_generic_params(generic_params);
                 self.print_fields_and_where_clause(fields, generic_params);
                 if matches!(fields, Fields::Record(_)) {
@@ -302,7 +300,7 @@ impl Printer<'_> {
             ModItem::Union(it) => {
                 let Union { name, visibility, fields, generic_params, ast_id: _ } = &self.tree[it];
                 self.print_visibility(*visibility);
-                w!(self, "union {}", name.display(self.db));
+                w!(self, "union {}", name.display(self.db.upcast()));
                 self.print_generic_params(generic_params);
                 self.print_fields_and_where_clause(fields, generic_params);
                 if matches!(fields, Fields::Record(_)) {
@@ -314,14 +312,14 @@ impl Printer<'_> {
             ModItem::Enum(it) => {
                 let Enum { name, visibility, variants, generic_params, ast_id: _ } = &self.tree[it];
                 self.print_visibility(*visibility);
-                w!(self, "enum {}", name.display(self.db));
+                w!(self, "enum {}", name.display(self.db.upcast()));
                 self.print_generic_params(generic_params);
                 self.print_where_clause_and_opening_brace(generic_params);
                 self.indented(|this| {
                     for variant in variants.clone() {
                         let Variant { name, fields, ast_id: _ } = &this.tree[variant];
                         this.print_attrs_of(variant, "\n");
-                        w!(this, "{}", name.display(self.db));
+                        w!(this, "{}", name.display(self.db.upcast()));
                         this.print_fields(fields);
                         wln!(this, ",");
                     }
@@ -333,7 +331,7 @@ impl Printer<'_> {
                 self.print_visibility(*visibility);
                 w!(self, "const ");
                 match name {
-                    Some(name) => w!(self, "{}", name.display(self.db)),
+                    Some(name) => w!(self, "{}", name.display(self.db.upcast())),
                     None => w!(self, "_"),
                 }
                 w!(self, ": ");
@@ -347,7 +345,7 @@ impl Printer<'_> {
                 if *mutable {
                     w!(self, "mut ");
                 }
-                w!(self, "{}: ", name.display(self.db));
+                w!(self, "{}: ", name.display(self.db.upcast()));
                 self.print_type_ref(type_ref);
                 w!(self, " = _;");
                 wln!(self);
@@ -369,7 +367,7 @@ impl Printer<'_> {
                 if *is_auto {
                     w!(self, "auto ");
                 }
-                w!(self, "trait {}", name.display(self.db));
+                w!(self, "trait {}", name.display(self.db.upcast()));
                 self.print_generic_params(generic_params);
                 self.print_where_clause_and_opening_brace(generic_params);
                 self.indented(|this| {
@@ -382,7 +380,7 @@ impl Printer<'_> {
             ModItem::TraitAlias(it) => {
                 let TraitAlias { name, visibility, generic_params, ast_id: _ } = &self.tree[it];
                 self.print_visibility(*visibility);
-                w!(self, "trait {}", name.display(self.db));
+                w!(self, "trait {}", name.display(self.db.upcast()));
                 self.print_generic_params(generic_params);
                 w!(self, " = ");
                 self.print_where_clause(generic_params);
@@ -415,7 +413,7 @@ impl Printer<'_> {
                 let TypeAlias { name, visibility, bounds, type_ref, generic_params, ast_id: _ } =
                     &self.tree[it];
                 self.print_visibility(*visibility);
-                w!(self, "type {}", name.display(self.db));
+                w!(self, "type {}", name.display(self.db.upcast()));
                 self.print_generic_params(generic_params);
                 if !bounds.is_empty() {
                     w!(self, ": ");
@@ -432,7 +430,7 @@ impl Printer<'_> {
             ModItem::Mod(it) => {
                 let Mod { name, visibility, kind, ast_id: _ } = &self.tree[it];
                 self.print_visibility(*visibility);
-                w!(self, "mod {}", name.display(self.db));
+                w!(self, "mod {}", name.display(self.db.upcast()));
                 match kind {
                     ModKind::Inline { items } => {
                         w!(self, " {{");
@@ -450,16 +448,16 @@ impl Printer<'_> {
             }
             ModItem::MacroCall(it) => {
                 let MacroCall { path, ast_id: _, expand_to: _ } = &self.tree[it];
-                wln!(self, "{}!(...);", path.display(self.db));
+                wln!(self, "{}!(...);", path.display(self.db.upcast()));
             }
             ModItem::MacroRules(it) => {
                 let MacroRules { name, ast_id: _ } = &self.tree[it];
-                wln!(self, "macro_rules! {} {{ ... }}", name.display(self.db));
+                wln!(self, "macro_rules! {} {{ ... }}", name.display(self.db.upcast()));
             }
             ModItem::MacroDef(it) => {
                 let MacroDef { name, visibility, ast_id: _ } = &self.tree[it];
                 self.print_visibility(*visibility);
-                wln!(self, "macro {} {{ ... }}", name.display(self.db));
+                wln!(self, "macro {} {{ ... }}", name.display(self.db.upcast()));
             }
         }
 
@@ -491,7 +489,7 @@ impl Printer<'_> {
             }
             first = false;
             self.print_attrs_of(idx, " ");
-            w!(self, "{}", lt.name.display(self.db));
+            w!(self, "{}", lt.name.display(self.db.upcast()));
         }
         for (idx, x) in params.type_or_consts.iter() {
             if !first {
@@ -501,11 +499,11 @@ impl Printer<'_> {
             self.print_attrs_of(idx, " ");
             match x {
                 TypeOrConstParamData::TypeParamData(ty) => match &ty.name {
-                    Some(name) => w!(self, "{}", name.display(self.db)),
+                    Some(name) => w!(self, "{}", name.display(self.db.upcast())),
                     None => w!(self, "_anon_{}", idx.into_raw()),
                 },
                 TypeOrConstParamData::ConstParamData(konst) => {
-                    w!(self, "const {}: ", konst.name.display(self.db));
+                    w!(self, "const {}: ", konst.name.display(self.db.upcast()));
                     self.print_type_ref(&konst.ty);
                 }
             }
@@ -540,8 +538,8 @@ impl Printer<'_> {
                         wln!(
                             this,
                             "{}: {},",
-                            target.name.display(self.db),
-                            bound.name.display(self.db)
+                            target.name.display(self.db.upcast()),
+                            bound.name.display(self.db.upcast())
                         );
                         continue;
                     }
@@ -551,7 +549,7 @@ impl Printer<'_> {
                             if i != 0 {
                                 w!(this, ", ");
                             }
-                            w!(this, "{}", lt.display(self.db));
+                            w!(this, "{}", lt.display(self.db.upcast()));
                         }
                         w!(this, "> ");
                         (target, bound)
@@ -562,7 +560,7 @@ impl Printer<'_> {
                     WherePredicateTypeTarget::TypeRef(ty) => this.print_type_ref(ty),
                     WherePredicateTypeTarget::TypeOrConstParam(id) => {
                         match &params.type_or_consts[*id].name() {
-                            Some(name) => w!(this, "{}", name.display(self.db)),
+                            Some(name) => w!(this, "{}", name.display(self.db.upcast())),
                             None => w!(this, "_anon_{}", id.into_raw()),
                         }
                     }
diff --git a/crates/hir-def/src/lang_item.rs b/crates/hir-def/src/lang_item.rs
index 627479bb7c1..1ae6bd4c919 100644
--- a/crates/hir-def/src/lang_item.rs
+++ b/crates/hir-def/src/lang_item.rs
@@ -2,6 +2,7 @@
 //!
 //! This attribute to tell the compiler about semi built-in std library
 //! features, such as Fn family of traits.
+use hir_expand::name::Name;
 use rustc_hash::FxHashMap;
 use syntax::SmolStr;
 use triomphe::Arc;
@@ -238,7 +239,17 @@ impl LangItem {
 
     pub fn path(&self, db: &dyn DefDatabase, start_crate: CrateId) -> Option<Path> {
         let t = db.lang_item(start_crate, *self)?;
-        Some(Path::LangItem(t))
+        Some(Path::LangItem(t, None))
+    }
+
+    pub fn ty_rel_path(
+        &self,
+        db: &dyn DefDatabase,
+        start_crate: CrateId,
+        seg: Name,
+    ) -> Option<Path> {
+        let t = db.lang_item(start_crate, *self)?;
+        Some(Path::LangItem(t, Some(seg)))
     }
 }
 
diff --git a/crates/hir-def/src/path.rs b/crates/hir-def/src/path.rs
index 06530cc7ebd..3894172a5ad 100644
--- a/crates/hir-def/src/path.rs
+++ b/crates/hir-def/src/path.rs
@@ -47,7 +47,7 @@ pub enum Path {
     },
     /// A link to a lang item. It is used in desugaring of things like `it?`. We can show these
     /// links via a normal path since they might be private and not accessible in the usage place.
-    LangItem(LangItemTarget),
+    LangItem(LangItemTarget, Option<Name>),
 }
 
 /// Generic arguments to a path segment (e.g. the `i32` in `Option<i32>`). This
@@ -122,33 +122,40 @@ impl Path {
     pub fn kind(&self) -> &PathKind {
         match self {
             Path::Normal { mod_path, .. } => &mod_path.kind,
-            Path::LangItem(_) => &PathKind::Abs,
+            Path::LangItem(..) => &PathKind::Abs,
         }
     }
 
     pub fn type_anchor(&self) -> Option<&TypeRef> {
         match self {
             Path::Normal { type_anchor, .. } => type_anchor.as_deref(),
-            Path::LangItem(_) => None,
+            Path::LangItem(..) => None,
         }
     }
 
     pub fn segments(&self) -> PathSegments<'_> {
-        let Path::Normal { mod_path, generic_args, .. } = self else {
-            return PathSegments { segments: &[], generic_args: None };
-        };
-        let s =
-            PathSegments { segments: mod_path.segments(), generic_args: generic_args.as_deref() };
-        if let Some(generic_args) = s.generic_args {
-            assert_eq!(s.segments.len(), generic_args.len());
+        match self {
+            Path::Normal { mod_path, generic_args, .. } => {
+                let s = PathSegments {
+                    segments: mod_path.segments(),
+                    generic_args: generic_args.as_deref(),
+                };
+                if let Some(generic_args) = s.generic_args {
+                    assert_eq!(s.segments.len(), generic_args.len());
+                }
+                s
+            }
+            Path::LangItem(_, seg) => PathSegments {
+                segments: seg.as_ref().map_or(&[], |seg| std::slice::from_ref(seg)),
+                generic_args: None,
+            },
         }
-        s
     }
 
     pub fn mod_path(&self) -> Option<&ModPath> {
         match self {
             Path::Normal { mod_path, .. } => Some(&mod_path),
-            Path::LangItem(_) => None,
+            Path::LangItem(..) => None,
         }
     }
 
diff --git a/crates/hir-def/src/pretty.rs b/crates/hir-def/src/pretty.rs
index 11d58a6ba09..f4f5541e373 100644
--- a/crates/hir-def/src/pretty.rs
+++ b/crates/hir-def/src/pretty.rs
@@ -2,18 +2,54 @@
 
 use std::fmt::{self, Write};
 
-use hir_expand::{db::ExpandDatabase, mod_path::PathKind};
+use hir_expand::mod_path::PathKind;
 use intern::Interned;
 use itertools::Itertools;
 
 use crate::{
+    db::DefDatabase,
+    lang_item::LangItemTarget,
     path::{GenericArg, GenericArgs, Path},
     type_ref::{Mutability, TraitBoundModifier, TypeBound, TypeRef},
 };
 
-pub(crate) fn print_path(db: &dyn ExpandDatabase, path: &Path, buf: &mut dyn Write) -> fmt::Result {
-    if let Path::LangItem(it) = path {
-        return write!(buf, "$lang_item::{it:?}");
+pub(crate) fn print_path(db: &dyn DefDatabase, path: &Path, buf: &mut dyn Write) -> fmt::Result {
+    if let Path::LangItem(it, s) = path {
+        write!(buf, "builtin#lang(")?;
+        match *it {
+            LangItemTarget::ImplDef(it) => write!(buf, "{it:?}")?,
+            LangItemTarget::EnumId(it) => {
+                write!(buf, "{}", db.enum_data(it).name.display(db.upcast()))?
+            }
+            LangItemTarget::Function(it) => {
+                write!(buf, "{}", db.function_data(it).name.display(db.upcast()))?
+            }
+            LangItemTarget::Static(it) => {
+                write!(buf, "{}", db.static_data(it).name.display(db.upcast()))?
+            }
+            LangItemTarget::Struct(it) => {
+                write!(buf, "{}", db.struct_data(it).name.display(db.upcast()))?
+            }
+            LangItemTarget::Union(it) => {
+                write!(buf, "{}", db.union_data(it).name.display(db.upcast()))?
+            }
+            LangItemTarget::TypeAlias(it) => {
+                write!(buf, "{}", db.type_alias_data(it).name.display(db.upcast()))?
+            }
+            LangItemTarget::Trait(it) => {
+                write!(buf, "{}", db.trait_data(it).name.display(db.upcast()))?
+            }
+            LangItemTarget::EnumVariant(it) => write!(
+                buf,
+                "{}",
+                db.enum_data(it.parent).variants[it.local_id].name.display(db.upcast())
+            )?,
+        }
+
+        if let Some(s) = s {
+            write!(buf, "::{}", s.display(db.upcast()))?;
+        }
+        return write!(buf, ")");
     }
     match path.type_anchor() {
         Some(anchor) => {
@@ -44,7 +80,7 @@ pub(crate) fn print_path(db: &dyn ExpandDatabase, path: &Path, buf: &mut dyn Wri
             write!(buf, "::")?;
         }
 
-        write!(buf, "{}", segment.name.display(db))?;
+        write!(buf, "{}", segment.name.display(db.upcast()))?;
         if let Some(generics) = segment.args_and_bindings {
             write!(buf, "::<")?;
             print_generic_args(db, generics, buf)?;
@@ -57,7 +93,7 @@ pub(crate) fn print_path(db: &dyn ExpandDatabase, path: &Path, buf: &mut dyn Wri
 }
 
 pub(crate) fn print_generic_args(
-    db: &dyn ExpandDatabase,
+    db: &dyn DefDatabase,
     generics: &GenericArgs,
     buf: &mut dyn Write,
 ) -> fmt::Result {
@@ -83,7 +119,7 @@ pub(crate) fn print_generic_args(
             write!(buf, ", ")?;
         }
         first = false;
-        write!(buf, "{}", binding.name.display(db))?;
+        write!(buf, "{}", binding.name.display(db.upcast()))?;
         if !binding.bounds.is_empty() {
             write!(buf, ": ")?;
             print_type_bounds(db, &binding.bounds, buf)?;
@@ -97,19 +133,19 @@ pub(crate) fn print_generic_args(
 }
 
 pub(crate) fn print_generic_arg(
-    db: &dyn ExpandDatabase,
+    db: &dyn DefDatabase,
     arg: &GenericArg,
     buf: &mut dyn Write,
 ) -> fmt::Result {
     match arg {
         GenericArg::Type(ty) => print_type_ref(db, ty, buf),
-        GenericArg::Const(c) => write!(buf, "{}", c.display(db)),
-        GenericArg::Lifetime(lt) => write!(buf, "{}", lt.name.display(db)),
+        GenericArg::Const(c) => write!(buf, "{}", c.display(db.upcast())),
+        GenericArg::Lifetime(lt) => write!(buf, "{}", lt.name.display(db.upcast())),
     }
 }
 
 pub(crate) fn print_type_ref(
-    db: &dyn ExpandDatabase,
+    db: &dyn DefDatabase,
     type_ref: &TypeRef,
     buf: &mut dyn Write,
 ) -> fmt::Result {
@@ -143,7 +179,7 @@ pub(crate) fn print_type_ref(
             };
             write!(buf, "&")?;
             if let Some(lt) = lt {
-                write!(buf, "{} ", lt.name.display(db))?;
+                write!(buf, "{} ", lt.name.display(db.upcast()))?;
             }
             write!(buf, "{mtbl}")?;
             print_type_ref(db, pointee, buf)?;
@@ -151,7 +187,7 @@ pub(crate) fn print_type_ref(
         TypeRef::Array(elem, len) => {
             write!(buf, "[")?;
             print_type_ref(db, elem, buf)?;
-            write!(buf, "; {}]", len.display(db))?;
+            write!(buf, "; {}]", len.display(db.upcast()))?;
         }
         TypeRef::Slice(elem) => {
             write!(buf, "[")?;
@@ -198,7 +234,7 @@ pub(crate) fn print_type_ref(
 }
 
 pub(crate) fn print_type_bounds(
-    db: &dyn ExpandDatabase,
+    db: &dyn DefDatabase,
     bounds: &[Interned<TypeBound>],
     buf: &mut dyn Write,
 ) -> fmt::Result {
@@ -216,10 +252,14 @@ pub(crate) fn print_type_bounds(
                 print_path(db, path, buf)?;
             }
             TypeBound::ForLifetime(lifetimes, path) => {
-                write!(buf, "for<{}> ", lifetimes.iter().map(|it| it.display(db)).format(", "))?;
+                write!(
+                    buf,
+                    "for<{}> ",
+                    lifetimes.iter().map(|it| it.display(db.upcast())).format(", ")
+                )?;
                 print_path(db, path, buf)?;
             }
-            TypeBound::Lifetime(lt) => write!(buf, "{}", lt.name.display(db))?,
+            TypeBound::Lifetime(lt) => write!(buf, "{}", lt.name.display(db.upcast()))?,
             TypeBound::Error => write!(buf, "{{unknown}}")?,
         }
     }
diff --git a/crates/hir-def/src/resolver.rs b/crates/hir-def/src/resolver.rs
index 2f9187009e2..50da9ed06a0 100644
--- a/crates/hir-def/src/resolver.rs
+++ b/crates/hir-def/src/resolver.rs
@@ -156,22 +156,19 @@ impl Resolver {
     ) -> Option<(TypeNs, Option<usize>, Option<ImportOrExternCrate>)> {
         let path = match path {
             Path::Normal { mod_path, .. } => mod_path,
-            Path::LangItem(l) => {
-                return Some((
-                    match *l {
-                        LangItemTarget::Union(it) => TypeNs::AdtId(it.into()),
-                        LangItemTarget::TypeAlias(it) => TypeNs::TypeAliasId(it),
-                        LangItemTarget::Struct(it) => TypeNs::AdtId(it.into()),
-                        LangItemTarget::EnumVariant(it) => TypeNs::EnumVariantId(it),
-                        LangItemTarget::EnumId(it) => TypeNs::AdtId(it.into()),
-                        LangItemTarget::Trait(it) => TypeNs::TraitId(it),
-                        LangItemTarget::Function(_)
-                        | LangItemTarget::ImplDef(_)
-                        | LangItemTarget::Static(_) => return None,
-                    },
-                    None,
-                    None,
-                ))
+            Path::LangItem(l, seg) => {
+                let type_ns = match *l {
+                    LangItemTarget::Union(it) => TypeNs::AdtId(it.into()),
+                    LangItemTarget::TypeAlias(it) => TypeNs::TypeAliasId(it),
+                    LangItemTarget::Struct(it) => TypeNs::AdtId(it.into()),
+                    LangItemTarget::EnumVariant(it) => TypeNs::EnumVariantId(it),
+                    LangItemTarget::EnumId(it) => TypeNs::AdtId(it.into()),
+                    LangItemTarget::Trait(it) => TypeNs::TraitId(it),
+                    LangItemTarget::Function(_)
+                    | LangItemTarget::ImplDef(_)
+                    | LangItemTarget::Static(_) => return None,
+                };
+                return Some((type_ns, seg.as_ref().map(|_| 1), None));
             }
         };
         let first_name = path.segments().first()?;
@@ -256,7 +253,7 @@ impl Resolver {
     ) -> Option<ResolveValueResult> {
         let path = match path {
             Path::Normal { mod_path, .. } => mod_path,
-            Path::LangItem(l) => {
+            Path::LangItem(l, None) => {
                 return Some(ResolveValueResult::ValueNs(
                     match *l {
                         LangItemTarget::Function(it) => ValueNs::FunctionId(it),
@@ -272,6 +269,20 @@ impl Resolver {
                     None,
                 ))
             }
+            Path::LangItem(l, Some(_)) => {
+                let type_ns = match *l {
+                    LangItemTarget::Union(it) => TypeNs::AdtId(it.into()),
+                    LangItemTarget::TypeAlias(it) => TypeNs::TypeAliasId(it),
+                    LangItemTarget::Struct(it) => TypeNs::AdtId(it.into()),
+                    LangItemTarget::EnumVariant(it) => TypeNs::EnumVariantId(it),
+                    LangItemTarget::EnumId(it) => TypeNs::AdtId(it.into()),
+                    LangItemTarget::Trait(it) => TypeNs::TraitId(it),
+                    LangItemTarget::Function(_)
+                    | LangItemTarget::ImplDef(_)
+                    | LangItemTarget::Static(_) => return None,
+                };
+                return Some(ResolveValueResult::Partial(type_ns, 1, None));
+            }
         };
         let n_segments = path.segments().len();
         let tmp = name![self];
diff --git a/crates/hir-expand/src/name.rs b/crates/hir-expand/src/name.rs
index 5395b867c96..a876f48bda4 100644
--- a/crates/hir-expand/src/name.rs
+++ b/crates/hir-expand/src/name.rs
@@ -308,6 +308,16 @@ pub mod known {
         rust_2018,
         rust_2021,
         v1,
+        new_display,
+        new_debug,
+        new_lower_exp,
+        new_upper_exp,
+        new_octal,
+        new_pointer,
+        new_binary,
+        new_lower_hex,
+        new_upper_hex,
+        from_usize,
         // Components of known path (type name)
         Iterator,
         IntoIterator,
@@ -333,6 +343,13 @@ pub mod known {
         Not,
         None,
         Index,
+        Left,
+        Right,
+        Center,
+        Unknown,
+        Is,
+        Param,
+        Implied,
         // Components of known path (function name)
         filter_map,
         next,
@@ -341,6 +358,8 @@ pub mod known {
         is_empty,
         as_str,
         new,
+        new_v1_formatted,
+        none,
         // Builtin macros
         asm,
         assert,
diff --git a/crates/hir-ty/src/infer/closure.rs b/crates/hir-ty/src/infer/closure.rs
index 9431599ac79..1f040393f1f 100644
--- a/crates/hir-ty/src/infer/closure.rs
+++ b/crates/hir-ty/src/infer/closure.rs
@@ -9,10 +9,7 @@ use chalk_ir::{
 };
 use hir_def::{
     data::adt::VariantData,
-    hir::{
-        format_args::FormatArgumentKind, Array, BinaryOp, BindingId, CaptureBy, Expr, ExprId, Pat,
-        PatId, Statement, UnaryOp,
-    },
+    hir::{Array, BinaryOp, BindingId, CaptureBy, Expr, ExprId, Pat, PatId, Statement, UnaryOp},
     lang_item::LangItem,
     resolver::{resolver_for_expr, ResolveValueResult, ValueNs},
     DefWithBodyId, FieldId, HasModule, VariantId,
@@ -456,14 +453,6 @@ impl InferenceContext<'_> {
     fn walk_expr_without_adjust(&mut self, tgt_expr: ExprId) {
         match &self.body[tgt_expr] {
             Expr::OffsetOf(_) => (),
-            Expr::FormatArgs(fa) => {
-                self.walk_expr_without_adjust(fa.template_expr);
-                fa.arguments
-                    .arguments
-                    .iter()
-                    .filter(|it| !matches!(it.kind, FormatArgumentKind::Captured(_)))
-                    .for_each(|it| self.walk_expr_without_adjust(it.expr));
-            }
             Expr::InlineAsm(e) => self.walk_expr_without_adjust(e.e),
             Expr::If { condition, then_branch, else_branch } => {
                 self.consume_expr(*condition);
diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs
index 06742f7b36d..555a9fae48e 100644
--- a/crates/hir-ty/src/infer/expr.rs
+++ b/crates/hir-ty/src/infer/expr.rs
@@ -9,8 +9,7 @@ use chalk_ir::{cast::Cast, fold::Shift, DebruijnIndex, Mutability, TyVariableKin
 use hir_def::{
     generics::TypeOrConstParamData,
     hir::{
-        format_args::FormatArgumentKind, ArithOp, Array, BinaryOp, ClosureKind, Expr, ExprId,
-        LabelId, Literal, Statement, UnaryOp,
+        ArithOp, Array, BinaryOp, ClosureKind, Expr, ExprId, LabelId, Literal, Statement, UnaryOp,
     },
     lang_item::{LangItem, LangItemTarget},
     path::{GenericArg, GenericArgs},
@@ -849,25 +848,6 @@ impl InferenceContext<'_> {
                 self.infer_expr_no_expect(it.e);
                 self.result.standard_types.unit.clone()
             }
-            Expr::FormatArgs(fa) => {
-                fa.arguments
-                    .arguments
-                    .iter()
-                    .filter(|it| !matches!(it.kind, FormatArgumentKind::Captured(_)))
-                    .for_each(|it| _ = self.infer_expr_no_expect(it.expr));
-
-                match self
-                    .resolve_lang_item(LangItem::FormatArguments)
-                    .and_then(|it| it.as_struct())
-                {
-                    Some(s) => {
-                        // NOTE: This struct has a lifetime parameter, but we don't currently emit
-                        // those to chalk
-                        TyKind::Adt(AdtId(s.into()), Substitution::empty(Interner)).intern(Interner)
-                    }
-                    None => self.err_ty(),
-                }
-            }
         };
         // use a new type variable if we got unknown here
         let ty = self.insert_type_vars_shallow(ty);
diff --git a/crates/hir-ty/src/infer/mutability.rs b/crates/hir-ty/src/infer/mutability.rs
index 763f4ed2f96..b8a1af96fba 100644
--- a/crates/hir-ty/src/infer/mutability.rs
+++ b/crates/hir-ty/src/infer/mutability.rs
@@ -3,10 +3,7 @@
 
 use chalk_ir::Mutability;
 use hir_def::{
-    hir::{
-        format_args::FormatArgumentKind, Array, BinaryOp, BindingAnnotation, Expr, ExprId, PatId,
-        Statement, UnaryOp,
-    },
+    hir::{Array, BinaryOp, BindingAnnotation, Expr, ExprId, PatId, Statement, UnaryOp},
     lang_item::LangItem,
 };
 use hir_expand::name;
@@ -40,13 +37,6 @@ impl InferenceContext<'_> {
             Expr::Missing => (),
             Expr::InlineAsm(e) => self.infer_mut_expr_without_adjust(e.e, Mutability::Not),
             Expr::OffsetOf(_) => (),
-            Expr::FormatArgs(fa) => {
-                fa.arguments
-                    .arguments
-                    .iter()
-                    .filter(|it| !matches!(it.kind, FormatArgumentKind::Captured(_)))
-                    .for_each(|arg| self.infer_mut_expr_without_adjust(arg.expr, Mutability::Not));
-            }
             &Expr::If { condition, then_branch, else_branch } => {
                 self.infer_mut_expr(condition, Mutability::Not);
                 self.infer_mut_expr(then_branch, Mutability::Not);
diff --git a/crates/hir-ty/src/infer/path.rs b/crates/hir-ty/src/infer/path.rs
index 2a51c84db3a..81daf66ac16 100644
--- a/crates/hir-ty/src/infer/path.rs
+++ b/crates/hir-ty/src/infer/path.rs
@@ -178,13 +178,30 @@ impl InferenceContext<'_> {
         remaining_index: usize,
         id: ExprOrPatId,
     ) -> Option<(ValueNs, Substitution)> {
-        assert!(remaining_index < path.segments().len());
         // there may be more intermediate segments between the resolved one and
         // the end. Only the last segment needs to be resolved to a value; from
         // the segments before that, we need to get either a type or a trait ref.
 
-        let resolved_segment = path.segments().get(remaining_index - 1).unwrap();
-        let remaining_segments = path.segments().skip(remaining_index);
+        let _d;
+        let (resolved_segment, remaining_segments) = match path {
+            Path::Normal { .. } => {
+                assert!(remaining_index < path.segments().len());
+                (
+                    path.segments().get(remaining_index - 1).unwrap(),
+                    path.segments().skip(remaining_index),
+                )
+            }
+            Path::LangItem(_, seg) => (
+                PathSegment {
+                    name: {
+                        _d = hir_expand::name::known::Unknown;
+                        &_d
+                    },
+                    args_and_bindings: None,
+                },
+                path.segments(),
+            ),
+        };
         let is_before_last = remaining_segments.len() == 1;
 
         match (def, is_before_last) {
diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs
index 9cc98684bf3..b6408cea502 100644
--- a/crates/hir-ty/src/mir/lower.rs
+++ b/crates/hir-ty/src/mir/lower.rs
@@ -376,9 +376,6 @@ impl<'ctx> MirLowerCtx<'ctx> {
             Expr::InlineAsm(_) => {
                 not_supported!("builtin#asm")
             }
-            Expr::FormatArgs(_) => {
-                not_supported!("builtin#format_args")
-            }
             Expr::Missing => {
                 if let DefWithBodyId::FunctionId(f) = self.owner {
                     let assoc = f.lookup(self.db.upcast());
diff --git a/crates/hir-ty/src/tests/simple.rs b/crates/hir-ty/src/tests/simple.rs
index 0c73370a686..8140c4107b8 100644
--- a/crates/hir-ty/src/tests/simple.rs
+++ b/crates/hir-ty/src/tests/simple.rs
@@ -3615,22 +3615,15 @@ fn main() {
 
 #[test]
 fn builtin_format_args() {
-    check_infer(
+    check(
         r#"
-#[lang = "format_arguments"]
-pub struct Arguments<'a>;
+//- minicore: fmt
 fn main() {
     let are = "are";
-    builtin#format_args("hello {} friends, we {are} {0}{last}", "fancy", last = "!");
+    let count = 10;
+    builtin#format_args("hello {count:02} {} friends, we {are:?} {0}{last}", "fancy", last = "!");
+ // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type: Arguments<'_>
 }
 "#,
-        expect![[r#"
-            65..175 '{     ...!"); }': ()
-            75..78 'are': &str
-            81..86 '"are"': &str
-            92..172 'builti...= "!")': Arguments<'_>
-            152..159 '"fancy"': &str
-            168..171 '"!"': &str
-        "#]],
     );
 }
diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs
index c765f42447a..0fc95635e66 100644
--- a/crates/test-utils/src/minicore.rs
+++ b/crates/test-utils/src/minicore.rs
@@ -899,32 +899,90 @@ pub mod fmt {
         fn fmt(&self, f: &mut Formatter<'_>) -> Result;
     }
 
-    extern "C" {
-        type Opaque;
-    }
+    mod rt {
 
-    #[lang = "format_argument"]
-    pub struct Argument<'a> {
-        value: &'a Opaque,
-        formatter: fn(&Opaque, &mut Formatter<'_>) -> Result,
-    }
+        extern "C" {
+            type Opaque;
+        }
+
+        #[lang = "format_argument"]
+        pub struct Argument<'a> {
+            value: &'a Opaque,
+            formatter: fn(&Opaque, &mut Formatter<'_>) -> Result,
+        }
+
+        impl<'a> Argument<'a> {
+            pub fn new<'b, T>(x: &'b T, f: fn(&T, &mut Formatter<'_>) -> Result) -> Argument<'b> {
+                use crate::mem::transmute;
+                unsafe { Argument { formatter: transmute(f), value: transmute(x) } }
+            }
+        }
+
+        #[lang = "format_alignment"]
+        pub enum Alignment {
+            Left,
+            Right,
+            Center,
+            Unknown,
+        }
 
-    impl<'a> Argument<'a> {
-        pub fn new<'b, T>(x: &'b T, f: fn(&T, &mut Formatter<'_>) -> Result) -> Argument<'b> {
-            use crate::mem::transmute;
-            unsafe { Argument { formatter: transmute(f), value: transmute(x) } }
+        #[lang = "format_count"]
+        pub enum Count {
+            Is(usize),
+            Param(usize),
+            Implied,
+        }
+
+        #[lang = "format_placeholder"]
+        pub struct Placeholder {
+            pub position: usize,
+            pub fill: char,
+            pub align: Alignment,
+            pub flags: u32,
+            pub precision: Count,
+            pub width: Count,
+        }
+
+        impl Placeholder {
+            pub const fn new(
+                position: usize,
+                fill: char,
+                align: Alignment,
+                flags: u32,
+                precision: Count,
+                width: Count,
+            ) -> Self;
+        }
+
+        #[lang = "format_unsafe_arg"]
+        pub struct UnsafeArg {
+            _private: (),
+        }
+
+        impl UnsafeArg {
+            pub unsafe fn new() -> Self;
         }
     }
 
     #[lang = "format_arguments"]
     pub struct Arguments<'a> {
         pieces: &'a [&'static str],
-        args: &'a [Argument<'a>],
+        fmt: Option<&'a [rt::Placeholder]>,
+        args: &'a [rt::Argument<'a>],
     }
 
     impl<'a> Arguments<'a> {
         pub const fn new_v1(pieces: &'a [&'static str], args: &'a [Argument<'a>]) -> Arguments<'a> {
-            Arguments { pieces, args }
+            Arguments { pieces, fmt: None, args }
+        }
+
+        pub 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 }
         }
     }