mirror of
https://github.com/rust-lang/rust.git
synced 2026-04-27 18:57:42 +03:00
Auto merge of #150015 - lnicola:sync-from-ra, r=lnicola
`rust-analyzer` subtree update Subtree update of `rust-analyzer` to https://github.com/rust-lang/rust-analyzer/commit/3d78b3f9e00dd8ca76ea31f01edbf93d41da98bb. Created using https://github.com/rust-lang/josh-sync. r? `@ghost`
This commit is contained in:
@@ -64,6 +64,28 @@ fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
||||
};
|
||||
}
|
||||
|
||||
/// # 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;
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -8,12 +8,12 @@
|
||||
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]
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
//! representation.
|
||||
|
||||
mod asm;
|
||||
mod format_args;
|
||||
mod generics;
|
||||
mod path;
|
||||
|
||||
@@ -19,7 +20,7 @@
|
||||
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 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 @@
|
||||
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 @@ pub fn new(
|
||||
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 @@ fn lower_type_bound(
|
||||
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 @@ fn maybe_collect_expr(&mut self, expr: ast::Expr) -> Option<ExprId> {
|
||||
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 @@ fn maybe_collect_expr(&mut self, expr: ast::Expr) -> Option<ExprId> {
|
||||
}
|
||||
}
|
||||
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 closure_kind = if this.is_lowering_coroutine {
|
||||
let movability = if e.static_token().is_some() {
|
||||
Movability::Static
|
||||
let awaitable = if e.async_token().is_some() {
|
||||
Awaitable::Yes
|
||||
} else {
|
||||
Movability::Movable
|
||||
Awaitable::No("non-async 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 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
|
||||
} else {
|
||||
Movability::Movable
|
||||
};
|
||||
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_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 @@ fn maybe_collect_expr(&mut self, expr: ast::Expr) -> Option<ExprId> {
|
||||
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 @@ fn collect_tuple(
|
||||
}
|
||||
}
|
||||
|
||||
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 @@ fn collect_as_a_binding_owner_bad(
|
||||
/// 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 @@ fn collect_for_loop(&mut self, syntax_ptr: AstPtr<ast::Expr>, e: ast::ForExpr) -
|
||||
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 @@ fn collect_try_operator(&mut self, syntax_ptr: AstPtr<ast::Expr>, e: ast::TryExp
|
||||
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 @@ fn collect_try_operator(&mut self, syntax_ptr: AstPtr<ast::Expr>, e: ast::TryExp
|
||||
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 @@ fn collect_block_(
|
||||
) -> 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 @@ fn collect_pat(&mut self, pat: ast::Pat, binding_list: &mut BindingList) -> PatI
|
||||
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 @@ fn with_opt_labeled_rib<T>(
|
||||
}
|
||||
// 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 @@ fn expand_macros_to_string(&mut self, expr: ast::Expr) -> Option<(ast::String, b
|
||||
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 @@ fn lang_path(&self, lang: Option<impl Into<LangItemTarget>>) -> Option<Path> {
|
||||
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 @@ fn alloc_binding(
|
||||
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,
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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,
|
||||
@@ -343,6 +343,59 @@ fn main() {
|
||||
.assert_eq(&body.pretty_print(&db, def, Edition::CURRENT))
|
||||
}
|
||||
|
||||
#[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(
|
||||
@@ -382,27 +435,16 @@ pub(crate) fn new(message: impl Into<core::fmt::Arguments>) -> 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,
|
||||
)
|
||||
}
|
||||
},
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
),
|
||||
}"#]],
|
||||
);
|
||||
|
||||
@@ -197,3 +197,15 @@ fn allowed3<Param[0], Param[1]>(Param[1])
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn regression_21138() {
|
||||
lower_and_print(
|
||||
r#"
|
||||
fn foo(v: for<'a> Trait1 + Trait2) {}
|
||||
"#,
|
||||
expect![[r#"
|
||||
fn foo(dyn for<'a> Trait1 + Trait2) {...}
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
use rustc_hash::FxHashSet;
|
||||
|
||||
use crate::{
|
||||
FindPathConfig, ModuleDefId, ModuleIdLt,
|
||||
FindPathConfig, ModuleDefId, ModuleId,
|
||||
db::DefDatabase,
|
||||
item_scope::ItemInNs,
|
||||
nameres::DefMap,
|
||||
@@ -24,7 +24,7 @@
|
||||
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;
|
||||
|
||||
@@ -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 @@ fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
|
||||
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);
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
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()
|
||||
|
||||
@@ -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 @@ pub(crate) fn local_def_map(self, db: &'db dyn DefDatabase) -> (&'db DefMap, &'d
|
||||
}
|
||||
}
|
||||
|
||||
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 @@ pub fn name(self, db: &'db dyn DefDatabase) -> Option<Name> {
|
||||
|
||||
/// 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 {
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
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 @@ pub fn parent(&self) -> Option<ModuleId> {
|
||||
|
||||
/// 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 @@ pub(crate) fn resolve_path_locally(
|
||||
///
|
||||
/// 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 @@ fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
}
|
||||
}
|
||||
|
||||
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:#?}"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -881,7 +881,7 @@ fn append_expr_scope<'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);
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
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 @@ pub(crate) fn is_visible_from_other_crate(self) -> bool {
|
||||
}
|
||||
|
||||
#[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 @@ pub fn is_visible_from<'db>(
|
||||
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 @@ pub(crate) fn is_visible_from_def_map<'db>(
|
||||
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
|
||||
|
||||
@@ -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),
|
||||
),
|
||||
}
|
||||
};
|
||||
|
||||
@@ -43,6 +43,7 @@ fn ty(&self, ctx: &mut InferenceContext<'_, 'db>) -> Ty<'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 @@ fn restrict_precision_for_unsafe(&mut self) {
|
||||
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");
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
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 {
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
|
||||
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 @@ pub fn for_crate(db: &'db dyn HirDatabase, krate: Crate) -> Self {
|
||||
}
|
||||
|
||||
#[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 @@ pub fn for_self_ty(&self, self_ty: &SimplifiedType) -> &[ImplId] {
|
||||
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 @@ pub fn for_crate(db: &'db dyn HirDatabase, krate: Crate) -> Arc<Self> {
|
||||
}
|
||||
|
||||
#[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 @@ pub fn for_block(db: &'db dyn HirDatabase, block: BlockIdLt<'db>) -> Option<Box<
|
||||
}
|
||||
|
||||
#[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 @@ pub fn for_self_ty(&self, self_ty: &SimplifiedType, mut callback: impl FnMut(&[I
|
||||
}
|
||||
}
|
||||
|
||||
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 @@ pub fn for_each_crate_and_block_trait_and_type<'db>(
|
||||
// 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)
|
||||
|
||||
@@ -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 @@ pub fn projected_ty(
|
||||
|
||||
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),
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -722,6 +722,7 @@ fn projected_ty(&self, ty: Ty<'db>, proj: PlaceElem<'db>) -> Ty<'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);
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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: () }
|
||||
|
||||
@@ -1104,14 +1104,7 @@ fn variances_of(self, def_id: Self::DefId) -> Self::VariancesOf {
|
||||
|
||||
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.
|
||||
//
|
||||
|
||||
@@ -177,45 +177,52 @@ fn fetch_eligible_assoc_item(
|
||||
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 @@ fn is_transmutable(
|
||||
_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(
|
||||
|
||||
@@ -226,6 +226,62 @@ fn debug(_: &dyn Foo) {}
|
||||
|
||||
impl Foo for i32 {}
|
||||
|
||||
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);
|
||||
}"#,
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
// };
|
||||
|
||||
@@ -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(' ')?;
|
||||
|
||||
@@ -590,7 +590,7 @@ pub fn nearest_non_block_module(self, db: &dyn HirDatabase) -> 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 @@ pub fn all_for_type<'db>(
|
||||
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() {
|
||||
|
||||
@@ -267,7 +267,7 @@ pub fn lint_attrs(
|
||||
&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();
|
||||
|
||||
@@ -226,7 +226,7 @@ fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, file_id: Hi
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 @@
|
||||
/// 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 @@ pub fn new(db: &'a dyn HirDatabase, collect_pub_only: bool) -> Self {
|
||||
}
|
||||
|
||||
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 @@ pub fn collect(&mut self, module: Module) {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn finish(self) -> Box<[FileSymbol]> {
|
||||
pub fn finish(self) -> Box<[FileSymbol<'a>]> {
|
||||
self.symbols.into_iter().collect()
|
||||
}
|
||||
|
||||
@@ -217,6 +220,7 @@ fn collect_from_module(&mut self, module_id: ModuleId) {
|
||||
is_assoc: false,
|
||||
is_import: true,
|
||||
do_not_complete: Complete::Yes,
|
||||
_marker: PhantomData,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -251,6 +255,7 @@ fn collect_from_module(&mut self, module_id: ModuleId) {
|
||||
is_assoc: false,
|
||||
is_import: false,
|
||||
do_not_complete: Complete::Yes,
|
||||
_marker: PhantomData,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -428,6 +433,7 @@ fn push_decl<L>(
|
||||
is_assoc,
|
||||
is_import: false,
|
||||
do_not_complete,
|
||||
_marker: PhantomData,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -441,6 +447,7 @@ fn push_decl<L>(
|
||||
is_assoc,
|
||||
is_import: false,
|
||||
do_not_complete,
|
||||
_marker: PhantomData,
|
||||
});
|
||||
|
||||
do_not_complete
|
||||
@@ -474,6 +481,7 @@ fn push_module(&mut self, module_id: ModuleId, name: &Name) {
|
||||
is_assoc: false,
|
||||
is_import: false,
|
||||
do_not_complete,
|
||||
_marker: PhantomData,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -487,6 +495,7 @@ fn push_module(&mut self, module_id: ModuleId, name: &Name) {
|
||||
is_assoc: false,
|
||||
is_import: false,
|
||||
do_not_complete,
|
||||
_marker: PhantomData,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
@@ -176,6 +176,18 @@ fn keep_underscore_used() {
|
||||
bind_unused_param,
|
||||
r#"
|
||||
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;
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
+64
-5
@@ -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()),
|
||||
@@ -186,6 +195,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(
|
||||
|
||||
+11
-7
@@ -3,7 +3,7 @@
|
||||
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);
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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 @@
|
||||
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);
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -115,6 +125,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(
|
||||
@@ -185,6 +299,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(
|
||||
|
||||
+250
-195
@@ -14,15 +14,15 @@
|
||||
};
|
||||
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 @@ pub(crate) fn delegate(&self, field: Field, acc: &mut Assists, ctx: &AssistConte
|
||||
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_()?));
|
||||
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)?;
|
||||
// 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()
|
||||
});
|
||||
|
||||
let assoc = process_assoc_item(item, qualified_path_type.clone(), field_name)?;
|
||||
delegate_assoc_items.add_item(assoc);
|
||||
}
|
||||
|
||||
// 5) Remove useless where clauses
|
||||
if let Some(wc) = delegate.where_clause() {
|
||||
remove_useless_where_clauses(&delegate.trait_()?, &delegate.self_ty()?, wc);
|
||||
}
|
||||
Some(delegate)
|
||||
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,36 +511,78 @@ 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);
|
||||
let predicates_to_remove: Vec<_> = wc.predicates().filter(has_no_live_generics).collect();
|
||||
let remaining_predicates = wc.predicates().count() - predicates_to_remove.len();
|
||||
|
||||
ted::insert(
|
||||
ted::Position::after(wc.syntax()),
|
||||
NodeOrToken::Token(make::token(SyntaxKind::WHITESPACE)),
|
||||
);
|
||||
// Remove where clause
|
||||
ted::remove(wc.syntax());
|
||||
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.
|
||||
//
|
||||
// For example, say we have implementation `impl<A, B, C> Trait for B<A>`,
|
||||
@@ -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, ¶ms_to_remove));
|
||||
(result, trait_params)
|
||||
}
|
||||
|
||||
fn rename_strukt_args<N>(
|
||||
@@ -570,41 +648,37 @@ fn rename_strukt_args<N>(
|
||||
{
|
||||
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(¶ms))
|
||||
.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 {
|
||||
|
||||
|
||||
@@ -269,6 +269,22 @@ fn generate_fn_alias_unnamed_generics_bounds() {
|
||||
);
|
||||
}
|
||||
|
||||
#[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(
|
||||
@@ -405,6 +421,22 @@ fn generate_fn_alias_named_generics_bounds() {
|
||||
);
|
||||
}
|
||||
|
||||
#[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(
|
||||
|
||||
+83
-10
@@ -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 replace_and_then_with_and() {
|
||||
//- 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#"
|
||||
@@ -359,6 +388,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(
|
||||
@@ -395,6 +444,30 @@ fn foo() {
|
||||
let foo = true;
|
||||
let x = foo.then_some(2);
|
||||
}
|
||||
"#,
|
||||
)
|
||||
}
|
||||
|
||||
#[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)());
|
||||
}
|
||||
"#,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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 @@ fn test_base_case() {
|
||||
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 @@ fn test_unignore() {
|
||||
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() {}
|
||||
}
|
||||
"#,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -247,7 +247,6 @@ pub(crate) fn all() -> &'static [Handler] {
|
||||
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 @@ pub(crate) fn all() -> &'static [Handler] {
|
||||
// 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,
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
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()
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -96,6 +100,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(
|
||||
|
||||
@@ -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")]
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
};
|
||||
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 @@ fn library_symbols(
|
||||
|
||||
/// 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 @@ struct InternedModuleId {
|
||||
}
|
||||
|
||||
#[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 @@ fn module_symbols(db: &dyn HirDatabase, module: InternedModuleId<'_>) -> SymbolI
|
||||
}
|
||||
}
|
||||
|
||||
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 @@ pub fn len(&self) -> usize {
|
||||
}
|
||||
|
||||
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 @@ fn map_value_to_range(value: u64) -> (usize, usize) {
|
||||
}
|
||||
|
||||
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 @@ pub(crate) fn search<'sym, T>(
|
||||
}
|
||||
}
|
||||
|
||||
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() {
|
||||
|
||||
@@ -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<&()>,
|
||||
},
|
||||
],
|
||||
),
|
||||
|
||||
@@ -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<&()>,
|
||||
},
|
||||
],
|
||||
),
|
||||
|
||||
@@ -32,5 +32,6 @@
|
||||
is_assoc: false,
|
||||
is_import: false,
|
||||
do_not_complete: Yes,
|
||||
_marker: PhantomData<&()>,
|
||||
},
|
||||
]
|
||||
|
||||
@@ -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<&()>,
|
||||
},
|
||||
]
|
||||
|
||||
@@ -1000,7 +1000,8 @@ fn BAD_CASE() {}
|
||||
// ^^^^^^^^^^^^ 💡 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`
|
||||
|
||||
@@ -296,8 +296,12 @@ fn no_missing_unsafe_diagnostic_with_deprecated_safe_2024() {
|
||||
#[rustc_deprecated_safe_2024]
|
||||
fn set_var() {}
|
||||
|
||||
#[rustc_deprecated_safe_2024(audit_that = "something")]
|
||||
fn set_var2() {}
|
||||
|
||||
fn main() {
|
||||
set_var();
|
||||
set_var2();
|
||||
}
|
||||
"#,
|
||||
);
|
||||
|
||||
@@ -995,6 +995,10 @@ fn f() {
|
||||
}
|
||||
"#,
|
||||
);
|
||||
// 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`
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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}");
|
||||
@@ -357,6 +358,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(
|
||||
|
||||
@@ -182,6 +182,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(
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
```
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -225,7 +225,7 @@ pub(crate) fn from_syntax(
|
||||
}
|
||||
}
|
||||
|
||||
impl TryToNav for FileSymbol {
|
||||
impl<'db> TryToNav for FileSymbol<'db> {
|
||||
fn try_to_nav(
|
||||
&self,
|
||||
sema: &Semantics<'_, RootDatabase>,
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
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 @@ fn add_file(&mut self, file_id: FileId) {
|
||||
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 @@ fn add_file(&mut self, file_id: FileId) {
|
||||
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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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")]
|
||||
|
||||
@@ -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")]
|
||||
|
||||
@@ -603,6 +603,29 @@ pub fn func() {}
|
||||
);
|
||||
}
|
||||
|
||||
#[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(
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
/// 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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -437,11 +437,17 @@ fn handle_event(&mut self, event: Event) {
|
||||
}
|
||||
}
|
||||
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 @@ fn handle_cargo_test_msg(&mut self, message: CargoTestMessage) {
|
||||
}
|
||||
}
|
||||
|
||||
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 @@ fn handle_flycheck_msg(&mut self, message: FlycheckMessage) {
|
||||
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 @@ fn handle_flycheck_msg(&mut self, message: FlycheckMessage) {
|
||||
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)
|
||||
}
|
||||
};
|
||||
|
||||
@@ -48,6 +48,7 @@ pub(crate) fn with_fixture(fixture: &str) -> Project<'_> {
|
||||
"enable": false,
|
||||
},
|
||||
},
|
||||
"checkOnSave": false,
|
||||
"procMacro": {
|
||||
"enable": false,
|
||||
}
|
||||
|
||||
@@ -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 fn ws(&self) -> SyntaxToken {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[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#"
|
||||
PARAM@0..3
|
||||
PATH_TYPE@0..3
|
||||
PATH@0..3
|
||||
PATH_SEGMENT@0..3
|
||||
NAME_REF@0..3
|
||||
IDENT@0..3 "Vec"
|
||||
"#]],
|
||||
);
|
||||
|
||||
check(
|
||||
unnamed_param(ty("Vec<T>")),
|
||||
expect![[r#"
|
||||
PARAM@0..6
|
||||
PATH_TYPE@0..6
|
||||
PATH@0..6
|
||||
PATH_SEGMENT@0..6
|
||||
NAME_REF@0..3
|
||||
IDENT@0..3 "Vec"
|
||||
GENERIC_ARG_LIST@3..6
|
||||
L_ANGLE@3..4 "<"
|
||||
TYPE_ARG@4..5
|
||||
PATH_TYPE@4..5
|
||||
PATH@4..5
|
||||
PATH_SEGMENT@4..5
|
||||
NAME_REF@4..5
|
||||
IDENT@4..5 "T"
|
||||
R_ANGLE@5..6 ">"
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_untyped_param() {
|
||||
check(
|
||||
untyped_param(path_pat(ext::ident_path("name"))),
|
||||
expect![[r#"
|
||||
PARAM@0..4
|
||||
IDENT_PAT@0..4
|
||||
NAME@0..4
|
||||
IDENT@0..4 "name"
|
||||
"#]],
|
||||
);
|
||||
|
||||
check(
|
||||
untyped_param(
|
||||
range_pat(
|
||||
Some(path_pat(ext::ident_path("start"))),
|
||||
Some(path_pat(ext::ident_path("end"))),
|
||||
)
|
||||
.into(),
|
||||
),
|
||||
expect![[r#"
|
||||
PARAM@0..10
|
||||
RANGE_PAT@0..10
|
||||
IDENT_PAT@0..5
|
||||
NAME@0..5
|
||||
IDENT@0..5 "start"
|
||||
DOT2@5..7 ".."
|
||||
IDENT_PAT@7..10
|
||||
NAME@7..10
|
||||
IDENT@7..10 "end"
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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!()
|
||||
}
|
||||
|
||||
@@ -71,6 +71,188 @@ pub fn type_param(
|
||||
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 @@ pub fn expr_while_loop(&self, condition: ast::Expr, body: ast::BlockExpr) -> ast
|
||||
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 @@ pub fn fn_(
|
||||
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();
|
||||
|
||||
|
||||
@@ -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 enum Alignment {
|
||||
Unknown,
|
||||
}
|
||||
|
||||
// region:fmt_before_1_93_0
|
||||
#[lang = "format_count"]
|
||||
pub enum Count {
|
||||
Is(usize),
|
||||
@@ -1288,6 +1290,7 @@ pub const fn new(
|
||||
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 unsafe fn new() -> Self {
|
||||
// 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 unsafe fn new_v1_formatted(
|
||||
}
|
||||
// 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 const fn as_str(&self) -> Option<&'static str> {
|
||||
}
|
||||
}
|
||||
}
|
||||
// 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 @@ pub const fn panic_fmt(fmt: crate::fmt::Arguments<'_>) -> ! {
|
||||
|
||||
#[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
|
||||
|
||||
+22
-16
@@ -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"
|
||||
|
||||
@@ -1 +1 @@
|
||||
dfe1b8c97bcde283102f706d5dcdc3649e5e12e3
|
||||
0208ee09be465f69005a7a12c28d5eccac7d5f34
|
||||
|
||||
@@ -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]
|
||||
|
||||
Reference in New Issue
Block a user