Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-def/src/expr_store/lower.rs')
-rw-r--r--crates/hir-def/src/expr_store/lower.rs985
1 files changed, 152 insertions, 833 deletions
diff --git a/crates/hir-def/src/expr_store/lower.rs b/crates/hir-def/src/expr_store/lower.rs
index 3794cb18e9..42b076abb2 100644
--- a/crates/hir-def/src/expr_store/lower.rs
+++ b/crates/hir-def/src/expr_store/lower.rs
@@ -2,17 +2,17 @@
//! representation.
mod asm;
+mod format_args;
mod generics;
mod path;
-use std::mem;
+use std::{cell::OnceCell, mem};
use base_db::FxIndexSet;
use cfg::CfgOptions;
use either::Either;
use hir_expand::{
HirFileId, InFile, MacroDefId,
- mod_path::tool_path,
name::{AsName, Name},
span_map::SpanMapRef,
};
@@ -20,7 +20,7 @@ use intern::{Symbol, sym};
use rustc_hash::FxHashMap;
use stdx::never;
use syntax::{
- AstNode, AstPtr, AstToken as _, SyntaxNodePtr,
+ AstNode, AstPtr, SyntaxNodePtr,
ast::{
self, ArrayExprKind, AstChildren, BlockExpr, HasArgList, HasAttrs, HasGenericArgs,
HasGenericParams, HasLoopBody, HasName, HasTypeBounds, IsString, RangeItem,
@@ -34,7 +34,7 @@ use tt::TextRange;
use crate::{
AdtId, BlockId, BlockLoc, DefWithBodyId, FunctionId, GenericDefId, ImplId, MacroId,
ModuleDefId, ModuleId, TraitId, TypeAliasId, UnresolvedMacro,
- builtin_type::BuiltinUint,
+ attrs::AttrFlags,
db::DefDatabase,
expr_store::{
Body, BodySourceMap, ExprPtr, ExpressionStore, ExpressionStoreBuilder,
@@ -47,17 +47,11 @@ use crate::{
hir::{
Array, Binding, BindingAnnotation, BindingId, BindingProblems, CaptureBy, ClosureKind,
Expr, ExprId, Item, Label, LabelId, Literal, MatchArm, Movability, OffsetOf, Pat, PatId,
- RecordFieldPat, RecordLitField, Statement,
- format_args::{
- self, FormatAlignment, FormatArgs, FormatArgsPiece, FormatArgument, FormatArgumentKind,
- FormatArgumentsCollector, FormatCount, FormatDebugHex, FormatOptions,
- FormatPlaceholder, FormatSign, FormatTrait,
- },
- generics::GenericParams,
+ RecordFieldPat, RecordLitField, Statement, generics::GenericParams,
},
item_scope::BuiltinShadowMode,
item_tree::FieldsShape,
- lang_item::LangItem,
+ lang_item::{LangItemTarget, LangItems},
nameres::{DefMap, LocalDefMap, MacroSubNs, block_def_map},
type_ref::{
ArrayType, ConstRef, FnType, LifetimeRef, LifetimeRefId, Mutability, PathId, Rawness,
@@ -87,14 +81,16 @@ pub(super) fn lower_body(
let mut params = vec![];
let mut collector = ExprCollector::new(db, module, current_file_id);
- let skip_body = match owner {
- DefWithBodyId::FunctionId(it) => db.attrs(it.into()),
- DefWithBodyId::StaticId(it) => db.attrs(it.into()),
- DefWithBodyId::ConstId(it) => db.attrs(it.into()),
- DefWithBodyId::VariantId(it) => db.attrs(it.into()),
- }
- .rust_analyzer_tool()
- .any(|attr| *attr.path() == tool_path![skip]);
+ let skip_body = AttrFlags::query(
+ db,
+ match owner {
+ DefWithBodyId::FunctionId(it) => it.into(),
+ DefWithBodyId::StaticId(it) => it.into(),
+ DefWithBodyId::ConstId(it) => it.into(),
+ DefWithBodyId::VariantId(it) => it.into(),
+ },
+ )
+ .contains(AttrFlags::RUST_ANALYZER_SKIP);
// If #[rust_analyzer::skip] annotated, only construct enough information for the signature
// and skip the body.
if skip_body {
@@ -416,6 +412,7 @@ pub struct ExprCollector<'db> {
def_map: &'db DefMap,
local_def_map: &'db LocalDefMap,
module: ModuleId,
+ lang_items: OnceCell<&'db LangItems>,
pub store: ExpressionStoreBuilder,
// state stuff
@@ -431,9 +428,12 @@ pub struct ExprCollector<'db> {
current_try_block_label: Option<LabelId>,
label_ribs: Vec<LabelRib>,
- current_binding_owner: Option<ExprId>,
+ unowned_bindings: Vec<BindingId>,
awaitable_context: Option<Awaitable>,
+ krate: base_db::Crate,
+
+ name_generator_index: usize,
}
#[derive(Clone, Debug)]
@@ -513,7 +513,7 @@ impl BindingList {
}
}
-impl ExprCollector<'_> {
+impl<'db> ExprCollector<'db> {
pub fn new(
db: &dyn DefDatabase,
module: ModuleId,
@@ -521,24 +521,39 @@ impl ExprCollector<'_> {
) -> ExprCollector<'_> {
let (def_map, local_def_map) = module.local_def_map(db);
let expander = Expander::new(db, current_file_id, def_map);
+ let krate = module.krate(db);
ExprCollector {
db,
- cfg_options: module.krate().cfg_options(db),
+ cfg_options: krate.cfg_options(db),
module,
def_map,
local_def_map,
+ lang_items: OnceCell::new(),
store: ExpressionStoreBuilder::default(),
expander,
current_try_block_label: None,
is_lowering_coroutine: false,
label_ribs: Vec::new(),
- current_binding_owner: None,
+ unowned_bindings: Vec::new(),
awaitable_context: None,
current_block_legacy_macro_defs_count: FxHashMap::default(),
outer_impl_trait: false,
+ krate,
+ name_generator_index: 0,
}
}
+ fn generate_new_name(&mut self) -> Name {
+ let index = self.name_generator_index;
+ self.name_generator_index += 1;
+ Name::generate_new_name(index)
+ }
+
+ #[inline]
+ pub(crate) fn lang_items(&self) -> &'db LangItems {
+ self.lang_items.get_or_init(|| crate::lang_item::lang_items(self.db, self.def_map.krate()))
+ }
+
#[inline]
pub(crate) fn span_map(&self) -> SpanMapRef<'_> {
self.expander.span_map()
@@ -937,7 +952,8 @@ impl ExprCollector<'_> {
node: ast::TypeBound,
impl_trait_lower_fn: ImplTraitLowerFn<'_>,
) -> TypeBound {
- match node.kind() {
+ let Some(kind) = node.kind() else { return TypeBound::Error };
+ match kind {
ast::TypeBoundKind::PathType(binder, path_type) => {
let binder = match binder.and_then(|it| it.generic_param_list()) {
Some(gpl) => gpl
@@ -1053,12 +1069,10 @@ impl ExprCollector<'_> {
Some(ast::BlockModifier::Const(_)) => {
self.with_label_rib(RibKind::Constant, |this| {
this.with_awaitable_block(Awaitable::No("constant block"), |this| {
- let (result_expr_id, prev_binding_owner) =
- this.initialize_binding_owner(syntax_ptr);
- let inner_expr = this.collect_block(e);
- this.store.exprs[result_expr_id] = Expr::Const(inner_expr);
- this.current_binding_owner = prev_binding_owner;
- result_expr_id
+ this.with_binding_owner(|this| {
+ let inner_expr = this.collect_block(e);
+ this.alloc_expr(Expr::Const(inner_expr), syntax_ptr)
+ })
})
})
}
@@ -1269,64 +1283,65 @@ impl ExprCollector<'_> {
}
}
ast::Expr::ClosureExpr(e) => self.with_label_rib(RibKind::Closure, |this| {
- let (result_expr_id, prev_binding_owner) =
- this.initialize_binding_owner(syntax_ptr);
- let mut args = Vec::new();
- let mut arg_types = Vec::new();
- if let Some(pl) = e.param_list() {
- let num_params = pl.params().count();
- args.reserve_exact(num_params);
- arg_types.reserve_exact(num_params);
- for param in pl.params() {
- let pat = this.collect_pat_top(param.pat());
- let type_ref =
- param.ty().map(|it| this.lower_type_ref_disallow_impl_trait(it));
- args.push(pat);
- arg_types.push(type_ref);
+ this.with_binding_owner(|this| {
+ let mut args = Vec::new();
+ let mut arg_types = Vec::new();
+ if let Some(pl) = e.param_list() {
+ let num_params = pl.params().count();
+ args.reserve_exact(num_params);
+ arg_types.reserve_exact(num_params);
+ for param in pl.params() {
+ let pat = this.collect_pat_top(param.pat());
+ let type_ref =
+ param.ty().map(|it| this.lower_type_ref_disallow_impl_trait(it));
+ args.push(pat);
+ arg_types.push(type_ref);
+ }
}
- }
- let ret_type = e
- .ret_type()
- .and_then(|r| r.ty())
- .map(|it| this.lower_type_ref_disallow_impl_trait(it));
+ let ret_type = e
+ .ret_type()
+ .and_then(|r| r.ty())
+ .map(|it| this.lower_type_ref_disallow_impl_trait(it));
- let prev_is_lowering_coroutine = mem::take(&mut this.is_lowering_coroutine);
- let prev_try_block_label = this.current_try_block_label.take();
+ let prev_is_lowering_coroutine = mem::take(&mut this.is_lowering_coroutine);
+ let prev_try_block_label = this.current_try_block_label.take();
- let awaitable = if e.async_token().is_some() {
- Awaitable::Yes
- } else {
- Awaitable::No("non-async closure")
- };
- let body =
- this.with_awaitable_block(awaitable, |this| this.collect_expr_opt(e.body()));
+ let awaitable = if e.async_token().is_some() {
+ Awaitable::Yes
+ } else {
+ Awaitable::No("non-async closure")
+ };
+ let body = this
+ .with_awaitable_block(awaitable, |this| this.collect_expr_opt(e.body()));
- let closure_kind = if this.is_lowering_coroutine {
- let movability = if e.static_token().is_some() {
- Movability::Static
+ let closure_kind = if this.is_lowering_coroutine {
+ let movability = if e.static_token().is_some() {
+ Movability::Static
+ } else {
+ Movability::Movable
+ };
+ ClosureKind::Coroutine(movability)
+ } else if e.async_token().is_some() {
+ ClosureKind::Async
} else {
- Movability::Movable
+ ClosureKind::Closure
};
- ClosureKind::Coroutine(movability)
- } else if e.async_token().is_some() {
- ClosureKind::Async
- } else {
- ClosureKind::Closure
- };
- let capture_by =
- if e.move_token().is_some() { CaptureBy::Value } else { CaptureBy::Ref };
- this.is_lowering_coroutine = prev_is_lowering_coroutine;
- this.current_binding_owner = prev_binding_owner;
- this.current_try_block_label = prev_try_block_label;
- this.store.exprs[result_expr_id] = Expr::Closure {
- args: args.into(),
- arg_types: arg_types.into(),
- ret_type,
- body,
- closure_kind,
- capture_by,
- };
- result_expr_id
+ let capture_by =
+ if e.move_token().is_some() { CaptureBy::Value } else { CaptureBy::Ref };
+ this.is_lowering_coroutine = prev_is_lowering_coroutine;
+ this.current_try_block_label = prev_try_block_label;
+ this.alloc_expr(
+ Expr::Closure {
+ args: args.into(),
+ arg_types: arg_types.into(),
+ ret_type,
+ body,
+ closure_kind,
+ capture_by,
+ },
+ syntax_ptr,
+ )
+ })
}),
ast::Expr::BinExpr(e) => {
let op = e.op_kind();
@@ -1362,11 +1377,7 @@ impl ExprCollector<'_> {
let initializer = self.collect_expr_opt(initializer);
let repeat = self.with_label_rib(RibKind::Constant, |this| {
if let Some(repeat) = repeat {
- let syntax_ptr = AstPtr::new(&repeat);
- this.collect_as_a_binding_owner_bad(
- |this| this.collect_expr(repeat),
- syntax_ptr,
- )
+ this.with_binding_owner(|this| this.collect_expr(repeat))
} else {
this.missing_expr()
}
@@ -1623,41 +1634,22 @@ impl ExprCollector<'_> {
}
}
- fn initialize_binding_owner(
- &mut self,
- syntax_ptr: AstPtr<ast::Expr>,
- ) -> (ExprId, Option<ExprId>) {
- let result_expr_id = self.alloc_expr(Expr::Missing, syntax_ptr);
- let prev_binding_owner = self.current_binding_owner.take();
- self.current_binding_owner = Some(result_expr_id);
-
- (result_expr_id, prev_binding_owner)
- }
-
- /// FIXME: This function is bad. It will produce a dangling `Missing` expr which wastes memory. Currently
- /// it is used only for const blocks and repeat expressions, which are also hacky and ideally should have
- /// their own body. Don't add more usage for this function so that we can remove this function after
- /// separating those bodies.
- fn collect_as_a_binding_owner_bad(
- &mut self,
- job: impl FnOnce(&mut ExprCollector<'_>) -> ExprId,
- syntax_ptr: AstPtr<ast::Expr>,
- ) -> ExprId {
- let (id, prev_owner) = self.initialize_binding_owner(syntax_ptr);
- let tmp = job(self);
- self.store.exprs[id] = mem::replace(&mut self.store.exprs[tmp], Expr::Missing);
- self.current_binding_owner = prev_owner;
- id
+ fn with_binding_owner(&mut self, create_expr: impl FnOnce(&mut Self) -> ExprId) -> ExprId {
+ let prev_unowned_bindings_len = self.unowned_bindings.len();
+ let expr_id = create_expr(self);
+ for binding in self.unowned_bindings.drain(prev_unowned_bindings_len..) {
+ self.store.binding_owners.insert(binding, expr_id);
+ }
+ expr_id
}
/// Desugar `try { <stmts>; <expr> }` into `'<new_label>: { <stmts>; ::std::ops::Try::from_output(<expr>) }`,
/// `try { <stmts>; }` into `'<new_label>: { <stmts>; ::std::ops::Try::from_output(()) }`
/// and save the `<new_label>` to use it as a break target for desugaring of the `?` operator.
fn desugar_try_block(&mut self, e: BlockExpr) -> ExprId {
- let try_from_output = self.lang_path(LangItem::TryTraitFromOutput);
- let label = self.alloc_label_desugared(Label {
- name: Name::generate_new_name(self.store.labels.len()),
- });
+ let try_from_output = self.lang_path(self.lang_items().TryTraitFromOutput);
+ let label = self.generate_new_name();
+ let label = self.alloc_label_desugared(Label { name: label });
let old_label = self.current_try_block_label.replace(label);
let ptr = AstPtr::new(&e).upcast();
@@ -1753,10 +1745,11 @@ impl ExprCollector<'_> {
/// }
/// ```
fn collect_for_loop(&mut self, syntax_ptr: AstPtr<ast::Expr>, e: ast::ForExpr) -> ExprId {
- let into_iter_fn = self.lang_path(LangItem::IntoIterIntoIter);
- let iter_next_fn = self.lang_path(LangItem::IteratorNext);
- let option_some = self.lang_path(LangItem::OptionSome);
- let option_none = self.lang_path(LangItem::OptionNone);
+ let lang_items = self.lang_items();
+ let into_iter_fn = self.lang_path(lang_items.IntoIterIntoIter);
+ let iter_next_fn = self.lang_path(lang_items.IteratorNext);
+ let option_some = self.lang_path(lang_items.OptionSome);
+ let option_none = self.lang_path(lang_items.OptionNone);
let head = self.collect_expr_opt(e.iterable());
let into_iter_fn_expr =
self.alloc_expr(into_iter_fn.map_or(Expr::Missing, Expr::Path), syntax_ptr);
@@ -1784,7 +1777,7 @@ impl ExprCollector<'_> {
this.collect_expr_opt(e.loop_body().map(|it| it.into()))
}),
};
- let iter_name = Name::generate_new_name(self.store.exprs.len());
+ let iter_name = self.generate_new_name();
let iter_expr = self.alloc_expr(Expr::Path(Path::from(iter_name.clone())), syntax_ptr);
let iter_expr_mut = self.alloc_expr(
Expr::Ref { expr: iter_expr, rawness: Rawness::Ref, mutability: Mutability::Mut },
@@ -1836,15 +1829,16 @@ impl ExprCollector<'_> {
/// }
/// ```
fn collect_try_operator(&mut self, syntax_ptr: AstPtr<ast::Expr>, e: ast::TryExpr) -> ExprId {
- let try_branch = self.lang_path(LangItem::TryTraitBranch);
- let cf_continue = self.lang_path(LangItem::ControlFlowContinue);
- let cf_break = self.lang_path(LangItem::ControlFlowBreak);
- let try_from_residual = self.lang_path(LangItem::TryTraitFromResidual);
+ let lang_items = self.lang_items();
+ let try_branch = self.lang_path(lang_items.TryTraitBranch);
+ let cf_continue = self.lang_path(lang_items.ControlFlowContinue);
+ let cf_break = self.lang_path(lang_items.ControlFlowBreak);
+ let try_from_residual = self.lang_path(lang_items.TryTraitFromResidual);
let operand = self.collect_expr_opt(e.expr());
let try_branch = self.alloc_expr(try_branch.map_or(Expr::Missing, Expr::Path), syntax_ptr);
let expr = self
.alloc_expr(Expr::Call { callee: try_branch, args: Box::new([operand]) }, syntax_ptr);
- let continue_name = Name::generate_new_name(self.store.bindings.len());
+ let continue_name = self.generate_new_name();
let continue_binding = self.alloc_binding(
continue_name.clone(),
BindingAnnotation::Unannotated,
@@ -1862,7 +1856,7 @@ impl ExprCollector<'_> {
guard: None,
expr: self.alloc_expr(Expr::Path(Path::from(continue_name)), syntax_ptr),
};
- let break_name = Name::generate_new_name(self.store.bindings.len());
+ let break_name = self.generate_new_name();
let break_binding =
self.alloc_binding(break_name.clone(), BindingAnnotation::Unannotated, HygieneId::ROOT);
let break_bpat = self.alloc_pat_desugared(Pat::Bind { id: break_binding, subpat: None });
@@ -1904,9 +1898,8 @@ impl ExprCollector<'_> {
T: ast::AstNode,
{
let macro_call_ptr = self.expander.in_file(syntax_ptr);
- let module = self.module.local_id;
- let block_call = self.def_map.modules[self.module.local_id].scope.macro_invoc(
+ let block_call = self.def_map.modules[self.module].scope.macro_invoc(
self.expander.in_file(self.expander.ast_id_map().ast_id_for_ptr(syntax_ptr)),
);
let res = match block_call {
@@ -1918,7 +1911,7 @@ impl ExprCollector<'_> {
.resolve_path(
self.local_def_map,
self.db,
- module,
+ self.module,
path,
crate::item_scope::BuiltinShadowMode::Other,
Some(MacroSubNs::Bang),
@@ -1929,7 +1922,7 @@ impl ExprCollector<'_> {
self.expander.enter_expand(
self.db,
mcall,
- self.module.krate(),
+ self.krate,
resolver,
&mut |ptr, call| {
_ = self.store.expansions.insert(ptr.map(|(it, _)| it), call);
@@ -2047,7 +2040,8 @@ impl ExprCollector<'_> {
return;
};
let name = name.as_name();
- let macro_id = self.def_map.modules[DefMap::ROOT].scope.get(&name).take_macros();
+ let macro_id =
+ self.def_map.modules[self.def_map.root].scope.get(&name).take_macros();
self.collect_macro_def(statements, macro_id);
}
ast::Stmt::Item(ast::Item::MacroRules(macro_)) => {
@@ -2061,7 +2055,7 @@ impl ExprCollector<'_> {
let name = name.as_name();
let macro_defs_count =
self.current_block_legacy_macro_defs_count.entry(name.clone()).or_insert(0);
- let macro_id = self.def_map.modules[DefMap::ROOT]
+ let macro_id = self.def_map.modules[self.def_map.root]
.scope
.get_legacy_macro(&name)
.and_then(|it| it.get(*macro_defs_count))
@@ -2107,7 +2101,7 @@ impl ExprCollector<'_> {
match block_id.map(|block_id| (block_def_map(self.db, block_id), block_id)) {
Some((def_map, block_id)) => {
self.store.block_scopes.push(block_id);
- (def_map.module_id(DefMap::ROOT), def_map)
+ (def_map.root_module_id(), def_map)
}
None => (self.module, self.def_map),
};
@@ -2190,7 +2184,7 @@ impl ExprCollector<'_> {
let (resolved, _) = self.def_map.resolve_path(
self.local_def_map,
self.db,
- self.module.local_id,
+ self.module,
&name.clone().into(),
BuiltinShadowMode::Other,
None,
@@ -2357,11 +2351,7 @@ impl ExprCollector<'_> {
ast::Pat::ConstBlockPat(const_block_pat) => {
if let Some(block) = const_block_pat.block_expr() {
let expr_id = self.with_label_rib(RibKind::Constant, |this| {
- let syntax_ptr = AstPtr::new(&block.clone().into());
- this.collect_as_a_binding_owner_bad(
- |this| this.collect_block(block),
- syntax_ptr,
- )
+ this.with_binding_owner(|this| this.collect_block(block))
});
Pat::ConstBlock(expr_id)
} else {
@@ -2409,7 +2399,11 @@ impl ExprCollector<'_> {
};
let start = range_part_lower(p.start());
let end = range_part_lower(p.end());
- Pat::Range { start, end }
+ // FIXME: Exclusive ended pattern range is stabilised
+ match p.op_kind() {
+ Some(range_type) => Pat::Range { start, end, range_type },
+ None => Pat::Missing,
+ }
}
};
let ptr = AstPtr::new(&pat);
@@ -2485,7 +2479,7 @@ impl ExprCollector<'_> {
/// Returns `None` (and emits diagnostics) when `owner` if `#[cfg]`d out, and `Some(())` when
/// not.
fn check_cfg(&mut self, owner: &dyn ast::HasAttrs) -> bool {
- let enabled = self.expander.is_cfg_enabled(self.db, owner, self.cfg_options);
+ let enabled = self.expander.is_cfg_enabled(owner, self.cfg_options);
match enabled {
Ok(()) => true,
Err(cfg) => {
@@ -2617,7 +2611,6 @@ impl ExprCollector<'_> {
}
// endregion: labels
- // region: format
fn expand_macros_to_string(&mut self, expr: ast::Expr) -> Option<(ast::String, bool)> {
let m = match expr {
ast::Expr::MacroExpr(m) => m,
@@ -2637,690 +2630,24 @@ impl ExprCollector<'_> {
Some((exp, false))
}
- fn collect_format_args(
- &mut self,
- f: ast::FormatArgsExpr,
- syntax_ptr: AstPtr<ast::Expr>,
- ) -> ExprId {
- let mut args = FormatArgumentsCollector::default();
- 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().and_then(|it| match it {
- ast::Expr::Literal(literal) => match literal.kind() {
- ast::LiteralKind::String(s) => Some(s.text().to_owned()),
- _ => None,
- },
- _ => None,
- });
- let mut mappings = vec![];
- let (fmt, hygiene) = match template.and_then(|template| {
- self.expand_macros_to_string(template.clone()).map(|it| (it, template))
- }) {
- Some(((s, is_direct_literal), template)) => {
- let call_ctx = self.expander.call_syntax_ctx();
- let hygiene = self.hygiene_id_for(s.syntax().text_range());
- let fmt = format_args::parse(
- &s,
- fmt_snippet,
- args,
- is_direct_literal,
- |name, range| {
- let expr_id = self.alloc_expr_desugared(Expr::Path(Path::from(name)));
- if let Some(range) = range {
- self.store
- .template_map
- .get_or_insert_with(Default::default)
- .implicit_capture_to_source
- .insert(
- expr_id,
- self.expander.in_file((AstPtr::new(&template), range)),
- );
- }
- if !hygiene.is_root() {
- self.store.ident_hygiene.insert(expr_id.into(), hygiene);
- }
- expr_id
- },
- |name, span| {
- if let Some(span) = span {
- mappings.push((span, name))
- }
- },
- call_ctx,
- );
- (fmt, hygiene)
- }
- None => (
- FormatArgs {
- template: Default::default(),
- arguments: args.finish(),
- orphans: Default::default(),
- },
- HygieneId::ROOT,
- ),
- };
-
- // 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(
- Symbol::empty(),
- ))))
- } else {
- None
- }
- }
- }
- })
- .collect();
- let lit_pieces =
- self.alloc_expr_desugared(Expr::Array(Array::ElementList { elements: lit_pieces }));
- 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 }));
- self.alloc_expr_desugared(Expr::Ref {
- expr: array,
- rawness: Rawness::Ref,
- mutability: Mutability::Shared,
- })
- };
-
- // Assume that rustc version >= 1.89.0 iff lang item `format_arguments` exists
- // but `format_unsafe_arg` does not
- let fmt_args =
- || crate::lang_item::lang_item(self.db, self.module.krate(), LangItem::FormatArguments);
- let fmt_unsafe_arg =
- || crate::lang_item::lang_item(self.db, self.module.krate(), LangItem::FormatUnsafeArg);
- let use_format_args_since_1_89_0 = fmt_args().is_some() && fmt_unsafe_arg().is_none();
-
- let idx = if use_format_args_since_1_89_0 {
- self.collect_format_args_impl(syntax_ptr, fmt, argmap, lit_pieces, format_options)
- } else {
- self.collect_format_args_before_1_89_0_impl(
- syntax_ptr,
- fmt,
- argmap,
- lit_pieces,
- format_options,
- )
- };
-
- self.store
- .template_map
- .get_or_insert_with(Default::default)
- .format_args_to_captures
- .insert(idx, (hygiene, mappings));
- idx
- }
-
- /// `format_args!` expansion implementation for rustc versions < `1.89.0`
- fn collect_format_args_before_1_89_0_impl(
- &mut self,
- syntax_ptr: AstPtr<ast::Expr>,
- fmt: FormatArgs,
- argmap: FxIndexSet<(usize, ArgumentType)>,
- lit_pieces: ExprId,
- format_options: ExprId,
- ) -> ExprId {
- let arguments = &*fmt.arguments.arguments;
-
- let args = if arguments.is_empty() {
- let expr = self
- .alloc_expr_desugared(Expr::Array(Array::ElementList { elements: Box::default() }));
- 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 }));
- 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 new_v1_formatted = LangItem::FormatArguments.ty_rel_path(
- self.db,
- self.module.krate(),
- Name::new_symbol_root(sym::new_v1_formatted),
- );
- let unsafe_arg_new = LangItem::FormatUnsafeArg.ty_rel_path(
- self.db,
- self.module.krate(),
- Name::new_symbol_root(sym::new),
- );
- let new_v1_formatted =
- self.alloc_expr_desugared(new_v1_formatted.map_or(Expr::Missing, Expr::Path));
-
- let unsafe_arg_new =
- self.alloc_expr_desugared(unsafe_arg_new.map_or(Expr::Missing, Expr::Path));
- let unsafe_arg_new =
- self.alloc_expr_desugared(Expr::Call { callee: unsafe_arg_new, args: Box::default() });
- let mut unsafe_arg_new = self.alloc_expr_desugared(Expr::Unsafe {
- id: None,
- statements: Box::new([]),
- tail: Some(unsafe_arg_new),
- });
- if !fmt.orphans.is_empty() {
- unsafe_arg_new = self.alloc_expr_desugared(Expr::Block {
- id: None,
- // We collect the unused expressions here so that we still infer them instead of
- // dropping them out of the expression tree. We cannot store them in the `Unsafe`
- // block because then unsafe blocks within them will get a false "unused unsafe"
- // diagnostic (rustc has a notion of builtin unsafe blocks, but we don't).
- statements: fmt
- .orphans
- .into_iter()
- .map(|expr| Statement::Expr { expr, has_semi: true })
- .collect(),
- tail: Some(unsafe_arg_new),
- label: None,
- });
- }
-
- self.alloc_expr(
- Expr::Call {
- callee: new_v1_formatted,
- args: Box::new([lit_pieces, args, format_options, unsafe_arg_new]),
- },
- syntax_ptr,
- )
- }
-
- /// `format_args!` expansion implementation for rustc versions >= `1.89.0`,
- /// especially since [this PR](https://github.com/rust-lang/rust/pull/140748)
- fn collect_format_args_impl(
- &mut self,
- syntax_ptr: AstPtr<ast::Expr>,
- fmt: FormatArgs,
- argmap: FxIndexSet<(usize, ArgumentType)>,
- lit_pieces: ExprId,
- format_options: ExprId,
- ) -> ExprId {
- let arguments = &*fmt.arguments.arguments;
-
- let (let_stmts, args) = if arguments.is_empty() {
- (
- // Generate:
- // []
- vec![],
- self.alloc_expr_desugared(Expr::Array(Array::ElementList {
- elements: Box::default(),
- })),
- )
- } else if argmap.len() == 1 && arguments.len() == 1 {
- // Only one argument, so we don't need to make the `args` tuple.
- //
- // Generate:
- // super let args = [<core::fmt::Arguments>::new_display(&arg)];
- let args = argmap
- .iter()
- .map(|&(arg_index, ty)| {
- let ref_arg = self.alloc_expr_desugared(Expr::Ref {
- expr: arguments[arg_index].expr,
- rawness: Rawness::Ref,
- mutability: Mutability::Shared,
- });
- self.make_argument(ref_arg, ty)
- })
- .collect();
- let args =
- self.alloc_expr_desugared(Expr::Array(Array::ElementList { elements: args }));
- let args_name = Name::new_symbol_root(sym::args);
- let args_binding = self.alloc_binding(
- args_name.clone(),
- BindingAnnotation::Unannotated,
- HygieneId::ROOT,
- );
- let args_pat = self.alloc_pat_desugared(Pat::Bind { id: args_binding, subpat: None });
- self.add_definition_to_binding(args_binding, args_pat);
- // TODO: We don't have `super let` yet.
- let let_stmt = Statement::Let {
- pat: args_pat,
- type_ref: None,
- initializer: Some(args),
- else_branch: None,
- };
- (vec![let_stmt], self.alloc_expr_desugared(Expr::Path(args_name.into())))
- } else {
- // Generate:
- // super let args = (&arg0, &arg1, &...);
- let args_name = Name::new_symbol_root(sym::args);
- let args_binding = self.alloc_binding(
- args_name.clone(),
- BindingAnnotation::Unannotated,
- HygieneId::ROOT,
- );
- let args_pat = self.alloc_pat_desugared(Pat::Bind { id: args_binding, subpat: None });
- self.add_definition_to_binding(args_binding, args_pat);
- let elements = arguments
- .iter()
- .map(|arg| {
- self.alloc_expr_desugared(Expr::Ref {
- expr: arg.expr,
- rawness: Rawness::Ref,
- mutability: Mutability::Shared,
- })
- })
- .collect();
- let args_tuple = self.alloc_expr_desugared(Expr::Tuple { exprs: elements });
- // TODO: We don't have `super let` yet
- let let_stmt1 = Statement::Let {
- pat: args_pat,
- type_ref: None,
- initializer: Some(args_tuple),
- else_branch: None,
- };
-
- // Generate:
- // super let 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 args_ident_expr =
- self.alloc_expr_desugared(Expr::Path(args_name.clone().into()));
- let arg = self.alloc_expr_desugared(Expr::Field {
- expr: args_ident_expr,
- name: Name::new_tuple_field(arg_index),
- });
- self.make_argument(arg, ty)
- })
- .collect();
- let array =
- self.alloc_expr_desugared(Expr::Array(Array::ElementList { elements: args }));
- let args_binding = self.alloc_binding(
- args_name.clone(),
- BindingAnnotation::Unannotated,
- HygieneId::ROOT,
- );
- let args_pat = self.alloc_pat_desugared(Pat::Bind { id: args_binding, subpat: None });
- self.add_definition_to_binding(args_binding, args_pat);
- let let_stmt2 = Statement::Let {
- pat: args_pat,
- type_ref: None,
- initializer: Some(array),
- else_branch: None,
- };
- (vec![let_stmt1, let_stmt2], self.alloc_expr_desugared(Expr::Path(args_name.into())))
- };
-
- // Generate:
- // &args
- let args = self.alloc_expr_desugared(Expr::Ref {
- expr: args,
- rawness: Rawness::Ref,
- mutability: Mutability::Shared,
- });
-
- let call_block = {
- // Generate:
- // unsafe {
- // <core::fmt::Arguments>::new_v1_formatted(
- // lit_pieces,
- // args,
- // format_options,
- // )
- // }
-
- let new_v1_formatted = LangItem::FormatArguments.ty_rel_path(
- self.db,
- self.module.krate(),
- Name::new_symbol_root(sym::new_v1_formatted),
- );
- let new_v1_formatted =
- self.alloc_expr_desugared(new_v1_formatted.map_or(Expr::Missing, Expr::Path));
- let args = [lit_pieces, args, format_options];
- let call = self
- .alloc_expr_desugared(Expr::Call { callee: new_v1_formatted, args: args.into() });
-
- Expr::Unsafe { id: None, statements: Box::default(), tail: Some(call) }
- };
-
- if !let_stmts.is_empty() {
- // Generate:
- // {
- // super let …
- // super let …
- // <core::fmt::Arguments>::new_…(…)
- // }
- let call = self.alloc_expr_desugared(call_block);
- self.alloc_expr(
- Expr::Block {
- id: None,
- statements: let_stmts.into(),
- tail: Some(call),
- label: None,
- },
- syntax_ptr,
- )
- } else {
- self.alloc_expr(call_block, 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 precision_expr = self.make_count(precision, argmap);
- let width_expr = self.make_count(width, argmap);
-
- if self.module.krate().workspace_data(self.db).is_atleast_187() {
- // These need to match the constants in library/core/src/fmt/rt.rs.
- let align = match alignment {
- Some(FormatAlignment::Left) => 0,
- Some(FormatAlignment::Right) => 1,
- Some(FormatAlignment::Center) => 2,
- None => 3,
- };
- // This needs to match `Flag` in library/core/src/fmt/rt.rs.
- let flags = fill.unwrap_or(' ') as u32
- | ((sign == Some(FormatSign::Plus)) as u32) << 21
- | ((sign == Some(FormatSign::Minus)) as u32) << 22
- | (alternate as u32) << 23
- | (zero_pad as u32) << 24
- | ((debug_hex == Some(FormatDebugHex::Lower)) as u32) << 25
- | ((debug_hex == Some(FormatDebugHex::Upper)) as u32) << 26
- | (width.is_some() as u32) << 27
- | (precision.is_some() as u32) << 28
- | align << 29
- | 1 << 31; // Highest bit always set.
- let flags = self.alloc_expr_desugared(Expr::Literal(Literal::Uint(
- flags as u128,
- Some(BuiltinUint::U32),
- )));
-
- let position =
- RecordLitField { name: Name::new_symbol_root(sym::position), expr: position };
- let flags = RecordLitField { name: Name::new_symbol_root(sym::flags), expr: flags };
- let precision = RecordLitField {
- name: Name::new_symbol_root(sym::precision),
- expr: precision_expr,
- };
- let width =
- RecordLitField { name: Name::new_symbol_root(sym::width), expr: width_expr };
- self.alloc_expr_desugared(Expr::RecordLit {
- path: LangItem::FormatPlaceholder.path(self.db, self.module.krate()).map(Box::new),
- fields: Box::new([position, flags, precision, width]),
- spread: None,
- })
- } else {
- let format_placeholder_new = {
- let format_placeholder_new = LangItem::FormatPlaceholder.ty_rel_path(
- self.db,
- self.module.krate(),
- Name::new_symbol_root(sym::new),
- );
- match format_placeholder_new {
- Some(path) => self.alloc_expr_desugared(Expr::Path(path)),
- None => self.missing_expr(),
- }
- };
- // 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 fill = self.alloc_expr_desugared(Expr::Literal(Literal::Char(fill.unwrap_or(' '))));
- let align = {
- let align = LangItem::FormatAlignment.ty_rel_path(
- self.db,
- self.module.krate(),
- match alignment {
- Some(FormatAlignment::Left) => Name::new_symbol_root(sym::Left),
- Some(FormatAlignment::Right) => Name::new_symbol_root(sym::Right),
- Some(FormatAlignment::Center) => Name::new_symbol_root(sym::Center),
- None => Name::new_symbol_root(sym::Unknown),
- },
- );
- match align {
- Some(path) => self.alloc_expr_desugared(Expr::Path(path)),
- None => self.missing_expr(),
- }
- };
- self.alloc_expr_desugared(Expr::Call {
- callee: format_placeholder_new,
- args: Box::new([position, fill, align, flags, precision_expr, width_expr]),
- })
- }
- }
-
- /// 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 args = self.alloc_expr_desugared(Expr::Literal(Literal::Uint(
- *n as u128,
- // FIXME: Change this to Some(BuiltinUint::U16) once we drop support for toolchains < 1.88
- None,
- )));
- let count_is = match LangItem::FormatCount.ty_rel_path(
- self.db,
- self.module.krate(),
- Name::new_symbol_root(sym::Is),
- ) {
- Some(count_is) => self.alloc_expr_desugared(Expr::Path(count_is)),
- None => self.missing_expr(),
- };
- self.alloc_expr_desugared(Expr::Call { callee: count_is, args: Box::new([args]) })
- }
- Some(FormatCount::Argument(arg)) => {
- if let Ok(arg_index) = arg.index {
- let (i, _) = argmap.insert_full((arg_index, ArgumentType::Usize));
-
- let args = self.alloc_expr_desugared(Expr::Literal(Literal::Uint(
- i as u128,
- Some(BuiltinUint::Usize),
- )));
- let count_param = match LangItem::FormatCount.ty_rel_path(
- self.db,
- self.module.krate(),
- Name::new_symbol_root(sym::Param),
- ) {
- Some(count_param) => self.alloc_expr_desugared(Expr::Path(count_param)),
- None => self.missing_expr(),
- };
- self.alloc_expr_desugared(Expr::Call {
- callee: count_param,
- args: Box::new([args]),
- })
- } else {
- // FIXME: This drops arg causing it to potentially not be resolved/type checked
- // when typing?
- self.missing_expr()
- }
- }
- None => match LangItem::FormatCount.ty_rel_path(
- self.db,
- self.module.krate(),
- Name::new_symbol_root(sym::Implied),
- ) {
- Some(count_param) => self.alloc_expr_desugared(Expr::Path(count_param)),
- None => self.missing_expr(),
- },
- }
+ fn lang_path(&self, lang: Option<impl Into<LangItemTarget>>) -> Option<Path> {
+ Some(Path::LangItem(lang?.into(), None))
}
- /// 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 new_fn = match LangItem::FormatArgument.ty_rel_path(
- self.db,
- self.module.krate(),
- Name::new_symbol_root(match ty {
- Format(Display) => sym::new_display,
- Format(Debug) => sym::new_debug,
- Format(LowerExp) => sym::new_lower_exp,
- Format(UpperExp) => sym::new_upper_exp,
- Format(Octal) => sym::new_octal,
- Format(Pointer) => sym::new_pointer,
- Format(Binary) => sym::new_binary,
- Format(LowerHex) => sym::new_lower_hex,
- Format(UpperHex) => sym::new_upper_hex,
- Usize => sym::from_usize,
- }),
- ) {
- Some(new_fn) => self.alloc_expr_desugared(Expr::Path(new_fn)),
- None => self.missing_expr(),
- };
- self.alloc_expr_desugared(Expr::Call { callee: new_fn, args: Box::new([arg]) })
+ fn ty_rel_lang_path(
+ &self,
+ lang: Option<impl Into<LangItemTarget>>,
+ relative_name: Symbol,
+ ) -> Option<Path> {
+ Some(Path::LangItem(lang?.into(), Some(Name::new_symbol_root(relative_name))))
}
- // endregion: format
-
- fn lang_path(&self, lang: LangItem) -> Option<Path> {
- lang.path(self.db, self.module.krate())
+ fn ty_rel_lang_path_expr(
+ &self,
+ lang: Option<impl Into<LangItemTarget>>,
+ relative_name: Symbol,
+ ) -> Expr {
+ self.ty_rel_lang_path(lang, relative_name).map_or(Expr::Missing, Expr::Path)
}
}
@@ -3365,9 +2692,7 @@ impl ExprCollector<'_> {
hygiene: HygieneId,
) -> BindingId {
let binding = self.store.bindings.alloc(Binding { name, mode, problems: None, hygiene });
- if let Some(owner) = self.current_binding_owner {
- self.store.binding_owners.insert(binding, owner);
- }
+ self.unowned_bindings.push(binding);
binding
}
@@ -3439,12 +2764,6 @@ fn comma_follows_token(t: Option<syntax::SyntaxToken>) -> bool {
.is_some_and(|it| it.kind() == syntax::T![,])
}
-#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
-enum ArgumentType {
- Format(FormatTrait),
- Usize,
-}
-
/// This function find the AST fragment that corresponds to an `AssociatedTypeBinding` in the HIR.
pub fn hir_assoc_type_binding_to_ast(
segment_args: &ast::GenericArgList,