Unnamed repository; edit this file 'description' to name the repository.
Merge pull request #4774 from rust-lang/rustup-2025-12-18
Automatic Rustup
Ralf Jung 4 months ago
parent 86355bc · parent 1163a8a · commit 2129909
-rw-r--r--crates/base-db/src/lib.rs22
-rw-r--r--crates/hir-def/src/attrs.rs3
-rw-r--r--crates/hir-def/src/db.rs15
-rw-r--r--crates/hir-def/src/expr_store/lower.rs895
-rw-r--r--crates/hir-def/src/expr_store/lower/format_args.rs1012
-rw-r--r--crates/hir-def/src/expr_store/tests/body.rs98
-rw-r--r--crates/hir-def/src/expr_store/tests/body/block.rs52
-rw-r--r--crates/hir-def/src/expr_store/tests/signatures.rs12
-rw-r--r--crates/hir-def/src/find_path.rs66
-rw-r--r--crates/hir-def/src/import_map.rs6
-rw-r--r--crates/hir-def/src/item_tree.rs8
-rw-r--r--crates/hir-def/src/lib.rs61
-rw-r--r--crates/hir-def/src/nameres.rs32
-rw-r--r--crates/hir-def/src/resolver.rs4
-rw-r--r--crates/hir-def/src/visibility.rs30
-rw-r--r--crates/hir-ty/src/dyn_compatibility.rs24
-rw-r--r--crates/hir-ty/src/infer/closure/analysis.rs2
-rw-r--r--crates/hir-ty/src/layout.rs4
-rw-r--r--crates/hir-ty/src/method_resolution.rs49
-rw-r--r--crates/hir-ty/src/mir.rs3
-rw-r--r--crates/hir-ty/src/mir/borrowck.rs11
-rw-r--r--crates/hir-ty/src/mir/eval.rs1
-rw-r--r--crates/hir-ty/src/mir/lower/tests.rs59
-rw-r--r--crates/hir-ty/src/next_solver/infer/traits.rs4
-rw-r--r--crates/hir-ty/src/next_solver/interner.rs9
-rw-r--r--crates/hir-ty/src/next_solver/solver.rs89
-rw-r--r--crates/hir-ty/src/tests/regression/new_solver.rs56
-rw-r--r--crates/hir-ty/src/tests/traits.rs20
-rw-r--r--crates/hir/src/db.rs37
-rw-r--r--crates/hir/src/display.rs8
-rw-r--r--crates/hir/src/lib.rs4
-rw-r--r--crates/hir/src/semantics.rs2
-rw-r--r--crates/hir/src/semantics/child_by_source.rs2
-rw-r--r--crates/hir/src/symbols.rs23
-rw-r--r--crates/ide-assists/src/handlers/bind_unused_param.rs14
-rw-r--r--crates/ide-assists/src/handlers/convert_for_to_while_let.rs69
-rw-r--r--crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs18
-rw-r--r--crates/ide-assists/src/handlers/convert_while_to_loop.rs224
-rw-r--r--crates/ide-assists/src/handlers/generate_delegate_trait.rs449
-rw-r--r--crates/ide-assists/src/handlers/generate_fn_type_alias.rs32
-rw-r--r--crates/ide-assists/src/handlers/replace_method_eager_lazy.rs93
-rw-r--r--crates/ide-assists/src/handlers/toggle_ignore.rs35
-rw-r--r--crates/ide-assists/src/lib.rs2
-rw-r--r--crates/ide-assists/src/utils.rs24
-rw-r--r--crates/ide-completion/src/completions/format_string.rs94
-rw-r--r--crates/ide-completion/src/lib.rs1
-rw-r--r--crates/ide-db/src/symbol_index.rs77
-rw-r--r--crates/ide-db/src/test_data/test_doc_alias.txt7
-rw-r--r--crates/ide-db/src/test_data/test_symbol_index_collection.txt33
-rw-r--r--crates/ide-db/src/test_data/test_symbols_exclude_imports.txt1
-rw-r--r--crates/ide-db/src/test_data/test_symbols_with_imports.txt2
-rw-r--r--crates/ide-diagnostics/src/handlers/incorrect_case.rs3
-rw-r--r--crates/ide-diagnostics/src/handlers/missing_unsafe.rs4
-rw-r--r--crates/ide-diagnostics/src/handlers/mutability_errors.rs6
-rw-r--r--crates/ide-diagnostics/src/handlers/no_such_field.rs31
-rw-r--r--crates/ide-diagnostics/src/handlers/unused_variables.rs55
-rw-r--r--crates/ide-diagnostics/src/lib.rs10
-rw-r--r--crates/ide/src/hover/tests.rs57
-rw-r--r--crates/ide/src/navigation_target.rs2
-rw-r--r--crates/ide/src/static_index.rs22
-rw-r--r--crates/parser/src/lib.rs4
-rw-r--r--crates/proc-macro-api/src/lib.rs1
-rw-r--r--crates/project-model/src/lib.rs1
-rw-r--r--crates/rust-analyzer/src/cli/scip.rs23
-rw-r--r--crates/rust-analyzer/src/lib.rs2
-rw-r--r--crates/rust-analyzer/src/lsp/capabilities.rs6
-rw-r--r--crates/rust-analyzer/src/main_loop.rs16
-rw-r--r--crates/rust-analyzer/tests/slow-tests/support.rs1
-rw-r--r--crates/syntax/src/ast/make.rs99
-rw-r--r--crates/syntax/src/ast/node_ext.rs11
-rw-r--r--crates/syntax/src/ast/syntax_factory/constructors.rs219
-rw-r--r--crates/test-utils/src/minicore.rs51
-rw-r--r--editors/code/package-lock.json38
-rw-r--r--rust-version2
-rw-r--r--triagebot.toml3
75 files changed, 3069 insertions, 1496 deletions
diff --git a/crates/base-db/src/lib.rs b/crates/base-db/src/lib.rs
index b6e3465211..aa06cdefe6 100644
--- a/crates/base-db/src/lib.rs
+++ b/crates/base-db/src/lib.rs
@@ -64,6 +64,28 @@ macro_rules! impl_intern_key {
};
}
+/// # SAFETY
+///
+/// `old_pointer` must be valid for unique writes
+pub unsafe fn unsafe_update_eq<T>(old_pointer: *mut T, new_value: T) -> bool
+where
+ T: PartialEq,
+{
+ // SAFETY: Caller obligation
+ let old_ref: &mut T = unsafe { &mut *old_pointer };
+
+ if *old_ref != new_value {
+ *old_ref = new_value;
+ true
+ } else {
+ // Subtle but important: Eq impls can be buggy or define equality
+ // in surprising ways. If it says that the value has not changed,
+ // we do not modify the existing value, and thus do not have to
+ // update the revision, as downstream code will not see the new value.
+ false
+ }
+}
+
pub const DEFAULT_FILE_TEXT_LRU_CAP: u16 = 16;
pub const DEFAULT_PARSE_LRU_CAP: u16 = 128;
pub const DEFAULT_BORROWCK_LRU_CAP: u16 = 2024;
diff --git a/crates/hir-def/src/attrs.rs b/crates/hir-def/src/attrs.rs
index 607638f32b..febc794b5a 100644
--- a/crates/hir-def/src/attrs.rs
+++ b/crates/hir-def/src/attrs.rs
@@ -155,6 +155,9 @@ fn match_attr_flags(attr_flags: &mut AttrFlags, attr: Meta) -> ControlFlow<Infal
"rustc_skip_during_method_dispatch" => {
extract_rustc_skip_during_method_dispatch(attr_flags, tt)
}
+ "rustc_deprecated_safe_2024" => {
+ attr_flags.insert(AttrFlags::RUSTC_DEPRECATED_SAFE_2024)
+ }
_ => {}
},
2 => match path.segments[0].text() {
diff --git a/crates/hir-def/src/db.rs b/crates/hir-def/src/db.rs
index 98df8d0ff4..ccd4bc9be8 100644
--- a/crates/hir-def/src/db.rs
+++ b/crates/hir-def/src/db.rs
@@ -8,12 +8,12 @@ use la_arena::ArenaMap;
use triomphe::Arc;
use crate::{
- AssocItemId, AttrDefId, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, EnumVariantId,
- EnumVariantLoc, ExternBlockId, ExternBlockLoc, ExternCrateId, ExternCrateLoc, FunctionId,
- FunctionLoc, GenericDefId, ImplId, ImplLoc, LocalFieldId, Macro2Id, Macro2Loc, MacroExpander,
- MacroId, MacroRulesId, MacroRulesLoc, MacroRulesLocFlags, ProcMacroId, ProcMacroLoc, StaticId,
- StaticLoc, StructId, StructLoc, TraitId, TraitLoc, TypeAliasId, TypeAliasLoc, UnionId,
- UnionLoc, UseId, UseLoc, VariantId,
+ AssocItemId, AttrDefId, BlockId, BlockLoc, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc,
+ EnumVariantId, EnumVariantLoc, ExternBlockId, ExternBlockLoc, ExternCrateId, ExternCrateLoc,
+ FunctionId, FunctionLoc, GenericDefId, ImplId, ImplLoc, LocalFieldId, Macro2Id, Macro2Loc,
+ MacroExpander, MacroId, MacroRulesId, MacroRulesLoc, MacroRulesLocFlags, ProcMacroId,
+ ProcMacroLoc, StaticId, StaticLoc, StructId, StructLoc, TraitId, TraitLoc, TypeAliasId,
+ TypeAliasLoc, UnionId, UnionLoc, UseId, UseLoc, VariantId,
attrs::AttrFlags,
expr_store::{
Body, BodySourceMap, ExpressionStore, ExpressionStoreSourceMap, scope::ExprScopes,
@@ -82,6 +82,9 @@ pub trait InternDatabase: RootQueryDb {
#[salsa::interned]
fn intern_macro_rules(&self, loc: MacroRulesLoc) -> MacroRulesId;
// endregion: items
+
+ #[salsa::interned]
+ fn intern_block(&self, loc: BlockLoc) -> BlockId;
}
#[query_group::query_group]
diff --git a/crates/hir-def/src/expr_store/lower.rs b/crates/hir-def/src/expr_store/lower.rs
index f374dd2cc9..42b076abb2 100644
--- a/crates/hir-def/src/expr_store/lower.rs
+++ b/crates/hir-def/src/expr_store/lower.rs
@@ -2,6 +2,7 @@
//! representation.
mod asm;
+mod format_args;
mod generics;
mod path;
@@ -19,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,
@@ -31,10 +32,9 @@ use triomphe::Arc;
use tt::TextRange;
use crate::{
- AdtId, BlockId, BlockIdLt, DefWithBodyId, FunctionId, GenericDefId, ImplId, MacroId,
+ AdtId, BlockId, BlockLoc, DefWithBodyId, FunctionId, GenericDefId, ImplId, MacroId,
ModuleDefId, ModuleId, TraitId, TypeAliasId, UnresolvedMacro,
attrs::AttrFlags,
- builtin_type::BuiltinUint,
db::DefDatabase,
expr_store::{
Body, BodySourceMap, ExprPtr, ExpressionStore, ExpressionStoreBuilder,
@@ -47,13 +47,7 @@ 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,
@@ -434,10 +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)]
@@ -538,14 +534,21 @@ impl<'db> ExprCollector<'db> {
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()))
@@ -949,7 +952,8 @@ impl<'db> ExprCollector<'db> {
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
@@ -1065,12 +1069,10 @@ impl<'db> ExprCollector<'db> {
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)
+ })
})
})
}
@@ -1281,64 +1283,65 @@ impl<'db> ExprCollector<'db> {
}
}
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();
@@ -1374,11 +1377,7 @@ impl<'db> ExprCollector<'db> {
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()
}
@@ -1635,31 +1634,13 @@ impl<'db> ExprCollector<'db> {
}
}
- 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>) }`,
@@ -1667,9 +1648,8 @@ impl<'db> ExprCollector<'db> {
/// 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(self.lang_items().TryTraitFromOutput);
- let label = self.alloc_label_desugared(Label {
- name: Name::generate_new_name(self.store.labels.len()),
- });
+ 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();
@@ -1797,7 +1777,7 @@ impl<'db> ExprCollector<'db> {
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 },
@@ -1858,7 +1838,7 @@ impl<'db> ExprCollector<'db> {
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,
@@ -1876,7 +1856,7 @@ impl<'db> ExprCollector<'db> {
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 });
@@ -2114,7 +2094,7 @@ impl<'db> ExprCollector<'db> {
) -> ExprId {
let block_id = self.expander.ast_id_map().ast_id_for_block(&block).map(|file_local_id| {
let ast_id = self.expander.in_file(file_local_id);
- unsafe { BlockIdLt::new(self.db, ast_id, self.module).to_static() }
+ self.db.intern_block(BlockLoc { ast_id, module: self.module })
});
let (module, def_map) =
@@ -2371,11 +2351,7 @@ impl<'db> ExprCollector<'db> {
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 {
@@ -2635,7 +2611,6 @@ impl<'db> ExprCollector<'db> {
}
// 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,
@@ -2655,676 +2630,6 @@ impl<'db> ExprCollector<'db> {
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 lang_items = self.lang_items();
- let fmt_args = lang_items.FormatArguments;
- let fmt_unsafe_arg = lang_items.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 lang_items = self.lang_items();
- let new_v1_formatted = self.ty_rel_lang_path(
- lang_items.FormatArguments,
- Name::new_symbol_root(sym::new_v1_formatted),
- );
- let unsafe_arg_new =
- self.ty_rel_lang_path(lang_items.FormatUnsafeArg, 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 = self.ty_rel_lang_path(
- self.lang_items().FormatArguments,
- 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 lang_items = self.lang_items();
- 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.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: self.lang_path(lang_items.FormatPlaceholder).map(Box::new),
- fields: Box::new([position, flags, precision, width]),
- spread: None,
- })
- } else {
- let format_placeholder_new = {
- let format_placeholder_new = self.ty_rel_lang_path(
- lang_items.FormatPlaceholder,
- 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 = self.ty_rel_lang_path(
- lang_items.FormatAlignment,
- 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 {
- let lang_items = self.lang_items();
- 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 self
- .ty_rel_lang_path(lang_items.FormatCount, 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 self
- .ty_rel_lang_path(lang_items.FormatCount, 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 self
- .ty_rel_lang_path(lang_items.FormatCount, Name::new_symbol_root(sym::Implied))
- {
- Some(count_param) => self.alloc_expr_desugared(Expr::Path(count_param)),
- None => self.missing_expr(),
- },
- }
- }
-
- /// 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 self.ty_rel_lang_path(
- self.lang_items().FormatArgument,
- 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]) })
- }
-
- // endregion: format
-
fn lang_path(&self, lang: Option<impl Into<LangItemTarget>>) -> Option<Path> {
Some(Path::LangItem(lang?.into(), None))
}
@@ -3332,9 +2637,17 @@ impl<'db> ExprCollector<'db> {
fn ty_rel_lang_path(
&self,
lang: Option<impl Into<LangItemTarget>>,
- relative_name: Name,
+ relative_name: Symbol,
) -> Option<Path> {
- Some(Path::LangItem(lang?.into(), Some(relative_name)))
+ Some(Path::LangItem(lang?.into(), Some(Name::new_symbol_root(relative_name))))
+ }
+
+ 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)
}
}
@@ -3379,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
}
@@ -3453,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,
diff --git a/crates/hir-def/src/expr_store/lower/format_args.rs b/crates/hir-def/src/expr_store/lower/format_args.rs
new file mode 100644
index 0000000000..4bbfc5b144
--- /dev/null
+++ b/crates/hir-def/src/expr_store/lower/format_args.rs
@@ -0,0 +1,1012 @@
+//! Lowering of `format_args!()`.
+
+use base_db::FxIndexSet;
+use hir_expand::name::{AsName, Name};
+use intern::{Symbol, sym};
+use syntax::{
+ AstPtr, AstToken as _,
+ ast::{self, HasName},
+};
+
+use crate::{
+ builtin_type::BuiltinUint,
+ expr_store::{HygieneId, lower::ExprCollector, path::Path},
+ hir::{
+ Array, BindingAnnotation, Expr, ExprId, Literal, Pat, RecordLitField, Statement,
+ format_args::{
+ self, FormatAlignment, FormatArgs, FormatArgsPiece, FormatArgument, FormatArgumentKind,
+ FormatArgumentsCollector, FormatCount, FormatDebugHex, FormatOptions,
+ FormatPlaceholder, FormatSign, FormatTrait,
+ },
+ },
+ lang_item::LangItemTarget,
+ type_ref::{Mutability, Rawness},
+};
+
+impl<'db> ExprCollector<'db> {
+ pub(super) 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,
+ ),
+ };
+
+ let idx = if self.lang_items().FormatCount.is_none() {
+ self.collect_format_args_after_1_93_0_impl(syntax_ptr, fmt)
+ } else {
+ self.collect_format_args_before_1_93_0_impl(syntax_ptr, fmt)
+ };
+
+ self.store
+ .template_map
+ .get_or_insert_with(Default::default)
+ .format_args_to_captures
+ .insert(idx, (hygiene, mappings));
+ idx
+ }
+
+ fn collect_format_args_after_1_93_0_impl(
+ &mut self,
+ syntax_ptr: AstPtr<ast::Expr>,
+ fmt: FormatArgs,
+ ) -> ExprId {
+ let lang_items = self.lang_items();
+
+ // Create a list of all _unique_ (argument, format trait) combinations.
+ // E.g. "{0} {0:x} {0} {1}" -> [(0, Display), (0, LowerHex), (1, Display)]
+ //
+ // We use usize::MAX for arguments that don't exist, because that can never be a valid index
+ // into the arguments array.
+ let mut argmap = FxIndexSet::default();
+
+ let mut incomplete_lit = String::new();
+
+ let mut implicit_arg_index = 0;
+
+ let mut bytecode = Vec::new();
+
+ let template = if fmt.template.is_empty() {
+ // Treat empty templates as a single literal piece (with an empty string),
+ // so we produce `from_str("")` for those.
+ &[FormatArgsPiece::Literal(sym::__empty)][..]
+ } else {
+ &fmt.template[..]
+ };
+
+ // See library/core/src/fmt/mod.rs for the format string encoding format.
+
+ for (i, piece) in template.iter().enumerate() {
+ match piece {
+ FormatArgsPiece::Literal(sym) => {
+ // Coalesce adjacent literal pieces.
+ if let Some(FormatArgsPiece::Literal(_)) = template.get(i + 1) {
+ incomplete_lit.push_str(sym.as_str());
+ continue;
+ }
+ let mut s = if incomplete_lit.is_empty() {
+ sym.as_str()
+ } else {
+ incomplete_lit.push_str(sym.as_str());
+ &incomplete_lit
+ };
+
+ // If this is the last piece and was the only piece, that means
+ // there are no placeholders and the entire format string is just a literal.
+ //
+ // In that case, we can just use `from_str`.
+ if i + 1 == template.len() && bytecode.is_empty() {
+ // Generate:
+ // <core::fmt::Arguments>::from_str("meow")
+ let from_str = self.ty_rel_lang_path_desugared_expr(
+ lang_items.FormatArguments,
+ sym::from_str,
+ );
+ let sym =
+ if incomplete_lit.is_empty() { sym.clone() } else { Symbol::intern(s) };
+ let s = self.alloc_expr_desugared(Expr::Literal(Literal::String(sym)));
+ let from_str = self.alloc_expr(
+ Expr::Call { callee: from_str, args: Box::new([s]) },
+ syntax_ptr,
+ );
+ return if !fmt.arguments.arguments.is_empty() {
+ // With an incomplete format string (e.g. only an opening `{`), it's possible for `arguments`
+ // to be non-empty when reaching this code path.
+ self.alloc_expr(
+ Expr::Block {
+ id: None,
+ statements: fmt
+ .arguments
+ .arguments
+ .iter()
+ .map(|arg| Statement::Expr {
+ expr: arg.expr,
+ has_semi: true,
+ })
+ .collect(),
+ tail: Some(from_str),
+ label: None,
+ },
+ syntax_ptr,
+ )
+ } else {
+ from_str
+ };
+ }
+
+ // Encode the literal in chunks of up to u16::MAX bytes, split at utf-8 boundaries.
+ while !s.is_empty() {
+ let len = s.floor_char_boundary(usize::from(u16::MAX));
+ if len < 0x80 {
+ bytecode.push(len as u8);
+ } else {
+ bytecode.push(0x80);
+ bytecode.extend_from_slice(&(len as u16).to_le_bytes());
+ }
+ bytecode.extend(&s.as_bytes()[..len]);
+ s = &s[len..];
+ }
+
+ incomplete_lit.clear();
+ }
+ FormatArgsPiece::Placeholder(p) => {
+ // Push the start byte and remember its index so we can set the option bits later.
+ let i = bytecode.len();
+ bytecode.push(0xC0);
+
+ let position = match &p.argument.index {
+ &Ok(it) => it,
+ Err(_) => usize::MAX,
+ };
+ let position = argmap
+ .insert_full((position, ArgumentType::Format(p.format_trait)))
+ .0 as u64;
+
+ // This needs to match the constants in library/core/src/fmt/mod.rs.
+ let o = &p.format_options;
+ let align = match o.alignment {
+ Some(FormatAlignment::Left) => 0,
+ Some(FormatAlignment::Right) => 1,
+ Some(FormatAlignment::Center) => 2,
+ None => 3,
+ };
+ let default_flags = 0x6000_0020;
+ let flags: u32 = o.fill.unwrap_or(' ') as u32
+ | ((o.sign == Some(FormatSign::Plus)) as u32) << 21
+ | ((o.sign == Some(FormatSign::Minus)) as u32) << 22
+ | (o.alternate as u32) << 23
+ | (o.zero_pad as u32) << 24
+ | ((o.debug_hex == Some(FormatDebugHex::Lower)) as u32) << 25
+ | ((o.debug_hex == Some(FormatDebugHex::Upper)) as u32) << 26
+ | (o.width.is_some() as u32) << 27
+ | (o.precision.is_some() as u32) << 28
+ | align << 29;
+ if flags != default_flags {
+ bytecode[i] |= 1;
+ bytecode.extend_from_slice(&flags.to_le_bytes());
+ if let Some(val) = &o.width {
+ let (indirect, val) = self.make_count_after_1_93_0(val, &mut argmap);
+ // Only encode if nonzero; zero is the default.
+ if indirect || val != 0 {
+ bytecode[i] |= 1 << 1 | (indirect as u8) << 4;
+ bytecode.extend_from_slice(&val.to_le_bytes());
+ }
+ }
+ if let Some(val) = &o.precision {
+ let (indirect, val) = self.make_count_after_1_93_0(val, &mut argmap);
+ // Only encode if nonzero; zero is the default.
+ if indirect || val != 0 {
+ bytecode[i] |= 1 << 2 | (indirect as u8) << 5;
+ bytecode.extend_from_slice(&val.to_le_bytes());
+ }
+ }
+ }
+ if implicit_arg_index != position {
+ bytecode[i] |= 1 << 3;
+ bytecode.extend_from_slice(&(position as u16).to_le_bytes());
+ }
+ implicit_arg_index = position + 1;
+ }
+ }
+ }
+
+ assert!(incomplete_lit.is_empty());
+
+ // Zero terminator.
+ bytecode.push(0);
+
+ // Ensure all argument indexes actually fit in 16 bits, as we truncated them to 16 bits before.
+ if argmap.len() > u16::MAX as usize {
+ // FIXME: Emit an error.
+ // ctx.dcx().span_err(macsp, "too many format arguments");
+ }
+
+ let arguments = &fmt.arguments.arguments[..];
+
+ let (mut statements, args) = if arguments.is_empty() {
+ // Generate:
+ // []
+ (
+ Vec::new(),
+ self.alloc_expr_desugared(Expr::Array(Array::ElementList {
+ elements: Box::new([]),
+ })),
+ )
+ } else {
+ // Generate:
+ // super let args = (&arg0, &arg1, &…);
+ let args_name = self.generate_new_name();
+ let args_path = Path::from(args_name.clone());
+ 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 });
+ // FIXME: Make this a `super let` when we have this statement.
+ let let_statement_1 = 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_path.clone()));
+ 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 args =
+ self.alloc_expr_desugared(Expr::Array(Array::ElementList { elements: args }));
+ let args_binding =
+ self.alloc_binding(args_name, 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);
+ // FIXME: Make this a `super let` when we have this statement.
+ let let_statement_2 = Statement::Let {
+ pat: args_pat,
+ type_ref: None,
+ initializer: Some(args),
+ else_branch: None,
+ };
+ (
+ vec![let_statement_1, let_statement_2],
+ self.alloc_expr_desugared(Expr::Path(args_path)),
+ )
+ };
+
+ // Generate:
+ // unsafe {
+ // <core::fmt::Arguments>::new(b"…", &args)
+ // }
+ let template = self
+ .alloc_expr_desugared(Expr::Literal(Literal::ByteString(bytecode.into_boxed_slice())));
+ let call = {
+ let new = self.ty_rel_lang_path_desugared_expr(lang_items.FormatArguments, sym::new);
+ let args = self.alloc_expr_desugared(Expr::Ref {
+ expr: args,
+ rawness: Rawness::Ref,
+ mutability: Mutability::Shared,
+ });
+ self.alloc_expr_desugared(Expr::Call { callee: new, args: Box::new([template, args]) })
+ };
+ let call = self.alloc_expr(
+ Expr::Unsafe { id: None, statements: Box::new([]), tail: Some(call) },
+ syntax_ptr,
+ );
+
+ // 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
+ .extend(fmt.orphans.into_iter().map(|expr| Statement::Expr { expr, has_semi: true }));
+
+ if !statements.is_empty() {
+ // Generate:
+ // {
+ // super let …
+ // super let …
+ // <core::fmt::Arguments>::new(…)
+ // }
+ self.alloc_expr(
+ Expr::Block {
+ id: None,
+ statements: statements.into_boxed_slice(),
+ tail: Some(call),
+ label: None,
+ },
+ syntax_ptr,
+ )
+ } else {
+ call
+ }
+ }
+
+ /// Get the value for a `width` or `precision` field.
+ ///
+ /// Returns the value and whether it is indirect (an indexed argument) or not.
+ fn make_count_after_1_93_0(
+ &self,
+ count: &FormatCount,
+ argmap: &mut FxIndexSet<(usize, ArgumentType)>,
+ ) -> (bool, u16) {
+ match count {
+ FormatCount::Literal(n) => (false, *n),
+ FormatCount::Argument(arg) => {
+ let index = match &arg.index {
+ &Ok(it) => it,
+ Err(_) => usize::MAX,
+ };
+ (true, argmap.insert_full((index, ArgumentType::Usize)).0 as u16)
+ }
+ }
+ }
+
+ fn collect_format_args_before_1_93_0_impl(
+ &mut self,
+ syntax_ptr: AstPtr<ast::Expr>,
+ fmt: FormatArgs,
+ ) -> ExprId {
+ // 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 lang_items = self.lang_items();
+ let fmt_args = lang_items.FormatArguments;
+ let fmt_unsafe_arg = lang_items.FormatUnsafeArg;
+ let use_format_args_since_1_89_0 = fmt_args.is_some() && fmt_unsafe_arg.is_none();
+
+ if use_format_args_since_1_89_0 {
+ self.collect_format_args_after_1_89_0_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,
+ )
+ }
+ }
+
+ /// `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 lang_items = self.lang_items();
+ let new_v1_formatted =
+ self.ty_rel_lang_path_desugared_expr(lang_items.FormatArguments, sym::new_v1_formatted);
+ let unsafe_arg_new =
+ self.ty_rel_lang_path_desugared_expr(lang_items.FormatUnsafeArg, sym::new);
+ 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_after_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 (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 = self.generate_new_name();
+ 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 = self.generate_new_name();
+ 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 = self.ty_rel_lang_path_desugared_expr(
+ self.lang_items().FormatArguments,
+ sym::new_v1_formatted,
+ );
+ 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 lang_items = self.lang_items();
+ 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_before_1_93_0(precision, argmap);
+ let width_expr = self.make_count_before_1_93_0(width, argmap);
+
+ if self.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: self.lang_path(lang_items.FormatPlaceholder).map(Box::new),
+ fields: Box::new([position, flags, precision, width]),
+ spread: None,
+ })
+ } else {
+ let format_placeholder_new =
+ self.ty_rel_lang_path_desugared_expr(lang_items.FormatPlaceholder, sym::new);
+ // 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 = self.ty_rel_lang_path_desugared_expr(
+ lang_items.FormatAlignment,
+ match alignment {
+ Some(FormatAlignment::Left) => sym::Left,
+ Some(FormatAlignment::Right) => sym::Right,
+ Some(FormatAlignment::Center) => sym::Center,
+ None => sym::Unknown,
+ },
+ );
+ 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_before_1_93_0(
+ &mut self,
+ count: &Option<FormatCount>,
+ argmap: &mut FxIndexSet<(usize, ArgumentType)>,
+ ) -> ExprId {
+ let lang_items = self.lang_items();
+ 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 =
+ self.ty_rel_lang_path_desugared_expr(lang_items.FormatCount, sym::Is);
+ 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 =
+ self.ty_rel_lang_path_desugared_expr(lang_items.FormatCount, sym::Param);
+ 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 self.ty_rel_lang_path(lang_items.FormatCount, sym::Implied) {
+ Some(count_param) => self.alloc_expr_desugared(Expr::Path(count_param)),
+ None => self.missing_expr(),
+ },
+ }
+ }
+
+ /// 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 = self.ty_rel_lang_path_desugared_expr(
+ self.lang_items().FormatArgument,
+ 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,
+ },
+ );
+ self.alloc_expr_desugared(Expr::Call { callee: new_fn, args: Box::new([arg]) })
+ }
+
+ fn ty_rel_lang_path_desugared_expr(
+ &mut self,
+ lang: Option<impl Into<LangItemTarget>>,
+ relative_name: Symbol,
+ ) -> ExprId {
+ self.alloc_expr_desugared(self.ty_rel_lang_path_expr(lang, relative_name))
+ }
+}
+
+#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
+enum ArgumentType {
+ Format(FormatTrait),
+ Usize,
+}
diff --git a/crates/hir-def/src/expr_store/tests/body.rs b/crates/hir-def/src/expr_store/tests/body.rs
index 22ade43875..504c310684 100644
--- a/crates/hir-def/src/expr_store/tests/body.rs
+++ b/crates/hir-def/src/expr_store/tests/body.rs
@@ -161,9 +161,9 @@ fn main() {
match builtin#lang(into_iter)(
0..10,
) {
- mut <ra@gennew>11 => loop {
+ mut <ra@gennew>0 => loop {
match builtin#lang(next)(
- &mut <ra@gennew>11,
+ &mut <ra@gennew>0,
) {
builtin#lang(None) => break,
builtin#lang(Some)(ident) => {
@@ -261,10 +261,10 @@ fn main() {
}
#[test]
-fn desugar_builtin_format_args() {
+fn desugar_builtin_format_args_before_1_93_0() {
let (db, body, def) = lower(
r#"
-//- minicore: fmt
+//- minicore: fmt_before_1_93_0
fn main() {
let are = "are";
let count = 10;
@@ -278,16 +278,16 @@ fn main() {
let are = "are";
let count = 10;
{
- let args = (&"fancy", &(), &"!", &count, &are, );
- let args = [
+ let <ra@gennew>0 = (&"fancy", &(), &"!", &count, &are, );
+ let <ra@gennew>0 = [
builtin#lang(Argument::new_display)(
- args.3,
+ <ra@gennew>0.3,
), builtin#lang(Argument::new_display)(
- args.0,
+ <ra@gennew>0.0,
), builtin#lang(Argument::new_debug)(
- args.4,
+ <ra@gennew>0.4,
), builtin#lang(Argument::new_display)(
- args.2,
+ <ra@gennew>0.2,
),
];
unsafe {
@@ -295,7 +295,7 @@ fn main() {
&[
"\u{1b}hello ", " ", " friends, we ", " ", "",
],
- &args,
+ &<ra@gennew>0,
&[
builtin#lang(Placeholder::new)(
0usize,
@@ -344,6 +344,59 @@ fn main() {
}
#[test]
+fn desugar_builtin_format_args() {
+ let (db, body, def) = lower(
+ r#"
+//- minicore: fmt
+fn main() {
+ let are = "are";
+ let count = 10;
+ builtin#format_args("\u{1b}hello {count:02} {} friends, we {are:?} {0}{last}", "fancy", orphan = (), last = "!");
+ builtin#format_args("hello world");
+ builtin#format_args("hello world", orphan = ());
+}
+"#,
+ );
+
+ expect![[r#"
+ fn main() {
+ let are = "are";
+ let count = 10;
+ {
+ let <ra@gennew>0 = (&"fancy", &(), &"!", &count, &are, );
+ let <ra@gennew>0 = [
+ builtin#lang(Argument::new_display)(
+ <ra@gennew>0.3,
+ ), builtin#lang(Argument::new_display)(
+ <ra@gennew>0.0,
+ ), builtin#lang(Argument::new_debug)(
+ <ra@gennew>0.4,
+ ), builtin#lang(Argument::new_display)(
+ <ra@gennew>0.2,
+ ),
+ ];
+ ();
+ unsafe {
+ builtin#lang(Arguments::new)(
+ "\x07\x1bhello \xc3 \x00\x00i\x02\x00\x01 \xc0\r friends, we \xc0\x01 \xc8\x01\x00\xc8\x03\x00\x00",
+ &<ra@gennew>0,
+ )
+ }
+ };
+ builtin#lang(Arguments::from_str)(
+ "hello world",
+ );
+ {
+ ();
+ builtin#lang(Arguments::from_str)(
+ "hello world",
+ )
+ };
+ }"#]]
+ .assert_eq(&body.pretty_print(&db, def, Edition::CURRENT))
+}
+
+#[test]
fn test_macro_hygiene() {
let (db, body, def) = lower(
r##"
@@ -382,27 +435,16 @@ impl SsrError {
fn main() {
_ = ra_test_fixture::error::SsrError::new(
{
- let args = [
+ let <ra@gennew>0 = (&node.text(), );
+ let <ra@gennew>0 = [
builtin#lang(Argument::new_display)(
- &node.text(),
+ <ra@gennew>0.0,
),
];
unsafe {
- builtin#lang(Arguments::new_v1_formatted)(
- &[
- "Failed to resolve path `", "`",
- ],
- &args,
- &[
- builtin#lang(Placeholder::new)(
- 0usize,
- ' ',
- builtin#lang(Alignment::Unknown),
- 0u32,
- builtin#lang(Count::Implied),
- builtin#lang(Count::Implied),
- ),
- ],
+ builtin#lang(Arguments::new)(
+ "\x18Failed to resolve path `\xc0\x01`\x00",
+ &<ra@gennew>0,
)
}
},
diff --git a/crates/hir-def/src/expr_store/tests/body/block.rs b/crates/hir-def/src/expr_store/tests/body/block.rs
index 2d60f44092..836a079e77 100644
--- a/crates/hir-def/src/expr_store/tests/body/block.rs
+++ b/crates/hir-def/src/expr_store/tests/body/block.rs
@@ -195,55 +195,9 @@ fn f() {
Id(1c00),
),
block: Some(
- BlockIdLt {
- [salsa id]: Id(3c01),
- ast_id: InFileWrapper {
- file_id: FileId(
- EditionedFileIdData {
- editioned_file_id: EditionedFileId(
- 0,
- Edition2024,
- ),
- krate: Crate(
- Id(1c00),
- ),
- },
- ),
- value: FileAstId::<syntax::ast::generated::nodes::BlockExpr>(ErasedFileAstId { kind: BlockExpr, index: 0, hash: F9BF }),
- },
- module: ModuleIdLt {
- [salsa id]: Id(3002),
- krate: Crate(
- Id(1c00),
- ),
- block: Some(
- BlockIdLt {
- [salsa id]: Id(3c00),
- ast_id: InFileWrapper {
- file_id: FileId(
- EditionedFileIdData {
- editioned_file_id: EditionedFileId(
- 0,
- Edition2024,
- ),
- krate: Crate(
- Id(1c00),
- ),
- },
- ),
- value: FileAstId::<syntax::ast::generated::nodes::BlockExpr>(ErasedFileAstId { kind: BlockExpr, index: 0, hash: C181 }),
- },
- module: ModuleIdLt {
- [salsa id]: Id(3000),
- krate: Crate(
- Id(1c00),
- ),
- block: None,
- },
- },
- ),
- },
- },
+ BlockId(
+ 3c01,
+ ),
),
}"#]],
);
diff --git a/crates/hir-def/src/expr_store/tests/signatures.rs b/crates/hir-def/src/expr_store/tests/signatures.rs
index 2dac4e7fc8..f1db00cf6a 100644
--- a/crates/hir-def/src/expr_store/tests/signatures.rs
+++ b/crates/hir-def/src/expr_store/tests/signatures.rs
@@ -197,3 +197,15 @@ fn allowed3(baz: impl Baz<Assoc = Qux<impl Foo>>) {}
"#]],
);
}
+
+#[test]
+fn regression_21138() {
+ lower_and_print(
+ r#"
+fn foo(v: for<'a> Trait1 + Trait2) {}
+ "#,
+ expect![[r#"
+ fn foo(dyn for<'a> Trait1 + Trait2) {...}
+ "#]],
+ );
+}
diff --git a/crates/hir-def/src/find_path.rs b/crates/hir-def/src/find_path.rs
index cc0594f00d..5d1cac8e93 100644
--- a/crates/hir-def/src/find_path.rs
+++ b/crates/hir-def/src/find_path.rs
@@ -12,7 +12,7 @@ use intern::sym;
use rustc_hash::FxHashSet;
use crate::{
- FindPathConfig, ModuleDefId, ModuleIdLt,
+ FindPathConfig, ModuleDefId, ModuleId,
db::DefDatabase,
item_scope::ItemInNs,
nameres::DefMap,
@@ -24,7 +24,7 @@ use crate::{
pub fn find_path(
db: &dyn DefDatabase,
item: ItemInNs,
- from: ModuleIdLt<'_>,
+ from: ModuleId,
mut prefix_kind: PrefixKind,
ignore_local_imports: bool,
mut cfg: FindPathConfig,
@@ -102,14 +102,14 @@ struct FindPathCtx<'db> {
cfg: FindPathConfig,
ignore_local_imports: bool,
is_std_item: bool,
- from: ModuleIdLt<'db>,
+ from: ModuleId,
from_crate: Crate,
- crate_root: ModuleIdLt<'db>,
+ crate_root: ModuleId,
from_def_map: &'db DefMap,
fuel: Cell<usize>,
}
-/// Attempts to find a path to refer to the given `item` visible from the `from` ModuleIdLt<'_>
+/// Attempts to find a path to refer to the given `item` visible from the `from` ModuleId
fn find_path_inner(ctx: &FindPathCtx<'_>, item: ItemInNs, max_len: usize) -> Option<ModPath> {
// - if the item is a module, jump straight to module search
if !ctx.is_std_item
@@ -157,10 +157,10 @@ fn find_path_inner(ctx: &FindPathCtx<'_>, item: ItemInNs, max_len: usize) -> Opt
}
#[tracing::instrument(skip_all)]
-fn find_path_for_module<'db>(
- ctx: &'db FindPathCtx<'db>,
- visited_modules: &mut FxHashSet<(ItemInNs, ModuleIdLt<'db>)>,
- module_id: ModuleIdLt<'db>,
+fn find_path_for_module(
+ ctx: &FindPathCtx<'_>,
+ visited_modules: &mut FxHashSet<(ItemInNs, ModuleId)>,
+ module_id: ModuleId,
maybe_extern: bool,
max_len: usize,
) -> Option<Choice> {
@@ -217,7 +217,7 @@ fn find_path_for_module<'db>(
ctx.db,
ctx.from_def_map,
ctx.from,
- ItemInNs::Types(unsafe { module_id.to_static() }.into()),
+ ItemInNs::Types(module_id.into()),
ctx.ignore_local_imports,
);
if let Some(scope_name) = scope_name {
@@ -244,7 +244,7 @@ fn find_path_for_module<'db>(
}
// - if the module is in the prelude, return it by that path
- let item = ItemInNs::Types(unsafe { module_id.to_static() }.into());
+ let item = ItemInNs::Types(module_id.into());
if let Some(choice) = find_in_prelude(ctx.db, ctx.from_def_map, item, ctx.from) {
return Some(choice);
}
@@ -257,10 +257,10 @@ fn find_path_for_module<'db>(
best_choice
}
-fn find_in_scope<'db>(
- db: &'db dyn DefDatabase,
+fn find_in_scope(
+ db: &dyn DefDatabase,
def_map: &DefMap,
- from: ModuleIdLt<'db>,
+ from: ModuleId,
item: ItemInNs,
ignore_local_imports: bool,
) -> Option<Name> {
@@ -278,7 +278,7 @@ fn find_in_prelude(
db: &dyn DefDatabase,
local_def_map: &DefMap,
item: ItemInNs,
- from: ModuleIdLt<'_>,
+ from: ModuleId,
) -> Option<Choice> {
let (prelude_module, _) = local_def_map.prelude()?;
let prelude_def_map = prelude_module.def_map(db);
@@ -310,8 +310,8 @@ fn find_in_prelude(
fn is_kw_kind_relative_to_from(
db: &dyn DefDatabase,
def_map: &DefMap,
- item: ModuleIdLt<'_>,
- from: ModuleIdLt<'_>,
+ item: ModuleId,
+ from: ModuleId,
) -> Option<PathKind> {
if item.krate(db) != from.krate(db) || item.block(db).is_some() || from.block(db).is_some() {
return None;
@@ -332,9 +332,9 @@ fn is_kw_kind_relative_to_from(
}
#[tracing::instrument(skip_all)]
-fn calculate_best_path<'db>(
- ctx: &'db FindPathCtx<'db>,
- visited_modules: &mut FxHashSet<(ItemInNs, ModuleIdLt<'db>)>,
+fn calculate_best_path(
+ ctx: &FindPathCtx<'_>,
+ visited_modules: &mut FxHashSet<(ItemInNs, ModuleId)>,
item: ItemInNs,
max_len: usize,
best_choice: &mut Option<Choice>,
@@ -372,9 +372,9 @@ fn calculate_best_path<'db>(
}
}
-fn find_in_sysroot<'db>(
- ctx: &'db FindPathCtx<'db>,
- visited_modules: &mut FxHashSet<(ItemInNs, ModuleIdLt<'db>)>,
+fn find_in_sysroot(
+ ctx: &FindPathCtx<'_>,
+ visited_modules: &mut FxHashSet<(ItemInNs, ModuleId)>,
item: ItemInNs,
max_len: usize,
best_choice: &mut Option<Choice>,
@@ -418,9 +418,9 @@ fn find_in_sysroot<'db>(
});
}
-fn find_in_dep<'db>(
- ctx: &'db FindPathCtx<'db>,
- visited_modules: &mut FxHashSet<(ItemInNs, ModuleIdLt<'db>)>,
+fn find_in_dep(
+ ctx: &FindPathCtx<'_>,
+ visited_modules: &mut FxHashSet<(ItemInNs, ModuleId)>,
item: ItemInNs,
max_len: usize,
best_choice: &mut Option<Choice>,
@@ -461,9 +461,9 @@ fn find_in_dep<'db>(
}
}
-fn calculate_best_path_local<'db>(
- ctx: &'db FindPathCtx<'db>,
- visited_modules: &mut FxHashSet<(ItemInNs, ModuleIdLt<'db>)>,
+fn calculate_best_path_local(
+ ctx: &FindPathCtx<'_>,
+ visited_modules: &mut FxHashSet<(ItemInNs, ModuleId)>,
item: ItemInNs,
max_len: usize,
best_choice: &mut Option<Choice>,
@@ -558,11 +558,11 @@ fn path_kind_len(kind: PathKind) -> usize {
}
/// Finds locations in `from.krate` from which `item` can be imported by `from`.
-fn find_local_import_locations<'db>(
- ctx: &'db FindPathCtx<'db>,
+fn find_local_import_locations(
+ ctx: &FindPathCtx<'_>,
item: ItemInNs,
- visited_modules: &mut FxHashSet<(ItemInNs, ModuleIdLt<'db>)>,
- mut cb: impl FnMut(&mut FxHashSet<(ItemInNs, ModuleIdLt<'db>)>, &Name, ModuleIdLt<'db>),
+ visited_modules: &mut FxHashSet<(ItemInNs, ModuleId)>,
+ mut cb: impl FnMut(&mut FxHashSet<(ItemInNs, ModuleId)>, &Name, ModuleId),
) {
let _p = tracing::info_span!("find_local_import_locations").entered();
let db = ctx.db;
diff --git a/crates/hir-def/src/import_map.rs b/crates/hir-def/src/import_map.rs
index 433aead77a..6c5d226cac 100644
--- a/crates/hir-def/src/import_map.rs
+++ b/crates/hir-def/src/import_map.rs
@@ -496,7 +496,7 @@ mod tests {
use expect_test::{Expect, expect};
use test_fixture::WithFixture;
- use crate::{ItemContainerId, Lookup, ModuleIdLt, nameres::assoc::TraitItems, test_db::TestDB};
+ use crate::{ItemContainerId, Lookup, nameres::assoc::TraitItems, test_db::TestDB};
use super::*;
@@ -628,8 +628,8 @@ mod tests {
expect.assert_eq(&actual)
}
- fn render_path<'db>(db: &'db dyn DefDatabase, info: &ImportInfo) -> String {
- let mut module: ModuleIdLt<'db> = info.container;
+ fn render_path(db: &dyn DefDatabase, info: &ImportInfo) -> String {
+ let mut module = info.container;
let mut segments = vec![&info.name];
let def_map = module.def_map(db);
diff --git a/crates/hir-def/src/item_tree.rs b/crates/hir-def/src/item_tree.rs
index 1228d1999b..2a104fff2b 100644
--- a/crates/hir-def/src/item_tree.rs
+++ b/crates/hir-def/src/item_tree.rs
@@ -58,7 +58,7 @@ use syntax::{SyntaxKind, ast, match_ast};
use thin_vec::ThinVec;
use triomphe::Arc;
-use crate::{BlockId, db::DefDatabase};
+use crate::{BlockId, Lookup, db::DefDatabase};
pub(crate) use crate::item_tree::{
attrs::*,
@@ -150,10 +150,10 @@ pub(crate) fn block_item_tree_query(db: &dyn DefDatabase, block: BlockId) -> Arc
let _p = tracing::info_span!("block_item_tree_query", ?block).entered();
static EMPTY: OnceLock<Arc<ItemTree>> = OnceLock::new();
- let ast_id = block.ast_id(db);
- let block = ast_id.to_node(db);
+ let loc = block.lookup(db);
+ let block = loc.ast_id.to_node(db);
- let ctx = lower::Ctx::new(db, ast_id.file_id);
+ let ctx = lower::Ctx::new(db, loc.ast_id.file_id);
let mut item_tree = ctx.lower_block(&block);
let ItemTree { top_level, top_attrs, attrs, vis, big_data, small_data } = &item_tree;
if small_data.is_empty()
diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs
index e58cb7bad7..97af8ad93d 100644
--- a/crates/hir-def/src/lib.rs
+++ b/crates/hir-def/src/lib.rs
@@ -420,31 +420,13 @@ pub struct ProcMacroLoc {
impl_intern!(ProcMacroId, ProcMacroLoc, intern_proc_macro, lookup_intern_proc_macro);
impl_loc!(ProcMacroLoc, id: Fn, container: ModuleId);
-#[salsa_macros::tracked(debug)]
-#[derive(PartialOrd, Ord)]
-pub struct BlockIdLt<'db> {
+#[derive(Debug, Hash, PartialEq, Eq, Clone)]
+pub struct BlockLoc {
pub ast_id: AstId<ast::BlockExpr>,
/// The containing module.
- pub module: ModuleIdLt<'db>,
-}
-pub type BlockId = BlockIdLt<'static>;
-
-impl BlockIdLt<'_> {
- /// # Safety
- ///
- /// The caller must ensure that the `ModuleId` is not leaked outside of query computations.
- pub unsafe fn to_static(self) -> BlockId {
- unsafe { std::mem::transmute(self) }
- }
-}
-impl BlockId {
- /// # Safety
- ///
- /// The caller must ensure that the `BlockId` comes from the given database.
- pub unsafe fn to_db<'db>(self, _db: &'db dyn DefDatabase) -> BlockIdLt<'db> {
- unsafe { std::mem::transmute(self) }
- }
+ pub module: ModuleId,
}
+impl_intern!(BlockId, BlockLoc, intern_block, lookup_intern_block);
#[salsa_macros::tracked(debug)]
#[derive(PartialOrd, Ord)]
@@ -454,26 +436,34 @@ pub struct ModuleIdLt<'db> {
/// If this `ModuleId` was derived from a `DefMap` for a block expression, this stores the
/// `BlockId` of that block expression. If `None`, this module is part of the crate-level
/// `DefMap` of `krate`.
- pub block: Option<BlockIdLt<'db>>,
+ pub block: Option<BlockId>,
}
pub type ModuleId = ModuleIdLt<'static>;
-impl<'db> ModuleIdLt<'db> {
+impl ModuleIdLt<'_> {
/// # Safety
///
/// The caller must ensure that the `ModuleId` is not leaked outside of query computations.
pub unsafe fn to_static(self) -> ModuleId {
unsafe { std::mem::transmute(self) }
}
+}
+impl ModuleId {
+ /// # Safety
+ ///
+ /// The caller must ensure that the `ModuleId` comes from the given database.
+ pub unsafe fn to_db<'db>(self, _db: &'db dyn DefDatabase) -> ModuleIdLt<'db> {
+ unsafe { std::mem::transmute(self) }
+ }
- pub fn def_map(self, db: &'db dyn DefDatabase) -> &'db DefMap {
+ pub fn def_map(self, db: &dyn DefDatabase) -> &DefMap {
match self.block(db) {
Some(block) => block_def_map(db, block),
None => crate_def_map(db, self.krate(db)),
}
}
- pub(crate) fn local_def_map(self, db: &'db dyn DefDatabase) -> (&'db DefMap, &'db LocalDefMap) {
+ pub(crate) fn local_def_map(self, db: &dyn DefDatabase) -> (&DefMap, &LocalDefMap) {
match self.block(db) {
Some(block) => (block_def_map(db, block), self.only_local_def_map(db)),
None => {
@@ -483,15 +473,15 @@ impl<'db> ModuleIdLt<'db> {
}
}
- pub(crate) fn only_local_def_map(self, db: &'db dyn DefDatabase) -> &'db LocalDefMap {
+ pub(crate) fn only_local_def_map(self, db: &dyn DefDatabase) -> &LocalDefMap {
crate_local_def_map(db, self.krate(db)).local(db)
}
- pub fn crate_def_map(self, db: &'db dyn DefDatabase) -> &'db DefMap {
+ pub fn crate_def_map(self, db: &dyn DefDatabase) -> &DefMap {
crate_def_map(db, self.krate(db))
}
- pub fn name(self, db: &'db dyn DefDatabase) -> Option<Name> {
+ pub fn name(self, db: &dyn DefDatabase) -> Option<Name> {
let def_map = self.def_map(db);
let parent = def_map[self].parent?;
def_map[parent].children.iter().find_map(|(name, module_id)| {
@@ -501,24 +491,15 @@ impl<'db> ModuleIdLt<'db> {
/// Returns the module containing `self`, either the parent `mod`, or the module (or block) containing
/// the block, if `self` corresponds to a block expression.
- pub fn containing_module(self, db: &'db dyn DefDatabase) -> Option<ModuleIdLt<'db>> {
+ pub fn containing_module(self, db: &dyn DefDatabase) -> Option<ModuleId> {
self.def_map(db).containing_module(self)
}
- pub fn is_block_module(self, db: &'db dyn DefDatabase) -> bool {
+ pub fn is_block_module(self, db: &dyn DefDatabase) -> bool {
self.block(db).is_some() && self.def_map(db).root_module_id() == self
}
}
-impl ModuleId {
- /// # Safety
- ///
- /// The caller must ensure that the `ModuleId` comes from the given database.
- pub unsafe fn to_db<'db>(self, _db: &'db dyn DefDatabase) -> ModuleIdLt<'db> {
- unsafe { std::mem::transmute(self) }
- }
-}
-
impl HasModule for ModuleId {
#[inline]
fn module(&self, _db: &dyn DefDatabase) -> ModuleId {
diff --git a/crates/hir-def/src/nameres.rs b/crates/hir-def/src/nameres.rs
index a85237662c..3f29619bcb 100644
--- a/crates/hir-def/src/nameres.rs
+++ b/crates/hir-def/src/nameres.rs
@@ -75,7 +75,7 @@ use triomphe::Arc;
use tt::TextRange;
use crate::{
- AstId, BlockId, BlockIdLt, ExternCrateId, FunctionId, FxIndexMap, Lookup, MacroCallStyles,
+ AstId, BlockId, BlockLoc, ExternCrateId, FunctionId, FxIndexMap, Lookup, MacroCallStyles,
MacroExpander, MacroId, ModuleId, ModuleIdLt, ProcMacroId, UseId,
db::DefDatabase,
item_scope::{BuiltinShadowMode, ItemScope},
@@ -247,12 +247,12 @@ struct BlockInfo {
parent: ModuleId,
}
-impl std::ops::Index<ModuleIdLt<'_>> for DefMap {
+impl std::ops::Index<ModuleId> for DefMap {
type Output = ModuleData;
- fn index(&self, id: ModuleIdLt<'_>) -> &ModuleData {
+ fn index(&self, id: ModuleId) -> &ModuleData {
self.modules
- .get(&unsafe { id.to_static() })
+ .get(&id)
.unwrap_or_else(|| panic!("ModuleId not found in ModulesMap {:#?}: {id:#?}", self.root))
}
}
@@ -400,10 +400,8 @@ pub(crate) fn crate_local_def_map(db: &dyn DefDatabase, crate_id: Crate) -> DefM
}
#[salsa_macros::tracked(returns(ref))]
-pub fn block_def_map<'db>(db: &'db dyn DefDatabase, block_id: BlockIdLt<'db>) -> DefMap {
- let block_id = unsafe { block_id.to_static() };
- let ast_id = block_id.ast_id(db);
- let module = unsafe { block_id.module(db).to_static() };
+pub fn block_def_map(db: &dyn DefDatabase, block_id: BlockId) -> DefMap {
+ let BlockLoc { ast_id, module } = block_id.lookup(db);
let visibility = Visibility::Module(module, VisibilityExplicitness::Implicit);
let module_data =
@@ -559,7 +557,7 @@ impl DefMap {
/// Returns the module containing `local_mod`, either the parent `mod`, or the module (or block) containing
/// the block, if `self` corresponds to a block expression.
- pub fn containing_module(&self, local_mod: ModuleIdLt<'_>) -> Option<ModuleId> {
+ pub fn containing_module(&self, local_mod: ModuleId) -> Option<ModuleId> {
match self[local_mod].parent {
Some(parent) => Some(parent),
None => self.block.map(|BlockInfo { parent, .. }| parent),
@@ -664,11 +662,11 @@ impl DefMap {
///
/// If `f` returns `Some(val)`, iteration is stopped and `Some(val)` is returned. If `f` returns
/// `None`, iteration continues.
- pub(crate) fn with_ancestor_maps<'db, T>(
+ pub(crate) fn with_ancestor_maps<T>(
&self,
- db: &'db dyn DefDatabase,
- local_mod: ModuleIdLt<'db>,
- f: &mut dyn FnMut(&DefMap, ModuleIdLt<'db>) -> Option<T>,
+ db: &dyn DefDatabase,
+ local_mod: ModuleId,
+ f: &mut dyn FnMut(&DefMap, ModuleId) -> Option<T>,
) -> Option<T> {
if let Some(it) = f(self, local_mod) {
return Some(it);
@@ -854,13 +852,11 @@ impl DerefMut for ModulesMap {
}
}
-impl Index<ModuleIdLt<'_>> for ModulesMap {
+impl Index<ModuleId> for ModulesMap {
type Output = ModuleData;
- fn index(&self, id: ModuleIdLt<'_>) -> &ModuleData {
- self.inner
- .get(&unsafe { id.to_static() })
- .unwrap_or_else(|| panic!("ModuleId not found in ModulesMap: {id:#?}"))
+ fn index(&self, id: ModuleId) -> &ModuleData {
+ self.inner.get(&id).unwrap_or_else(|| panic!("ModuleId not found in ModulesMap: {id:#?}"))
}
}
diff --git a/crates/hir-def/src/resolver.rs b/crates/hir-def/src/resolver.rs
index 45d5dc9fcd..263f603a0b 100644
--- a/crates/hir-def/src/resolver.rs
+++ b/crates/hir-def/src/resolver.rs
@@ -881,7 +881,7 @@ impl<'db> Resolver<'db> {
}));
if let Some(block) = expr_scopes.block(scope_id) {
let def_map = block_def_map(db, block);
- let local_def_map = block.module(db).only_local_def_map(db);
+ let local_def_map = block.lookup(db).module.only_local_def_map(db);
resolver.scopes.push(Scope::BlockScope(ModuleItemMap {
def_map,
local_def_map,
@@ -1087,7 +1087,7 @@ fn resolver_for_scope_<'db>(
for scope in scope_chain.into_iter().rev() {
if let Some(block) = scopes.block(scope) {
let def_map = block_def_map(db, block);
- let local_def_map = block.module(db).only_local_def_map(db);
+ let local_def_map = block.lookup(db).module.only_local_def_map(db);
// Using `DefMap::ROOT` is okay here since inside modules other than the root,
// there can't directly be expressions.
r = r.push_block_scope(def_map, local_def_map, def_map.root);
diff --git a/crates/hir-def/src/visibility.rs b/crates/hir-def/src/visibility.rs
index 95554c63b9..a1645de6ec 100644
--- a/crates/hir-def/src/visibility.rs
+++ b/crates/hir-def/src/visibility.rs
@@ -9,8 +9,8 @@ use syntax::ast::{self, HasVisibility};
use triomphe::Arc;
use crate::{
- AssocItemId, HasModule, ItemContainerId, LocalFieldId, ModuleId, ModuleIdLt, TraitId,
- VariantId, db::DefDatabase, nameres::DefMap, resolver::HasResolver, src::HasSource,
+ AssocItemId, HasModule, ItemContainerId, LocalFieldId, ModuleId, TraitId, VariantId,
+ db::DefDatabase, nameres::DefMap, resolver::HasResolver, src::HasSource,
};
pub use crate::item_tree::{RawVisibility, VisibilityExplicitness};
@@ -41,13 +41,9 @@ impl Visibility {
}
#[tracing::instrument(skip_all)]
- pub fn is_visible_from<'db>(
- self,
- db: &'db dyn DefDatabase,
- from_module: ModuleIdLt<'db>,
- ) -> bool {
+ pub fn is_visible_from(self, db: &dyn DefDatabase, from_module: ModuleId) -> bool {
let to_module = match self {
- Visibility::Module(m, _) => unsafe { m.to_db(db) },
+ Visibility::Module(m, _) => m,
Visibility::PubCrate(krate) => return from_module.krate(db) == krate,
Visibility::Public => return true,
};
@@ -63,11 +59,11 @@ impl Visibility {
Self::is_visible_from_def_map_(db, def_map, to_module, from_module)
}
- pub(crate) fn is_visible_from_def_map<'db>(
+ pub(crate) fn is_visible_from_def_map(
self,
- db: &'db dyn DefDatabase,
- def_map: &'db DefMap,
- from_module: ModuleIdLt<'db>,
+ db: &dyn DefDatabase,
+ def_map: &DefMap,
+ from_module: ModuleId,
) -> bool {
if cfg!(debug_assertions) {
_ = def_map.modules[from_module];
@@ -93,11 +89,11 @@ impl Visibility {
Self::is_visible_from_def_map_(db, def_map, to_module, from_module)
}
- fn is_visible_from_def_map_<'db>(
- db: &'db dyn DefDatabase,
- def_map: &'db DefMap,
- mut to_module: ModuleIdLt<'db>,
- mut from_module: ModuleIdLt<'db>,
+ fn is_visible_from_def_map_(
+ db: &dyn DefDatabase,
+ def_map: &DefMap,
+ mut to_module: ModuleId,
+ mut from_module: ModuleId,
) -> bool {
debug_assert_eq!(to_module.krate(db), def_map.krate());
// `to_module` might be the root module of a block expression. Those have the same
diff --git a/crates/hir-ty/src/dyn_compatibility.rs b/crates/hir-ty/src/dyn_compatibility.rs
index 506c4abc83..64b15eb017 100644
--- a/crates/hir-ty/src/dyn_compatibility.rs
+++ b/crates/hir-ty/src/dyn_compatibility.rs
@@ -427,9 +427,14 @@ fn receiver_is_dispatchable<'db>(
};
let meta_sized_did = lang_items.MetaSized;
- let Some(meta_sized_did) = meta_sized_did else {
- return false;
- };
+
+ // TODO: This is for supporting dyn compatibility for toolchains doesn't contain `MetaSized`
+ // trait. Uncomment and short circuit here once `MINIMUM_SUPPORTED_TOOLCHAIN_VERSION`
+ // become > 1.88.0
+ //
+ // let Some(meta_sized_did) = meta_sized_did else {
+ // return false;
+ // };
// Type `U`
// FIXME: That seems problematic to fake a generic param like that?
@@ -450,17 +455,16 @@ fn receiver_is_dispatchable<'db>(
});
let trait_predicate = TraitRef::new_from_args(interner, trait_.into(), args);
- let meta_sized_predicate =
- TraitRef::new(interner, meta_sized_did.into(), [unsized_self_ty]);
+ let meta_sized_predicate = meta_sized_did
+ .map(|did| TraitRef::new(interner, did.into(), [unsized_self_ty]).upcast(interner));
ParamEnv {
clauses: Clauses::new_from_iter(
interner,
- generic_predicates.iter_identity_copied().chain([
- unsize_predicate.upcast(interner),
- trait_predicate.upcast(interner),
- meta_sized_predicate.upcast(interner),
- ]),
+ generic_predicates
+ .iter_identity_copied()
+ .chain([unsize_predicate.upcast(interner), trait_predicate.upcast(interner)])
+ .chain(meta_sized_predicate),
),
}
};
diff --git a/crates/hir-ty/src/infer/closure/analysis.rs b/crates/hir-ty/src/infer/closure/analysis.rs
index d6d63891bb..308c01865a 100644
--- a/crates/hir-ty/src/infer/closure/analysis.rs
+++ b/crates/hir-ty/src/infer/closure/analysis.rs
@@ -43,6 +43,7 @@ impl<'db> HirPlace<'db> {
for p in &self.projections {
ty = p.projected_ty(
&ctx.table.infer_ctxt,
+ ctx.table.param_env,
ty,
|_, _, _| {
unreachable!("Closure field only happens in MIR");
@@ -839,6 +840,7 @@ impl<'db> InferenceContext<'_, 'db> {
for (i, p) in capture.place.projections.iter().enumerate() {
ty = p.projected_ty(
&self.table.infer_ctxt,
+ self.table.param_env,
ty,
|_, _, _| {
unreachable!("Closure field only happens in MIR");
diff --git a/crates/hir-ty/src/layout.rs b/crates/hir-ty/src/layout.rs
index 565063fb13..4b20d6eb32 100644
--- a/crates/hir-ty/src/layout.rs
+++ b/crates/hir-ty/src/layout.rs
@@ -25,7 +25,7 @@ use crate::{
consteval::try_const_usize,
db::HirDatabase,
next_solver::{
- DbInterner, GenericArgs, ParamEnv, Ty, TyKind, TypingMode,
+ DbInterner, GenericArgs, Ty, TyKind, TypingMode,
infer::{DbInternerInferExt, traits::ObligationCause},
},
};
@@ -170,7 +170,7 @@ pub fn layout_of_ty_query<'db>(
let cx = LayoutCx::new(dl);
let infer_ctxt = interner.infer_ctxt().build(TypingMode::PostAnalysis);
let cause = ObligationCause::dummy();
- let ty = infer_ctxt.at(&cause, ParamEnv::empty()).deeply_normalize(ty).unwrap_or(ty);
+ let ty = infer_ctxt.at(&cause, trait_env.param_env).deeply_normalize(ty).unwrap_or(ty);
let result = match ty.kind() {
TyKind::Adt(def, args) => {
match def.inner().id {
diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs
index d9cfe6d84c..868ae00329 100644
--- a/crates/hir-ty/src/method_resolution.rs
+++ b/crates/hir-ty/src/method_resolution.rs
@@ -13,8 +13,8 @@ use tracing::{debug, instrument};
use base_db::Crate;
use hir_def::{
- AssocItemId, BlockIdLt, ConstId, FunctionId, GenericParamId, HasModule, ImplId,
- ItemContainerId, ModuleId, TraitId,
+ AssocItemId, BlockId, ConstId, FunctionId, GenericParamId, HasModule, ImplId, ItemContainerId,
+ ModuleId, TraitId,
attrs::AttrFlags,
expr_store::path::GenericArgs as HirGenericArgs,
hir::ExprId,
@@ -558,9 +558,9 @@ pub struct InherentImpls {
}
#[salsa::tracked]
-impl<'db> InherentImpls {
+impl InherentImpls {
#[salsa::tracked(returns(ref))]
- pub fn for_crate(db: &'db dyn HirDatabase, krate: Crate) -> Self {
+ pub fn for_crate(db: &dyn HirDatabase, krate: Crate) -> Self {
let _p = tracing::info_span!("inherent_impls_in_crate_query", ?krate).entered();
let crate_def_map = crate_def_map(db, krate);
@@ -569,7 +569,7 @@ impl<'db> InherentImpls {
}
#[salsa::tracked(returns(ref))]
- pub fn for_block(db: &'db dyn HirDatabase, block: BlockIdLt<'db>) -> Option<Box<Self>> {
+ pub fn for_block(db: &dyn HirDatabase, block: BlockId) -> Option<Box<Self>> {
let _p = tracing::info_span!("inherent_impls_in_block_query").entered();
let block_def_map = block_def_map(db, block);
@@ -627,13 +627,13 @@ impl InherentImpls {
self.map.get(self_ty).map(|it| &**it).unwrap_or_default()
}
- pub fn for_each_crate_and_block<'db>(
- db: &'db dyn HirDatabase,
+ pub fn for_each_crate_and_block(
+ db: &dyn HirDatabase,
krate: Crate,
- block: Option<BlockIdLt<'db>>,
+ block: Option<BlockId>,
for_each: &mut dyn FnMut(&InherentImpls),
) {
- let blocks = std::iter::successors(block, |block| block.module(db).block(db));
+ let blocks = std::iter::successors(block, |block| block.loc(db).module.block(db));
blocks.filter_map(|block| Self::for_block(db, block).as_deref()).for_each(&mut *for_each);
for_each(Self::for_crate(db, krate));
}
@@ -670,9 +670,9 @@ pub struct TraitImpls {
}
#[salsa::tracked]
-impl<'db> TraitImpls {
+impl TraitImpls {
#[salsa::tracked(returns(ref))]
- pub fn for_crate(db: &'db dyn HirDatabase, krate: Crate) -> Arc<Self> {
+ pub fn for_crate(db: &dyn HirDatabase, krate: Crate) -> Arc<Self> {
let _p = tracing::info_span!("inherent_impls_in_crate_query", ?krate).entered();
let crate_def_map = crate_def_map(db, krate);
@@ -681,7 +681,7 @@ impl<'db> TraitImpls {
}
#[salsa::tracked(returns(ref))]
- pub fn for_block(db: &'db dyn HirDatabase, block: BlockIdLt<'db>) -> Option<Box<Self>> {
+ pub fn for_block(db: &dyn HirDatabase, block: BlockId) -> Option<Box<Self>> {
let _p = tracing::info_span!("inherent_impls_in_block_query").entered();
let block_def_map = block_def_map(db, block);
@@ -690,7 +690,7 @@ impl<'db> TraitImpls {
}
#[salsa::tracked(returns(ref))]
- pub fn for_crate_and_deps(db: &'db dyn HirDatabase, krate: Crate) -> Box<[Arc<Self>]> {
+ pub fn for_crate_and_deps(db: &dyn HirDatabase, krate: Crate) -> Box<[Arc<Self>]> {
krate.transitive_deps(db).iter().map(|&dep| Self::for_crate(db, dep).clone()).collect()
}
}
@@ -792,23 +792,23 @@ impl TraitImpls {
}
}
- pub fn for_each_crate_and_block<'db>(
- db: &'db dyn HirDatabase,
+ pub fn for_each_crate_and_block(
+ db: &dyn HirDatabase,
krate: Crate,
- block: Option<BlockIdLt<'db>>,
+ block: Option<BlockId>,
for_each: &mut dyn FnMut(&TraitImpls),
) {
- let blocks = std::iter::successors(block, |block| block.module(db).block(db));
+ let blocks = std::iter::successors(block, |block| block.loc(db).module.block(db));
blocks.filter_map(|block| Self::for_block(db, block).as_deref()).for_each(&mut *for_each);
Self::for_crate_and_deps(db, krate).iter().map(|it| &**it).for_each(for_each);
}
/// Like [`Self::for_each_crate_and_block()`], but takes in account two blocks, one for a trait and one for a self type.
- pub fn for_each_crate_and_block_trait_and_type<'db>(
- db: &'db dyn HirDatabase,
+ pub fn for_each_crate_and_block_trait_and_type(
+ db: &dyn HirDatabase,
krate: Crate,
- type_block: Option<BlockIdLt<'db>>,
- trait_block: Option<BlockIdLt<'db>>,
+ type_block: Option<BlockId>,
+ trait_block: Option<BlockId>,
for_each: &mut dyn FnMut(&TraitImpls),
) {
let in_self_and_deps = TraitImpls::for_crate_and_deps(db, krate);
@@ -819,11 +819,10 @@ impl TraitImpls {
// that means there can't be duplicate impls; if they meet, we stop the search of the deeper block.
// This breaks when they are equal (both will stop immediately), therefore we handle this case
// specifically.
- let blocks_iter = |block: Option<BlockIdLt<'db>>| {
- std::iter::successors(block, |block| block.module(db).block(db))
+ let blocks_iter = |block: Option<BlockId>| {
+ std::iter::successors(block, |block| block.loc(db).module.block(db))
};
- let for_each_block = |current_block: Option<BlockIdLt<'db>>,
- other_block: Option<BlockIdLt<'db>>| {
+ let for_each_block = |current_block: Option<BlockId>, other_block: Option<BlockId>| {
blocks_iter(current_block)
.take_while(move |&block| {
other_block.is_none_or(|other_block| other_block != block)
diff --git a/crates/hir-ty/src/mir.rs b/crates/hir-ty/src/mir.rs
index 3cafb6aa25..836c20a433 100644
--- a/crates/hir-ty/src/mir.rs
+++ b/crates/hir-ty/src/mir.rs
@@ -157,6 +157,7 @@ impl<'db, V: PartialEq> ProjectionElem<'db, V> {
pub fn projected_ty(
&self,
infcx: &InferCtxt<'db>,
+ env: ParamEnv<'db>,
mut base: Ty<'db>,
closure_field: impl FnOnce(InternedClosureId, GenericArgs<'db>, usize) -> Ty<'db>,
krate: Crate,
@@ -173,8 +174,6 @@ impl<'db, V: PartialEq> ProjectionElem<'db, V> {
if matches!(base.kind(), TyKind::Alias(..)) {
let mut ocx = ObligationCtxt::new(infcx);
- // FIXME: we should get this from caller
- let env = ParamEnv::empty();
match ocx.structurally_normalize_ty(&ObligationCause::dummy(), env, base) {
Ok(it) => base = it,
Err(_) => return Ty::new_error(interner, ErrorGuaranteed),
diff --git a/crates/hir-ty/src/mir/borrowck.rs b/crates/hir-ty/src/mir/borrowck.rs
index 4d76a9f3fb..b39c9bc065 100644
--- a/crates/hir-ty/src/mir/borrowck.rs
+++ b/crates/hir-ty/src/mir/borrowck.rs
@@ -106,7 +106,7 @@ pub fn borrowck_query<'db>(
// FIXME(next-solver): Opaques.
let infcx = interner.infer_ctxt().build(typing_mode);
res.push(BorrowckResult {
- mutability_of_locals: mutability_of_locals(&infcx, &body),
+ mutability_of_locals: mutability_of_locals(&infcx, env, &body),
moved_out_of_ref: moved_out_of_ref(&infcx, env, &body),
partially_moved: partially_moved(&infcx, env, &body),
borrow_regions: borrow_regions(db, &body),
@@ -146,6 +146,7 @@ fn moved_out_of_ref<'db>(
}
ty = proj.projected_ty(
infcx,
+ env,
ty,
make_fetch_closure_field(db),
body.owner.module(db).krate(db),
@@ -242,6 +243,7 @@ fn partially_moved<'db>(
for proj in p.projection.lookup(&body.projection_store) {
ty = proj.projected_ty(
infcx,
+ env,
ty,
make_fetch_closure_field(db),
body.owner.module(db).krate(db),
@@ -374,6 +376,7 @@ enum ProjectionCase {
fn place_case<'db>(
infcx: &InferCtxt<'db>,
+ env: ParamEnv<'db>,
body: &MirBody<'db>,
lvalue: &Place<'db>,
) -> ProjectionCase {
@@ -395,6 +398,7 @@ fn place_case<'db>(
}
ty = proj.projected_ty(
infcx,
+ env,
ty,
make_fetch_closure_field(db),
body.owner.module(db).krate(db),
@@ -535,6 +539,7 @@ fn record_usage_for_operand<'db>(
fn mutability_of_locals<'db>(
infcx: &InferCtxt<'db>,
+ env: ParamEnv<'db>,
body: &MirBody<'db>,
) -> ArenaMap<LocalId<'db>, MutabilityReason> {
let db = infcx.interner.db;
@@ -547,7 +552,7 @@ fn mutability_of_locals<'db>(
for statement in &block.statements {
match &statement.kind {
StatementKind::Assign(place, value) => {
- match place_case(infcx, body, place) {
+ match place_case(infcx, env, body, place) {
ProjectionCase::Direct => {
if ever_init_map.get(place.local).copied().unwrap_or_default() {
push_mut_span(place.local, statement.span, &mut result);
@@ -596,7 +601,7 @@ fn mutability_of_locals<'db>(
},
p,
) = value
- && place_case(infcx, body, p) != ProjectionCase::Indirect
+ && place_case(infcx, env, body, p) != ProjectionCase::Indirect
{
push_mut_span(p.local, statement.span, &mut result);
}
diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs
index 35b45174c2..3b4913cae3 100644
--- a/crates/hir-ty/src/mir/eval.rs
+++ b/crates/hir-ty/src/mir/eval.rs
@@ -722,6 +722,7 @@ impl<'db> Evaluator<'db> {
let (ty, proj) = pair;
let r = proj.projected_ty(
&self.infcx,
+ self.param_env.param_env,
ty,
|c, subst, f| {
let InternedClosure(def, _) = self.db.lookup_intern_closure(c);
diff --git a/crates/hir-ty/src/mir/lower/tests.rs b/crates/hir-ty/src/mir/lower/tests.rs
index 357f617a21..73399dab7f 100644
--- a/crates/hir-ty/src/mir/lower/tests.rs
+++ b/crates/hir-ty/src/mir/lower/tests.rs
@@ -1,3 +1,4 @@
+use hir_def::DefWithBodyId;
use test_fixture::WithFixture;
use crate::{db::HirDatabase, setup_tracing, test_db::TestDB};
@@ -49,3 +50,61 @@ fn foo() {
"#,
);
}
+
+fn check_borrowck(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
+ let _tracing = setup_tracing();
+ let (db, file_ids) = TestDB::with_many_files(ra_fixture);
+ crate::attach_db(&db, || {
+ let file_id = *file_ids.last().unwrap();
+ let module_id = db.module_for_file(file_id.file_id(&db));
+ let def_map = module_id.def_map(&db);
+ let scope = &def_map[module_id].scope;
+
+ let mut bodies: Vec<DefWithBodyId> = Vec::new();
+
+ for decl in scope.declarations() {
+ if let hir_def::ModuleDefId::FunctionId(f) = decl {
+ bodies.push(f.into());
+ }
+ }
+
+ for impl_id in scope.impls() {
+ let impl_items = impl_id.impl_items(&db);
+ for (_, item) in impl_items.items.iter() {
+ if let hir_def::AssocItemId::FunctionId(f) = item {
+ bodies.push((*f).into());
+ }
+ }
+ }
+
+ for body in bodies {
+ let _ = db.borrowck(body);
+ }
+ })
+}
+
+#[test]
+fn regression_21173_const_generic_impl_with_assoc_type() {
+ check_borrowck(
+ r#"
+pub trait Tr {
+ type Assoc;
+ fn f(&self, handle: Self::Assoc) -> i32;
+}
+
+pub struct ConstGeneric<const N: usize>;
+
+impl<const N: usize> Tr for &ConstGeneric<N> {
+ type Assoc = AssocTy;
+
+ fn f(&self, a: Self::Assoc) -> i32 {
+ a.x
+ }
+}
+
+pub struct AssocTy {
+ x: i32,
+}
+ "#,
+ );
+}
diff --git a/crates/hir-ty/src/next_solver/infer/traits.rs b/crates/hir-ty/src/next_solver/infer/traits.rs
index 4f000c24cc..3409de17a1 100644
--- a/crates/hir-ty/src/next_solver/infer/traits.rs
+++ b/crates/hir-ty/src/next_solver/infer/traits.rs
@@ -36,10 +36,6 @@ pub struct ObligationCause {
}
impl ObligationCause {
- #[expect(
- clippy::new_without_default,
- reason = "`new` is temporary, eventually we will provide span etc. here"
- )]
#[inline]
pub fn new() -> ObligationCause {
ObligationCause { _private: () }
diff --git a/crates/hir-ty/src/next_solver/interner.rs b/crates/hir-ty/src/next_solver/interner.rs
index 2edf442a8c..8b24a20a5b 100644
--- a/crates/hir-ty/src/next_solver/interner.rs
+++ b/crates/hir-ty/src/next_solver/interner.rs
@@ -1104,14 +1104,7 @@ impl<'db> Interner for DbInterner<'db> {
fn type_of(self, def_id: Self::DefId) -> EarlyBinder<Self, Self::Ty> {
match def_id {
- SolverDefId::TypeAliasId(id) => {
- use hir_def::Lookup;
- match id.lookup(self.db()).container {
- ItemContainerId::ImplId(it) => it,
- _ => panic!("assoc ty value should be in impl"),
- };
- self.db().ty(id.into())
- }
+ SolverDefId::TypeAliasId(id) => self.db().ty(id.into()),
SolverDefId::AdtId(id) => self.db().ty(id.into()),
// FIXME(next-solver): This uses the types of `query mir_borrowck` in rustc.
//
diff --git a/crates/hir-ty/src/next_solver/solver.rs b/crates/hir-ty/src/next_solver/solver.rs
index b5ed770e16..40a3f17cf1 100644
--- a/crates/hir-ty/src/next_solver/solver.rs
+++ b/crates/hir-ty/src/next_solver/solver.rs
@@ -177,45 +177,52 @@ impl<'db> SolverDelegate for SolverContext<'db> {
impl_id: ImplIdWrapper,
) -> Result<Option<SolverDefId>, ErrorGuaranteed> {
let impl_items = impl_id.0.impl_items(self.0.interner.db());
- let id = match trait_assoc_def_id {
- SolverDefId::TypeAliasId(trait_assoc_id) => {
- let trait_assoc_data = self.0.interner.db.type_alias_signature(trait_assoc_id);
- impl_items
- .items
- .iter()
- .find_map(|(impl_assoc_name, impl_assoc_id)| {
- if let AssocItemId::TypeAliasId(impl_assoc_id) = *impl_assoc_id
- && *impl_assoc_name == trait_assoc_data.name
- {
- Some(impl_assoc_id)
- } else {
- None
- }
- })
- .map(SolverDefId::TypeAliasId)
- }
- SolverDefId::ConstId(trait_assoc_id) => {
- let trait_assoc_data = self.0.interner.db.const_signature(trait_assoc_id);
- let trait_assoc_name = trait_assoc_data
- .name
- .as_ref()
- .expect("unnamed consts should not get passed to the solver");
- impl_items
- .items
- .iter()
- .find_map(|(impl_assoc_name, impl_assoc_id)| {
- if let AssocItemId::ConstId(impl_assoc_id) = *impl_assoc_id
- && impl_assoc_name == trait_assoc_name
- {
- Some(impl_assoc_id)
- } else {
- None
- }
- })
- .map(SolverDefId::ConstId)
- }
- _ => panic!("Unexpected SolverDefId"),
- };
+ let id =
+ match trait_assoc_def_id {
+ SolverDefId::TypeAliasId(trait_assoc_id) => {
+ let trait_assoc_data = self.0.interner.db.type_alias_signature(trait_assoc_id);
+ impl_items
+ .items
+ .iter()
+ .find_map(|(impl_assoc_name, impl_assoc_id)| {
+ if let AssocItemId::TypeAliasId(impl_assoc_id) = *impl_assoc_id
+ && *impl_assoc_name == trait_assoc_data.name
+ {
+ Some(impl_assoc_id)
+ } else {
+ None
+ }
+ })
+ .or_else(|| {
+ if trait_assoc_data.ty.is_some() { Some(trait_assoc_id) } else { None }
+ })
+ .map(SolverDefId::TypeAliasId)
+ }
+ SolverDefId::ConstId(trait_assoc_id) => {
+ let trait_assoc_data = self.0.interner.db.const_signature(trait_assoc_id);
+ let trait_assoc_name = trait_assoc_data
+ .name
+ .as_ref()
+ .expect("unnamed consts should not get passed to the solver");
+ impl_items
+ .items
+ .iter()
+ .find_map(|(impl_assoc_name, impl_assoc_id)| {
+ if let AssocItemId::ConstId(impl_assoc_id) = *impl_assoc_id
+ && impl_assoc_name == trait_assoc_name
+ {
+ Some(impl_assoc_id)
+ } else {
+ None
+ }
+ })
+ .or_else(|| {
+ if trait_assoc_data.has_body() { Some(trait_assoc_id) } else { None }
+ })
+ .map(SolverDefId::ConstId)
+ }
+ _ => panic!("Unexpected SolverDefId"),
+ };
Ok(id)
}
@@ -225,7 +232,9 @@ impl<'db> SolverDelegate for SolverContext<'db> {
_src: Ty<'db>,
_assume: <Self::Interner as rustc_type_ir::Interner>::Const,
) -> Result<Certainty, NoSolution> {
- unimplemented!()
+ // It's better to return some value while not fully implement
+ // then panic in the mean time
+ Ok(Certainty::Yes)
}
fn evaluate_const(
diff --git a/crates/hir-ty/src/tests/regression/new_solver.rs b/crates/hir-ty/src/tests/regression/new_solver.rs
index 5c1f85cb2a..e11cc85e7f 100644
--- a/crates/hir-ty/src/tests/regression/new_solver.rs
+++ b/crates/hir-ty/src/tests/regression/new_solver.rs
@@ -230,6 +230,62 @@ fn main() {
debug(&1);
}"#,
);
+
+ // toolchains <= 1.88.0, before sized-hierarchy.
+ check_no_mismatches(
+ r#"
+#[lang = "sized"]
+pub trait Sized {}
+
+#[lang = "unsize"]
+pub trait Unsize<T: ?Sized> {}
+
+#[lang = "coerce_unsized"]
+pub trait CoerceUnsized<T: ?Sized> {}
+
+impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a mut U> for &'a mut T {}
+
+impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b mut T {}
+
+impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*mut U> for &'a mut T {}
+
+impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*const U> for &'a mut T {}
+
+impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b T {}
+
+impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*const U> for &'a T {}
+
+impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*mut U> for *mut T {}
+
+impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*const U> for *mut T {}
+
+impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*const U> for *const T {}
+
+#[lang = "dispatch_from_dyn"]
+pub trait DispatchFromDyn<T> {}
+
+impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<&'a U> for &'a T {}
+
+impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<&'a mut U> for &'a mut T {}
+
+impl<T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<*const U> for *const T {}
+
+impl<T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<*mut U> for *mut T {}
+
+trait Foo {
+ fn bar(&self) -> u32 {
+ 0xCAFE
+ }
+}
+
+fn debug(_: &dyn Foo) {}
+
+impl Foo for i32 {}
+
+fn main() {
+ debug(&1);
+}"#,
+ );
}
#[test]
diff --git a/crates/hir-ty/src/tests/traits.rs b/crates/hir-ty/src/tests/traits.rs
index 677e35775d..a54c0a799d 100644
--- a/crates/hir-ty/src/tests/traits.rs
+++ b/crates/hir-ty/src/tests/traits.rs
@@ -5079,3 +5079,23 @@ fn foo(base_layer_two: &dyn BaseLayerOne) {
"#,
);
}
+
+#[test]
+fn default_assoc_types() {
+ check_types(
+ r#"
+trait Trait<T> {
+ type Assoc<U> = (T, U);
+ fn method(self) -> Self::Assoc<i32> { loop {} }
+}
+
+struct Struct<T>(T);
+impl<T> Trait<((), T)> for Struct<T> {}
+
+fn foo(v: Struct<f32>) {
+ v.method();
+ // ^^^^^^^^^^ (((), f32), i32)
+}
+ "#,
+ );
+}
diff --git a/crates/hir/src/db.rs b/crates/hir/src/db.rs
index 64d97b3f2a..3021ccf402 100644
--- a/crates/hir/src/db.rs
+++ b/crates/hir/src/db.rs
@@ -4,42 +4,5 @@
//!
//! But we need this for at least LRU caching at the query level.
pub use hir_def::db::DefDatabase;
-// AttrsQuery, BlockDefMapQuery, BlockItemTreeQuery, BlockItemTreeWithSourceMapQuery, BodyQuery,
-// BodyWithSourceMapQuery, ConstDataQuery, ConstVisibilityQuery, CrateDefMapQuery,
-// CrateLangItemsQuery, CrateNotableTraitsQuery, CrateSupportsNoStdQuery, DefDatabase,
-// DefDatabaseStorage, EnumDataQuery, EnumVariantDataWithDiagnosticsQuery,
-// ExpandProcAttrMacrosQuery, ExprScopesQuery, ExternCrateDeclDataQuery, FieldVisibilitiesQuery,
-// FieldsAttrsQuery, FieldsAttrsSourceMapQuery, FileItemTreeQuery, FileItemTreeWithSourceMapQuery,
-// FunctionDataQuery, FunctionVisibilityQuery, GenericParamsQuery,
-// GenericParamsWithSourceMapQuery, ImplItemsWithDiagnosticsQuery, ImportMapQuery,
-// IncludeMacroInvocQuery, InternAnonymousConstQuery, InternBlockQuery, InternConstQuery,
-// InternDatabase, InternDatabaseStorage, InternEnumQuery, InternExternBlockQuery,
-// InternExternCrateQuery, InternFunctionQuery, InternImplQuery, InternInTypeConstQuery,
-// InternMacro2Query, InternMacroRulesQuery, InternProcMacroQuery, InternStaticQuery,
-// InternStructQuery, InternTraitAliasQuery, InternTraitQuery, InternTypeAliasQuery,
-// InternUnionQuery, InternUseQuery, LangItemQuery, Macro2DataQuery, MacroDefQuery,
-// MacroRulesDataQuery, NotableTraitsInDepsQuery, ProcMacroDataQuery, StaticDataQuery,
-// StructDataWithDiagnosticsQuery, TraitAliasDataQuery, TraitItemsWithDiagnosticsQuery,
-// TypeAliasDataQuery, UnionDataWithDiagnosticsQuery,
-// };
pub use hir_expand::db::ExpandDatabase;
-// AstIdMapQuery, DeclMacroExpanderQuery, ExpandDatabase, ExpandDatabaseStorage,
-// ExpandProcMacroQuery, InternMacroCallQuery, InternSyntaxContextQuery, MacroArgQuery,
-// ParseMacroExpansionErrorQuery, ParseMacroExpansionQuery, ProcMacroSpanQuery, ProcMacrosQuery,
-// RealSpanMapQuery,
pub use hir_ty::db::HirDatabase;
-// AdtDatumQuery, AdtVarianceQuery, AssociatedTyDataQuery, AssociatedTyValueQuery, BorrowckQuery,
-// CallableItemSignatureQuery, ConstEvalDiscriminantQuery, ConstEvalQuery, ConstEvalStaticQuery,
-// ConstParamTyQuery, DynCompatibilityOfTraitQuery, FieldTypesQuery, FnDefDatumQuery,
-// FnDefVarianceQuery, GenericDefaultsQuery, GenericPredicatesForParamQuery,
-// GenericPredicatesQuery, GenericPredicatesWithoutParentQuery, HirDatabase, HirDatabaseStorage,
-// ImplDatumQuery, ImplSelfTyQuery, ImplTraitQuery, IncoherentInherentImplCratesQuery, InferQuery,
-// InherentImplsInBlockQuery, InherentImplsInCrateQuery, InternCallableDefQuery,
-// InternClosureQuery, InternCoroutineQuery, InternImplTraitIdQuery, InternLifetimeParamIdQuery,
-// InternTypeOrConstParamIdQuery, LayoutOfAdtQuery, LayoutOfTyQuery, LookupImplMethodQuery,
-// MirBodyForClosureQuery, MirBodyQuery, MonomorphizedMirBodyForClosureQuery,
-// MonomorphizedMirBodyQuery, ProgramClausesForChalkEnvQuery, ReturnTypeImplTraitsQuery,
-// TargetDataLayoutQuery, TraitDatumQuery, TraitEnvironmentQuery, TraitImplsInBlockQuery,
-// TraitImplsInCrateQuery, TraitImplsInDepsQuery, TraitSolveQuery, TyQuery,
-// TypeAliasImplTraitsQuery, ValueTyQuery,
-// };
diff --git a/crates/hir/src/display.rs b/crates/hir/src/display.rs
index 07e61a83c4..d0d8c4877d 100644
--- a/crates/hir/src/display.rs
+++ b/crates/hir/src/display.rs
@@ -192,9 +192,11 @@ fn write_impl_header<'db>(impl_: &Impl, f: &mut HirFormatter<'_, 'db>) -> Result
let def_id = GenericDefId::ImplId(impl_.id);
write_generic_params(def_id, f)?;
- if let Some(trait_) = impl_.trait_(db) {
- let trait_data = db.trait_signature(trait_.id);
- write!(f, " {} for", trait_data.name.display(db, f.edition()))?;
+ let impl_data = db.impl_signature(impl_.id);
+ if let Some(target_trait) = &impl_data.target_trait {
+ f.write_char(' ')?;
+ hir_display_with_store(&impl_data.store[target_trait.path], &impl_data.store).hir_fmt(f)?;
+ f.write_str(" for")?;
}
f.write_char(' ')?;
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index e57f031f00..a50a736ccd 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -590,7 +590,7 @@ impl Module {
while id.is_block_module(db) {
id = id.containing_module(db).expect("block without parent module");
}
- Module { id: unsafe { id.to_static() } }
+ Module { id }
}
pub fn path_to_root(self, db: &dyn HirDatabase) -> Vec<Module> {
@@ -4352,7 +4352,7 @@ impl Impl {
module.block(db),
&mut |impls| extend_with_impls(impls.for_self_ty(&simplified_ty)),
);
- std::iter::successors(module.block(db), |block| block.module(db).block(db))
+ std::iter::successors(module.block(db), |block| block.loc(db).module.block(db))
.filter_map(|block| TraitImpls::for_block(db, block).as_deref())
.for_each(|impls| impls.for_self_ty(&simplified_ty, &mut extend_with_impls));
for &krate in &**db.all_crates() {
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index ffb518b1e6..b15e642daa 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -267,7 +267,7 @@ impl<DB: HirDatabase + ?Sized> Semantics<'_, DB> {
&self,
krate: Crate,
item: ast::AnyHasAttrs,
- ) -> impl Iterator<Item = (LintAttr, SmolStr)> {
+ ) -> impl DoubleEndedIterator<Item = (LintAttr, SmolStr)> {
let mut cfg_options = None;
let cfg_options = || *cfg_options.get_or_insert_with(|| krate.id.cfg_options(self.db));
let mut result = Vec::new();
diff --git a/crates/hir/src/semantics/child_by_source.rs b/crates/hir/src/semantics/child_by_source.rs
index d924aaa25d..c1f72debe5 100644
--- a/crates/hir/src/semantics/child_by_source.rs
+++ b/crates/hir/src/semantics/child_by_source.rs
@@ -226,7 +226,7 @@ impl ChildBySource for DefWithBodyId {
// All block expressions are merged into the same map, because they logically all add
// inner items to the containing `DefWithBodyId`.
def_map[def_map.root].scope.child_by_source_to(db, res, file_id);
- res[keys::BLOCK].insert(block.ast_id(db).to_ptr(db), block);
+ res[keys::BLOCK].insert(block.lookup(db).ast_id.to_ptr(db), block);
}
}
}
diff --git a/crates/hir/src/symbols.rs b/crates/hir/src/symbols.rs
index a9320fdda7..073142670d 100644
--- a/crates/hir/src/symbols.rs
+++ b/crates/hir/src/symbols.rs
@@ -1,5 +1,7 @@
//! File symbol extraction.
+use std::marker::PhantomData;
+
use base_db::FxIndexSet;
use either::Either;
use hir_def::{
@@ -25,7 +27,7 @@ use crate::{HasCrate, Module, ModuleDef, Semantics};
/// The actual data that is stored in the index. It should be as compact as
/// possible.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub struct FileSymbol {
+pub struct FileSymbol<'db> {
pub name: Symbol,
pub def: ModuleDef,
pub loc: DeclarationLocation,
@@ -35,6 +37,7 @@ pub struct FileSymbol {
pub is_assoc: bool,
pub is_import: bool,
pub do_not_complete: Complete,
+ _marker: PhantomData<&'db ()>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -61,9 +64,9 @@ struct SymbolCollectorWork {
parent: Option<Name>,
}
-pub struct SymbolCollector<'a> {
- db: &'a dyn HirDatabase,
- symbols: FxIndexSet<FileSymbol>,
+pub struct SymbolCollector<'db> {
+ db: &'db dyn HirDatabase,
+ symbols: FxIndexSet<FileSymbol<'db>>,
work: Vec<SymbolCollectorWork>,
current_container_name: Option<Symbol>,
collect_pub_only: bool,
@@ -83,10 +86,10 @@ impl<'a> SymbolCollector<'a> {
}
pub fn new_module(
- db: &dyn HirDatabase,
+ db: &'a dyn HirDatabase,
module: Module,
collect_pub_only: bool,
- ) -> Box<[FileSymbol]> {
+ ) -> Box<[FileSymbol<'a>]> {
let mut symbol_collector = SymbolCollector::new(db, collect_pub_only);
symbol_collector.collect(module);
symbol_collector.finish()
@@ -105,7 +108,7 @@ impl<'a> SymbolCollector<'a> {
}
}
- pub fn finish(self) -> Box<[FileSymbol]> {
+ pub fn finish(self) -> Box<[FileSymbol<'a>]> {
self.symbols.into_iter().collect()
}
@@ -217,6 +220,7 @@ impl<'a> SymbolCollector<'a> {
is_assoc: false,
is_import: true,
do_not_complete: Complete::Yes,
+ _marker: PhantomData,
});
};
@@ -251,6 +255,7 @@ impl<'a> SymbolCollector<'a> {
is_assoc: false,
is_import: false,
do_not_complete: Complete::Yes,
+ _marker: PhantomData,
});
};
@@ -428,6 +433,7 @@ impl<'a> SymbolCollector<'a> {
is_assoc,
is_import: false,
do_not_complete,
+ _marker: PhantomData,
});
}
}
@@ -441,6 +447,7 @@ impl<'a> SymbolCollector<'a> {
is_assoc,
is_import: false,
do_not_complete,
+ _marker: PhantomData,
});
do_not_complete
@@ -474,6 +481,7 @@ impl<'a> SymbolCollector<'a> {
is_assoc: false,
is_import: false,
do_not_complete,
+ _marker: PhantomData,
});
}
}
@@ -487,6 +495,7 @@ impl<'a> SymbolCollector<'a> {
is_assoc: false,
is_import: false,
do_not_complete,
+ _marker: PhantomData,
});
}
}
diff --git a/crates/ide-assists/src/handlers/bind_unused_param.rs b/crates/ide-assists/src/handlers/bind_unused_param.rs
index 1b24f7fe7f..771e80bb92 100644
--- a/crates/ide-assists/src/handlers/bind_unused_param.rs
+++ b/crates/ide-assists/src/handlers/bind_unused_param.rs
@@ -33,7 +33,7 @@ pub(crate) fn bind_unused_param(acc: &mut Assists, ctx: &AssistContext<'_>) -> O
return None;
}
- let func = param.syntax().ancestors().find_map(ast::Fn::cast)?;
+ let func = param.syntax().ancestors().nth(2).and_then(ast::Fn::cast)?;
let stmt_list = func.body()?.stmt_list()?;
let l_curly_range = stmt_list.l_curly_token()?.text_range();
let r_curly_range = stmt_list.r_curly_token()?.text_range();
@@ -179,4 +179,16 @@ fn foo($0_x: i32, y: i32) {}
"#,
);
}
+
+ #[test]
+ fn not_applicable_closure() {
+ check_assist_not_applicable(
+ bind_unused_param,
+ r#"
+fn foo() {
+ let _ = |$0x| 2;
+}
+"#,
+ );
+ }
}
diff --git a/crates/ide-assists/src/handlers/convert_for_to_while_let.rs b/crates/ide-assists/src/handlers/convert_for_to_while_let.rs
index ef2dc355b8..d64e9ceda2 100644
--- a/crates/ide-assists/src/handlers/convert_for_to_while_let.rs
+++ b/crates/ide-assists/src/handlers/convert_for_to_while_let.rs
@@ -1,11 +1,8 @@
-use hir::{
- Name,
- sym::{self},
-};
+use hir::{Name, sym};
use ide_db::{famous_defs::FamousDefs, syntax_helpers::suggest_name};
use syntax::{
AstNode,
- ast::{self, HasLoopBody, edit::IndentLevel, make, syntax_factory::SyntaxFactory},
+ ast::{self, HasAttrs, HasLoopBody, edit::IndentLevel, make, syntax_factory::SyntaxFactory},
syntax_editor::Position,
};
@@ -82,6 +79,18 @@ pub(crate) fn convert_for_loop_to_while_let(
Some(iterable),
);
let indent = IndentLevel::from_node(for_loop.syntax());
+
+ if let Some(label) = for_loop.label() {
+ let label = label.syntax().clone_for_update();
+ editor.insert(Position::before(for_loop.syntax()), make.whitespace(" "));
+ editor.insert(Position::before(for_loop.syntax()), label);
+ }
+ crate::utils::insert_attributes(
+ for_loop.syntax(),
+ &mut editor,
+ for_loop.attrs().map(|it| it.clone_for_update()),
+ );
+
editor.insert(
Position::before(for_loop.syntax()),
make::tokens::whitespace(format!("\n{indent}").as_str()),
@@ -187,6 +196,56 @@ fn main() {
}
#[test]
+ fn each_to_for_with_label() {
+ check_assist(
+ convert_for_loop_to_while_let,
+ r"
+fn main() {
+ let mut x = vec![1, 2, 3];
+ 'a: for $0v in x {
+ v *= 2;
+ break 'a;
+ };
+}",
+ r"
+fn main() {
+ let mut x = vec![1, 2, 3];
+ let mut tmp = x.into_iter();
+ 'a: while let Some(v) = tmp.next() {
+ v *= 2;
+ break 'a;
+ };
+}",
+ )
+ }
+
+ #[test]
+ fn each_to_for_with_attributes() {
+ check_assist(
+ convert_for_loop_to_while_let,
+ r"
+fn main() {
+ let mut x = vec![1, 2, 3];
+ #[allow(unused)]
+ #[deny(unsafe_code)]
+ for $0v in x {
+ v *= 2;
+ };
+}",
+ r"
+fn main() {
+ let mut x = vec![1, 2, 3];
+ let mut tmp = x.into_iter();
+ #[allow(unused)]
+ #[deny(unsafe_code)]
+ while let Some(v) = tmp.next() {
+ v *= 2;
+ };
+}",
+ )
+ }
+
+ #[test]
fn each_to_for_for_in_range() {
check_assist(
convert_for_loop_to_while_let,
diff --git a/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs b/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs
index 0c0458468d..2eea4f71ed 100644
--- a/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs
+++ b/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs
@@ -3,7 +3,7 @@ use ide_db::famous_defs::FamousDefs;
use stdx::format_to;
use syntax::{
AstNode,
- ast::{self, HasArgList, HasLoopBody, edit_in_place::Indent, make},
+ ast::{self, HasArgList, HasLoopBody, edit_in_place::Indent, syntax_factory::SyntaxFactory},
};
use crate::{AssistContext, AssistId, Assists};
@@ -57,18 +57,22 @@ pub(crate) fn convert_iter_for_each_to_for(
"Replace this `Iterator::for_each` with a for loop",
range,
|builder| {
+ let make = SyntaxFactory::with_mappings();
let indent =
stmt.as_ref().map_or_else(|| method.indent_level(), ast::ExprStmt::indent_level);
let block = match body {
- ast::Expr::BlockExpr(block) => block,
- _ => make::block_expr(Vec::new(), Some(body)),
- }
- .clone_for_update();
+ ast::Expr::BlockExpr(block) => block.clone_for_update(),
+ _ => make.block_expr(Vec::new(), Some(body)),
+ };
block.reindent_to(indent);
- let expr_for_loop = make::expr_for_loop(param, receiver, block);
- builder.replace(range, expr_for_loop.to_string())
+ let expr_for_loop = make.expr_for_loop(param, receiver, block);
+
+ let target_node = stmt.as_ref().map_or(method.syntax(), AstNode::syntax);
+ let mut editor = builder.make_editor(target_node);
+ editor.replace(target_node, expr_for_loop.syntax());
+ builder.add_file_edits(ctx.vfs_file_id(), editor);
},
)
}
diff --git a/crates/ide-assists/src/handlers/convert_while_to_loop.rs b/crates/ide-assists/src/handlers/convert_while_to_loop.rs
index dbe3ee0ed6..9fd8b4b315 100644
--- a/crates/ide-assists/src/handlers/convert_while_to_loop.rs
+++ b/crates/ide-assists/src/handlers/convert_while_to_loop.rs
@@ -1,6 +1,5 @@
use std::iter;
-use either::Either;
use ide_db::syntax_helpers::node_ext::is_pattern_cond;
use syntax::{
AstNode, T,
@@ -9,6 +8,7 @@ use syntax::{
edit::{AstNodeEdit, IndentLevel},
make,
},
+ syntax_editor::{Element, Position},
};
use crate::{
@@ -44,43 +44,53 @@ pub(crate) fn convert_while_to_loop(acc: &mut Assists, ctx: &AssistContext<'_>)
let while_expr = while_kw.parent().and_then(ast::WhileExpr::cast)?;
let while_body = while_expr.loop_body()?;
let while_cond = while_expr.condition()?;
+ let l_curly = while_body.stmt_list()?.l_curly_token()?;
let target = while_expr.syntax().text_range();
acc.add(
AssistId::refactor_rewrite("convert_while_to_loop"),
"Convert while to loop",
target,
- |edit| {
+ |builder| {
+ let mut edit = builder.make_editor(while_expr.syntax());
let while_indent_level = IndentLevel::from_node(while_expr.syntax());
let break_block = make::block_expr(
iter::once(make::expr_stmt(make::expr_break(None, None)).into()),
None,
)
- .indent(while_indent_level);
- let block_expr = if is_pattern_cond(while_cond.clone()) {
- let if_expr = make::expr_if(while_cond, while_body, Some(break_block.into()));
+ .indent(IndentLevel(1));
+
+ edit.replace_all(
+ while_kw.syntax_element()..=while_cond.syntax().syntax_element(),
+ vec![make::token(T![loop]).syntax_element()],
+ );
+
+ if is_pattern_cond(while_cond.clone()) {
+ let then_branch = while_body.reset_indent().indent(IndentLevel(1));
+ let if_expr = make::expr_if(while_cond, then_branch, Some(break_block.into()));
let stmts = iter::once(make::expr_stmt(if_expr.into()).into());
- make::block_expr(stmts, None)
+ let block_expr = make::block_expr(stmts, None);
+ edit.replace(while_body.syntax(), block_expr.indent(while_indent_level).syntax());
} else {
let if_cond = invert_boolean_expression_legacy(while_cond);
- let if_expr = make::expr_if(if_cond, break_block, None).syntax().clone().into();
- let elements = while_body.stmt_list().map_or_else(
- || Either::Left(iter::empty()),
- |stmts| {
- Either::Right(stmts.syntax().children_with_tokens().filter(|node_or_tok| {
- // Filter out the trailing expr
- !node_or_tok
- .as_node()
- .is_some_and(|node| ast::Expr::can_cast(node.kind()))
- }))
- },
+ let if_expr = make::expr_if(if_cond, break_block, None).indent(while_indent_level);
+ if !while_body.syntax().text().contains_char('\n') {
+ edit.insert(
+ Position::after(&l_curly),
+ make::tokens::whitespace(&format!("\n{while_indent_level}")),
+ );
+ }
+ edit.insert_all(
+ Position::after(&l_curly),
+ vec![
+ make::tokens::whitespace(&format!("\n{}", while_indent_level + 1)).into(),
+ if_expr.syntax().syntax_element(),
+ ],
);
- make::hacky_block_expr(iter::once(if_expr).chain(elements), while_body.tail_expr())
};
- let replacement = make::expr_loop(block_expr.indent(while_indent_level));
- edit.replace(target, replacement.syntax().text())
+ builder.add_file_edits(ctx.vfs_file_id(), edit);
},
)
}
@@ -116,6 +126,110 @@ fn main() {
}
#[test]
+ fn convert_with_label() {
+ check_assist(
+ convert_while_to_loop,
+ r#"
+fn main() {
+ 'x: while$0 cond {
+ foo();
+ break 'x
+ }
+}
+"#,
+ r#"
+fn main() {
+ 'x: loop {
+ if !cond {
+ break;
+ }
+ foo();
+ break 'x
+ }
+}
+"#,
+ );
+
+ check_assist(
+ convert_while_to_loop,
+ r#"
+fn main() {
+ 'x: while$0 let Some(x) = cond {
+ foo();
+ break 'x
+ }
+}
+"#,
+ r#"
+fn main() {
+ 'x: loop {
+ if let Some(x) = cond {
+ foo();
+ break 'x
+ } else {
+ break;
+ }
+ }
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn convert_with_attributes() {
+ check_assist(
+ convert_while_to_loop,
+ r#"
+fn main() {
+ #[allow(unused)]
+ while$0 cond {
+ foo();
+ break 'x
+ }
+}
+"#,
+ r#"
+fn main() {
+ #[allow(unused)]
+ loop {
+ if !cond {
+ break;
+ }
+ foo();
+ break 'x
+ }
+}
+"#,
+ );
+
+ check_assist(
+ convert_while_to_loop,
+ r#"
+fn main() {
+ #[allow(unused)]
+ #[deny(unsafe_code)]
+ while$0 let Some(x) = cond {
+ foo();
+ }
+}
+"#,
+ r#"
+fn main() {
+ #[allow(unused)]
+ #[deny(unsafe_code)]
+ loop {
+ if let Some(x) = cond {
+ foo();
+ } else {
+ break;
+ }
+ }
+}
+"#,
+ );
+ }
+
+ #[test]
fn convert_busy_wait() {
check_assist(
convert_while_to_loop,
@@ -186,6 +300,76 @@ fn main() {
}
#[test]
+ fn indentation() {
+ check_assist(
+ convert_while_to_loop,
+ r#"
+fn main() {
+ {
+ {
+ while$0 cond {
+ foo(
+ "xxx",
+ );
+ }
+ }
+ }
+}
+"#,
+ r#"
+fn main() {
+ {
+ {
+ loop {
+ if !cond {
+ break;
+ }
+ foo(
+ "xxx",
+ );
+ }
+ }
+ }
+}
+"#,
+ );
+
+ check_assist(
+ convert_while_to_loop,
+ r#"
+fn main() {
+ {
+ {
+ while$0 let Some(_) = foo() {
+ bar(
+ "xxx",
+ );
+ }
+ }
+ }
+}
+"#,
+ r#"
+fn main() {
+ {
+ {
+ loop {
+ if let Some(_) = foo() {
+ bar(
+ "xxx",
+ );
+ } else {
+ break;
+ }
+ }
+ }
+ }
+}
+"#,
+ );
+ }
+
+ #[test]
fn ignore_cursor_in_body() {
check_assist_not_applicable(
convert_while_to_loop,
diff --git a/crates/ide-assists/src/handlers/generate_delegate_trait.rs b/crates/ide-assists/src/handlers/generate_delegate_trait.rs
index a2d49063e6..921f04f2a5 100644
--- a/crates/ide-assists/src/handlers/generate_delegate_trait.rs
+++ b/crates/ide-assists/src/handlers/generate_delegate_trait.rs
@@ -14,15 +14,15 @@ use ide_db::{
};
use itertools::Itertools;
use syntax::{
- AstNode, Edition, NodeOrToken, SmolStr, SyntaxKind, ToSmolStr,
+ AstNode, Edition, SmolStr, SyntaxElement, SyntaxKind, ToSmolStr,
ast::{
self, AssocItem, GenericArgList, GenericParamList, HasAttrs, HasGenericArgs,
HasGenericParams, HasName, HasTypeBounds, HasVisibility as astHasVisibility, Path,
WherePred,
edit::{self, AstNodeEdit},
- make,
+ syntax_factory::SyntaxFactory,
},
- ted::{self, Position},
+ syntax_editor::SyntaxEditor,
};
// Assist: generate_delegate_trait
@@ -169,10 +169,15 @@ enum Delegee {
}
impl Delegee {
+ fn trait_(&self) -> &hir::Trait {
+ match self {
+ Delegee::Bound(it) | Delegee::Impls(it, _) => it,
+ }
+ }
+
fn signature(&self, db: &dyn HirDatabase, edition: Edition) -> String {
let mut s = String::new();
-
- let (Delegee::Bound(it) | Delegee::Impls(it, _)) = self;
+ let it = self.trait_();
for m in it.module(db).path_to_root(db).iter().rev() {
if let Some(name) = m.name(db) {
@@ -201,15 +206,12 @@ impl Struct {
let db = ctx.db();
for (index, delegee) in field.impls.iter().enumerate() {
- let trait_ = match delegee {
- Delegee::Bound(b) => b,
- Delegee::Impls(i, _) => i,
- };
+ let trait_ = delegee.trait_();
// Skip trait that has `Self` type, which cannot be delegated
//
// See [`test_self_ty`]
- if has_self_type(*trait_, ctx).is_some() {
+ if has_self_type(*trait_, ctx) {
continue;
}
@@ -254,9 +256,10 @@ fn generate_impl(
delegee: &Delegee,
edition: Edition,
) -> Option<ast::Impl> {
+ let make = SyntaxFactory::without_mappings();
let db = ctx.db();
let ast_strukt = &strukt.strukt;
- let strukt_ty = make::ty_path(make::ext::ident_path(&strukt.name.to_string()));
+ let strukt_ty = make.ty_path(make.ident_path(&strukt.name.to_string())).into();
let strukt_params = ast_strukt.generic_param_list();
match delegee {
@@ -264,7 +267,7 @@ fn generate_impl(
let bound_def = ctx.sema.source(delegee.to_owned())?.value;
let bound_params = bound_def.generic_param_list();
- let delegate = make::impl_trait(
+ let delegate = make.impl_trait(
None,
delegee.is_unsafe(db),
bound_params.clone(),
@@ -272,33 +275,28 @@ fn generate_impl(
strukt_params.clone(),
strukt_params.map(|params| params.to_generic_args()),
delegee.is_auto(db),
- make::ty(&delegee.name(db).display_no_db(edition).to_smolstr()),
+ make.ty(&delegee.name(db).display_no_db(edition).to_smolstr()),
strukt_ty,
bound_def.where_clause(),
ast_strukt.where_clause(),
None,
- )
- .clone_for_update();
+ );
// Goto link : https://doc.rust-lang.org/reference/paths.html#qualified-paths
let qualified_path_type =
- make::path_from_text(&format!("<{} as {}>", field_ty, delegate.trait_()?));
+ make.path_from_text(&format!("<{} as {}>", field_ty, delegate.trait_()?));
- let delegate_assoc_items = delegate.get_or_create_assoc_item_list();
- if let Some(ai) = bound_def.assoc_item_list() {
+ // Collect assoc items
+ let assoc_items: Option<Vec<ast::AssocItem>> = bound_def.assoc_item_list().map(|ai| {
ai.assoc_items()
.filter(|item| matches!(item, AssocItem::MacroCall(_)).not())
- .for_each(|item| {
- let assoc = process_assoc_item(
- item.clone_for_update(),
- qualified_path_type.clone(),
- field_name,
- );
- if let Some(assoc) = assoc {
- delegate_assoc_items.add_item(assoc);
- }
- });
- };
+ .filter_map(|item| {
+ process_assoc_item(item, qualified_path_type.clone(), field_name)
+ })
+ .collect()
+ });
+
+ let delegate = finalize_delegate(&make, &delegate, assoc_items, false)?;
let target_scope = ctx.sema.scope(strukt.strukt.syntax())?;
let source_scope = ctx.sema.scope(bound_def.syntax())?;
@@ -324,7 +322,7 @@ fn generate_impl(
.and_then(|wc| rename_strukt_args(ctx, ast_strukt, &wc, &args));
(field_ty, where_clause)
}
- None => (field_ty.clone_for_update(), None),
+ None => (field_ty.clone(), None),
};
// 2) Handle instantiated generics in `field_ty`.
@@ -347,38 +345,38 @@ fn generate_impl(
);
// 2.2) Generate generic args applied on impl.
- let transform_args = generate_args_for_impl(
+ let (transform_args, trait_gen_params) = generate_args_for_impl(
old_impl_params,
&old_impl.self_ty()?,
&field_ty,
- &trait_gen_params,
+ trait_gen_params,
&old_impl_trait_args,
);
// 2.3) Instantiate generics with `transform_impl`, this step also
// remove unused params.
- let trait_gen_args = old_impl.trait_()?.generic_arg_list().and_then(|trait_args| {
- let trait_args = &mut trait_args.clone_for_update();
- if let Some(new_args) = transform_impl(
- ctx,
- ast_strukt,
- &old_impl,
- &transform_args,
- trait_args.clone_subtree(),
- ) {
- *trait_args = new_args.clone_subtree();
- Some(new_args)
- } else {
- None
- }
- });
+ let trait_gen_args =
+ old_impl.trait_()?.generic_arg_list().and_then(|mut trait_args| {
+ let trait_args = &mut trait_args;
+ if let Some(new_args) = transform_impl(
+ ctx,
+ ast_strukt,
+ &old_impl,
+ &transform_args,
+ trait_args.clone_subtree(),
+ ) {
+ *trait_args = new_args.clone_subtree();
+ Some(new_args)
+ } else {
+ None
+ }
+ });
let type_gen_args = strukt_params.clone().map(|params| params.to_generic_args());
- let path_type =
- make::ty(&trait_.name(db).display_no_db(edition).to_smolstr()).clone_for_update();
+ let path_type = make.ty(&trait_.name(db).display_no_db(edition).to_smolstr());
let path_type = transform_impl(ctx, ast_strukt, &old_impl, &transform_args, path_type)?;
// 3) Generate delegate trait impl
- let delegate = make::impl_trait(
+ let delegate = make.impl_trait(
None,
trait_.is_unsafe(db),
trait_gen_params,
@@ -388,34 +386,27 @@ fn generate_impl(
trait_.is_auto(db),
path_type,
strukt_ty,
- old_impl.where_clause().map(|wc| wc.clone_for_update()),
+ old_impl.where_clause(),
ty_where_clause,
None,
- )
- .clone_for_update();
+ );
// Goto link : https://doc.rust-lang.org/reference/paths.html#qualified-paths
let qualified_path_type =
- make::path_from_text(&format!("<{} as {}>", field_ty, delegate.trait_()?));
-
- // 4) Transform associated items in delegte trait impl
- let delegate_assoc_items = delegate.get_or_create_assoc_item_list();
- for item in old_impl
- .get_or_create_assoc_item_list()
- .assoc_items()
- .filter(|item| matches!(item, AssocItem::MacroCall(_)).not())
- {
- let item = item.clone_for_update();
- let item = transform_impl(ctx, ast_strukt, &old_impl, &transform_args, item)?;
-
- let assoc = process_assoc_item(item, qualified_path_type.clone(), field_name)?;
- delegate_assoc_items.add_item(assoc);
- }
+ make.path_from_text(&format!("<{} as {}>", field_ty, delegate.trait_()?));
- // 5) Remove useless where clauses
- if let Some(wc) = delegate.where_clause() {
- remove_useless_where_clauses(&delegate.trait_()?, &delegate.self_ty()?, wc);
- }
- Some(delegate)
+ // 4) Transform associated items in delegate trait impl
+ let assoc_items: Option<Vec<ast::AssocItem>> = old_impl.assoc_item_list().map(|ail| {
+ ail.assoc_items()
+ .filter(|item| matches!(item, AssocItem::MacroCall(_)).not())
+ .filter_map(|item| {
+ let item =
+ transform_impl(ctx, ast_strukt, &old_impl, &transform_args, item)?;
+ process_assoc_item(item, qualified_path_type.clone(), field_name)
+ })
+ .collect()
+ });
+
+ finalize_delegate(&make, &delegate, assoc_items, true)
}
}
}
@@ -446,6 +437,35 @@ fn transform_impl<N: ast::AstNode>(
N::cast(transform.apply(syntax.syntax()))
}
+/// Extracts the name from a generic parameter.
+fn generic_param_name(param: &ast::GenericParam) -> Option<String> {
+ match param {
+ ast::GenericParam::TypeParam(t) => t.name().map(|n| n.to_string()),
+ ast::GenericParam::ConstParam(c) => c.name().map(|n| n.to_string()),
+ ast::GenericParam::LifetimeParam(l) => l.lifetime().map(|lt| lt.to_string()),
+ }
+}
+
+/// Filters generic params, keeping only those whose names are not in `names_to_remove`.
+fn filter_generic_params(
+ gpl: ast::GenericParamList,
+ names_to_remove: &FxHashSet<String>,
+) -> Option<ast::GenericParamList> {
+ let remaining_params: Vec<_> = gpl
+ .generic_params()
+ .filter(|param| {
+ generic_param_name(param).is_none_or(|name| !names_to_remove.contains(&name))
+ })
+ .collect();
+
+ if remaining_params.is_empty() {
+ None
+ } else {
+ let make = SyntaxFactory::without_mappings();
+ Some(make.generic_param_list(remaining_params))
+ }
+}
+
fn remove_instantiated_params(
self_ty: &ast::Type,
old_impl_params: Option<GenericParamList>,
@@ -454,10 +474,8 @@ fn remove_instantiated_params(
match self_ty {
ast::Type::PathType(path_type) => {
old_impl_params.and_then(|gpl| {
- // Remove generic parameters in field_ty (which is instantiated).
- let new_gpl = gpl.clone_for_update();
-
- path_type
+ // Collect generic args that should be removed (instantiated params)
+ let args_to_remove: FxHashSet<String> = path_type
.path()?
.segments()
.filter_map(|seg| seg.generic_arg_list())
@@ -466,16 +484,25 @@ fn remove_instantiated_params(
// it shouldn't be removed now, which will be instantiated in
// later `path_transform`
.filter(|arg| !old_trait_args.contains(&arg.to_string()))
- .for_each(|arg| new_gpl.remove_generic_arg(&arg));
- (new_gpl.generic_params().count() > 0).then_some(new_gpl)
+ .map(|arg| arg.to_string())
+ .collect();
+
+ filter_generic_params(gpl, &args_to_remove)
})
}
_ => old_impl_params,
}
}
-fn remove_useless_where_clauses(trait_ty: &ast::Type, self_ty: &ast::Type, wc: ast::WhereClause) {
- let live_generics = [trait_ty, self_ty]
+fn remove_useless_where_clauses(editor: &mut SyntaxEditor, delegate: &ast::Impl) {
+ let Some(wc) = delegate.where_clause() else {
+ return;
+ };
+ let (Some(trait_ty), Some(self_ty)) = (delegate.trait_(), delegate.self_ty()) else {
+ return;
+ };
+
+ let live_generics = [&trait_ty, &self_ty]
.into_iter()
.flat_map(|ty| ty.generic_arg_list())
.flat_map(|gal| gal.generic_args())
@@ -484,34 +511,76 @@ fn remove_useless_where_clauses(trait_ty: &ast::Type, self_ty: &ast::Type, wc: a
// Keep where-clauses that have generics after substitution, and remove the
// rest.
- let has_live_generics = |pred: &WherePred| {
+ let has_no_live_generics = |pred: &WherePred| {
pred.syntax()
.descendants_with_tokens()
.filter_map(|e| e.into_token())
.any(|e| e.kind() == SyntaxKind::IDENT && live_generics.contains(&e.to_string()))
.not()
};
- wc.predicates().filter(has_live_generics).for_each(|pred| wc.remove_predicate(pred));
-
- if wc.predicates().count() == 0 {
- // Remove useless whitespaces
- [syntax::Direction::Prev, syntax::Direction::Next]
- .into_iter()
- .flat_map(|dir| {
- wc.syntax()
- .siblings_with_tokens(dir)
- .skip(1)
- .take_while(|node_or_tok| node_or_tok.kind() == SyntaxKind::WHITESPACE)
- })
- .for_each(ted::remove);
- ted::insert(
- ted::Position::after(wc.syntax()),
- NodeOrToken::Token(make::token(SyntaxKind::WHITESPACE)),
- );
- // Remove where clause
- ted::remove(wc.syntax());
+ let predicates_to_remove: Vec<_> = wc.predicates().filter(has_no_live_generics).collect();
+ let remaining_predicates = wc.predicates().count() - predicates_to_remove.len();
+
+ if remaining_predicates == 0 {
+ // Remove the entire where clause
+ editor.delete(wc.syntax().clone());
+ } else {
+ // Remove only the useless predicates
+ for pred in predicates_to_remove {
+ // Also remove the comma before or after the predicate
+ if let Some(previous) = pred.syntax().prev_sibling() {
+ // Remove from after previous sibling to predicate (inclusive)
+ if let Some(start) = previous.next_sibling_or_token() {
+ let end: SyntaxElement = pred.syntax().clone().into();
+ editor.delete_all(start..=end);
+ }
+ } else if let Some(next) = pred.syntax().next_sibling() {
+ // Remove from predicate to before next sibling (exclusive)
+ if let Some(end) = next.prev_sibling_or_token() {
+ let start: SyntaxElement = pred.syntax().clone().into();
+ editor.delete_all(start..=end);
+ }
+ } else {
+ editor.delete(pred.syntax().clone());
+ }
+ }
+ }
+}
+
+/// Finalize the delegate impl by:
+/// 1. Replacing the assoc_item_list with new items (if any)
+/// 2. Removing useless where clauses
+fn finalize_delegate(
+ make: &SyntaxFactory,
+ delegate: &ast::Impl,
+ assoc_items: Option<Vec<ast::AssocItem>>,
+ remove_where_clauses: bool,
+) -> Option<ast::Impl> {
+ let has_items = assoc_items.as_ref().is_some_and(|items| !items.is_empty());
+
+ if !has_items && !remove_where_clauses {
+ return Some(delegate.clone());
}
+
+ let mut editor = SyntaxEditor::new(delegate.syntax().clone_subtree());
+
+ // 1. Replace assoc_item_list if we have new items
+ if let Some(items) = assoc_items
+ && !items.is_empty()
+ {
+ let new_assoc_item_list = make.assoc_item_list(items);
+ if let Some(old_list) = delegate.assoc_item_list() {
+ editor.replace(old_list.syntax(), new_assoc_item_list.syntax());
+ }
+ }
+
+ // 2. Remove useless where clauses
+ if remove_where_clauses {
+ remove_useless_where_clauses(&mut editor, delegate);
+ }
+
+ ast::Impl::cast(editor.finish().new_root().clone())
}
// Generate generic args that should be apply to current impl.
@@ -524,10 +593,13 @@ fn generate_args_for_impl(
old_impl_gpl: Option<GenericParamList>,
self_ty: &ast::Type,
field_ty: &ast::Type,
- trait_params: &Option<GenericParamList>,
+ trait_params: Option<GenericParamList>,
old_trait_args: &FxHashSet<String>,
-) -> Option<ast::GenericArgList> {
- let old_impl_args = old_impl_gpl.map(|gpl| gpl.to_generic_args().generic_args())?;
+) -> (Option<ast::GenericArgList>, Option<GenericParamList>) {
+ let Some(old_impl_args) = old_impl_gpl.map(|gpl| gpl.to_generic_args().generic_args()) else {
+ return (None, trait_params);
+ };
+
// Create pairs of the args of `self_ty` and corresponding `field_ty` to
// form the substitution list
let mut arg_substs = FxHashMap::default();
@@ -542,6 +614,8 @@ fn generate_args_for_impl(
}
}
+ let mut params_to_remove = FxHashSet::default();
+
let args = old_impl_args
.map(|old_arg| {
arg_substs.get(&old_arg.to_string()).map_or_else(
@@ -549,14 +623,18 @@ fn generate_args_for_impl(
|replace_with| {
// The old_arg will be replaced, so it becomes redundant
if trait_params.is_some() && old_trait_args.contains(&old_arg.to_string()) {
- trait_params.as_ref().unwrap().remove_generic_arg(&old_arg)
+ params_to_remove.insert(old_arg.to_string());
}
replace_with.clone()
},
)
})
.collect_vec();
- args.is_empty().not().then(|| make::generic_arg_list(args))
+
+ let make = SyntaxFactory::without_mappings();
+ let result = args.is_empty().not().then(|| make.generic_arg_list(args, false));
+ let trait_params = trait_params.and_then(|gpl| filter_generic_params(gpl, &params_to_remove));
+ (result, trait_params)
}
fn rename_strukt_args<N>(
@@ -570,41 +648,37 @@ where
{
let hir_strukt = ctx.sema.to_struct_def(strukt)?;
let hir_adt = hir::Adt::from(hir_strukt);
-
- let item = item.clone_for_update();
let scope = ctx.sema.scope(item.syntax())?;
let transform = PathTransform::adt_transformation(&scope, &scope, hir_adt, args.clone());
N::cast(transform.apply(item.syntax()))
}
-fn has_self_type(trait_: hir::Trait, ctx: &AssistContext<'_>) -> Option<()> {
- let trait_source = ctx.sema.source(trait_)?.value;
- trait_source
- .syntax()
- .descendants_with_tokens()
- .filter_map(|e| e.into_token())
- .find(|e| e.kind() == SyntaxKind::SELF_TYPE_KW)
- .map(|_| ())
+fn has_self_type(trait_: hir::Trait, ctx: &AssistContext<'_>) -> bool {
+ ctx.sema
+ .source(trait_)
+ .and_then(|src| {
+ src.value
+ .syntax()
+ .descendants_with_tokens()
+ .filter_map(|e| e.into_token())
+ .find(|e| e.kind() == SyntaxKind::SELF_TYPE_KW)
+ })
+ .is_some()
}
fn resolve_name_conflicts(
strukt_params: Option<ast::GenericParamList>,
old_impl_params: &Option<ast::GenericParamList>,
) -> Option<ast::GenericParamList> {
+ let make = SyntaxFactory::without_mappings();
match (strukt_params, old_impl_params) {
(Some(old_strukt_params), Some(old_impl_params)) => {
- let params = make::generic_param_list(std::iter::empty()).clone_for_update();
+ let mut new_params: Vec<ast::GenericParam> = Vec::new();
for old_strukt_param in old_strukt_params.generic_params() {
// Get old name from `strukt`
- let name = SmolStr::from(match &old_strukt_param {
- ast::GenericParam::ConstParam(c) => c.name()?.to_string(),
- ast::GenericParam::LifetimeParam(l) => {
- l.lifetime()?.lifetime_ident_token()?.to_string()
- }
- ast::GenericParam::TypeParam(t) => t.name()?.to_string(),
- });
+ let name = SmolStr::from(generic_param_name(&old_strukt_param)?);
// The new name cannot be conflicted with generics in trait, and the renamed names.
let param_list_to_names = |param_list: &GenericParamList| {
@@ -613,8 +687,9 @@ fn resolve_name_conflicts(
p => Some(p.to_string()),
})
};
+ let new_params_list = make.generic_param_list(new_params.clone());
let existing_names = param_list_to_names(old_impl_params)
- .chain(param_list_to_names(&params))
+ .chain(param_list_to_names(&new_params_list))
.collect_vec();
let mut name_generator = suggest_name::NameGenerator::new_with_names(
existing_names.iter().map(|s| s.as_str()),
@@ -623,25 +698,21 @@ fn resolve_name_conflicts(
match old_strukt_param {
ast::GenericParam::ConstParam(c) => {
if let Some(const_ty) = c.ty() {
- let const_param = make::const_param(make::name(&name), const_ty);
- params.add_generic_param(ast::GenericParam::ConstParam(
- const_param.clone_for_update(),
- ));
+ let const_param = make.const_param(make.name(&name), const_ty);
+ new_params.push(ast::GenericParam::ConstParam(const_param));
}
}
p @ ast::GenericParam::LifetimeParam(_) => {
- params.add_generic_param(p.clone_for_update());
+ new_params.push(p.clone_for_update());
}
ast::GenericParam::TypeParam(t) => {
let type_bounds = t.type_bound_list();
- let type_param = make::type_param(make::name(&name), type_bounds);
- params.add_generic_param(ast::GenericParam::TypeParam(
- type_param.clone_for_update(),
- ));
+ let type_param = make.type_param(make.name(&name), type_bounds);
+ new_params.push(ast::GenericParam::TypeParam(type_param));
}
}
}
- Some(params)
+ Some(make.generic_param_list(new_params))
}
(Some(old_strukt_gpl), None) => Some(old_strukt_gpl),
_ => None,
@@ -666,7 +737,8 @@ fn process_assoc_item(
}
fn const_assoc_item(item: syntax::ast::Const, qual_path_ty: ast::Path) -> Option<AssocItem> {
- let path_expr_segment = make::path_from_text(item.name()?.to_string().as_str());
+ let make = SyntaxFactory::without_mappings();
+ let path_expr_segment = make.path_from_text(item.name()?.to_string().as_str());
// We want rhs of the const assignment to be a qualified path
// The general case for const assignment can be found [here](`https://doc.rust-lang.org/reference/items/constant-items.html`)
@@ -674,15 +746,14 @@ fn const_assoc_item(item: syntax::ast::Const, qual_path_ty: ast::Path) -> Option
// <Base as Trait<GenArgs>>::ConstName;
// FIXME : We can't rely on `make::path_qualified` for now but it would be nice to replace the following with it.
// make::path_qualified(qual_path_ty, path_expr_segment.as_single_segment().unwrap());
- let qualified_path = qualified_path(qual_path_ty, path_expr_segment);
- let inner = make::item_const(
+ let qualified_path = make.path_from_text(&format!("{qual_path_ty}::{path_expr_segment}"));
+ let inner = make.item_const(
item.attrs(),
item.visibility(),
item.name()?,
item.ty()?,
- make::expr_path(qualified_path),
- )
- .clone_for_update();
+ make.expr_path(qualified_path),
+ );
Some(AssocItem::Const(inner))
}
@@ -692,59 +763,46 @@ fn func_assoc_item(
qual_path_ty: Path,
base_name: &str,
) -> Option<AssocItem> {
- let path_expr_segment = make::path_from_text(item.name()?.to_string().as_str());
- let qualified_path = qualified_path(qual_path_ty, path_expr_segment);
+ let make = SyntaxFactory::without_mappings();
+ let path_expr_segment = make.path_from_text(item.name()?.to_string().as_str());
+ let qualified_path = make.path_from_text(&format!("{qual_path_ty}::{path_expr_segment}"));
let call = match item.param_list() {
// Methods and funcs should be handled separately.
// We ask if the func has a `self` param.
Some(l) => match l.self_param() {
Some(slf) => {
- let mut self_kw = make::expr_path(make::path_from_text("self"));
- self_kw = make::expr_field(self_kw, base_name);
+ let self_kw = make.expr_path(make.path_from_text("self"));
+ let self_kw = make.expr_field(self_kw, base_name).into();
let tail_expr_self = match slf.kind() {
ast::SelfParamKind::Owned => self_kw,
- ast::SelfParamKind::Ref => make::expr_ref(self_kw, false),
- ast::SelfParamKind::MutRef => make::expr_ref(self_kw, true),
+ ast::SelfParamKind::Ref => make.expr_ref(self_kw, false),
+ ast::SelfParamKind::MutRef => make.expr_ref(self_kw, true),
};
- let param_count = l.params().count();
- let args = convert_param_list_to_arg_list(l).clone_for_update();
- let pos_after_l_paren = Position::after(args.l_paren_token()?);
- if param_count > 0 {
- // Add SelfParam and a TOKEN::COMMA
- ted::insert_all_raw(
- pos_after_l_paren,
- vec![
- NodeOrToken::Node(tail_expr_self.syntax().clone_for_update()),
- NodeOrToken::Token(make::token(SyntaxKind::COMMA)),
- NodeOrToken::Token(make::token(SyntaxKind::WHITESPACE)),
- ],
- );
- } else {
- // Add SelfParam only
- ted::insert_raw(
- pos_after_l_paren,
- NodeOrToken::Node(tail_expr_self.syntax().clone_for_update()),
- );
- }
+ // Build argument list with self expression prepended
+ let other_args = convert_param_list_to_arg_list(l);
+ let all_args: Vec<ast::Expr> =
+ std::iter::once(tail_expr_self).chain(other_args.args()).collect();
+ let args = make.arg_list(all_args);
- make::expr_call(make::expr_path(qualified_path), args)
- }
- None => {
- make::expr_call(make::expr_path(qualified_path), convert_param_list_to_arg_list(l))
+ make.expr_call(make.expr_path(qualified_path), args).into()
}
+ None => make
+ .expr_call(make.expr_path(qualified_path), convert_param_list_to_arg_list(l))
+ .into(),
},
- None => make::expr_call(
- make::expr_path(qualified_path),
- convert_param_list_to_arg_list(make::param_list(None, Vec::new())),
- ),
- }
- .clone_for_update();
+ None => make
+ .expr_call(
+ make.expr_path(qualified_path),
+ convert_param_list_to_arg_list(make.param_list(None, Vec::new())),
+ )
+ .into(),
+ };
- let body = make::block_expr(vec![], Some(call.into())).clone_for_update();
- let func = make::fn_(
+ let body = make.block_expr(vec![], Some(call));
+ let func = make.fn_(
item.attrs(),
item.visibility(),
item.name()?,
@@ -757,35 +815,32 @@ fn func_assoc_item(
item.const_token().is_some(),
item.unsafe_token().is_some(),
item.gen_token().is_some(),
- )
- .clone_for_update();
+ );
Some(AssocItem::Fn(func.indent(edit::IndentLevel(1))))
}
fn ty_assoc_item(item: syntax::ast::TypeAlias, qual_path_ty: Path) -> Option<AssocItem> {
- let path_expr_segment = make::path_from_text(item.name()?.to_string().as_str());
- let qualified_path = qualified_path(qual_path_ty, path_expr_segment);
- let ty = make::ty_path(qualified_path);
+ let make = SyntaxFactory::without_mappings();
+ let path_expr_segment = make.path_from_text(item.name()?.to_string().as_str());
+ let qualified_path = make.path_from_text(&format!("{qual_path_ty}::{path_expr_segment}"));
+ let ty = make.ty_path(qualified_path).into();
let ident = item.name()?.to_string();
- let alias = make::ty_alias(
- item.attrs(),
- ident.as_str(),
- item.generic_param_list(),
- None,
- item.where_clause(),
- Some((ty, None)),
- )
- .indent(edit::IndentLevel(1));
+ let alias = make
+ .ty_alias(
+ item.attrs(),
+ ident.as_str(),
+ item.generic_param_list(),
+ None,
+ item.where_clause(),
+ Some((ty, None)),
+ )
+ .indent(edit::IndentLevel(1));
Some(AssocItem::TypeAlias(alias))
}
-fn qualified_path(qual_path_ty: ast::Path, path_expr_seg: ast::Path) -> ast::Path {
- make::path_from_text(&format!("{qual_path_ty}::{path_expr_seg}"))
-}
-
#[cfg(test)]
mod test {
diff --git a/crates/ide-assists/src/handlers/generate_fn_type_alias.rs b/crates/ide-assists/src/handlers/generate_fn_type_alias.rs
index 0b7eca2290..7fd94b4bed 100644
--- a/crates/ide-assists/src/handlers/generate_fn_type_alias.rs
+++ b/crates/ide-assists/src/handlers/generate_fn_type_alias.rs
@@ -270,6 +270,22 @@ fn foo<A: Trait, B: Trait>(a: A, b: B) -> i32 { return 42; }
}
#[test]
+ fn generate_fn_alias_unnamed_complex_types() {
+ check_assist_by_label(
+ generate_fn_type_alias,
+ r#"
+fn fo$0o(x: Vec<i32>) {}
+"#,
+ r#"
+type ${0:FooFn} = fn(Vec<i32>);
+
+fn foo(x: Vec<i32>) {}
+"#,
+ ParamStyle::Unnamed.label(),
+ );
+ }
+
+ #[test]
fn generate_fn_alias_unnamed_self() {
check_assist_by_label(
generate_fn_type_alias,
@@ -406,6 +422,22 @@ fn foo<A: Trait, B: Trait>(a: A, b: B) -> i32 { return 42; }
}
#[test]
+ fn generate_fn_alias_named_complex_types() {
+ check_assist_by_label(
+ generate_fn_type_alias,
+ r#"
+fn fo$0o(x: Vec<i32>) {}
+"#,
+ r#"
+type ${0:FooFn} = fn(x: Vec<i32>);
+
+fn foo(x: Vec<i32>) {}
+"#,
+ ParamStyle::Named.label(),
+ );
+ }
+
+ #[test]
fn generate_fn_alias_named_self() {
check_assist_by_label(
generate_fn_type_alias,
diff --git a/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs b/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs
index c85ec734c0..6ca3e26ca0 100644
--- a/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs
+++ b/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs
@@ -1,4 +1,5 @@
-use ide_db::assists::AssistId;
+use hir::Semantics;
+use ide_db::{RootDatabase, assists::AssistId, defs::Definition};
use syntax::{
AstNode,
ast::{self, Expr, HasArgList, make},
@@ -60,8 +61,8 @@ pub(crate) fn replace_with_lazy_method(acc: &mut Assists, ctx: &AssistContext<'_
format!("Replace {method_name} with {method_name_lazy}"),
call.syntax().text_range(),
|builder| {
+ let closured = into_closure(&last_arg, &method_name_lazy);
builder.replace(method_name.syntax().text_range(), method_name_lazy);
- let closured = into_closure(&last_arg);
builder.replace_ast(last_arg, closured);
},
)
@@ -79,7 +80,7 @@ fn lazy_method_name(name: &str) -> String {
}
}
-fn into_closure(param: &Expr) -> Expr {
+fn into_closure(param: &Expr, name_lazy: &str) -> Expr {
(|| {
if let ast::Expr::CallExpr(call) = param {
if call.arg_list()?.args().count() == 0 { Some(call.expr()?) } else { None }
@@ -87,7 +88,11 @@ fn into_closure(param: &Expr) -> Expr {
None
}
})()
- .unwrap_or_else(|| make::expr_closure(None, param.clone()).into())
+ .unwrap_or_else(|| {
+ let pats = (name_lazy == "and_then")
+ .then(|| make::untyped_param(make::ext::simple_ident_pat(make::name("it")).into()));
+ make::expr_closure(pats, param.clone()).into()
+ })
}
// Assist: replace_with_eager_method
@@ -146,21 +151,39 @@ pub(crate) fn replace_with_eager_method(acc: &mut Assists, ctx: &AssistContext<'
call.syntax().text_range(),
|builder| {
builder.replace(method_name.syntax().text_range(), method_name_eager);
- let called = into_call(&last_arg);
+ let called = into_call(&last_arg, &ctx.sema);
builder.replace_ast(last_arg, called);
},
)
}
-fn into_call(param: &Expr) -> Expr {
+fn into_call(param: &Expr, sema: &Semantics<'_, RootDatabase>) -> Expr {
(|| {
if let ast::Expr::ClosureExpr(closure) = param {
- if closure.param_list()?.params().count() == 0 { Some(closure.body()?) } else { None }
+ let mut params = closure.param_list()?.params();
+ match params.next() {
+ Some(_) if params.next().is_none() => {
+ let params = sema.resolve_expr_as_callable(param)?.params();
+ let used_param = Definition::Local(params.first()?.as_local(sema.db)?)
+ .usages(sema)
+ .at_least_one();
+ if used_param { None } else { Some(closure.body()?) }
+ }
+ None => Some(closure.body()?),
+ Some(_) => None,
+ }
} else {
None
}
})()
- .unwrap_or_else(|| make::expr_call(param.clone(), make::arg_list(Vec::new())).into())
+ .unwrap_or_else(|| {
+ let callable = if needs_parens_in_call(param) {
+ make::expr_paren(param.clone()).into()
+ } else {
+ param.clone()
+ };
+ make::expr_call(callable, make::arg_list(Vec::new())).into()
+ })
}
fn eager_method_name(name: &str) -> Option<&str> {
@@ -177,6 +200,12 @@ fn ends_is(name: &str, end: &str) -> bool {
name.strip_suffix(end).is_some_and(|s| s.is_empty() || s.ends_with('_'))
}
+fn needs_parens_in_call(param: &Expr) -> bool {
+ let call = make::expr_call(make::ext::expr_unit(), make::arg_list(Vec::new()));
+ let callable = call.expr().expect("invalid make call");
+ param.needs_parens_in_place_of(call.syntax(), callable.syntax())
+}
+
#[cfg(test)]
mod tests {
use crate::tests::check_assist;
@@ -333,7 +362,7 @@ fn foo() {
r#"
fn foo() {
let foo = Some("foo");
- return foo.and_then(|| Some("bar"));
+ return foo.and_then(|it| Some("bar"));
}
"#,
)
@@ -347,7 +376,7 @@ fn foo() {
//- minicore: option, fn
fn foo() {
let foo = Some("foo");
- return foo.and_then$0(|| Some("bar"));
+ return foo.and_then$0(|it| Some("bar"));
}
"#,
r#"
@@ -360,6 +389,26 @@ fn foo() {
}
#[test]
+ fn replace_and_then_with_and_used_param() {
+ check_assist(
+ replace_with_eager_method,
+ r#"
+//- minicore: option, fn
+fn foo() {
+ let foo = Some("foo");
+ return foo.and_then$0(|it| Some(it.strip_suffix("bar")));
+}
+"#,
+ r#"
+fn foo() {
+ let foo = Some("foo");
+ return foo.and((|it| Some(it.strip_suffix("bar")))());
+}
+"#,
+ )
+ }
+
+ #[test]
fn replace_then_some_with_then() {
check_assist(
replace_with_lazy_method,
@@ -398,4 +447,28 @@ fn foo() {
"#,
)
}
+
+ #[test]
+ fn replace_then_with_then_some_needs_parens() {
+ check_assist(
+ replace_with_eager_method,
+ r#"
+//- minicore: option, fn, bool_impl
+struct Func { f: fn() -> i32 }
+fn foo() {
+ let foo = true;
+ let func = Func { f: || 2 };
+ let x = foo.then$0(func.f);
+}
+"#,
+ r#"
+struct Func { f: fn() -> i32 }
+fn foo() {
+ let foo = true;
+ let func = Func { f: || 2 };
+ let x = foo.then_some((func.f)());
+}
+"#,
+ )
+ }
}
diff --git a/crates/ide-assists/src/handlers/toggle_ignore.rs b/crates/ide-assists/src/handlers/toggle_ignore.rs
index 386625b86b..a088fb178d 100644
--- a/crates/ide-assists/src/handlers/toggle_ignore.rs
+++ b/crates/ide-assists/src/handlers/toggle_ignore.rs
@@ -1,6 +1,6 @@
use syntax::{
AstNode, AstToken,
- ast::{self, HasAttrs},
+ ast::{self, HasAttrs, edit::AstNodeEdit},
};
use crate::{AssistContext, AssistId, Assists, utils::test_related_attribute_syn};
@@ -27,13 +27,16 @@ pub(crate) fn toggle_ignore(acc: &mut Assists, ctx: &AssistContext<'_>) -> Optio
let attr: ast::Attr = ctx.find_node_at_offset()?;
let func = attr.syntax().parent().and_then(ast::Fn::cast)?;
let attr = test_related_attribute_syn(&func)?;
+ let indent = attr.indent_level();
match has_ignore_attribute(&func) {
None => acc.add(
AssistId::refactor("toggle_ignore"),
"Ignore this test",
attr.syntax().text_range(),
- |builder| builder.insert(attr.syntax().text_range().end(), "\n#[ignore]"),
+ |builder| {
+ builder.insert(attr.syntax().text_range().end(), format!("\n{indent}#[ignore]"))
+ },
),
Some(ignore_attr) => acc.add(
AssistId::refactor("toggle_ignore"),
@@ -69,13 +72,17 @@ mod tests {
check_assist(
toggle_ignore,
r#"
- #[test$0]
- fn test() {}
+ mod indent {
+ #[test$0]
+ fn test() {}
+ }
"#,
r#"
- #[test]
- #[ignore]
- fn test() {}
+ mod indent {
+ #[test]
+ #[ignore]
+ fn test() {}
+ }
"#,
)
}
@@ -85,13 +92,17 @@ mod tests {
check_assist(
toggle_ignore,
r#"
- #[test$0]
- #[ignore]
- fn test() {}
+ mod indent {
+ #[test$0]
+ #[ignore]
+ fn test() {}
+ }
"#,
r#"
- #[test]
- fn test() {}
+ mod indent {
+ #[test]
+ fn test() {}
+ }
"#,
)
}
diff --git a/crates/ide-assists/src/lib.rs b/crates/ide-assists/src/lib.rs
index 16b5684c16..47cb4c8e74 100644
--- a/crates/ide-assists/src/lib.rs
+++ b/crates/ide-assists/src/lib.rs
@@ -247,7 +247,6 @@ mod handlers {
add_label_to_loop::add_label_to_loop,
add_lifetime_to_type::add_lifetime_to_type,
add_missing_match_arms::add_missing_match_arms,
- add_return_type::add_return_type,
add_turbo_fish::add_turbo_fish,
apply_demorgan::apply_demorgan_iterator,
apply_demorgan::apply_demorgan,
@@ -392,6 +391,7 @@ mod handlers {
// used as a tie-breaker.
add_missing_impl_members::add_missing_impl_members,
add_missing_impl_members::add_missing_default_members,
+ add_return_type::add_return_type,
//
replace_string_with_char::replace_string_with_char,
replace_string_with_char::replace_char_with_string,
diff --git a/crates/ide-assists/src/utils.rs b/crates/ide-assists/src/utils.rs
index de8c4b6bca..9a96374c00 100644
--- a/crates/ide-assists/src/utils.rs
+++ b/crates/ide-assists/src/utils.rs
@@ -27,7 +27,7 @@ use syntax::{
make,
syntax_factory::SyntaxFactory,
},
- syntax_editor::{Removable, SyntaxEditor},
+ syntax_editor::{Element, Removable, SyntaxEditor},
};
use crate::{
@@ -384,6 +384,28 @@ fn invert_special_case_legacy(expr: &ast::Expr) -> Option<ast::Expr> {
}
}
+pub(crate) fn insert_attributes(
+ before: impl Element,
+ edit: &mut SyntaxEditor,
+ attrs: impl IntoIterator<Item = ast::Attr>,
+) {
+ let mut attrs = attrs.into_iter().peekable();
+ if attrs.peek().is_none() {
+ return;
+ }
+ let elem = before.syntax_element();
+ let indent = IndentLevel::from_element(&elem);
+ let whitespace = format!("\n{indent}");
+ edit.insert_all(
+ syntax::syntax_editor::Position::before(elem),
+ attrs
+ .flat_map(|attr| {
+ [attr.syntax().clone().into(), make::tokens::whitespace(&whitespace).into()]
+ })
+ .collect(),
+ );
+}
+
pub(crate) fn next_prev() -> impl Iterator<Item = Direction> {
[Direction::Next, Direction::Prev].into_iter()
}
diff --git a/crates/ide-completion/src/completions/format_string.rs b/crates/ide-completion/src/completions/format_string.rs
index 5ae65b05bc..eaacd8d1a8 100644
--- a/crates/ide-completion/src/completions/format_string.rs
+++ b/crates/ide-completion/src/completions/format_string.rs
@@ -22,13 +22,8 @@ pub(crate) fn format_string(
let cursor_in_lit = cursor - lit_start;
let prefix = &original.text()[..cursor_in_lit.into()];
- let braces = prefix.char_indices().rev().skip_while(|&(_, c)| c.is_alphanumeric()).next_tuple();
- let brace_offset = match braces {
- // escaped brace
- Some(((_, '{'), (_, '{'))) => return,
- Some(((idx, '{'), _)) => lit_start + TextSize::from(idx as u32 + 1),
- _ => return,
- };
+ let Some(brace_offset) = unescaped_brace(prefix) else { return };
+ let brace_offset = lit_start + brace_offset + TextSize::of('{');
let source_range = TextRange::new(brace_offset, cursor);
ctx.locals.iter().sorted_by_key(|&(k, _)| k.clone()).for_each(|(name, _)| {
@@ -59,6 +54,15 @@ pub(crate) fn format_string(
});
}
+fn unescaped_brace(prefix: &str) -> Option<TextSize> {
+ let is_ident_char = |ch: char| ch.is_alphanumeric() || ch == '_';
+ prefix
+ .trim_end_matches(is_ident_char)
+ .strip_suffix('{')
+ .filter(|it| it.chars().rev().take_while(|&ch| ch == '{').count() % 2 == 0)
+ .map(|s| TextSize::new(s.len() as u32))
+}
+
#[cfg(test)]
mod tests {
use expect_test::expect;
@@ -97,6 +101,82 @@ fn main() {
}
#[test]
+ fn no_completion_after_escaped() {
+ check_no_kw(
+ r#"
+//- minicore: fmt
+fn main() {
+ let foobar = 1;
+ format_args!("{{f$0");
+}
+"#,
+ expect![[]],
+ );
+ check_no_kw(
+ r#"
+//- minicore: fmt
+fn main() {
+ let foobar = 1;
+ format_args!("some text {{{{f$0");
+}
+"#,
+ expect![[]],
+ );
+ }
+
+ #[test]
+ fn completes_unescaped_after_escaped() {
+ check_edit(
+ "foobar",
+ r#"
+//- minicore: fmt
+fn main() {
+ let foobar = 1;
+ format_args!("{{{f$0");
+}
+"#,
+ r#"
+fn main() {
+ let foobar = 1;
+ format_args!("{{{foobar");
+}
+"#,
+ );
+ check_edit(
+ "foobar",
+ r#"
+//- minicore: fmt
+fn main() {
+ let foobar = 1;
+ format_args!("{{{{{f$0");
+}
+"#,
+ r#"
+fn main() {
+ let foobar = 1;
+ format_args!("{{{{{foobar");
+}
+"#,
+ );
+ check_edit(
+ "foobar",
+ r#"
+//- minicore: fmt
+fn main() {
+ let foobar = 1;
+ format_args!("}}{f$0");
+}
+"#,
+ r#"
+fn main() {
+ let foobar = 1;
+ format_args!("}}{foobar");
+}
+"#,
+ );
+ }
+
+ #[test]
fn completes_locals() {
check_edit(
"foobar",
diff --git a/crates/ide-completion/src/lib.rs b/crates/ide-completion/src/lib.rs
index c9d5971cd0..33ab43fa61 100644
--- a/crates/ide-completion/src/lib.rs
+++ b/crates/ide-completion/src/lib.rs
@@ -2,7 +2,6 @@
// It's useful to refer to code that is private in doc comments.
#![allow(rustdoc::private_intra_doc_links)]
-
#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))]
#[cfg(feature = "in-rust-tree")]
diff --git a/crates/ide-db/src/symbol_index.rs b/crates/ide-db/src/symbol_index.rs
index c5ead45a20..e15d0b33bb 100644
--- a/crates/ide-db/src/symbol_index.rs
+++ b/crates/ide-db/src/symbol_index.rs
@@ -37,6 +37,7 @@ use hir::{
};
use rayon::prelude::*;
use rustc_hash::FxHashSet;
+use salsa::Update;
use crate::RootDatabase;
@@ -118,7 +119,7 @@ pub struct LocalRoots {
}
/// The symbol indices of modules that make up a given crate.
-pub fn crate_symbols(db: &dyn HirDatabase, krate: Crate) -> Box<[&SymbolIndex]> {
+pub fn crate_symbols(db: &dyn HirDatabase, krate: Crate) -> Box<[&SymbolIndex<'_>]> {
let _p = tracing::info_span!("crate_symbols").entered();
krate.modules(db).into_iter().map(|module| SymbolIndex::module_symbols(db, module)).collect()
}
@@ -148,7 +149,7 @@ pub fn crate_symbols(db: &dyn HirDatabase, krate: Crate) -> Box<[&SymbolIndex]>
// | Editor | Shortcut |
// |---------|-----------|
// | VS Code | <kbd>Ctrl+T</kbd>
-pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec<FileSymbol> {
+pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec<FileSymbol<'_>> {
let _p = tracing::info_span!("world_symbols", query = ?query.query).entered();
let indices: Vec<_> = if query.libs {
@@ -170,9 +171,7 @@ pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec<FileSymbol> {
crates
.par_iter()
.for_each_with(db.clone(), |snap, &krate| _ = crate_symbols(snap, krate.into()));
- let indices: Vec<_> =
- crates.into_iter().map(|krate| crate_symbols(db, krate.into())).collect();
- indices.iter().flat_map(|indices| indices.iter().cloned()).collect()
+ crates.into_iter().flat_map(|krate| Vec::from(crate_symbols(db, krate.into()))).collect()
};
let mut res = vec![];
@@ -184,24 +183,27 @@ pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec<FileSymbol> {
}
#[derive(Default)]
-pub struct SymbolIndex {
- symbols: Box<[FileSymbol]>,
+pub struct SymbolIndex<'db> {
+ symbols: Box<[FileSymbol<'db>]>,
map: fst::Map<Vec<u8>>,
}
-impl SymbolIndex {
+impl<'db> SymbolIndex<'db> {
/// The symbol index for a given source root within library_roots.
- pub fn library_symbols(db: &dyn HirDatabase, source_root_id: SourceRootId) -> &SymbolIndex {
+ pub fn library_symbols(
+ db: &'db dyn HirDatabase,
+ source_root_id: SourceRootId,
+ ) -> &'db SymbolIndex<'db> {
// FIXME:
#[salsa::interned]
struct InternedSourceRootId {
id: SourceRootId,
}
#[salsa::tracked(returns(ref))]
- fn library_symbols(
- db: &dyn HirDatabase,
- source_root_id: InternedSourceRootId<'_>,
- ) -> SymbolIndex {
+ fn library_symbols<'db>(
+ db: &'db dyn HirDatabase,
+ source_root_id: InternedSourceRootId<'db>,
+ ) -> SymbolIndex<'db> {
let _p = tracing::info_span!("library_symbols").entered();
// We call this without attaching because this runs in parallel, so we need to attach here.
@@ -224,7 +226,7 @@ impl SymbolIndex {
/// The symbol index for a given module. These modules should only be in source roots that
/// are inside local_roots.
- pub fn module_symbols(db: &dyn HirDatabase, module: Module) -> &SymbolIndex {
+ pub fn module_symbols(db: &dyn HirDatabase, module: Module) -> &SymbolIndex<'_> {
// FIXME:
#[salsa::interned]
struct InternedModuleId {
@@ -232,7 +234,10 @@ impl SymbolIndex {
}
#[salsa::tracked(returns(ref))]
- fn module_symbols(db: &dyn HirDatabase, module: InternedModuleId<'_>) -> SymbolIndex {
+ fn module_symbols<'db>(
+ db: &'db dyn HirDatabase,
+ module: InternedModuleId<'db>,
+ ) -> SymbolIndex<'db> {
let _p = tracing::info_span!("module_symbols").entered();
// We call this without attaching because this runs in parallel, so we need to attach here.
@@ -250,29 +255,41 @@ impl SymbolIndex {
}
}
-impl fmt::Debug for SymbolIndex {
+impl fmt::Debug for SymbolIndex<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("SymbolIndex").field("n_symbols", &self.symbols.len()).finish()
}
}
-impl PartialEq for SymbolIndex {
- fn eq(&self, other: &SymbolIndex) -> bool {
+impl PartialEq for SymbolIndex<'_> {
+ fn eq(&self, other: &SymbolIndex<'_>) -> bool {
self.symbols == other.symbols
}
}
-impl Eq for SymbolIndex {}
+impl Eq for SymbolIndex<'_> {}
-impl Hash for SymbolIndex {
+impl Hash for SymbolIndex<'_> {
fn hash<H: Hasher>(&self, hasher: &mut H) {
self.symbols.hash(hasher)
}
}
-impl SymbolIndex {
- fn new(mut symbols: Box<[FileSymbol]>) -> SymbolIndex {
- fn cmp(lhs: &FileSymbol, rhs: &FileSymbol) -> Ordering {
+unsafe impl Update for SymbolIndex<'_> {
+ unsafe fn maybe_update(old_pointer: *mut Self, new_value: Self) -> bool {
+ let this = unsafe { &mut *old_pointer };
+ if *this == new_value {
+ false
+ } else {
+ *this = new_value;
+ true
+ }
+ }
+}
+
+impl<'db> SymbolIndex<'db> {
+ fn new(mut symbols: Box<[FileSymbol<'db>]>) -> SymbolIndex<'db> {
+ fn cmp(lhs: &FileSymbol<'_>, rhs: &FileSymbol<'_>) -> Ordering {
let lhs_chars = lhs.name.as_str().chars().map(|c| c.to_ascii_lowercase());
let rhs_chars = rhs.name.as_str().chars().map(|c| c.to_ascii_lowercase());
lhs_chars.cmp(rhs_chars)
@@ -318,7 +335,7 @@ impl SymbolIndex {
}
pub fn memory_size(&self) -> usize {
- self.map.as_fst().size() + self.symbols.len() * size_of::<FileSymbol>()
+ self.map.as_fst().size() + self.symbols.len() * size_of::<FileSymbol<'_>>()
}
fn range_to_map_value(start: usize, end: usize) -> u64 {
@@ -336,10 +353,10 @@ impl SymbolIndex {
}
impl Query {
- pub(crate) fn search<'sym, T>(
+ pub(crate) fn search<'db, T>(
self,
- indices: &'sym [&SymbolIndex],
- cb: impl FnMut(&'sym FileSymbol) -> ControlFlow<T>,
+ indices: &[&'db SymbolIndex<'db>],
+ cb: impl FnMut(&'db FileSymbol<'db>) -> ControlFlow<T>,
) -> Option<T> {
let _p = tracing::info_span!("symbol_index::Query::search").entered();
let mut op = fst::map::OpBuilder::new();
@@ -371,11 +388,11 @@ impl Query {
}
}
- fn search_maps<'sym, T>(
+ fn search_maps<'db, T>(
&self,
- indices: &'sym [&SymbolIndex],
+ indices: &[&'db SymbolIndex<'db>],
mut stream: fst::map::Union<'_>,
- mut cb: impl FnMut(&'sym FileSymbol) -> ControlFlow<T>,
+ mut cb: impl FnMut(&'db FileSymbol<'db>) -> ControlFlow<T>,
) -> Option<T> {
let ignore_underscore_prefixed = !self.query.starts_with("__");
while let Some((_, indexed_values)) = stream.next() {
diff --git a/crates/ide-db/src/test_data/test_doc_alias.txt b/crates/ide-db/src/test_data/test_doc_alias.txt
index c8a9231fa9..5783d97564 100644
--- a/crates/ide-db/src/test_data/test_doc_alias.txt
+++ b/crates/ide-db/src/test_data/test_doc_alias.txt
@@ -39,6 +39,7 @@
is_assoc: false,
is_import: false,
do_not_complete: Yes,
+ _marker: PhantomData<&()>,
},
FileSymbol {
name: "Struct",
@@ -73,6 +74,7 @@
is_assoc: false,
is_import: false,
do_not_complete: Yes,
+ _marker: PhantomData<&()>,
},
FileSymbol {
name: "mul1",
@@ -107,6 +109,7 @@
is_assoc: false,
is_import: false,
do_not_complete: Yes,
+ _marker: PhantomData<&()>,
},
FileSymbol {
name: "mul2",
@@ -141,6 +144,7 @@
is_assoc: false,
is_import: false,
do_not_complete: Yes,
+ _marker: PhantomData<&()>,
},
FileSymbol {
name: "s1",
@@ -175,6 +179,7 @@
is_assoc: false,
is_import: false,
do_not_complete: Yes,
+ _marker: PhantomData<&()>,
},
FileSymbol {
name: "s1",
@@ -209,6 +214,7 @@
is_assoc: false,
is_import: false,
do_not_complete: Yes,
+ _marker: PhantomData<&()>,
},
FileSymbol {
name: "s2",
@@ -243,6 +249,7 @@
is_assoc: false,
is_import: false,
do_not_complete: Yes,
+ _marker: PhantomData<&()>,
},
],
),
diff --git a/crates/ide-db/src/test_data/test_symbol_index_collection.txt b/crates/ide-db/src/test_data/test_symbol_index_collection.txt
index cc879cf84f..953bc73da9 100644
--- a/crates/ide-db/src/test_data/test_symbol_index_collection.txt
+++ b/crates/ide-db/src/test_data/test_symbol_index_collection.txt
@@ -39,6 +39,7 @@
is_assoc: true,
is_import: false,
do_not_complete: Yes,
+ _marker: PhantomData<&()>,
},
FileSymbol {
name: "Alias",
@@ -71,6 +72,7 @@
is_assoc: false,
is_import: false,
do_not_complete: Yes,
+ _marker: PhantomData<&()>,
},
FileSymbol {
name: "B",
@@ -105,6 +107,7 @@
is_assoc: true,
is_import: false,
do_not_complete: Yes,
+ _marker: PhantomData<&()>,
},
FileSymbol {
name: "CONST",
@@ -137,6 +140,7 @@
is_assoc: false,
is_import: false,
do_not_complete: Yes,
+ _marker: PhantomData<&()>,
},
FileSymbol {
name: "CONST_WITH_INNER",
@@ -169,6 +173,7 @@
is_assoc: false,
is_import: false,
do_not_complete: Yes,
+ _marker: PhantomData<&()>,
},
FileSymbol {
name: "Enum",
@@ -203,6 +208,7 @@
is_assoc: false,
is_import: false,
do_not_complete: Yes,
+ _marker: PhantomData<&()>,
},
FileSymbol {
name: "ItemLikeMacro",
@@ -237,6 +243,7 @@
is_assoc: false,
is_import: true,
do_not_complete: Yes,
+ _marker: PhantomData<&()>,
},
FileSymbol {
name: "Macro",
@@ -271,6 +278,7 @@
is_assoc: false,
is_import: false,
do_not_complete: Yes,
+ _marker: PhantomData<&()>,
},
FileSymbol {
name: "STATIC",
@@ -303,6 +311,7 @@
is_assoc: false,
is_import: false,
do_not_complete: Yes,
+ _marker: PhantomData<&()>,
},
FileSymbol {
name: "Struct",
@@ -337,6 +346,7 @@
is_assoc: false,
is_import: false,
do_not_complete: Yes,
+ _marker: PhantomData<&()>,
},
FileSymbol {
name: "StructFromMacro",
@@ -371,6 +381,7 @@
is_assoc: false,
is_import: false,
do_not_complete: Yes,
+ _marker: PhantomData<&()>,
},
FileSymbol {
name: "StructInFn",
@@ -407,6 +418,7 @@
is_assoc: false,
is_import: false,
do_not_complete: Yes,
+ _marker: PhantomData<&()>,
},
FileSymbol {
name: "StructInNamedConst",
@@ -443,6 +455,7 @@
is_assoc: false,
is_import: false,
do_not_complete: Yes,
+ _marker: PhantomData<&()>,
},
FileSymbol {
name: "StructInUnnamedConst",
@@ -477,6 +490,7 @@
is_assoc: false,
is_import: false,
do_not_complete: Yes,
+ _marker: PhantomData<&()>,
},
FileSymbol {
name: "StructT",
@@ -511,6 +525,7 @@
is_assoc: false,
is_import: false,
do_not_complete: Yes,
+ _marker: PhantomData<&()>,
},
FileSymbol {
name: "Trait",
@@ -543,6 +558,7 @@
is_assoc: false,
is_import: false,
do_not_complete: Yes,
+ _marker: PhantomData<&()>,
},
FileSymbol {
name: "Trait",
@@ -577,6 +593,7 @@
is_assoc: false,
is_import: true,
do_not_complete: Yes,
+ _marker: PhantomData<&()>,
},
FileSymbol {
name: "Union",
@@ -611,6 +628,7 @@
is_assoc: false,
is_import: false,
do_not_complete: Yes,
+ _marker: PhantomData<&()>,
},
FileSymbol {
name: "a_mod",
@@ -643,6 +661,7 @@
is_assoc: false,
is_import: false,
do_not_complete: Yes,
+ _marker: PhantomData<&()>,
},
FileSymbol {
name: "b_mod",
@@ -675,6 +694,7 @@
is_assoc: false,
is_import: false,
do_not_complete: Yes,
+ _marker: PhantomData<&()>,
},
FileSymbol {
name: "define_struct",
@@ -709,6 +729,7 @@
is_assoc: false,
is_import: false,
do_not_complete: Yes,
+ _marker: PhantomData<&()>,
},
FileSymbol {
name: "generic_impl_fn",
@@ -743,6 +764,7 @@
is_assoc: true,
is_import: false,
do_not_complete: Yes,
+ _marker: PhantomData<&()>,
},
FileSymbol {
name: "impl_fn",
@@ -777,6 +799,7 @@
is_assoc: true,
is_import: false,
do_not_complete: Yes,
+ _marker: PhantomData<&()>,
},
FileSymbol {
name: "macro_rules_macro",
@@ -811,6 +834,7 @@
is_assoc: false,
is_import: false,
do_not_complete: Yes,
+ _marker: PhantomData<&()>,
},
FileSymbol {
name: "main",
@@ -843,6 +867,7 @@
is_assoc: false,
is_import: false,
do_not_complete: Yes,
+ _marker: PhantomData<&()>,
},
FileSymbol {
name: "really_define_struct",
@@ -877,6 +902,7 @@
is_assoc: false,
is_import: true,
do_not_complete: Yes,
+ _marker: PhantomData<&()>,
},
FileSymbol {
name: "trait_fn",
@@ -911,6 +937,7 @@
is_assoc: true,
is_import: false,
do_not_complete: Yes,
+ _marker: PhantomData<&()>,
},
],
),
@@ -954,6 +981,7 @@
is_assoc: false,
is_import: false,
do_not_complete: Yes,
+ _marker: PhantomData<&()>,
},
],
),
@@ -995,6 +1023,7 @@
is_assoc: false,
is_import: true,
do_not_complete: Yes,
+ _marker: PhantomData<&()>,
},
FileSymbol {
name: "IsThisJustATrait",
@@ -1029,6 +1058,7 @@
is_assoc: false,
is_import: true,
do_not_complete: Yes,
+ _marker: PhantomData<&()>,
},
FileSymbol {
name: "StructInModB",
@@ -1063,6 +1093,7 @@
is_assoc: false,
is_import: false,
do_not_complete: Yes,
+ _marker: PhantomData<&()>,
},
FileSymbol {
name: "SuperItemLikeMacro",
@@ -1097,6 +1128,7 @@
is_assoc: false,
is_import: true,
do_not_complete: Yes,
+ _marker: PhantomData<&()>,
},
FileSymbol {
name: "ThisStruct",
@@ -1131,6 +1163,7 @@
is_assoc: false,
is_import: true,
do_not_complete: Yes,
+ _marker: PhantomData<&()>,
},
],
),
diff --git a/crates/ide-db/src/test_data/test_symbols_exclude_imports.txt b/crates/ide-db/src/test_data/test_symbols_exclude_imports.txt
index dc512fe1b8..6f5f8f889c 100644
--- a/crates/ide-db/src/test_data/test_symbols_exclude_imports.txt
+++ b/crates/ide-db/src/test_data/test_symbols_exclude_imports.txt
@@ -32,5 +32,6 @@
is_assoc: false,
is_import: false,
do_not_complete: Yes,
+ _marker: PhantomData<&()>,
},
]
diff --git a/crates/ide-db/src/test_data/test_symbols_with_imports.txt b/crates/ide-db/src/test_data/test_symbols_with_imports.txt
index 0b2522775d..5d3fe4d265 100644
--- a/crates/ide-db/src/test_data/test_symbols_with_imports.txt
+++ b/crates/ide-db/src/test_data/test_symbols_with_imports.txt
@@ -32,6 +32,7 @@
is_assoc: false,
is_import: false,
do_not_complete: Yes,
+ _marker: PhantomData<&()>,
},
FileSymbol {
name: "Foo",
@@ -66,5 +67,6 @@
is_assoc: false,
is_import: true,
do_not_complete: Yes,
+ _marker: PhantomData<&()>,
},
]
diff --git a/crates/ide-diagnostics/src/handlers/incorrect_case.rs b/crates/ide-diagnostics/src/handlers/incorrect_case.rs
index fdc426c32c..4a12c5a26d 100644
--- a/crates/ide-diagnostics/src/handlers/incorrect_case.rs
+++ b/crates/ide-diagnostics/src/handlers/incorrect_case.rs
@@ -1000,7 +1000,8 @@ mod OtherBadCase;
// ^^^^^^^^^^^^ 💡 error: Module `OtherBadCase` should have snake_case name, e.g. `other_bad_case`
//- /BAD_CASE/OtherBadCase.rs
-#![deny(non_snake_case)]
+#![allow(non_snake_case)]
+#![deny(non_snake_case)] // The lint level has been overridden.
fn FOO() {}
// ^^^ 💡 error: Function `FOO` should have snake_case name, e.g. `foo`
diff --git a/crates/ide-diagnostics/src/handlers/missing_unsafe.rs b/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
index 59215f34a5..1abb50144d 100644
--- a/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
+++ b/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
@@ -296,8 +296,12 @@ fn main() {
#[rustc_deprecated_safe_2024]
fn set_var() {}
+#[rustc_deprecated_safe_2024(audit_that = "something")]
+fn set_var2() {}
+
fn main() {
set_var();
+ set_var2();
}
"#,
);
diff --git a/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/crates/ide-diagnostics/src/handlers/mutability_errors.rs
index 18280a4add..2887a32825 100644
--- a/crates/ide-diagnostics/src/handlers/mutability_errors.rs
+++ b/crates/ide-diagnostics/src/handlers/mutability_errors.rs
@@ -995,6 +995,10 @@ fn fn_once(mut x: impl FnOnce(u8) -> u8) -> u8 {
}
"#,
);
+ // FIXME: There should be no "unused variable" here, and there should be a mutability error,
+ // but our MIR infra is horribly broken and due to the order in which expressions are lowered
+ // there is no `StorageLive` for `x` in the closure (in fact, `x` should not even be a variable
+ // of the closure, the environment should be, but as I said, our MIR infra is horribly broken).
check_diagnostics(
r#"
//- minicore: copy, fn
@@ -1003,8 +1007,8 @@ fn f() {
|| {
|| {
let x = 2;
+ // ^ 💡 warn: unused variable
|| { || { x = 5; } }
- //^^^^^ 💡 error: cannot mutate immutable variable `x`
}
}
};
diff --git a/crates/ide-diagnostics/src/handlers/no_such_field.rs b/crates/ide-diagnostics/src/handlers/no_such_field.rs
index 0edab5e0b3..bcfe3a8aa5 100644
--- a/crates/ide-diagnostics/src/handlers/no_such_field.rs
+++ b/crates/ide-diagnostics/src/handlers/no_such_field.rs
@@ -102,7 +102,8 @@ fn missing_record_expr_field_fixes(
let indent = IndentLevel::from_node(last_field_syntax);
let mut new_field = new_field.to_string();
- if usage_file_id != def_file_id {
+ // FIXME: check submodule instead of FileId
+ if usage_file_id != def_file_id && !matches!(def_id, hir::VariantDef::Variant(_)) {
new_field = format!("pub(crate) {new_field}");
}
new_field = format!("\n{indent}{new_field}");
@@ -358,6 +359,34 @@ pub struct Foo {
}
#[test]
+ fn test_add_enum_variant_field_in_other_file_from_usage() {
+ check_fix(
+ r#"
+//- /main.rs
+mod foo;
+
+fn main() {
+ foo::Foo::Variant { bar: 3, $0baz: false};
+}
+//- /foo.rs
+pub enum Foo {
+ Variant {
+ bar: i32
+ }
+}
+"#,
+ r#"
+pub enum Foo {
+ Variant {
+ bar: i32,
+ baz: bool
+ }
+}
+"#,
+ )
+ }
+
+ #[test]
fn test_tuple_field_on_record_struct() {
check_no_fix(
r#"
diff --git a/crates/ide-diagnostics/src/handlers/unused_variables.rs b/crates/ide-diagnostics/src/handlers/unused_variables.rs
index 84e63acbc0..b7ec8fa53f 100644
--- a/crates/ide-diagnostics/src/handlers/unused_variables.rs
+++ b/crates/ide-diagnostics/src/handlers/unused_variables.rs
@@ -183,6 +183,61 @@ fn main2() {
}
#[test]
+ fn apply_last_lint_attribute_when_multiple_are_present() {
+ check_diagnostics(
+ r#"
+#![allow(unused_variables)]
+#![warn(unused_variables)]
+#![deny(unused_variables)]
+
+fn main() {
+ let x = 2;
+ //^ 💡 error: unused variable
+
+ #[deny(unused_variables)]
+ #[warn(unused_variables)]
+ #[allow(unused_variables)]
+ let y = 0;
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn prefer_closest_ancestor_lint_attribute() {
+ check_diagnostics(
+ r#"
+#![allow(unused_variables)]
+
+fn main() {
+ #![warn(unused_variables)]
+
+ #[deny(unused_variables)]
+ let x = 2;
+ //^ 💡 error: unused variable
+}
+
+#[warn(unused_variables)]
+fn main2() {
+ #[deny(unused_variables)]
+ let x = 2;
+ //^ 💡 error: unused variable
+}
+
+#[warn(unused_variables)]
+fn main3() {
+ let x = 2;
+ //^ 💡 warn: unused variable
+}
+
+fn main4() {
+ let x = 2;
+}
+"#,
+ );
+ }
+
+ #[test]
fn fix_unused_variable() {
check_fix(
r#"
diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs
index e001f4b3e4..2b8474c316 100644
--- a/crates/ide-diagnostics/src/lib.rs
+++ b/crates/ide-diagnostics/src/lib.rs
@@ -648,19 +648,13 @@ fn find_outline_mod_lint_severity(
let mod_def = sema.to_module_def(&mod_node)?;
let module_source_file = sema.module_definition_node(mod_def);
- let mut result = None;
let lint_groups = lint_groups(&diag.code, edition);
lint_attrs(
sema,
krate,
ast::AnyHasAttrs::cast(module_source_file.value).expect("SourceFile always has attrs"),
)
- .for_each(|(lint, severity)| {
- if lint_groups.contains(&lint) {
- result = Some(severity);
- }
- });
- result
+ .find_map(|(lint, severity)| lint_groups.contains(&lint).then_some(severity))
}
fn lint_severity_at(
@@ -687,7 +681,7 @@ fn lint_attrs(
krate: hir::Crate,
ancestor: ast::AnyHasAttrs,
) -> impl Iterator<Item = (SmolStr, Severity)> {
- sema.lint_attrs(krate, ancestor).map(|(lint_attr, lint)| {
+ sema.lint_attrs(krate, ancestor).rev().map(|(lint_attr, lint)| {
let severity = match lint_attr {
hir::LintAttr::Allow | hir::LintAttr::Expect => Severity::Allow,
hir::LintAttr::Warn => Severity::Warning,
diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs
index 071eacf660..5330b7eb99 100644
--- a/crates/ide/src/hover/tests.rs
+++ b/crates/ide/src/hover/tests.rs
@@ -11169,3 +11169,60 @@ fn foo() {
"#]],
);
}
+
+#[test]
+fn hover_trait_impl_shows_generic_args() {
+ // Single generic arg
+ check(
+ r#"
+trait Foo<T> {
+ fn foo(&self) {}
+}
+
+impl<T> Foo<()> for T {
+ fn fo$0o(&self) {}
+}
+
+fn bar() {
+ ().foo();
+}
+"#,
+ expect![[r#"
+ *foo*
+
+ ```rust
+ ra_test_fixture
+ ```
+
+ ```rust
+ impl<T> Foo<()> for T
+ fn foo(&self)
+ ```
+ "#]],
+ );
+
+ // Multiple generic args
+ check(
+ r#"
+trait Foo<A, B> {
+ fn foo(&self) {}
+}
+
+impl<T> Foo<i32, u64> for T {
+ fn fo$0o(&self) {}
+}
+"#,
+ expect![[r#"
+ *foo*
+
+ ```rust
+ ra_test_fixture
+ ```
+
+ ```rust
+ impl<T> Foo<i32, u64> for T
+ fn foo(&self)
+ ```
+ "#]],
+ );
+}
diff --git a/crates/ide/src/navigation_target.rs b/crates/ide/src/navigation_target.rs
index 67472507f6..29530ed02b 100644
--- a/crates/ide/src/navigation_target.rs
+++ b/crates/ide/src/navigation_target.rs
@@ -225,7 +225,7 @@ impl NavigationTarget {
}
}
-impl TryToNav for FileSymbol {
+impl<'db> TryToNav for FileSymbol<'db> {
fn try_to_nav(
&self,
sema: &Semantics<'_, RootDatabase>,
diff --git a/crates/ide/src/static_index.rs b/crates/ide/src/static_index.rs
index 30e8d62ea2..7749f8e2f2 100644
--- a/crates/ide/src/static_index.rs
+++ b/crates/ide/src/static_index.rs
@@ -10,7 +10,7 @@ use ide_db::{
documentation::Documentation,
famous_defs::FamousDefs,
};
-use syntax::{AstNode, SyntaxKind::*, SyntaxNode, SyntaxToken, T, TextRange};
+use syntax::{AstNode, SyntaxNode, SyntaxToken, TextRange};
use crate::navigation_target::UpmappingResult;
use crate::{
@@ -136,12 +136,12 @@ fn documentation_for_definition(
}
// FIXME: This is a weird function
-fn get_definitions(
- sema: &Semantics<'_, RootDatabase>,
+fn get_definitions<'db>(
+ sema: &Semantics<'db, RootDatabase>,
token: SyntaxToken,
-) -> Option<ArrayVec<Definition, 2>> {
+) -> Option<ArrayVec<(Definition, Option<hir::GenericSubstitution<'db>>), 2>> {
for token in sema.descend_into_macros_exact(token) {
- let def = IdentClass::classify_token(sema, &token).map(IdentClass::definitions_no_ops);
+ let def = IdentClass::classify_token(sema, &token).map(IdentClass::definitions);
if let Some(defs) = def
&& !defs.is_empty()
{
@@ -226,12 +226,6 @@ impl StaticIndex<'_> {
show_drop_glue: true,
minicore: MiniCore::default(),
};
- let tokens = tokens.filter(|token| {
- matches!(
- token.kind(),
- IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] | T![super] | T![crate] | T![Self]
- )
- });
let mut result = StaticIndexedFile { file_id, inlay_hints, folds, tokens: vec![] };
let mut add_token = |def: Definition, range: TextRange, scope_node: &SyntaxNode| {
@@ -291,9 +285,9 @@ impl StaticIndex<'_> {
let range = token.text_range();
let node = token.parent().unwrap();
match hir::attach_db(self.db, || get_definitions(&sema, token.clone())) {
- Some(it) => {
- for i in it {
- add_token(i, range, &node);
+ Some(defs) => {
+ for (def, _) in defs {
+ add_token(def, range, &node);
}
}
None => continue,
diff --git a/crates/parser/src/lib.rs b/crates/parser/src/lib.rs
index 81cdc18801..4478bf4e37 100644
--- a/crates/parser/src/lib.rs
+++ b/crates/parser/src/lib.rs
@@ -24,9 +24,9 @@
#[cfg(not(feature = "in-rust-tree"))]
extern crate ra_ap_rustc_lexer as rustc_lexer;
#[cfg(feature = "in-rust-tree")]
-extern crate rustc_lexer;
-#[cfg(feature = "in-rust-tree")]
extern crate rustc_driver as _;
+#[cfg(feature = "in-rust-tree")]
+extern crate rustc_lexer;
mod event;
mod frontmatter;
diff --git a/crates/proc-macro-api/src/lib.rs b/crates/proc-macro-api/src/lib.rs
index 8e1faca00a..85b250eddf 100644
--- a/crates/proc-macro-api/src/lib.rs
+++ b/crates/proc-macro-api/src/lib.rs
@@ -11,7 +11,6 @@
feature(proc_macro_internals, proc_macro_diagnostic, proc_macro_span)
)]
#![allow(internal_features)]
-
#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))]
#[cfg(feature = "in-rust-tree")]
diff --git a/crates/project-model/src/lib.rs b/crates/project-model/src/lib.rs
index 0d89e13ed3..3414b52d45 100644
--- a/crates/project-model/src/lib.rs
+++ b/crates/project-model/src/lib.rs
@@ -17,7 +17,6 @@
// It's useful to refer to code that is private in doc comments.
#![allow(rustdoc::private_intra_doc_links)]
-
#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))]
#[cfg(feature = "in-rust-tree")]
diff --git a/crates/rust-analyzer/src/cli/scip.rs b/crates/rust-analyzer/src/cli/scip.rs
index fbf3082e1b..271d2507bc 100644
--- a/crates/rust-analyzer/src/cli/scip.rs
+++ b/crates/rust-analyzer/src/cli/scip.rs
@@ -604,6 +604,29 @@ pub mod example_mod {
}
#[test]
+ fn operator_overload() {
+ check_symbol(
+ r#"
+//- minicore: add
+//- /workspace/lib.rs crate:main
+use core::ops::AddAssign;
+
+struct S;
+
+impl AddAssign for S {
+ fn add_assign(&mut self, _rhs: Self) {}
+}
+
+fn main() {
+ let mut s = S;
+ s +=$0 S;
+}
+"#,
+ "rust-analyzer cargo main . impl#[S][`AddAssign<Self>`]add_assign().",
+ );
+ }
+
+ #[test]
fn symbol_for_trait() {
check_symbol(
r#"
diff --git a/crates/rust-analyzer/src/lib.rs b/crates/rust-analyzer/src/lib.rs
index a6cd431392..971ae2a601 100644
--- a/crates/rust-analyzer/src/lib.rs
+++ b/crates/rust-analyzer/src/lib.rs
@@ -19,7 +19,7 @@ extern crate ra_ap_rustc_type_ir as rustc_type_ir;
/// Any toolchain less than this version will likely not work with rust-analyzer built from this revision.
pub const MINIMUM_SUPPORTED_TOOLCHAIN_VERSION: semver::Version = semver::Version {
major: 1,
- minor: 90,
+ minor: 78,
patch: 0,
pre: semver::Prerelease::EMPTY,
build: semver::BuildMetadata::EMPTY,
diff --git a/crates/rust-analyzer/src/lsp/capabilities.rs b/crates/rust-analyzer/src/lsp/capabilities.rs
index f94e7486ff..d6a694be91 100644
--- a/crates/rust-analyzer/src/lsp/capabilities.rs
+++ b/crates/rust-analyzer/src/lsp/capabilities.rs
@@ -37,7 +37,11 @@ pub fn server_capabilities(config: &Config) -> ServerCapabilities {
change: Some(TextDocumentSyncKind::INCREMENTAL),
will_save: None,
will_save_wait_until: None,
- save: Some(SaveOptions::default().into()),
+ save: if config.caps().did_save_text_document_dynamic_registration() {
+ None
+ } else {
+ Some(SaveOptions::default().into())
+ },
})),
hover_provider: Some(HoverProviderCapability::Simple(true)),
completion_provider: Some(CompletionOptions {
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 77dedf1272..1a1c0182f8 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -437,11 +437,17 @@ impl GlobalState {
}
}
Event::Flycheck(message) => {
- let _p = tracing::info_span!("GlobalState::handle_event/flycheck").entered();
- self.handle_flycheck_msg(message);
+ let mut cargo_finished = false;
+ self.handle_flycheck_msg(message, &mut cargo_finished);
// Coalesce many flycheck updates into a single loop turn
while let Ok(message) = self.flycheck_receiver.try_recv() {
- self.handle_flycheck_msg(message);
+ self.handle_flycheck_msg(message, &mut cargo_finished);
+ }
+ if cargo_finished {
+ self.send_request::<lsp_types::request::WorkspaceDiagnosticRefresh>(
+ (),
+ |_, _| (),
+ );
}
}
Event::TestResult(message) => {
@@ -1109,7 +1115,7 @@ impl GlobalState {
}
}
- fn handle_flycheck_msg(&mut self, message: FlycheckMessage) {
+ fn handle_flycheck_msg(&mut self, message: FlycheckMessage, cargo_finished: &mut bool) {
match message {
FlycheckMessage::AddDiagnostic {
id,
@@ -1167,6 +1173,7 @@ impl GlobalState {
flycheck::Progress::DidCheckCrate(target) => (Progress::Report, Some(target)),
flycheck::Progress::DidCancel => {
self.last_flycheck_error = None;
+ *cargo_finished = true;
(Progress::End, None)
}
flycheck::Progress::DidFailToRestart(err) => {
@@ -1177,6 +1184,7 @@ impl GlobalState {
flycheck::Progress::DidFinish(result) => {
self.last_flycheck_error =
result.err().map(|err| format!("cargo check failed to start: {err}"));
+ *cargo_finished = true;
(Progress::End, None)
}
};
diff --git a/crates/rust-analyzer/tests/slow-tests/support.rs b/crates/rust-analyzer/tests/slow-tests/support.rs
index b1b428e706..195ad226ae 100644
--- a/crates/rust-analyzer/tests/slow-tests/support.rs
+++ b/crates/rust-analyzer/tests/slow-tests/support.rs
@@ -48,6 +48,7 @@ impl Project<'_> {
"enable": false,
},
},
+ "checkOnSave": false,
"procMacro": {
"enable": false,
}
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs
index dba39204e3..b2904ce3c0 100644
--- a/crates/syntax/src/ast/make.rs
+++ b/crates/syntax/src/ast/make.rs
@@ -658,7 +658,7 @@ pub fn expr_if(
};
expr_from_text(&format!("if {condition} {then_branch} {else_branch}"))
}
-pub fn expr_for_loop(pat: ast::Pat, expr: ast::Expr, block: ast::BlockExpr) -> ast::Expr {
+pub fn expr_for_loop(pat: ast::Pat, expr: ast::Expr, block: ast::BlockExpr) -> ast::ForExpr {
expr_from_text(&format!("for {pat} in {expr} {block}"))
}
@@ -1016,7 +1016,19 @@ pub fn item_static(
}
pub fn unnamed_param(ty: ast::Type) -> ast::Param {
- ast_from_text(&format!("fn f({ty}) {{ }}"))
+ quote! {
+ Param {
+ #ty
+ }
+ }
+}
+
+pub fn untyped_param(pat: ast::Pat) -> ast::Param {
+ quote! {
+ Param {
+ #pat
+ }
+ }
}
pub fn param(pat: ast::Pat, ty: ast::Type) -> ast::Param {
@@ -1456,3 +1468,86 @@ pub mod tokens {
}
}
}
+
+#[cfg(test)]
+mod tests {
+ use expect_test::expect;
+
+ use super::*;
+
+ #[track_caller]
+ fn check(node: impl AstNode, expect: expect_test::Expect) {
+ let node_debug = format!("{:#?}", node.syntax());
+ expect.assert_eq(&node_debug);
+ }
+
+ #[test]
+ fn test_unnamed_param() {
+ check(
+ unnamed_param(ty("Vec")),
+ expect![[r#"
+ "#]],
+ );
+
+ check(
+ unnamed_param(ty("Vec<T>")),
+ expect![[r#"
+ "#]],
+ );
+ }
+
+ #[test]
+ fn test_untyped_param() {
+ check(
+ untyped_param(path_pat(ext::ident_path("name"))),
+ expect![[r#"
+ "#]],
+ );
+
+ check(
+ untyped_param(
+ range_pat(
+ Some(path_pat(ext::ident_path("start"))),
+ Some(path_pat(ext::ident_path("end"))),
+ )
+ .into(),
+ ),
+ expect![[r#"
+ "#]],
+ );
+ }
+}
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs
index 901d17bb14..b872221bf7 100644
--- a/crates/syntax/src/ast/node_ext.rs
+++ b/crates/syntax/src/ast/node_ext.rs
@@ -813,13 +813,16 @@ pub enum TypeBoundKind {
}
impl ast::TypeBound {
- pub fn kind(&self) -> TypeBoundKind {
+ pub fn kind(&self) -> Option<TypeBoundKind> {
if let Some(path_type) = support::children(self.syntax()).next() {
- TypeBoundKind::PathType(self.for_binder(), path_type)
+ Some(TypeBoundKind::PathType(self.for_binder(), path_type))
+ } else if let Some(for_binder) = support::children::<ast::ForType>(&self.syntax).next() {
+ let Some(ast::Type::PathType(path_type)) = for_binder.ty() else { return None };
+ Some(TypeBoundKind::PathType(for_binder.for_binder(), path_type))
} else if let Some(args) = self.use_bound_generic_args() {
- TypeBoundKind::Use(args)
+ Some(TypeBoundKind::Use(args))
} else if let Some(lifetime) = self.lifetime() {
- TypeBoundKind::Lifetime(lifetime)
+ Some(TypeBoundKind::Lifetime(lifetime))
} else {
unreachable!()
}
diff --git a/crates/syntax/src/ast/syntax_factory/constructors.rs b/crates/syntax/src/ast/syntax_factory/constructors.rs
index 9695523921..aca6fcfb2e 100644
--- a/crates/syntax/src/ast/syntax_factory/constructors.rs
+++ b/crates/syntax/src/ast/syntax_factory/constructors.rs
@@ -71,6 +71,188 @@ impl SyntaxFactory {
ast
}
+ pub fn path_from_text(&self, text: &str) -> ast::Path {
+ make::path_from_text(text).clone_for_update()
+ }
+
+ pub fn expr_field(&self, receiver: ast::Expr, field: &str) -> ast::FieldExpr {
+ let ast::Expr::FieldExpr(ast) =
+ make::expr_field(receiver.clone(), field).clone_for_update()
+ else {
+ unreachable!()
+ };
+
+ if let Some(mut mapping) = self.mappings() {
+ let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+ builder.map_node(receiver.syntax().clone(), ast.expr().unwrap().syntax().clone());
+ builder.finish(&mut mapping);
+ }
+
+ ast
+ }
+
+ pub fn impl_trait(
+ &self,
+ attrs: impl IntoIterator<Item = ast::Attr>,
+ is_unsafe: bool,
+ trait_gen_params: Option<ast::GenericParamList>,
+ trait_gen_args: Option<ast::GenericArgList>,
+ type_gen_params: Option<ast::GenericParamList>,
+ type_gen_args: Option<ast::GenericArgList>,
+ is_negative: bool,
+ path_type: ast::Type,
+ ty: ast::Type,
+ trait_where_clause: Option<ast::WhereClause>,
+ ty_where_clause: Option<ast::WhereClause>,
+ body: Option<ast::AssocItemList>,
+ ) -> ast::Impl {
+ let (attrs, attrs_input) = iterator_input(attrs);
+ let ast = make::impl_trait(
+ attrs,
+ is_unsafe,
+ trait_gen_params.clone(),
+ trait_gen_args.clone(),
+ type_gen_params.clone(),
+ type_gen_args.clone(),
+ is_negative,
+ path_type.clone(),
+ ty.clone(),
+ trait_where_clause.clone(),
+ ty_where_clause.clone(),
+ body.clone(),
+ )
+ .clone_for_update();
+
+ if let Some(mut mapping) = self.mappings() {
+ let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+ builder.map_children(attrs_input, ast.attrs().map(|attr| attr.syntax().clone()));
+ if let Some(trait_gen_params) = trait_gen_params {
+ builder.map_node(
+ trait_gen_params.syntax().clone(),
+ ast.generic_param_list().unwrap().syntax().clone(),
+ );
+ }
+ builder.map_node(path_type.syntax().clone(), ast.trait_().unwrap().syntax().clone());
+ builder.map_node(ty.syntax().clone(), ast.self_ty().unwrap().syntax().clone());
+ if let Some(ty_where_clause) = ty_where_clause {
+ builder.map_node(
+ ty_where_clause.syntax().clone(),
+ ast.where_clause().unwrap().syntax().clone(),
+ );
+ }
+ if let Some(body) = body {
+ builder.map_node(
+ body.syntax().clone(),
+ ast.assoc_item_list().unwrap().syntax().clone(),
+ );
+ }
+ builder.finish(&mut mapping);
+ }
+
+ ast
+ }
+
+ pub fn ty_alias(
+ &self,
+ attrs: impl IntoIterator<Item = ast::Attr>,
+ ident: &str,
+ generic_param_list: Option<ast::GenericParamList>,
+ type_param_bounds: Option<ast::TypeParam>,
+ where_clause: Option<ast::WhereClause>,
+ assignment: Option<(ast::Type, Option<ast::WhereClause>)>,
+ ) -> ast::TypeAlias {
+ let (attrs, attrs_input) = iterator_input(attrs);
+ let ast = make::ty_alias(
+ attrs,
+ ident,
+ generic_param_list.clone(),
+ type_param_bounds.clone(),
+ where_clause.clone(),
+ assignment.clone(),
+ )
+ .clone_for_update();
+
+ if let Some(mut mapping) = self.mappings() {
+ let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+ builder.map_children(attrs_input, ast.attrs().map(|attr| attr.syntax().clone()));
+ if let Some(generic_param_list) = generic_param_list {
+ builder.map_node(
+ generic_param_list.syntax().clone(),
+ ast.generic_param_list().unwrap().syntax().clone(),
+ );
+ }
+ if let Some(type_param_bounds) = type_param_bounds {
+ builder.map_node(
+ type_param_bounds.syntax().clone(),
+ ast.type_bound_list().unwrap().syntax().clone(),
+ );
+ }
+ if let Some(where_clause) = where_clause {
+ builder.map_node(
+ where_clause.syntax().clone(),
+ ast.where_clause().unwrap().syntax().clone(),
+ );
+ }
+ if let Some((ty, _)) = assignment {
+ builder.map_node(ty.syntax().clone(), ast.ty().unwrap().syntax().clone());
+ }
+ builder.finish(&mut mapping);
+ }
+
+ ast
+ }
+
+ pub fn param_list(
+ &self,
+ self_param: Option<ast::SelfParam>,
+ params: impl IntoIterator<Item = ast::Param>,
+ ) -> ast::ParamList {
+ let (params, input) = iterator_input(params);
+ let ast = make::param_list(self_param.clone(), params).clone_for_update();
+
+ if let Some(mut mapping) = self.mappings() {
+ let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+ if let Some(self_param) = self_param
+ && let Some(new_self_param) = ast.self_param()
+ {
+ builder.map_node(self_param.syntax().clone(), new_self_param.syntax().clone());
+ }
+ builder.map_children(input, ast.params().map(|p| p.syntax().clone()));
+ builder.finish(&mut mapping);
+ }
+
+ ast
+ }
+
+ pub fn const_param(&self, name: ast::Name, ty: ast::Type) -> ast::ConstParam {
+ let ast = make::const_param(name.clone(), ty.clone()).clone_for_update();
+
+ if let Some(mut mapping) = self.mappings() {
+ let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+ builder.map_node(name.syntax().clone(), ast.name().unwrap().syntax().clone());
+ builder.map_node(ty.syntax().clone(), ast.ty().unwrap().syntax().clone());
+ builder.finish(&mut mapping);
+ }
+
+ ast
+ }
+
+ pub fn generic_param_list(
+ &self,
+ params: impl IntoIterator<Item = ast::GenericParam>,
+ ) -> ast::GenericParamList {
+ let (params, input) = iterator_input(params);
+ let ast = make::generic_param_list(params).clone_for_update();
+
+ if let Some(mut mapping) = self.mappings() {
+ let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+ builder.map_children(input, ast.generic_params().map(|p| p.syntax().clone()));
+ builder.finish(&mut mapping);
+ }
+
+ ast
+ }
+
pub fn path_segment(&self, name_ref: ast::NameRef) -> ast::PathSegment {
let ast = make::path_segment(name_ref.clone()).clone_for_update();
@@ -671,6 +853,26 @@ impl SyntaxFactory {
ast
}
+ pub fn expr_for_loop(
+ &self,
+ pat: ast::Pat,
+ iterable: ast::Expr,
+ body: ast::BlockExpr,
+ ) -> ast::ForExpr {
+ let ast =
+ make::expr_for_loop(pat.clone(), iterable.clone(), body.clone()).clone_for_update();
+
+ if let Some(mut mapping) = self.mappings() {
+ let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+ builder.map_node(pat.syntax().clone(), ast.pat().unwrap().syntax().clone());
+ builder.map_node(iterable.syntax().clone(), ast.iterable().unwrap().syntax().clone());
+ builder.map_node(body.syntax().clone(), ast.loop_body().unwrap().syntax().clone());
+ builder.finish(&mut mapping);
+ }
+
+ ast
+ }
+
pub fn expr_let(&self, pattern: ast::Pat, expr: ast::Expr) -> ast::LetExpr {
let ast = make::expr_let(pattern.clone(), expr.clone()).clone_for_update();
@@ -1272,6 +1474,23 @@ impl SyntaxFactory {
ast
}
+ pub fn assoc_item_list(
+ &self,
+ items: impl IntoIterator<Item = ast::AssocItem>,
+ ) -> ast::AssocItemList {
+ let (items, input) = iterator_input(items);
+ let items_vec: Vec<_> = items.into_iter().collect();
+ let ast = make::assoc_item_list(Some(items_vec)).clone_for_update();
+
+ if let Some(mut mapping) = self.mappings() {
+ let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+ builder.map_children(input, ast.assoc_items().map(|item| item.syntax().clone()));
+ builder.finish(&mut mapping);
+ }
+
+ ast
+ }
+
pub fn attr_outer(&self, meta: ast::Meta) -> ast::Attr {
let ast = make::attr_outer(meta.clone()).clone_for_update();
diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs
index 0fe17e3075..b7c09391ec 100644
--- a/crates/test-utils/src/minicore.rs
+++ b/crates/test-utils/src/minicore.rs
@@ -34,7 +34,8 @@
//! eq: sized
//! error: fmt
//! fmt: option, result, transmute, coerce_unsized, copy, clone, derive
-//! fmt_before_1_89_0: fmt
+//! fmt_before_1_93_0: fmt
+//! fmt_before_1_89_0: fmt_before_1_93_0
//! fn: sized, tuple
//! from: sized, result
//! future: pin
@@ -1259,6 +1260,7 @@ pub mod fmt {
Unknown,
}
+ // region:fmt_before_1_93_0
#[lang = "format_count"]
pub enum Count {
Is(usize),
@@ -1288,6 +1290,7 @@ pub mod fmt {
Placeholder { position, fill, align, flags, precision, width }
}
}
+ // endregion:fmt_before_1_93_0
// region:fmt_before_1_89_0
#[lang = "format_unsafe_arg"]
@@ -1303,6 +1306,7 @@ pub mod fmt {
// endregion:fmt_before_1_89_0
}
+ // region:fmt_before_1_93_0
#[derive(Copy, Clone)]
#[lang = "format_arguments"]
pub struct Arguments<'a> {
@@ -1341,6 +1345,14 @@ pub mod fmt {
}
// endregion:!fmt_before_1_89_0
+ pub fn from_str_nonconst(s: &'static str) -> Arguments<'a> {
+ Self::from_str(s)
+ }
+
+ pub const fn from_str(s: &'static str) -> Arguments<'a> {
+ Arguments { pieces: &[s], fmt: None, args: &[] }
+ }
+
pub const fn as_str(&self) -> Option<&'static str> {
match (self.pieces, self.args) {
([], []) => Some(""),
@@ -1349,6 +1361,41 @@ pub mod fmt {
}
}
}
+ // endregion:fmt_before_1_93_0
+
+ // region:!fmt_before_1_93_0
+ #[lang = "format_arguments"]
+ #[derive(Copy, Clone)]
+ pub struct Arguments<'a> {
+ // This is a non-faithful representation of `core::fmt::Arguments`, because the real one
+ // is too complex for minicore.
+ message: Option<&'a str>,
+ }
+
+ impl<'a> Arguments<'a> {
+ pub unsafe fn new<const N: usize, const M: usize>(
+ _template: &'a [u8; N],
+ _args: &'a [rt::Argument<'a>; M],
+ ) -> Arguments<'a> {
+ Arguments { message: None }
+ }
+
+ pub fn from_str_nonconst(s: &'static str) -> Arguments<'a> {
+ Arguments { message: Some(s) }
+ }
+
+ pub const fn from_str(s: &'static str) -> Arguments<'a> {
+ Arguments { message: Some(s) }
+ }
+
+ pub fn as_str(&self) -> Option<&'static str> {
+ match self.message {
+ Some(s) => unsafe { Some(&*(s as *const str)) },
+ None => None,
+ }
+ }
+ }
+ // endregion:!fmt_before_1_93_0
// region:derive
pub(crate) mod derive {
@@ -1817,7 +1864,7 @@ mod panicking {
#[lang = "panic"]
pub const fn panic(expr: &'static str) -> ! {
- panic_fmt(crate::fmt::Arguments::new_const(&[expr]))
+ panic_fmt(crate::fmt::Arguments::from_str(expr))
}
}
// endregion:panic
diff --git a/editors/code/package-lock.json b/editors/code/package-lock.json
index d49d19fbe1..00d83e9068 100644
--- a/editors/code/package-lock.json
+++ b/editors/code/package-lock.json
@@ -1486,6 +1486,7 @@
"integrity": "sha512-4gbs64bnbSzu4FpgMiQ1A+D+urxkoJk/kqlDJ2W//5SygaEiAP2B4GoS7TEdxgwol2el03gckFV9lJ4QOMiiHg==",
"dev": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"@typescript-eslint/scope-manager": "8.25.0",
"@typescript-eslint/types": "8.25.0",
@@ -1869,6 +1870,7 @@
"integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==",
"dev": true,
"license": "MIT",
+ "peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -2838,6 +2840,7 @@
"resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",
"integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
"license": "ISC",
+ "peer": true,
"engines": {
"node": ">=12"
}
@@ -3319,6 +3322,7 @@
"integrity": "sha512-KjeihdFqTPhOMXTt7StsDxriV4n66ueuF/jfPNC3j/lduHwr/ijDwJMsF+wyMJethgiKi5wniIE243vi07d3pg==",
"dev": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.12.1",
@@ -4406,6 +4410,7 @@
"resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz",
"integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==",
"license": "MIT",
+ "peer": true,
"bin": {
"jiti": "lib/jiti-cli.mjs"
}
@@ -4508,25 +4513,25 @@
}
},
"node_modules/jsonwebtoken/node_modules/jwa": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
- "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==",
+ "version": "1.4.2",
+ "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz",
+ "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "buffer-equal-constant-time": "1.0.1",
+ "buffer-equal-constant-time": "^1.0.1",
"ecdsa-sig-formatter": "1.0.11",
"safe-buffer": "^5.0.1"
}
},
"node_modules/jsonwebtoken/node_modules/jws": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
- "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.3.tgz",
+ "integrity": "sha512-byiJ0FLRdLdSVSReO/U4E7RoEyOCKnEnEPMjq3HxWtvzLsV08/i5RQKsFVNkCldrCaPr2vDNAOMsfs8T/Hze7g==",
"dev": true,
"license": "MIT",
"dependencies": {
- "jwa": "^1.4.1",
+ "jwa": "^1.4.2",
"safe-buffer": "^5.0.1"
}
},
@@ -4544,25 +4549,25 @@
}
},
"node_modules/jwa": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz",
- "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==",
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz",
+ "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "buffer-equal-constant-time": "1.0.1",
+ "buffer-equal-constant-time": "^1.0.1",
"ecdsa-sig-formatter": "1.0.11",
"safe-buffer": "^5.0.1"
}
},
"node_modules/jws": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz",
- "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==",
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz",
+ "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "jwa": "^2.0.0",
+ "jwa": "^2.0.1",
"safe-buffer": "^5.0.1"
}
},
@@ -6673,6 +6678,7 @@
"integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==",
"dev": true,
"license": "Apache-2.0",
+ "peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
diff --git a/rust-version b/rust-version
index 7a84872f26..dcf82c94aa 100644
--- a/rust-version
+++ b/rust-version
@@ -1 +1 @@
-dfe1b8c97bcde283102f706d5dcdc3649e5e12e3
+0208ee09be465f69005a7a12c28d5eccac7d5f34
diff --git a/triagebot.toml b/triagebot.toml
index c9862495bc..ac4efd0a24 100644
--- a/triagebot.toml
+++ b/triagebot.toml
@@ -25,6 +25,3 @@ labels = ["has-merge-commits", "S-waiting-on-author"]
# Canonicalize issue numbers to avoid closing the wrong issue when upstreaming this subtree
[canonicalize-issue-links]
-
-# Prevents mentions in commits to avoid users being spammed
-[no-mentions]