mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-28 04:44:57 +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::{
|
use crate::{
|
||||||
body::{Body, BodyDiagnostic, BodySourceMap, ExprPtr, LabelPtr, PatPtr},
|
body::{Body, BodyDiagnostic, BodySourceMap, ExprPtr, LabelPtr, PatPtr},
|
||||||
|
builtin_type::BuiltinUint,
|
||||||
data::adt::StructKind,
|
data::adt::StructKind,
|
||||||
db::DefDatabase,
|
db::DefDatabase,
|
||||||
expander::Expander,
|
expander::Expander,
|
||||||
hir::{
|
hir::{
|
||||||
dummy_expr_id,
|
dummy_expr_id,
|
||||||
format_args::{
|
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,
|
Array, Binding, BindingAnnotation, BindingId, BindingProblems, CaptureBy, ClosureKind,
|
||||||
Expr, ExprId, InlineAsm, Label, LabelId, Literal, LiteralOrConst, MatchArm, Movability,
|
Expr, ExprId, InlineAsm, Label, LabelId, Literal, LiteralOrConst, MatchArm, Movability,
|
||||||
|
@ -46,6 +49,8 @@ use crate::{
|
||||||
AdtId, BlockId, BlockLoc, ConstBlockLoc, DefWithBodyId, ModuleDefId, UnresolvedMacro,
|
AdtId, BlockId, BlockLoc, ConstBlockLoc, DefWithBodyId, ModuleDefId, UnresolvedMacro,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type FxIndexSet<K> = indexmap::IndexSet<K, std::hash::BuildHasherDefault<rustc_hash::FxHasher>>;
|
||||||
|
|
||||||
pub(super) fn lower(
|
pub(super) fn lower(
|
||||||
db: &dyn DefDatabase,
|
db: &dyn DefDatabase,
|
||||||
owner: DefWithBodyId,
|
owner: DefWithBodyId,
|
||||||
|
@ -661,50 +666,10 @@ impl ExprCollector<'_> {
|
||||||
let fields = e.fields().map(|it| it.as_name()).collect();
|
let fields = e.fields().map(|it| it.as_name()).collect();
|
||||||
self.alloc_expr(Expr::OffsetOf(OffsetOf { container, fields }), syntax_ptr)
|
self.alloc_expr(Expr::OffsetOf(OffsetOf { container, fields }), syntax_ptr)
|
||||||
}
|
}
|
||||||
ast::Expr::FormatArgsExpr(f) => {
|
ast::Expr::FormatArgsExpr(f) => match self.collect_format_args(f, syntax_ptr) {
|
||||||
let mut args = FormatArgumentsCollector::new();
|
Ok(value) => value,
|
||||||
f.args().for_each(|arg| {
|
Err(value) => return value,
|
||||||
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,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1604,6 +1569,395 @@ impl ExprCollector<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// endregion: labels
|
// 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)> {
|
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))()
|
(|| syntax::algo::skip_trivia_token(t?.next_token()?, syntax::Direction::Next))()
|
||||||
.map_or(false, |it| it.kind() == syntax::T![,])
|
.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 std::fmt::{self, Write};
|
||||||
|
|
||||||
use hir_expand::db::ExpandDatabase;
|
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use syntax::ast::HasName;
|
use syntax::ast::HasName;
|
||||||
|
|
||||||
|
@ -52,8 +51,7 @@ pub(super) fn print_body_hir(db: &dyn DefDatabase, body: &Body, owner: DefWithBo
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut p =
|
let mut p = Printer { db, body, buf: header, indent_level: 0, needs_indent: false };
|
||||||
Printer { db: db.upcast(), body, buf: header, indent_level: 0, needs_indent: false };
|
|
||||||
if let DefWithBodyId::FunctionId(it) = owner {
|
if let DefWithBodyId::FunctionId(it) = owner {
|
||||||
p.buf.push('(');
|
p.buf.push('(');
|
||||||
body.params.iter().zip(&db.function_data(it).params).for_each(|(¶m, ty)| {
|
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,
|
_owner: DefWithBodyId,
|
||||||
expr: ExprId,
|
expr: ExprId,
|
||||||
) -> String {
|
) -> String {
|
||||||
let mut p =
|
let mut p = Printer { db, body, buf: String::new(), indent_level: 0, needs_indent: false };
|
||||||
Printer { db: db.upcast(), body, buf: String::new(), indent_level: 0, needs_indent: false };
|
|
||||||
p.print_expr(expr);
|
p.print_expr(expr);
|
||||||
p.buf
|
p.buf
|
||||||
}
|
}
|
||||||
|
@ -99,7 +96,7 @@ macro_rules! wln {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Printer<'a> {
|
struct Printer<'a> {
|
||||||
db: &'a dyn ExpandDatabase,
|
db: &'a dyn DefDatabase,
|
||||||
body: &'a Body,
|
body: &'a Body,
|
||||||
buf: String,
|
buf: String,
|
||||||
indent_level: usize,
|
indent_level: usize,
|
||||||
|
@ -156,18 +153,16 @@ impl Printer<'_> {
|
||||||
Expr::Missing => w!(self, "<EFBFBD>"),
|
Expr::Missing => w!(self, "<EFBFBD>"),
|
||||||
Expr::Underscore => w!(self, "_"),
|
Expr::Underscore => w!(self, "_"),
|
||||||
Expr::InlineAsm(_) => w!(self, "builtin#asm(_)"),
|
Expr::InlineAsm(_) => w!(self, "builtin#asm(_)"),
|
||||||
Expr::FormatArgs(_fmt_args) => {
|
|
||||||
w!(self, "builtin#format_args(");
|
|
||||||
// FIXME
|
|
||||||
w!(self, ")");
|
|
||||||
}
|
|
||||||
Expr::OffsetOf(offset_of) => {
|
Expr::OffsetOf(offset_of) => {
|
||||||
w!(self, "builtin#offset_of(");
|
w!(self, "builtin#offset_of(");
|
||||||
self.print_type_ref(&offset_of.container);
|
self.print_type_ref(&offset_of.container);
|
||||||
w!(
|
w!(
|
||||||
self,
|
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),
|
Expr::Path(path) => self.print_path(path),
|
||||||
|
@ -189,7 +184,7 @@ impl Printer<'_> {
|
||||||
}
|
}
|
||||||
Expr::Loop { body, label } => {
|
Expr::Loop { body, label } => {
|
||||||
if let Some(lbl) = 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 ");
|
w!(self, "loop ");
|
||||||
self.print_expr(*body);
|
self.print_expr(*body);
|
||||||
|
@ -209,7 +204,7 @@ impl Printer<'_> {
|
||||||
}
|
}
|
||||||
Expr::MethodCall { receiver, method_name, args, generic_args } => {
|
Expr::MethodCall { receiver, method_name, args, generic_args } => {
|
||||||
self.print_expr(*receiver);
|
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 {
|
if let Some(args) = generic_args {
|
||||||
w!(self, "::<");
|
w!(self, "::<");
|
||||||
print_generic_args(self.db, args, self).unwrap();
|
print_generic_args(self.db, args, self).unwrap();
|
||||||
|
@ -247,13 +242,13 @@ impl Printer<'_> {
|
||||||
Expr::Continue { label } => {
|
Expr::Continue { label } => {
|
||||||
w!(self, "continue");
|
w!(self, "continue");
|
||||||
if let Some(lbl) = 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()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expr::Break { expr, label } => {
|
Expr::Break { expr, label } => {
|
||||||
w!(self, "break");
|
w!(self, "break");
|
||||||
if let Some(lbl) = 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()));
|
||||||
}
|
}
|
||||||
if let Some(expr) = expr {
|
if let Some(expr) = expr {
|
||||||
self.whitespace();
|
self.whitespace();
|
||||||
|
@ -292,7 +287,7 @@ impl Printer<'_> {
|
||||||
w!(self, "{{");
|
w!(self, "{{");
|
||||||
self.indented(|p| {
|
self.indented(|p| {
|
||||||
for field in &**fields {
|
for field in &**fields {
|
||||||
w!(p, "{}: ", field.name.display(self.db));
|
w!(p, "{}: ", field.name.display(self.db.upcast()));
|
||||||
p.print_expr(field.expr);
|
p.print_expr(field.expr);
|
||||||
wln!(p, ",");
|
wln!(p, ",");
|
||||||
}
|
}
|
||||||
|
@ -309,7 +304,7 @@ impl Printer<'_> {
|
||||||
}
|
}
|
||||||
Expr::Field { expr, name } => {
|
Expr::Field { expr, name } => {
|
||||||
self.print_expr(*expr);
|
self.print_expr(*expr);
|
||||||
w!(self, ".{}", name.display(self.db));
|
w!(self, ".{}", name.display(self.db.upcast()));
|
||||||
}
|
}
|
||||||
Expr::Await { expr } => {
|
Expr::Await { expr } => {
|
||||||
self.print_expr(*expr);
|
self.print_expr(*expr);
|
||||||
|
@ -447,7 +442,8 @@ impl Printer<'_> {
|
||||||
}
|
}
|
||||||
Expr::Literal(lit) => self.print_literal(lit),
|
Expr::Literal(lit) => self.print_literal(lit),
|
||||||
Expr::Block { id: _, statements, tail, label } => {
|
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);
|
self.print_block(label.as_deref(), statements, tail);
|
||||||
}
|
}
|
||||||
Expr::Unsafe { id: _, statements, tail } => {
|
Expr::Unsafe { id: _, statements, tail } => {
|
||||||
|
@ -523,7 +519,7 @@ impl Printer<'_> {
|
||||||
w!(self, " {{");
|
w!(self, " {{");
|
||||||
self.indented(|p| {
|
self.indented(|p| {
|
||||||
for arg in args.iter() {
|
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);
|
p.print_pat(arg.pat);
|
||||||
wln!(p, ",");
|
wln!(p, ",");
|
||||||
}
|
}
|
||||||
|
@ -682,6 +678,6 @@ impl Printer<'_> {
|
||||||
BindingAnnotation::Ref => "ref ",
|
BindingAnnotation::Ref => "ref ",
|
||||||
BindingAnnotation::RefMut => "ref mut ",
|
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;
|
mod block;
|
||||||
|
|
||||||
use base_db::{fixture::WithFixture, SourceDatabase};
|
use base_db::{fixture::WithFixture, SourceDatabase};
|
||||||
use expect_test::Expect;
|
use expect_test::{expect, Expect};
|
||||||
|
|
||||||
use crate::{test_db::TestDB, ModuleDefId};
|
use crate::{test_db::TestDB, ModuleDefId};
|
||||||
|
|
||||||
use super::*;
|
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 db = TestDB::with_files(ra_fixture);
|
||||||
|
|
||||||
let krate = db.crate_graph().iter().next().unwrap();
|
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 {
|
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::{
|
use crate::{
|
||||||
builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint},
|
builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint},
|
||||||
hir::format_args::{FormatArgs, FormatArgumentKind},
|
|
||||||
path::{GenericArgs, Path},
|
path::{GenericArgs, Path},
|
||||||
type_ref::{Mutability, Rawness, TypeRef},
|
type_ref::{Mutability, Rawness, TypeRef},
|
||||||
BlockId, ConstBlockId,
|
BlockId, ConstBlockId,
|
||||||
|
@ -284,7 +283,6 @@ pub enum Expr {
|
||||||
Underscore,
|
Underscore,
|
||||||
OffsetOf(OffsetOf),
|
OffsetOf(OffsetOf),
|
||||||
InlineAsm(InlineAsm),
|
InlineAsm(InlineAsm),
|
||||||
FormatArgs(FormatArgs),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
@ -358,14 +356,6 @@ impl Expr {
|
||||||
Expr::Missing => {}
|
Expr::Missing => {}
|
||||||
Expr::Path(_) | Expr::OffsetOf(_) => {}
|
Expr::Path(_) | Expr::OffsetOf(_) => {}
|
||||||
Expr::InlineAsm(it) => f(it.e),
|
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 } => {
|
Expr::If { condition, then_branch, else_branch } => {
|
||||||
f(*condition);
|
f(*condition);
|
||||||
f(*then_branch);
|
f(*then_branch);
|
||||||
|
|
|
@ -6,7 +6,7 @@ use syntax::{
|
||||||
AstToken, SmolStr, TextRange,
|
AstToken, SmolStr, TextRange,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::hir::{dummy_expr_id, ExprId};
|
use crate::hir::ExprId;
|
||||||
|
|
||||||
mod parse;
|
mod parse;
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ pub enum FormatArgsPiece {
|
||||||
Placeholder(FormatPlaceholder),
|
Placeholder(FormatPlaceholder),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Copy, Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct FormatPlaceholder {
|
pub struct FormatPlaceholder {
|
||||||
/// Index into [`FormatArgs::arguments`].
|
/// Index into [`FormatArgs::arguments`].
|
||||||
pub argument: FormatArgPosition,
|
pub argument: FormatArgPosition,
|
||||||
|
@ -43,7 +43,7 @@ pub struct FormatPlaceholder {
|
||||||
pub format_options: FormatOptions,
|
pub format_options: FormatOptions,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Copy, Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct FormatArgPosition {
|
pub struct FormatArgPosition {
|
||||||
/// Which argument this position refers to (Ok),
|
/// Which argument this position refers to (Ok),
|
||||||
/// or would've referred to if it existed (Err).
|
/// or would've referred to if it existed (Err).
|
||||||
|
@ -64,7 +64,7 @@ pub enum FormatArgPositionKind {
|
||||||
Named,
|
Named,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
|
||||||
pub enum FormatTrait {
|
pub enum FormatTrait {
|
||||||
/// `{}`
|
/// `{}`
|
||||||
Display,
|
Display,
|
||||||
|
@ -86,7 +86,7 @@ pub enum FormatTrait {
|
||||||
UpperHex,
|
UpperHex,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Default, Debug, PartialEq, Eq)]
|
#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)]
|
||||||
pub struct FormatOptions {
|
pub struct FormatOptions {
|
||||||
/// The width. E.g. `{:5}` or `{:width$}`.
|
/// The width. E.g. `{:5}` or `{:width$}`.
|
||||||
pub width: Option<FormatCount>,
|
pub width: Option<FormatCount>,
|
||||||
|
@ -131,7 +131,7 @@ pub enum FormatAlignment {
|
||||||
Center,
|
Center,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
pub enum FormatCount {
|
pub enum FormatCount {
|
||||||
/// `{:5}` or `{:.5}`
|
/// `{:5}` or `{:.5}`
|
||||||
Literal(usize),
|
Literal(usize),
|
||||||
|
@ -171,6 +171,7 @@ pub(crate) fn parse(
|
||||||
fmt_snippet: Option<String>,
|
fmt_snippet: Option<String>,
|
||||||
mut args: FormatArgumentsCollector,
|
mut args: FormatArgumentsCollector,
|
||||||
is_direct_literal: bool,
|
is_direct_literal: bool,
|
||||||
|
mut synth: impl FnMut(Name) -> ExprId,
|
||||||
) -> FormatArgs {
|
) -> FormatArgs {
|
||||||
let text = s.text();
|
let text = s.text();
|
||||||
let str_style = match s.quote_offsets() {
|
let str_style = match s.quote_offsets() {
|
||||||
|
@ -252,10 +253,10 @@ pub(crate) fn parse(
|
||||||
// FIXME: Diagnose
|
// FIXME: Diagnose
|
||||||
}
|
}
|
||||||
Ok(args.add(FormatArgument {
|
Ok(args.add(FormatArgument {
|
||||||
kind: FormatArgumentKind::Captured(name),
|
kind: FormatArgumentKind::Captured(name.clone()),
|
||||||
// FIXME: This is problematic, we might want to synthesize a dummy
|
// FIXME: This is problematic, we might want to synthesize a dummy
|
||||||
// expression proper and/or desugar these.
|
// 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 {
|
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 {
|
fn data(&self) -> &ItemTreeData {
|
||||||
|
|
|
@ -2,8 +2,6 @@
|
||||||
|
|
||||||
use std::fmt::{self, Write};
|
use std::fmt::{self, Write};
|
||||||
|
|
||||||
use hir_expand::db::ExpandDatabase;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
generics::{TypeOrConstParamData, WherePredicate, WherePredicateTypeTarget},
|
generics::{TypeOrConstParamData, WherePredicate, WherePredicateTypeTarget},
|
||||||
pretty::{print_path, print_type_bounds, print_type_ref},
|
pretty::{print_path, print_type_bounds, print_type_ref},
|
||||||
|
@ -12,7 +10,7 @@ use crate::{
|
||||||
|
|
||||||
use super::*;
|
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 };
|
let mut p = Printer { db, tree, buf: String::new(), indent_level: 0, needs_indent: true };
|
||||||
|
|
||||||
if let Some(attrs) = tree.attrs.get(&AttrOwner::TopLevel) {
|
if let Some(attrs) = tree.attrs.get(&AttrOwner::TopLevel) {
|
||||||
|
@ -45,7 +43,7 @@ macro_rules! wln {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Printer<'a> {
|
struct Printer<'a> {
|
||||||
db: &'a dyn ExpandDatabase,
|
db: &'a dyn DefDatabase,
|
||||||
tree: &'a ItemTree,
|
tree: &'a ItemTree,
|
||||||
buf: String,
|
buf: String,
|
||||||
indent_level: usize,
|
indent_level: usize,
|
||||||
|
@ -91,7 +89,7 @@ impl Printer<'_> {
|
||||||
self,
|
self,
|
||||||
"#{}[{}{}]{}",
|
"#{}[{}{}]{}",
|
||||||
inner,
|
inner,
|
||||||
attr.path.display(self.db),
|
attr.path.display(self.db.upcast()),
|
||||||
attr.input.as_ref().map(|it| it.to_string()).unwrap_or_default(),
|
attr.input.as_ref().map(|it| it.to_string()).unwrap_or_default(),
|
||||||
separated_by,
|
separated_by,
|
||||||
);
|
);
|
||||||
|
@ -106,7 +104,7 @@ impl Printer<'_> {
|
||||||
|
|
||||||
fn print_visibility(&mut self, vis: RawVisibilityId) {
|
fn print_visibility(&mut self, vis: RawVisibilityId) {
|
||||||
match &self.tree[vis] {
|
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 "),
|
RawVisibility::Public => w!(self, "pub "),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -121,7 +119,7 @@ impl Printer<'_> {
|
||||||
let Field { visibility, name, type_ref, ast_id: _ } = &this.tree[field];
|
let Field { visibility, name, type_ref, ast_id: _ } = &this.tree[field];
|
||||||
this.print_attrs_of(field, "\n");
|
this.print_attrs_of(field, "\n");
|
||||||
this.print_visibility(*visibility);
|
this.print_visibility(*visibility);
|
||||||
w!(this, "{}: ", name.display(self.db));
|
w!(this, "{}: ", name.display(self.db.upcast()));
|
||||||
this.print_type_ref(type_ref);
|
this.print_type_ref(type_ref);
|
||||||
wln!(this, ",");
|
wln!(this, ",");
|
||||||
}
|
}
|
||||||
|
@ -135,7 +133,7 @@ impl Printer<'_> {
|
||||||
let Field { visibility, name, type_ref, ast_id: _ } = &this.tree[field];
|
let Field { visibility, name, type_ref, ast_id: _ } = &this.tree[field];
|
||||||
this.print_attrs_of(field, "\n");
|
this.print_attrs_of(field, "\n");
|
||||||
this.print_visibility(*visibility);
|
this.print_visibility(*visibility);
|
||||||
w!(this, "{}: ", name.display(self.db));
|
w!(this, "{}: ", name.display(self.db.upcast()));
|
||||||
this.print_type_ref(type_ref);
|
this.print_type_ref(type_ref);
|
||||||
wln!(this, ",");
|
wln!(this, ",");
|
||||||
}
|
}
|
||||||
|
@ -168,20 +166,20 @@ impl Printer<'_> {
|
||||||
fn print_use_tree(&mut self, use_tree: &UseTree) {
|
fn print_use_tree(&mut self, use_tree: &UseTree) {
|
||||||
match &use_tree.kind {
|
match &use_tree.kind {
|
||||||
UseTreeKind::Single { path, alias } => {
|
UseTreeKind::Single { path, alias } => {
|
||||||
w!(self, "{}", path.display(self.db));
|
w!(self, "{}", path.display(self.db.upcast()));
|
||||||
if let Some(alias) = alias {
|
if let Some(alias) = alias {
|
||||||
w!(self, " as {}", alias);
|
w!(self, " as {}", alias);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
UseTreeKind::Glob { path } => {
|
UseTreeKind::Glob { path } => {
|
||||||
if let Some(path) = path {
|
if let Some(path) = path {
|
||||||
w!(self, "{}::", path.display(self.db));
|
w!(self, "{}::", path.display(self.db.upcast()));
|
||||||
}
|
}
|
||||||
w!(self, "*");
|
w!(self, "*");
|
||||||
}
|
}
|
||||||
UseTreeKind::Prefixed { prefix, list } => {
|
UseTreeKind::Prefixed { prefix, list } => {
|
||||||
if let Some(prefix) = prefix {
|
if let Some(prefix) = prefix {
|
||||||
w!(self, "{}::", prefix.display(self.db));
|
w!(self, "{}::", prefix.display(self.db.upcast()));
|
||||||
}
|
}
|
||||||
w!(self, "{{");
|
w!(self, "{{");
|
||||||
for (i, tree) in list.iter().enumerate() {
|
for (i, tree) in list.iter().enumerate() {
|
||||||
|
@ -209,7 +207,7 @@ impl Printer<'_> {
|
||||||
ModItem::ExternCrate(it) => {
|
ModItem::ExternCrate(it) => {
|
||||||
let ExternCrate { name, alias, visibility, ast_id: _ } = &self.tree[it];
|
let ExternCrate { name, alias, visibility, ast_id: _ } = &self.tree[it];
|
||||||
self.print_visibility(*visibility);
|
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 {
|
if let Some(alias) = alias {
|
||||||
w!(self, " as {}", alias);
|
w!(self, " as {}", alias);
|
||||||
}
|
}
|
||||||
|
@ -256,7 +254,7 @@ impl Printer<'_> {
|
||||||
if let Some(abi) = abi {
|
if let Some(abi) = abi {
|
||||||
w!(self, "extern \"{}\" ", 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);
|
self.print_generic_params(explicit_generic_params);
|
||||||
w!(self, "(");
|
w!(self, "(");
|
||||||
if !params.is_empty() {
|
if !params.is_empty() {
|
||||||
|
@ -290,7 +288,7 @@ impl Printer<'_> {
|
||||||
ModItem::Struct(it) => {
|
ModItem::Struct(it) => {
|
||||||
let Struct { visibility, name, fields, generic_params, ast_id: _ } = &self.tree[it];
|
let Struct { visibility, name, fields, generic_params, ast_id: _ } = &self.tree[it];
|
||||||
self.print_visibility(*visibility);
|
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_generic_params(generic_params);
|
||||||
self.print_fields_and_where_clause(fields, generic_params);
|
self.print_fields_and_where_clause(fields, generic_params);
|
||||||
if matches!(fields, Fields::Record(_)) {
|
if matches!(fields, Fields::Record(_)) {
|
||||||
|
@ -302,7 +300,7 @@ impl Printer<'_> {
|
||||||
ModItem::Union(it) => {
|
ModItem::Union(it) => {
|
||||||
let Union { name, visibility, fields, generic_params, ast_id: _ } = &self.tree[it];
|
let Union { name, visibility, fields, generic_params, ast_id: _ } = &self.tree[it];
|
||||||
self.print_visibility(*visibility);
|
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_generic_params(generic_params);
|
||||||
self.print_fields_and_where_clause(fields, generic_params);
|
self.print_fields_and_where_clause(fields, generic_params);
|
||||||
if matches!(fields, Fields::Record(_)) {
|
if matches!(fields, Fields::Record(_)) {
|
||||||
|
@ -314,14 +312,14 @@ impl Printer<'_> {
|
||||||
ModItem::Enum(it) => {
|
ModItem::Enum(it) => {
|
||||||
let Enum { name, visibility, variants, generic_params, ast_id: _ } = &self.tree[it];
|
let Enum { name, visibility, variants, generic_params, ast_id: _ } = &self.tree[it];
|
||||||
self.print_visibility(*visibility);
|
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_generic_params(generic_params);
|
||||||
self.print_where_clause_and_opening_brace(generic_params);
|
self.print_where_clause_and_opening_brace(generic_params);
|
||||||
self.indented(|this| {
|
self.indented(|this| {
|
||||||
for variant in variants.clone() {
|
for variant in variants.clone() {
|
||||||
let Variant { name, fields, ast_id: _ } = &this.tree[variant];
|
let Variant { name, fields, ast_id: _ } = &this.tree[variant];
|
||||||
this.print_attrs_of(variant, "\n");
|
this.print_attrs_of(variant, "\n");
|
||||||
w!(this, "{}", name.display(self.db));
|
w!(this, "{}", name.display(self.db.upcast()));
|
||||||
this.print_fields(fields);
|
this.print_fields(fields);
|
||||||
wln!(this, ",");
|
wln!(this, ",");
|
||||||
}
|
}
|
||||||
|
@ -333,7 +331,7 @@ impl Printer<'_> {
|
||||||
self.print_visibility(*visibility);
|
self.print_visibility(*visibility);
|
||||||
w!(self, "const ");
|
w!(self, "const ");
|
||||||
match name {
|
match name {
|
||||||
Some(name) => w!(self, "{}", name.display(self.db)),
|
Some(name) => w!(self, "{}", name.display(self.db.upcast())),
|
||||||
None => w!(self, "_"),
|
None => w!(self, "_"),
|
||||||
}
|
}
|
||||||
w!(self, ": ");
|
w!(self, ": ");
|
||||||
|
@ -347,7 +345,7 @@ impl Printer<'_> {
|
||||||
if *mutable {
|
if *mutable {
|
||||||
w!(self, "mut ");
|
w!(self, "mut ");
|
||||||
}
|
}
|
||||||
w!(self, "{}: ", name.display(self.db));
|
w!(self, "{}: ", name.display(self.db.upcast()));
|
||||||
self.print_type_ref(type_ref);
|
self.print_type_ref(type_ref);
|
||||||
w!(self, " = _;");
|
w!(self, " = _;");
|
||||||
wln!(self);
|
wln!(self);
|
||||||
|
@ -369,7 +367,7 @@ impl Printer<'_> {
|
||||||
if *is_auto {
|
if *is_auto {
|
||||||
w!(self, "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_generic_params(generic_params);
|
||||||
self.print_where_clause_and_opening_brace(generic_params);
|
self.print_where_clause_and_opening_brace(generic_params);
|
||||||
self.indented(|this| {
|
self.indented(|this| {
|
||||||
|
@ -382,7 +380,7 @@ impl Printer<'_> {
|
||||||
ModItem::TraitAlias(it) => {
|
ModItem::TraitAlias(it) => {
|
||||||
let TraitAlias { name, visibility, generic_params, ast_id: _ } = &self.tree[it];
|
let TraitAlias { name, visibility, generic_params, ast_id: _ } = &self.tree[it];
|
||||||
self.print_visibility(*visibility);
|
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);
|
self.print_generic_params(generic_params);
|
||||||
w!(self, " = ");
|
w!(self, " = ");
|
||||||
self.print_where_clause(generic_params);
|
self.print_where_clause(generic_params);
|
||||||
|
@ -415,7 +413,7 @@ impl Printer<'_> {
|
||||||
let TypeAlias { name, visibility, bounds, type_ref, generic_params, ast_id: _ } =
|
let TypeAlias { name, visibility, bounds, type_ref, generic_params, ast_id: _ } =
|
||||||
&self.tree[it];
|
&self.tree[it];
|
||||||
self.print_visibility(*visibility);
|
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);
|
self.print_generic_params(generic_params);
|
||||||
if !bounds.is_empty() {
|
if !bounds.is_empty() {
|
||||||
w!(self, ": ");
|
w!(self, ": ");
|
||||||
|
@ -432,7 +430,7 @@ impl Printer<'_> {
|
||||||
ModItem::Mod(it) => {
|
ModItem::Mod(it) => {
|
||||||
let Mod { name, visibility, kind, ast_id: _ } = &self.tree[it];
|
let Mod { name, visibility, kind, ast_id: _ } = &self.tree[it];
|
||||||
self.print_visibility(*visibility);
|
self.print_visibility(*visibility);
|
||||||
w!(self, "mod {}", name.display(self.db));
|
w!(self, "mod {}", name.display(self.db.upcast()));
|
||||||
match kind {
|
match kind {
|
||||||
ModKind::Inline { items } => {
|
ModKind::Inline { items } => {
|
||||||
w!(self, " {{");
|
w!(self, " {{");
|
||||||
|
@ -450,16 +448,16 @@ impl Printer<'_> {
|
||||||
}
|
}
|
||||||
ModItem::MacroCall(it) => {
|
ModItem::MacroCall(it) => {
|
||||||
let MacroCall { path, ast_id: _, expand_to: _ } = &self.tree[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) => {
|
ModItem::MacroRules(it) => {
|
||||||
let MacroRules { name, ast_id: _ } = &self.tree[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) => {
|
ModItem::MacroDef(it) => {
|
||||||
let MacroDef { name, visibility, ast_id: _ } = &self.tree[it];
|
let MacroDef { name, visibility, ast_id: _ } = &self.tree[it];
|
||||||
self.print_visibility(*visibility);
|
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;
|
first = false;
|
||||||
self.print_attrs_of(idx, " ");
|
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() {
|
for (idx, x) in params.type_or_consts.iter() {
|
||||||
if !first {
|
if !first {
|
||||||
|
@ -501,11 +499,11 @@ impl Printer<'_> {
|
||||||
self.print_attrs_of(idx, " ");
|
self.print_attrs_of(idx, " ");
|
||||||
match x {
|
match x {
|
||||||
TypeOrConstParamData::TypeParamData(ty) => match &ty.name {
|
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()),
|
None => w!(self, "_anon_{}", idx.into_raw()),
|
||||||
},
|
},
|
||||||
TypeOrConstParamData::ConstParamData(konst) => {
|
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);
|
self.print_type_ref(&konst.ty);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -540,8 +538,8 @@ impl Printer<'_> {
|
||||||
wln!(
|
wln!(
|
||||||
this,
|
this,
|
||||||
"{}: {},",
|
"{}: {},",
|
||||||
target.name.display(self.db),
|
target.name.display(self.db.upcast()),
|
||||||
bound.name.display(self.db)
|
bound.name.display(self.db.upcast())
|
||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -551,7 +549,7 @@ impl Printer<'_> {
|
||||||
if i != 0 {
|
if i != 0 {
|
||||||
w!(this, ", ");
|
w!(this, ", ");
|
||||||
}
|
}
|
||||||
w!(this, "{}", lt.display(self.db));
|
w!(this, "{}", lt.display(self.db.upcast()));
|
||||||
}
|
}
|
||||||
w!(this, "> ");
|
w!(this, "> ");
|
||||||
(target, bound)
|
(target, bound)
|
||||||
|
@ -562,7 +560,7 @@ impl Printer<'_> {
|
||||||
WherePredicateTypeTarget::TypeRef(ty) => this.print_type_ref(ty),
|
WherePredicateTypeTarget::TypeRef(ty) => this.print_type_ref(ty),
|
||||||
WherePredicateTypeTarget::TypeOrConstParam(id) => {
|
WherePredicateTypeTarget::TypeOrConstParam(id) => {
|
||||||
match ¶ms.type_or_consts[*id].name() {
|
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()),
|
None => w!(this, "_anon_{}", id.into_raw()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
//!
|
//!
|
||||||
//! This attribute to tell the compiler about semi built-in std library
|
//! This attribute to tell the compiler about semi built-in std library
|
||||||
//! features, such as Fn family of traits.
|
//! features, such as Fn family of traits.
|
||||||
|
use hir_expand::name::Name;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use syntax::SmolStr;
|
use syntax::SmolStr;
|
||||||
use triomphe::Arc;
|
use triomphe::Arc;
|
||||||
|
@ -238,7 +239,17 @@ impl LangItem {
|
||||||
|
|
||||||
pub fn path(&self, db: &dyn DefDatabase, start_crate: CrateId) -> Option<Path> {
|
pub fn path(&self, db: &dyn DefDatabase, start_crate: CrateId) -> Option<Path> {
|
||||||
let t = db.lang_item(start_crate, *self)?;
|
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
|
/// 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.
|
/// 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
|
/// 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 {
|
pub fn kind(&self) -> &PathKind {
|
||||||
match self {
|
match self {
|
||||||
Path::Normal { mod_path, .. } => &mod_path.kind,
|
Path::Normal { mod_path, .. } => &mod_path.kind,
|
||||||
Path::LangItem(_) => &PathKind::Abs,
|
Path::LangItem(..) => &PathKind::Abs,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn type_anchor(&self) -> Option<&TypeRef> {
|
pub fn type_anchor(&self) -> Option<&TypeRef> {
|
||||||
match self {
|
match self {
|
||||||
Path::Normal { type_anchor, .. } => type_anchor.as_deref(),
|
Path::Normal { type_anchor, .. } => type_anchor.as_deref(),
|
||||||
Path::LangItem(_) => None,
|
Path::LangItem(..) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn segments(&self) -> PathSegments<'_> {
|
pub fn segments(&self) -> PathSegments<'_> {
|
||||||
let Path::Normal { mod_path, generic_args, .. } = self else {
|
match self {
|
||||||
return PathSegments { segments: &[], generic_args: None };
|
Path::Normal { mod_path, generic_args, .. } => {
|
||||||
|
let s = PathSegments {
|
||||||
|
segments: mod_path.segments(),
|
||||||
|
generic_args: generic_args.as_deref(),
|
||||||
};
|
};
|
||||||
let s =
|
|
||||||
PathSegments { segments: mod_path.segments(), generic_args: generic_args.as_deref() };
|
|
||||||
if let Some(generic_args) = s.generic_args {
|
if let Some(generic_args) = s.generic_args {
|
||||||
assert_eq!(s.segments.len(), generic_args.len());
|
assert_eq!(s.segments.len(), generic_args.len());
|
||||||
}
|
}
|
||||||
s
|
s
|
||||||
}
|
}
|
||||||
|
Path::LangItem(_, seg) => PathSegments {
|
||||||
|
segments: seg.as_ref().map_or(&[], |seg| std::slice::from_ref(seg)),
|
||||||
|
generic_args: None,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn mod_path(&self) -> Option<&ModPath> {
|
pub fn mod_path(&self) -> Option<&ModPath> {
|
||||||
match self {
|
match self {
|
||||||
Path::Normal { mod_path, .. } => Some(&mod_path),
|
Path::Normal { mod_path, .. } => Some(&mod_path),
|
||||||
Path::LangItem(_) => None,
|
Path::LangItem(..) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,18 +2,54 @@
|
||||||
|
|
||||||
use std::fmt::{self, Write};
|
use std::fmt::{self, Write};
|
||||||
|
|
||||||
use hir_expand::{db::ExpandDatabase, mod_path::PathKind};
|
use hir_expand::mod_path::PathKind;
|
||||||
use intern::Interned;
|
use intern::Interned;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
db::DefDatabase,
|
||||||
|
lang_item::LangItemTarget,
|
||||||
path::{GenericArg, GenericArgs, Path},
|
path::{GenericArg, GenericArgs, Path},
|
||||||
type_ref::{Mutability, TraitBoundModifier, TypeBound, TypeRef},
|
type_ref::{Mutability, TraitBoundModifier, TypeBound, TypeRef},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(crate) fn print_path(db: &dyn ExpandDatabase, path: &Path, buf: &mut dyn Write) -> fmt::Result {
|
pub(crate) fn print_path(db: &dyn DefDatabase, path: &Path, buf: &mut dyn Write) -> fmt::Result {
|
||||||
if let Path::LangItem(it) = path {
|
if let Path::LangItem(it, s) = path {
|
||||||
return write!(buf, "$lang_item::{it:?}");
|
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() {
|
match path.type_anchor() {
|
||||||
Some(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, "::")?;
|
||||||
}
|
}
|
||||||
|
|
||||||
write!(buf, "{}", segment.name.display(db))?;
|
write!(buf, "{}", segment.name.display(db.upcast()))?;
|
||||||
if let Some(generics) = segment.args_and_bindings {
|
if let Some(generics) = segment.args_and_bindings {
|
||||||
write!(buf, "::<")?;
|
write!(buf, "::<")?;
|
||||||
print_generic_args(db, generics, 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(
|
pub(crate) fn print_generic_args(
|
||||||
db: &dyn ExpandDatabase,
|
db: &dyn DefDatabase,
|
||||||
generics: &GenericArgs,
|
generics: &GenericArgs,
|
||||||
buf: &mut dyn Write,
|
buf: &mut dyn Write,
|
||||||
) -> fmt::Result {
|
) -> fmt::Result {
|
||||||
|
@ -83,7 +119,7 @@ pub(crate) fn print_generic_args(
|
||||||
write!(buf, ", ")?;
|
write!(buf, ", ")?;
|
||||||
}
|
}
|
||||||
first = false;
|
first = false;
|
||||||
write!(buf, "{}", binding.name.display(db))?;
|
write!(buf, "{}", binding.name.display(db.upcast()))?;
|
||||||
if !binding.bounds.is_empty() {
|
if !binding.bounds.is_empty() {
|
||||||
write!(buf, ": ")?;
|
write!(buf, ": ")?;
|
||||||
print_type_bounds(db, &binding.bounds, buf)?;
|
print_type_bounds(db, &binding.bounds, buf)?;
|
||||||
|
@ -97,19 +133,19 @@ pub(crate) fn print_generic_args(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn print_generic_arg(
|
pub(crate) fn print_generic_arg(
|
||||||
db: &dyn ExpandDatabase,
|
db: &dyn DefDatabase,
|
||||||
arg: &GenericArg,
|
arg: &GenericArg,
|
||||||
buf: &mut dyn Write,
|
buf: &mut dyn Write,
|
||||||
) -> fmt::Result {
|
) -> fmt::Result {
|
||||||
match arg {
|
match arg {
|
||||||
GenericArg::Type(ty) => print_type_ref(db, ty, buf),
|
GenericArg::Type(ty) => print_type_ref(db, ty, buf),
|
||||||
GenericArg::Const(c) => write!(buf, "{}", c.display(db)),
|
GenericArg::Const(c) => write!(buf, "{}", c.display(db.upcast())),
|
||||||
GenericArg::Lifetime(lt) => write!(buf, "{}", lt.name.display(db)),
|
GenericArg::Lifetime(lt) => write!(buf, "{}", lt.name.display(db.upcast())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn print_type_ref(
|
pub(crate) fn print_type_ref(
|
||||||
db: &dyn ExpandDatabase,
|
db: &dyn DefDatabase,
|
||||||
type_ref: &TypeRef,
|
type_ref: &TypeRef,
|
||||||
buf: &mut dyn Write,
|
buf: &mut dyn Write,
|
||||||
) -> fmt::Result {
|
) -> fmt::Result {
|
||||||
|
@ -143,7 +179,7 @@ pub(crate) fn print_type_ref(
|
||||||
};
|
};
|
||||||
write!(buf, "&")?;
|
write!(buf, "&")?;
|
||||||
if let Some(lt) = lt {
|
if let Some(lt) = lt {
|
||||||
write!(buf, "{} ", lt.name.display(db))?;
|
write!(buf, "{} ", lt.name.display(db.upcast()))?;
|
||||||
}
|
}
|
||||||
write!(buf, "{mtbl}")?;
|
write!(buf, "{mtbl}")?;
|
||||||
print_type_ref(db, pointee, buf)?;
|
print_type_ref(db, pointee, buf)?;
|
||||||
|
@ -151,7 +187,7 @@ pub(crate) fn print_type_ref(
|
||||||
TypeRef::Array(elem, len) => {
|
TypeRef::Array(elem, len) => {
|
||||||
write!(buf, "[")?;
|
write!(buf, "[")?;
|
||||||
print_type_ref(db, elem, buf)?;
|
print_type_ref(db, elem, buf)?;
|
||||||
write!(buf, "; {}]", len.display(db))?;
|
write!(buf, "; {}]", len.display(db.upcast()))?;
|
||||||
}
|
}
|
||||||
TypeRef::Slice(elem) => {
|
TypeRef::Slice(elem) => {
|
||||||
write!(buf, "[")?;
|
write!(buf, "[")?;
|
||||||
|
@ -198,7 +234,7 @@ pub(crate) fn print_type_ref(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn print_type_bounds(
|
pub(crate) fn print_type_bounds(
|
||||||
db: &dyn ExpandDatabase,
|
db: &dyn DefDatabase,
|
||||||
bounds: &[Interned<TypeBound>],
|
bounds: &[Interned<TypeBound>],
|
||||||
buf: &mut dyn Write,
|
buf: &mut dyn Write,
|
||||||
) -> fmt::Result {
|
) -> fmt::Result {
|
||||||
|
@ -216,10 +252,14 @@ pub(crate) fn print_type_bounds(
|
||||||
print_path(db, path, buf)?;
|
print_path(db, path, buf)?;
|
||||||
}
|
}
|
||||||
TypeBound::ForLifetime(lifetimes, path) => {
|
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)?;
|
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}}")?,
|
TypeBound::Error => write!(buf, "{{unknown}}")?,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -156,9 +156,8 @@ impl Resolver {
|
||||||
) -> Option<(TypeNs, Option<usize>, Option<ImportOrExternCrate>)> {
|
) -> Option<(TypeNs, Option<usize>, Option<ImportOrExternCrate>)> {
|
||||||
let path = match path {
|
let path = match path {
|
||||||
Path::Normal { mod_path, .. } => mod_path,
|
Path::Normal { mod_path, .. } => mod_path,
|
||||||
Path::LangItem(l) => {
|
Path::LangItem(l, seg) => {
|
||||||
return Some((
|
let type_ns = match *l {
|
||||||
match *l {
|
|
||||||
LangItemTarget::Union(it) => TypeNs::AdtId(it.into()),
|
LangItemTarget::Union(it) => TypeNs::AdtId(it.into()),
|
||||||
LangItemTarget::TypeAlias(it) => TypeNs::TypeAliasId(it),
|
LangItemTarget::TypeAlias(it) => TypeNs::TypeAliasId(it),
|
||||||
LangItemTarget::Struct(it) => TypeNs::AdtId(it.into()),
|
LangItemTarget::Struct(it) => TypeNs::AdtId(it.into()),
|
||||||
|
@ -168,10 +167,8 @@ impl Resolver {
|
||||||
LangItemTarget::Function(_)
|
LangItemTarget::Function(_)
|
||||||
| LangItemTarget::ImplDef(_)
|
| LangItemTarget::ImplDef(_)
|
||||||
| LangItemTarget::Static(_) => return None,
|
| LangItemTarget::Static(_) => return None,
|
||||||
},
|
};
|
||||||
None,
|
return Some((type_ns, seg.as_ref().map(|_| 1), None));
|
||||||
None,
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let first_name = path.segments().first()?;
|
let first_name = path.segments().first()?;
|
||||||
|
@ -256,7 +253,7 @@ impl Resolver {
|
||||||
) -> Option<ResolveValueResult> {
|
) -> Option<ResolveValueResult> {
|
||||||
let path = match path {
|
let path = match path {
|
||||||
Path::Normal { mod_path, .. } => mod_path,
|
Path::Normal { mod_path, .. } => mod_path,
|
||||||
Path::LangItem(l) => {
|
Path::LangItem(l, None) => {
|
||||||
return Some(ResolveValueResult::ValueNs(
|
return Some(ResolveValueResult::ValueNs(
|
||||||
match *l {
|
match *l {
|
||||||
LangItemTarget::Function(it) => ValueNs::FunctionId(it),
|
LangItemTarget::Function(it) => ValueNs::FunctionId(it),
|
||||||
|
@ -272,6 +269,20 @@ impl Resolver {
|
||||||
None,
|
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 n_segments = path.segments().len();
|
||||||
let tmp = name![self];
|
let tmp = name![self];
|
||||||
|
|
|
@ -308,6 +308,16 @@ pub mod known {
|
||||||
rust_2018,
|
rust_2018,
|
||||||
rust_2021,
|
rust_2021,
|
||||||
v1,
|
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)
|
// Components of known path (type name)
|
||||||
Iterator,
|
Iterator,
|
||||||
IntoIterator,
|
IntoIterator,
|
||||||
|
@ -333,6 +343,13 @@ pub mod known {
|
||||||
Not,
|
Not,
|
||||||
None,
|
None,
|
||||||
Index,
|
Index,
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
Center,
|
||||||
|
Unknown,
|
||||||
|
Is,
|
||||||
|
Param,
|
||||||
|
Implied,
|
||||||
// Components of known path (function name)
|
// Components of known path (function name)
|
||||||
filter_map,
|
filter_map,
|
||||||
next,
|
next,
|
||||||
|
@ -341,6 +358,8 @@ pub mod known {
|
||||||
is_empty,
|
is_empty,
|
||||||
as_str,
|
as_str,
|
||||||
new,
|
new,
|
||||||
|
new_v1_formatted,
|
||||||
|
none,
|
||||||
// Builtin macros
|
// Builtin macros
|
||||||
asm,
|
asm,
|
||||||
assert,
|
assert,
|
||||||
|
|
|
@ -9,10 +9,7 @@ use chalk_ir::{
|
||||||
};
|
};
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
data::adt::VariantData,
|
data::adt::VariantData,
|
||||||
hir::{
|
hir::{Array, BinaryOp, BindingId, CaptureBy, Expr, ExprId, Pat, PatId, Statement, UnaryOp},
|
||||||
format_args::FormatArgumentKind, Array, BinaryOp, BindingId, CaptureBy, Expr, ExprId, Pat,
|
|
||||||
PatId, Statement, UnaryOp,
|
|
||||||
},
|
|
||||||
lang_item::LangItem,
|
lang_item::LangItem,
|
||||||
resolver::{resolver_for_expr, ResolveValueResult, ValueNs},
|
resolver::{resolver_for_expr, ResolveValueResult, ValueNs},
|
||||||
DefWithBodyId, FieldId, HasModule, VariantId,
|
DefWithBodyId, FieldId, HasModule, VariantId,
|
||||||
|
@ -456,14 +453,6 @@ impl InferenceContext<'_> {
|
||||||
fn walk_expr_without_adjust(&mut self, tgt_expr: ExprId) {
|
fn walk_expr_without_adjust(&mut self, tgt_expr: ExprId) {
|
||||||
match &self.body[tgt_expr] {
|
match &self.body[tgt_expr] {
|
||||||
Expr::OffsetOf(_) => (),
|
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::InlineAsm(e) => self.walk_expr_without_adjust(e.e),
|
||||||
Expr::If { condition, then_branch, else_branch } => {
|
Expr::If { condition, then_branch, else_branch } => {
|
||||||
self.consume_expr(*condition);
|
self.consume_expr(*condition);
|
||||||
|
|
|
@ -9,8 +9,7 @@ use chalk_ir::{cast::Cast, fold::Shift, DebruijnIndex, Mutability, TyVariableKin
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
generics::TypeOrConstParamData,
|
generics::TypeOrConstParamData,
|
||||||
hir::{
|
hir::{
|
||||||
format_args::FormatArgumentKind, ArithOp, Array, BinaryOp, ClosureKind, Expr, ExprId,
|
ArithOp, Array, BinaryOp, ClosureKind, Expr, ExprId, LabelId, Literal, Statement, UnaryOp,
|
||||||
LabelId, Literal, Statement, UnaryOp,
|
|
||||||
},
|
},
|
||||||
lang_item::{LangItem, LangItemTarget},
|
lang_item::{LangItem, LangItemTarget},
|
||||||
path::{GenericArg, GenericArgs},
|
path::{GenericArg, GenericArgs},
|
||||||
|
@ -849,25 +848,6 @@ impl InferenceContext<'_> {
|
||||||
self.infer_expr_no_expect(it.e);
|
self.infer_expr_no_expect(it.e);
|
||||||
self.result.standard_types.unit.clone()
|
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
|
// use a new type variable if we got unknown here
|
||||||
let ty = self.insert_type_vars_shallow(ty);
|
let ty = self.insert_type_vars_shallow(ty);
|
||||||
|
|
|
@ -3,10 +3,7 @@
|
||||||
|
|
||||||
use chalk_ir::Mutability;
|
use chalk_ir::Mutability;
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
hir::{
|
hir::{Array, BinaryOp, BindingAnnotation, Expr, ExprId, PatId, Statement, UnaryOp},
|
||||||
format_args::FormatArgumentKind, Array, BinaryOp, BindingAnnotation, Expr, ExprId, PatId,
|
|
||||||
Statement, UnaryOp,
|
|
||||||
},
|
|
||||||
lang_item::LangItem,
|
lang_item::LangItem,
|
||||||
};
|
};
|
||||||
use hir_expand::name;
|
use hir_expand::name;
|
||||||
|
@ -40,13 +37,6 @@ impl InferenceContext<'_> {
|
||||||
Expr::Missing => (),
|
Expr::Missing => (),
|
||||||
Expr::InlineAsm(e) => self.infer_mut_expr_without_adjust(e.e, Mutability::Not),
|
Expr::InlineAsm(e) => self.infer_mut_expr_without_adjust(e.e, Mutability::Not),
|
||||||
Expr::OffsetOf(_) => (),
|
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 } => {
|
&Expr::If { condition, then_branch, else_branch } => {
|
||||||
self.infer_mut_expr(condition, Mutability::Not);
|
self.infer_mut_expr(condition, Mutability::Not);
|
||||||
self.infer_mut_expr(then_branch, Mutability::Not);
|
self.infer_mut_expr(then_branch, Mutability::Not);
|
||||||
|
|
|
@ -178,13 +178,30 @@ impl InferenceContext<'_> {
|
||||||
remaining_index: usize,
|
remaining_index: usize,
|
||||||
id: ExprOrPatId,
|
id: ExprOrPatId,
|
||||||
) -> Option<(ValueNs, Substitution)> {
|
) -> Option<(ValueNs, Substitution)> {
|
||||||
assert!(remaining_index < path.segments().len());
|
|
||||||
// there may be more intermediate segments between the resolved one and
|
// 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 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.
|
// 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 _d;
|
||||||
let remaining_segments = path.segments().skip(remaining_index);
|
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;
|
let is_before_last = remaining_segments.len() == 1;
|
||||||
|
|
||||||
match (def, is_before_last) {
|
match (def, is_before_last) {
|
||||||
|
|
|
@ -376,9 +376,6 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
||||||
Expr::InlineAsm(_) => {
|
Expr::InlineAsm(_) => {
|
||||||
not_supported!("builtin#asm")
|
not_supported!("builtin#asm")
|
||||||
}
|
}
|
||||||
Expr::FormatArgs(_) => {
|
|
||||||
not_supported!("builtin#format_args")
|
|
||||||
}
|
|
||||||
Expr::Missing => {
|
Expr::Missing => {
|
||||||
if let DefWithBodyId::FunctionId(f) = self.owner {
|
if let DefWithBodyId::FunctionId(f) = self.owner {
|
||||||
let assoc = f.lookup(self.db.upcast());
|
let assoc = f.lookup(self.db.upcast());
|
||||||
|
|
|
@ -3615,22 +3615,15 @@ fn main() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn builtin_format_args() {
|
fn builtin_format_args() {
|
||||||
check_infer(
|
check(
|
||||||
r#"
|
r#"
|
||||||
#[lang = "format_arguments"]
|
//- minicore: fmt
|
||||||
pub struct Arguments<'a>;
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let are = "are";
|
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,6 +899,8 @@ pub mod fmt {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result;
|
fn fmt(&self, f: &mut Formatter<'_>) -> Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mod rt {
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
type Opaque;
|
type Opaque;
|
||||||
}
|
}
|
||||||
|
@ -916,15 +918,71 @@ pub mod fmt {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[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"]
|
#[lang = "format_arguments"]
|
||||||
pub struct Arguments<'a> {
|
pub struct Arguments<'a> {
|
||||||
pieces: &'a [&'static str],
|
pieces: &'a [&'static str],
|
||||||
args: &'a [Argument<'a>],
|
fmt: Option<&'a [rt::Placeholder]>,
|
||||||
|
args: &'a [rt::Argument<'a>],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Arguments<'a> {
|
impl<'a> Arguments<'a> {
|
||||||
pub const fn new_v1(pieces: &'a [&'static str], args: &'a [Argument<'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…
Add table
Add a link
Reference in a new issue