mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-27 05:23:24 +00:00
Desugar builtin#format_args
This commit is contained in:
parent
abe8f1ece4
commit
e243a03da1
19 changed files with 783 additions and 243 deletions
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -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(|(¶m, 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()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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),
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 ¶ms.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()),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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}}")?,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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 }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue