Unnamed repository; edit this file 'description' to name the repository.
Desugar builtin#format_args
Lukas Wirth 2023-09-06
parent abe8f1e · commit e243a03
-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 bb0127c9ef..e6fdbba9cb 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 b67ed2fb38..31c2e9c1fb 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 d558201164..76880bf68e 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 98220de388..591ee77c70 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 8fa8b7246b..6197ee1649 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 3e1922750b..4c812b62a4 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 4b852dd613..417bd37c8a 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 627479bb7c..1ae6bd4c91 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 06530cc7eb..3894172a5a 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 11d58a6ba0..f4f5541e37 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 2f9187009e..50da9ed06a 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 5395b867c9..a876f48bda 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 9431599ac7..1f040393f1 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 06742f7b36..555a9fae48 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 763f4ed2f9..b8a1af96fb 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 2a51c84db3..81daf66ac1 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 9cc98684bf..b6408cea50 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 0c73370a68..8140c4107b 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 c765f42447..0fc95635e6 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 }
}
}