Desugar builtin#format_args

This commit is contained in:
Lukas Wirth 2023-09-06 15:21:41 +02:00
parent abe8f1ece4
commit e243a03da1
19 changed files with 783 additions and 243 deletions

View file

@ -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,
}

View file

@ -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, "<EFBFBD>"),
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()));
}
}

View file

@ -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))
}

View file

@ -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);

View file

@ -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),
}))
}
}

View file

@ -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 {

View file

@ -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()),
}
}

View file

@ -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)))
}
}

View file

@ -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,
}
}

View file

@ -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}}")?,
}
}

View file

@ -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];

View file

@ -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,

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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) {

View file

@ -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());

View file

@ -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
"#]],
);
}

View file

@ -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;
}
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_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,
}
#[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 }
}
}