Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit ec18471

Browse files
committedDec 4, 2023
Auto merge of #118592 - lnicola:sync-from-ra, r=lnicola
Subtree update of `rust-analyzer` r? `@ghost`
2 parents cf8d812 + 638ba5a commit ec18471

File tree

34 files changed

+821
-170
lines changed

34 files changed

+821
-170
lines changed
 

‎src/tools/rust-analyzer/Cargo.lock

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2000,9 +2000,9 @@ dependencies = [
20002000

20012001
[[package]]
20022002
name = "triomphe"
2003-
version = "0.1.8"
2003+
version = "0.1.10"
20042004
source = "registry+https://github.com/rust-lang/crates.io-index"
2005-
checksum = "f1ee9bd9239c339d714d657fac840c6d2a4f9c45f4f9ec7b0975113458be78db"
2005+
checksum = "d0c5a71827ac326072b6405552093e2ad2accd25a32fd78d4edc82d98c7f2409"
20062006

20072007
[[package]]
20082008
name = "tt"

‎src/tools/rust-analyzer/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ text-size = "1.1.1"
116116
rayon = "1.8.0"
117117
serde = { version = "1.0.192", features = ["derive"] }
118118
serde_json = "1.0.108"
119-
triomphe = { version = "0.1.8", default-features = false, features = ["std"] }
119+
triomphe = { version = "0.1.10", default-features = false, features = ["std"] }
120120
# can't upgrade due to dashmap depending on 0.12.3 currently
121121
hashbrown = { version = "0.12.3", features = [
122122
"inline-more",

‎src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs

Lines changed: 28 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ mod tests;
3838
use std::{
3939
fmt::{self, Debug},
4040
hash::{Hash, Hasher},
41-
marker::PhantomData,
4241
ops::Index,
4342
};
4443

@@ -340,34 +339,37 @@ pub trait ItemTreeNode: Clone {
340339
fn id_to_mod_item(id: FileItemTreeId<Self>) -> ModItem;
341340
}
342341

343-
pub struct FileItemTreeId<N: ItemTreeNode> {
344-
index: Idx<N>,
345-
_p: PhantomData<N>,
342+
pub struct FileItemTreeId<N: ItemTreeNode>(Idx<N>);
343+
344+
impl<N: ItemTreeNode> FileItemTreeId<N> {
345+
pub fn index(&self) -> Idx<N> {
346+
self.0
347+
}
346348
}
347349

348350
impl<N: ItemTreeNode> Clone for FileItemTreeId<N> {
349351
fn clone(&self) -> Self {
350-
Self { index: self.index, _p: PhantomData }
352+
Self(self.0)
351353
}
352354
}
353355
impl<N: ItemTreeNode> Copy for FileItemTreeId<N> {}
354356

355357
impl<N: ItemTreeNode> PartialEq for FileItemTreeId<N> {
356358
fn eq(&self, other: &FileItemTreeId<N>) -> bool {
357-
self.index == other.index
359+
self.0 == other.0
358360
}
359361
}
360362
impl<N: ItemTreeNode> Eq for FileItemTreeId<N> {}
361363

362364
impl<N: ItemTreeNode> Hash for FileItemTreeId<N> {
363365
fn hash<H: Hasher>(&self, state: &mut H) {
364-
self.index.hash(state)
366+
self.0.hash(state)
365367
}
366368
}
367369

368370
impl<N: ItemTreeNode> fmt::Debug for FileItemTreeId<N> {
369371
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
370-
self.index.fmt(f)
372+
self.0.fmt(f)
371373
}
372374
}
373375

@@ -548,7 +550,7 @@ impl Index<RawVisibilityId> for ItemTree {
548550
impl<N: ItemTreeNode> Index<FileItemTreeId<N>> for ItemTree {
549551
type Output = N;
550552
fn index(&self, id: FileItemTreeId<N>) -> &N {
551-
N::lookup(self, id.index)
553+
N::lookup(self, id.index())
552554
}
553555
}
554556

@@ -925,23 +927,23 @@ impl ModItem {
925927

926928
pub fn ast_id(&self, tree: &ItemTree) -> FileAstId<ast::Item> {
927929
match self {
928-
ModItem::Use(it) => tree[it.index].ast_id().upcast(),
929-
ModItem::ExternCrate(it) => tree[it.index].ast_id().upcast(),
930-
ModItem::ExternBlock(it) => tree[it.index].ast_id().upcast(),
931-
ModItem::Function(it) => tree[it.index].ast_id().upcast(),
932-
ModItem::Struct(it) => tree[it.index].ast_id().upcast(),
933-
ModItem::Union(it) => tree[it.index].ast_id().upcast(),
934-
ModItem::Enum(it) => tree[it.index].ast_id().upcast(),
935-
ModItem::Const(it) => tree[it.index].ast_id().upcast(),
936-
ModItem::Static(it) => tree[it.index].ast_id().upcast(),
937-
ModItem::Trait(it) => tree[it.index].ast_id().upcast(),
938-
ModItem::TraitAlias(it) => tree[it.index].ast_id().upcast(),
939-
ModItem::Impl(it) => tree[it.index].ast_id().upcast(),
940-
ModItem::TypeAlias(it) => tree[it.index].ast_id().upcast(),
941-
ModItem::Mod(it) => tree[it.index].ast_id().upcast(),
942-
ModItem::MacroCall(it) => tree[it.index].ast_id().upcast(),
943-
ModItem::MacroRules(it) => tree[it.index].ast_id().upcast(),
944-
ModItem::MacroDef(it) => tree[it.index].ast_id().upcast(),
930+
ModItem::Use(it) => tree[it.index()].ast_id().upcast(),
931+
ModItem::ExternCrate(it) => tree[it.index()].ast_id().upcast(),
932+
ModItem::ExternBlock(it) => tree[it.index()].ast_id().upcast(),
933+
ModItem::Function(it) => tree[it.index()].ast_id().upcast(),
934+
ModItem::Struct(it) => tree[it.index()].ast_id().upcast(),
935+
ModItem::Union(it) => tree[it.index()].ast_id().upcast(),
936+
ModItem::Enum(it) => tree[it.index()].ast_id().upcast(),
937+
ModItem::Const(it) => tree[it.index()].ast_id().upcast(),
938+
ModItem::Static(it) => tree[it.index()].ast_id().upcast(),
939+
ModItem::Trait(it) => tree[it.index()].ast_id().upcast(),
940+
ModItem::TraitAlias(it) => tree[it.index()].ast_id().upcast(),
941+
ModItem::Impl(it) => tree[it.index()].ast_id().upcast(),
942+
ModItem::TypeAlias(it) => tree[it.index()].ast_id().upcast(),
943+
ModItem::Mod(it) => tree[it.index()].ast_id().upcast(),
944+
ModItem::MacroCall(it) => tree[it.index()].ast_id().upcast(),
945+
ModItem::MacroRules(it) => tree[it.index()].ast_id().upcast(),
946+
ModItem::MacroDef(it) => tree[it.index()].ast_id().upcast(),
945947
}
946948
}
947949
}

‎src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use crate::{
1313
use super::*;
1414

1515
fn id<N: ItemTreeNode>(index: Idx<N>) -> FileItemTreeId<N> {
16-
FileItemTreeId { index, _p: PhantomData }
16+
FileItemTreeId(index)
1717
}
1818

1919
pub(super) struct Ctx<'a> {

‎src/tools/rust-analyzer/crates/hir-ty/src/infer.rs

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1152,20 +1152,15 @@ impl<'a> InferenceContext<'a> {
11521152
(ty, variant)
11531153
}
11541154
TypeNs::TypeAliasId(it) => {
1155-
let container = it.lookup(self.db.upcast()).container;
1156-
let parent_subst = match container {
1157-
ItemContainerId::TraitId(id) => {
1158-
let subst = TyBuilder::subst_for_def(self.db, id, None)
1159-
.fill_with_inference_vars(&mut self.table)
1160-
.build();
1161-
Some(subst)
1162-
}
1163-
// Type aliases do not exist in impls.
1164-
_ => None,
1155+
let resolved_seg = match unresolved {
1156+
None => path.segments().last().unwrap(),
1157+
Some(n) => path.segments().get(path.segments().len() - n - 1).unwrap(),
11651158
};
1166-
let ty = TyBuilder::def_ty(self.db, it.into(), parent_subst)
1167-
.fill_with_inference_vars(&mut self.table)
1168-
.build();
1159+
let substs =
1160+
ctx.substs_from_path_segment(resolved_seg, Some(it.into()), true, None);
1161+
let ty = self.db.ty(it.into());
1162+
let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
1163+
11691164
self.resolve_variant_on_alias(ty, unresolved, mod_path)
11701165
}
11711166
TypeNs::AdtSelfType(_) => {

‎src/tools/rust-analyzer/crates/hir-ty/src/lower.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -768,7 +768,7 @@ impl<'a> TyLoweringContext<'a> {
768768
}
769769
}
770770

771-
fn substs_from_path_segment(
771+
pub(super) fn substs_from_path_segment(
772772
&self,
773773
segment: PathSegment<'_>,
774774
def: Option<GenericDefId>,

‎src/tools/rust-analyzer/crates/hir-ty/src/mir.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,10 @@ impl ProjectionStore {
269269
impl ProjectionId {
270270
pub const EMPTY: ProjectionId = ProjectionId(0);
271271

272+
pub fn is_empty(self) -> bool {
273+
self == ProjectionId::EMPTY
274+
}
275+
272276
pub fn lookup(self, store: &ProjectionStore) -> &[PlaceElem] {
273277
store.id_to_proj.get(&self).unwrap()
274278
}
@@ -1069,6 +1073,10 @@ pub struct MirBody {
10691073
}
10701074

10711075
impl MirBody {
1076+
pub fn local_to_binding_map(&self) -> ArenaMap<LocalId, BindingId> {
1077+
self.binding_locals.iter().map(|(it, y)| (*y, it)).collect()
1078+
}
1079+
10721080
fn walk_places(&mut self, mut f: impl FnMut(&mut Place, &mut ProjectionStore)) {
10731081
fn for_operand(
10741082
op: &mut Operand,
@@ -1188,3 +1196,9 @@ pub enum MirSpan {
11881196
}
11891197

11901198
impl_from!(ExprId, PatId for MirSpan);
1199+
1200+
impl From<&ExprId> for MirSpan {
1201+
fn from(value: &ExprId) -> Self {
1202+
(*value).into()
1203+
}
1204+
}

‎src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs

Lines changed: 34 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -105,9 +105,14 @@ pub enum MirLowerError {
105105
/// A token to ensuring that each drop scope is popped at most once, thanks to the compiler that checks moves.
106106
struct DropScopeToken;
107107
impl DropScopeToken {
108-
fn pop_and_drop(self, ctx: &mut MirLowerCtx<'_>, current: BasicBlockId) -> BasicBlockId {
108+
fn pop_and_drop(
109+
self,
110+
ctx: &mut MirLowerCtx<'_>,
111+
current: BasicBlockId,
112+
span: MirSpan,
113+
) -> BasicBlockId {
109114
std::mem::forget(self);
110-
ctx.pop_drop_scope_internal(current)
115+
ctx.pop_drop_scope_internal(current, span)
111116
}
112117

113118
/// It is useful when we want a drop scope is syntaxically closed, but we don't want to execute any drop
@@ -582,7 +587,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
582587
self.lower_loop(current, place, *label, expr_id.into(), |this, begin| {
583588
let scope = this.push_drop_scope();
584589
if let Some((_, mut current)) = this.lower_expr_as_place(begin, *body, true)? {
585-
current = scope.pop_and_drop(this, current);
590+
current = scope.pop_and_drop(this, current, body.into());
586591
this.set_goto(current, begin, expr_id.into());
587592
} else {
588593
scope.pop_assume_dropped(this);
@@ -720,7 +725,8 @@ impl<'ctx> MirLowerCtx<'ctx> {
720725
.ok_or(MirLowerError::ContinueWithoutLoop)?,
721726
};
722727
let begin = loop_data.begin;
723-
current = self.drop_until_scope(loop_data.drop_scope_index, current);
728+
current =
729+
self.drop_until_scope(loop_data.drop_scope_index, current, expr_id.into());
724730
self.set_goto(current, begin, expr_id.into());
725731
Ok(None)
726732
}
@@ -759,7 +765,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
759765
self.current_loop_blocks.as_ref().unwrap().drop_scope_index,
760766
),
761767
};
762-
current = self.drop_until_scope(drop_scope, current);
768+
current = self.drop_until_scope(drop_scope, current, expr_id.into());
763769
self.set_goto(current, end, expr_id.into());
764770
Ok(None)
765771
}
@@ -773,7 +779,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
773779
return Ok(None);
774780
}
775781
}
776-
current = self.drop_until_scope(0, current);
782+
current = self.drop_until_scope(0, current, expr_id.into());
777783
self.set_terminator(current, TerminatorKind::Return, expr_id.into());
778784
Ok(None)
779785
}
@@ -1782,7 +1788,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
17821788
return Ok(None);
17831789
};
17841790
self.push_fake_read(c, p, expr.into());
1785-
current = scope2.pop_and_drop(self, c);
1791+
current = scope2.pop_and_drop(self, c, expr.into());
17861792
}
17871793
}
17881794
}
@@ -1793,7 +1799,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
17931799
};
17941800
current = c;
17951801
}
1796-
current = scope.pop_and_drop(self, current);
1802+
current = scope.pop_and_drop(self, current, span);
17971803
Ok(Some(current))
17981804
}
17991805

@@ -1873,9 +1879,14 @@ impl<'ctx> MirLowerCtx<'ctx> {
18731879
}
18741880
}
18751881

1876-
fn drop_until_scope(&mut self, scope_index: usize, mut current: BasicBlockId) -> BasicBlockId {
1882+
fn drop_until_scope(
1883+
&mut self,
1884+
scope_index: usize,
1885+
mut current: BasicBlockId,
1886+
span: MirSpan,
1887+
) -> BasicBlockId {
18771888
for scope in self.drop_scopes[scope_index..].to_vec().iter().rev() {
1878-
self.emit_drop_and_storage_dead_for_scope(scope, &mut current);
1889+
self.emit_drop_and_storage_dead_for_scope(scope, &mut current, span);
18791890
}
18801891
current
18811892
}
@@ -1891,17 +1902,22 @@ impl<'ctx> MirLowerCtx<'ctx> {
18911902
}
18921903

18931904
/// Don't call directly
1894-
fn pop_drop_scope_internal(&mut self, mut current: BasicBlockId) -> BasicBlockId {
1905+
fn pop_drop_scope_internal(
1906+
&mut self,
1907+
mut current: BasicBlockId,
1908+
span: MirSpan,
1909+
) -> BasicBlockId {
18951910
let scope = self.drop_scopes.pop().unwrap();
1896-
self.emit_drop_and_storage_dead_for_scope(&scope, &mut current);
1911+
self.emit_drop_and_storage_dead_for_scope(&scope, &mut current, span);
18971912
current
18981913
}
18991914

19001915
fn pop_drop_scope_assert_finished(
19011916
&mut self,
19021917
mut current: BasicBlockId,
1918+
span: MirSpan,
19031919
) -> Result<BasicBlockId> {
1904-
current = self.pop_drop_scope_internal(current);
1920+
current = self.pop_drop_scope_internal(current, span);
19051921
if !self.drop_scopes.is_empty() {
19061922
implementation_error!("Mismatched count between drop scope push and pops");
19071923
}
@@ -1912,20 +1928,18 @@ impl<'ctx> MirLowerCtx<'ctx> {
19121928
&mut self,
19131929
scope: &DropScope,
19141930
current: &mut Idx<BasicBlock>,
1931+
span: MirSpan,
19151932
) {
19161933
for &l in scope.locals.iter().rev() {
19171934
if !self.result.locals[l].ty.clone().is_copy(self.db, self.owner) {
19181935
let prev = std::mem::replace(current, self.new_basic_block());
19191936
self.set_terminator(
19201937
prev,
19211938
TerminatorKind::Drop { place: l.into(), target: *current, unwind: None },
1922-
MirSpan::Unknown,
1939+
span,
19231940
);
19241941
}
1925-
self.push_statement(
1926-
*current,
1927-
StatementKind::StorageDead(l).with_span(MirSpan::Unknown),
1928-
);
1942+
self.push_statement(*current, StatementKind::StorageDead(l).with_span(span));
19291943
}
19301944
}
19311945
}
@@ -2002,7 +2016,7 @@ pub fn mir_body_for_closure_query(
20022016
|_| true,
20032017
)?;
20042018
if let Some(current) = ctx.lower_expr_to_place(*root, return_slot().into(), current)? {
2005-
let current = ctx.pop_drop_scope_assert_finished(current)?;
2019+
let current = ctx.pop_drop_scope_assert_finished(current, root.into())?;
20062020
ctx.set_terminator(current, TerminatorKind::Return, (*root).into());
20072021
}
20082022
let mut upvar_map: FxHashMap<LocalId, Vec<(&CapturedItem, usize)>> = FxHashMap::default();
@@ -2146,7 +2160,7 @@ pub fn lower_to_mir(
21462160
ctx.lower_params_and_bindings([].into_iter(), binding_picker)?
21472161
};
21482162
if let Some(current) = ctx.lower_expr_to_place(root_expr, return_slot().into(), current)? {
2149-
let current = ctx.pop_drop_scope_assert_finished(current)?;
2163+
let current = ctx.pop_drop_scope_assert_finished(current, root_expr.into())?;
21502164
ctx.set_terminator(current, TerminatorKind::Return, root_expr.into());
21512165
}
21522166
Ok(ctx.result)

‎src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ impl<'a> MirPrettyCtx<'a> {
145145
let indent = mem::take(&mut self.indent);
146146
let mut ctx = MirPrettyCtx {
147147
body: &body,
148-
local_to_binding: body.binding_locals.iter().map(|(it, y)| (*y, it)).collect(),
148+
local_to_binding: body.local_to_binding_map(),
149149
result,
150150
indent,
151151
..*self
@@ -167,7 +167,7 @@ impl<'a> MirPrettyCtx<'a> {
167167
}
168168

169169
fn new(body: &'a MirBody, hir_body: &'a Body, db: &'a dyn HirDatabase) -> Self {
170-
let local_to_binding = body.binding_locals.iter().map(|(it, y)| (*y, it)).collect();
170+
let local_to_binding = body.local_to_binding_map();
171171
MirPrettyCtx {
172172
body,
173173
db,

‎src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1129,3 +1129,27 @@ fn foo() {
11291129
"#,
11301130
);
11311131
}
1132+
1133+
#[test]
1134+
fn generic_alias() {
1135+
check_types(
1136+
r#"
1137+
type Wrap<T> = T;
1138+
1139+
enum X {
1140+
A { cool: u32, stuff: u32 },
1141+
B,
1142+
}
1143+
1144+
fn main() {
1145+
let wrapped = Wrap::<X>::A {
1146+
cool: 100,
1147+
stuff: 100,
1148+
};
1149+
1150+
if let Wrap::<X>::A { cool, ..} = &wrapped {}
1151+
//^^^^ &u32
1152+
}
1153+
"#,
1154+
);
1155+
}

‎src/tools/rust-analyzer/crates/hir/src/lib.rs

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ use hir_ty::{
6767
known_const_to_ast,
6868
layout::{Layout as TyLayout, RustcEnumVariantIdx, RustcFieldIdx, TagEncoding},
6969
method_resolution::{self, TyFingerprint},
70-
mir::{self, interpret_mir},
70+
mir::interpret_mir,
7171
primitive::UintTy,
7272
traits::FnTrait,
7373
AliasTy, CallableDefId, CallableSig, Canonical, CanonicalVarKinds, Cast, ClosureId, GenericArg,
@@ -129,9 +129,10 @@ pub use {
129129
hir_ty::{
130130
display::{ClosureStyle, HirDisplay, HirDisplayError, HirWrite},
131131
layout::LayoutError,
132-
mir::MirEvalError,
133132
PointerCast, Safety,
134133
},
134+
// FIXME: Properly encapsulate mir
135+
hir_ty::{mir, Interner as ChalkTyInterner},
135136
};
136137

137138
// These are negative re-exports: pub using these names is forbidden, they
@@ -1914,17 +1915,20 @@ impl DefWithBody {
19141915
if let ast::Expr::MatchExpr(match_expr) =
19151916
&source_ptr.value.to_node(&root)
19161917
{
1917-
if let Some(scrut_expr) = match_expr.expr() {
1918-
acc.push(
1919-
MissingMatchArms {
1920-
scrutinee_expr: InFile::new(
1921-
source_ptr.file_id,
1922-
AstPtr::new(&scrut_expr),
1923-
),
1924-
uncovered_patterns,
1925-
}
1926-
.into(),
1927-
);
1918+
match match_expr.expr() {
1919+
Some(scrut_expr) if match_expr.match_arm_list().is_some() => {
1920+
acc.push(
1921+
MissingMatchArms {
1922+
scrutinee_expr: InFile::new(
1923+
source_ptr.file_id,
1924+
AstPtr::new(&scrut_expr),
1925+
),
1926+
uncovered_patterns,
1927+
}
1928+
.into(),
1929+
);
1930+
}
1931+
_ => {}
19281932
}
19291933
}
19301934
}

‎src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_parentheses.rs

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use syntax::{ast, AstNode};
1+
use syntax::{ast, AstNode, SyntaxKind, T};
22

33
use crate::{AssistContext, AssistId, AssistKind, Assists};
44

@@ -39,7 +39,19 @@ pub(crate) fn remove_parentheses(acc: &mut Assists, ctx: &AssistContext<'_>) ->
3939
AssistId("remove_parentheses", AssistKind::Refactor),
4040
"Remove redundant parentheses",
4141
target,
42-
|builder| builder.replace_ast(parens.into(), expr),
42+
|builder| {
43+
let prev_token = parens.syntax().first_token().and_then(|it| it.prev_token());
44+
let need_to_add_ws = match prev_token {
45+
Some(it) => {
46+
let tokens = vec![T![&], T![!], T!['('], T!['['], T!['{']];
47+
it.kind() != SyntaxKind::WHITESPACE && !tokens.contains(&it.kind())
48+
}
49+
None => false,
50+
};
51+
let expr = if need_to_add_ws { format!(" {}", expr) } else { expr.to_string() };
52+
53+
builder.replace(parens.syntax().text_range(), expr)
54+
},
4355
)
4456
}
4557

@@ -49,6 +61,15 @@ mod tests {
4961

5062
use super::*;
5163

64+
#[test]
65+
fn remove_parens_space() {
66+
check_assist(
67+
remove_parentheses,
68+
r#"fn f() { match$0(true) {} }"#,
69+
r#"fn f() { match true {} }"#,
70+
);
71+
}
72+
5273
#[test]
5374
fn remove_parens_simple() {
5475
check_assist(remove_parentheses, r#"fn f() { $0(2) + 2; }"#, r#"fn f() { 2 + 2; }"#);
@@ -94,8 +115,8 @@ mod tests {
94115
check_assist(remove_parentheses, r#"fn f() { f(($02 + 2)); }"#, r#"fn f() { f(2 + 2); }"#);
95116
check_assist(
96117
remove_parentheses,
97-
r#"fn f() { (1<2)&&$0(3>4); }"#,
98-
r#"fn f() { (1<2)&&3>4; }"#,
118+
r#"fn f() { (1<2) &&$0(3>4); }"#,
119+
r#"fn f() { (1<2) && 3>4; }"#,
99120
);
100121
}
101122

@@ -164,8 +185,8 @@ mod tests {
164185
fn remove_parens_weird_places() {
165186
check_assist(
166187
remove_parentheses,
167-
r#"fn f() { match () { _=>$0(()) } }"#,
168-
r#"fn f() { match () { _=>() } }"#,
188+
r#"fn f() { match () { _ =>$0(()) } }"#,
189+
r#"fn f() { match () { _ => () } }"#,
169190
);
170191

171192
check_assist(

‎src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs

Lines changed: 78 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,17 @@ pub(crate) fn complete_dot(
2626
item.add_to(acc, ctx.db);
2727
}
2828

29-
if let DotAccessKind::Method { .. } = dot_access.kind {
30-
cov_mark::hit!(test_no_struct_field_completion_for_method_call);
31-
} else {
32-
complete_fields(
33-
acc,
34-
ctx,
35-
receiver_ty,
36-
|acc, field, ty| acc.add_field(ctx, dot_access, None, field, &ty),
37-
|acc, field, ty| acc.add_tuple_field(ctx, None, field, &ty),
38-
);
39-
}
29+
let is_field_access = matches!(dot_access.kind, DotAccessKind::Field { .. });
30+
31+
complete_fields(
32+
acc,
33+
ctx,
34+
receiver_ty,
35+
|acc, field, ty| acc.add_field(ctx, dot_access, None, field, &ty),
36+
|acc, field, ty| acc.add_tuple_field(ctx, None, field, &ty),
37+
is_field_access,
38+
);
39+
4040
complete_methods(ctx, receiver_ty, |func| acc.add_method(ctx, dot_access, func, None, None));
4141
}
4242

@@ -82,6 +82,7 @@ pub(crate) fn complete_undotted_self(
8282
)
8383
},
8484
|acc, field, ty| acc.add_tuple_field(ctx, Some(hir::known::SELF_PARAM), field, &ty),
85+
true,
8586
);
8687
complete_methods(ctx, &ty, |func| {
8788
acc.add_method(
@@ -104,18 +105,23 @@ fn complete_fields(
104105
receiver: &hir::Type,
105106
mut named_field: impl FnMut(&mut Completions, hir::Field, hir::Type),
106107
mut tuple_index: impl FnMut(&mut Completions, usize, hir::Type),
108+
is_field_access: bool,
107109
) {
108110
let mut seen_names = FxHashSet::default();
109111
for receiver in receiver.autoderef(ctx.db) {
110112
for (field, ty) in receiver.fields(ctx.db) {
111-
if seen_names.insert(field.name(ctx.db)) {
113+
if seen_names.insert(field.name(ctx.db))
114+
&& (is_field_access || ty.is_fn() || ty.is_closure())
115+
{
112116
named_field(acc, field, ty);
113117
}
114118
}
115119
for (i, ty) in receiver.tuple_fields(ctx.db).into_iter().enumerate() {
116120
// Tuples are always the last type in a deref chain, so just check if the name is
117121
// already seen without inserting into the hashset.
118-
if !seen_names.contains(&hir::Name::new_tuple_field(i)) {
122+
if !seen_names.contains(&hir::Name::new_tuple_field(i))
123+
&& (is_field_access || ty.is_fn() || ty.is_closure())
124+
{
119125
// Tuple fields are always public (tuple struct fields are handled above).
120126
tuple_index(acc, i, ty);
121127
}
@@ -250,7 +256,6 @@ impl A {
250256

251257
#[test]
252258
fn test_no_struct_field_completion_for_method_call() {
253-
cov_mark::check!(test_no_struct_field_completion_for_method_call);
254259
check(
255260
r#"
256261
struct A { the_field: u32 }
@@ -1172,4 +1177,63 @@ impl<B: Bar, F: core::ops::Deref<Target = B>> Foo<F> {
11721177
"#]],
11731178
);
11741179
}
1180+
1181+
#[test]
1182+
fn test_struct_function_field_completion() {
1183+
check(
1184+
r#"
1185+
struct S { va_field: u32, fn_field: fn() }
1186+
fn foo() { S { va_field: 0, fn_field: || {} }.fi$0() }
1187+
"#,
1188+
expect![[r#"
1189+
fd fn_field fn()
1190+
"#]],
1191+
);
1192+
1193+
check_edit(
1194+
"fn_field",
1195+
r#"
1196+
struct S { va_field: u32, fn_field: fn() }
1197+
fn foo() { S { va_field: 0, fn_field: || {} }.fi$0() }
1198+
"#,
1199+
r#"
1200+
struct S { va_field: u32, fn_field: fn() }
1201+
fn foo() { (S { va_field: 0, fn_field: || {} }.fn_field)() }
1202+
"#,
1203+
);
1204+
}
1205+
1206+
#[test]
1207+
fn test_tuple_function_field_completion() {
1208+
check(
1209+
r#"
1210+
struct B(u32, fn())
1211+
fn foo() {
1212+
let b = B(0, || {});
1213+
b.$0()
1214+
}
1215+
"#,
1216+
expect![[r#"
1217+
fd 1 fn()
1218+
"#]],
1219+
);
1220+
1221+
check_edit(
1222+
"1",
1223+
r#"
1224+
struct B(u32, fn())
1225+
fn foo() {
1226+
let b = B(0, || {});
1227+
b.$0()
1228+
}
1229+
"#,
1230+
r#"
1231+
struct B(u32, fn())
1232+
fn foo() {
1233+
let b = B(0, || {});
1234+
(b.1)()
1235+
}
1236+
"#,
1237+
)
1238+
}
11751239
}

‎src/tools/rust-analyzer/crates/ide-completion/src/render.rs

Lines changed: 107 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,10 @@ use ide_db::{
1818
RootDatabase, SnippetCap, SymbolKind,
1919
};
2020
use syntax::{AstNode, SmolStr, SyntaxKind, TextRange};
21+
use text_edit::TextEdit;
2122

2223
use crate::{
23-
context::{DotAccess, PathCompletionCtx, PathKind, PatternContext},
24+
context::{DotAccess, DotAccessKind, PathCompletionCtx, PathKind, PatternContext},
2425
item::{Builder, CompletionRelevanceTypeMatch},
2526
render::{
2627
function::render_fn,
@@ -147,7 +148,42 @@ pub(crate) fn render_field(
147148
.set_documentation(field.docs(db))
148149
.set_deprecated(is_deprecated)
149150
.lookup_by(name);
150-
item.insert_text(field_with_receiver(db, receiver.as_ref(), &escaped_name));
151+
152+
let is_field_access = matches!(dot_access.kind, DotAccessKind::Field { .. });
153+
if !is_field_access || ty.is_fn() || ty.is_closure() {
154+
let mut builder = TextEdit::builder();
155+
// Using TextEdit, insert '(' before the struct name and ')' before the
156+
// dot access, then comes the field name and optionally insert function
157+
// call parens.
158+
159+
builder.replace(
160+
ctx.source_range(),
161+
field_with_receiver(db, receiver.as_ref(), &escaped_name).into(),
162+
);
163+
164+
let expected_fn_type =
165+
ctx.completion.expected_type.as_ref().is_some_and(|ty| ty.is_fn() || ty.is_closure());
166+
167+
if !expected_fn_type {
168+
if let Some(receiver) = &dot_access.receiver {
169+
if let Some(receiver) = ctx.completion.sema.original_ast_node(receiver.clone()) {
170+
builder.insert(receiver.syntax().text_range().start(), "(".to_string());
171+
builder.insert(ctx.source_range().end(), ")".to_string());
172+
}
173+
}
174+
175+
let is_parens_needed =
176+
!matches!(dot_access.kind, DotAccessKind::Method { has_parens: true });
177+
178+
if is_parens_needed {
179+
builder.insert(ctx.source_range().end(), "()".to_string());
180+
}
181+
}
182+
183+
item.text_edit(builder.finish());
184+
} else {
185+
item.insert_text(field_with_receiver(db, receiver.as_ref(), &escaped_name));
186+
}
151187
if let Some(receiver) = &dot_access.receiver {
152188
if let Some(original) = ctx.completion.sema.original_ast_node(receiver.clone()) {
153189
if let Some(ref_match) = compute_ref_match(ctx.completion, ty) {
@@ -1600,7 +1636,7 @@ fn main() {
16001636
fn struct_field_method_ref() {
16011637
check_kinds(
16021638
r#"
1603-
struct Foo { bar: u32 }
1639+
struct Foo { bar: u32, qux: fn() }
16041640
impl Foo { fn baz(&self) -> u32 { 0 } }
16051641
16061642
fn foo(f: Foo) { let _: &u32 = f.b$0 }
@@ -1610,30 +1646,92 @@ fn foo(f: Foo) { let _: &u32 = f.b$0 }
16101646
[
16111647
CompletionItem {
16121648
label: "baz()",
1613-
source_range: 98..99,
1614-
delete: 98..99,
1649+
source_range: 109..110,
1650+
delete: 109..110,
16151651
insert: "baz()$0",
16161652
kind: Method,
16171653
lookup: "baz",
16181654
detail: "fn(&self) -> u32",
1619-
ref_match: "&@96",
1655+
ref_match: "&@107",
16201656
},
16211657
CompletionItem {
16221658
label: "bar",
1623-
source_range: 98..99,
1624-
delete: 98..99,
1659+
source_range: 109..110,
1660+
delete: 109..110,
16251661
insert: "bar",
16261662
kind: SymbolKind(
16271663
Field,
16281664
),
16291665
detail: "u32",
1630-
ref_match: "&@96",
1666+
ref_match: "&@107",
1667+
},
1668+
CompletionItem {
1669+
label: "qux",
1670+
source_range: 109..110,
1671+
text_edit: TextEdit {
1672+
indels: [
1673+
Indel {
1674+
insert: "(",
1675+
delete: 107..107,
1676+
},
1677+
Indel {
1678+
insert: "qux)()",
1679+
delete: 109..110,
1680+
},
1681+
],
1682+
},
1683+
kind: SymbolKind(
1684+
Field,
1685+
),
1686+
detail: "fn()",
16311687
},
16321688
]
16331689
"#]],
16341690
);
16351691
}
16361692

1693+
#[test]
1694+
fn expected_fn_type_ref() {
1695+
check_kinds(
1696+
r#"
1697+
struct S { field: fn() }
1698+
1699+
fn foo() {
1700+
let foo: fn() = S { fields: || {}}.fi$0;
1701+
}
1702+
"#,
1703+
&[CompletionItemKind::SymbolKind(SymbolKind::Field)],
1704+
expect![[r#"
1705+
[
1706+
CompletionItem {
1707+
label: "field",
1708+
source_range: 76..78,
1709+
delete: 76..78,
1710+
insert: "field",
1711+
kind: SymbolKind(
1712+
Field,
1713+
),
1714+
detail: "fn()",
1715+
relevance: CompletionRelevance {
1716+
exact_name_match: false,
1717+
type_match: Some(
1718+
Exact,
1719+
),
1720+
is_local: false,
1721+
is_item_from_trait: false,
1722+
is_name_already_imported: false,
1723+
requires_import: false,
1724+
is_op_method: false,
1725+
is_private_editable: false,
1726+
postfix_match: None,
1727+
is_definite: false,
1728+
},
1729+
},
1730+
]
1731+
"#]],
1732+
)
1733+
}
1734+
16371735
#[test]
16381736
fn qualified_path_ref() {
16391737
check_kinds(

‎src/tools/rust-analyzer/crates/ide-completion/src/tests/pattern.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,35 @@ fn outer(Foo { bar$0 }: Foo) {}
354354
)
355355
}
356356

357+
#[test]
358+
fn completes_in_record_field_pat_with_generic_type_alias() {
359+
check_empty(
360+
r#"
361+
type Wrap<T> = T;
362+
363+
enum X {
364+
A { cool: u32, stuff: u32 },
365+
B,
366+
}
367+
368+
fn main() {
369+
let wrapped = Wrap::<X>::A {
370+
cool: 100,
371+
stuff: 100,
372+
};
373+
374+
if let Wrap::<X>::A { $0 } = &wrapped {};
375+
}
376+
"#,
377+
expect![[r#"
378+
fd cool u32
379+
fd stuff u32
380+
kw mut
381+
kw ref
382+
"#]],
383+
)
384+
}
385+
357386
#[test]
358387
fn completes_in_fn_param() {
359388
check_empty(

‎src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,31 @@ pub(crate) fn missing_match_arms(
1717

1818
#[cfg(test)]
1919
mod tests {
20-
use crate::tests::check_diagnostics;
20+
use crate::{
21+
tests::{check_diagnostics, check_diagnostics_with_config},
22+
DiagnosticsConfig,
23+
};
2124

2225
#[track_caller]
2326
fn check_diagnostics_no_bails(ra_fixture: &str) {
2427
cov_mark::check_count!(validate_match_bailed_out, 0);
2528
crate::tests::check_diagnostics(ra_fixture)
2629
}
2730

31+
#[test]
32+
fn empty_body() {
33+
let mut config = DiagnosticsConfig::test_sample();
34+
config.disabled.insert("syntax-error".to_string());
35+
check_diagnostics_with_config(
36+
config,
37+
r#"
38+
fn main() {
39+
match 0;
40+
}
41+
"#,
42+
);
43+
}
44+
2845
#[test]
2946
fn empty_tuple() {
3047
check_diagnostics_no_bails(

‎src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ mod discriminant;
3131
mod fn_lifetime_fn;
3232
mod implicit_static;
3333
mod param_name;
34+
mod implicit_drop;
3435

3536
#[derive(Clone, Debug, PartialEq, Eq)]
3637
pub struct InlayHintsConfig {
@@ -45,6 +46,7 @@ pub struct InlayHintsConfig {
4546
pub closure_return_type_hints: ClosureReturnTypeHints,
4647
pub closure_capture_hints: bool,
4748
pub binding_mode_hints: bool,
49+
pub implicit_drop_hints: bool,
4850
pub lifetime_elision_hints: LifetimeElisionHints,
4951
pub param_names_for_lifetime_elision_hints: bool,
5052
pub hide_named_constructor_hints: bool,
@@ -124,6 +126,7 @@ pub enum InlayKind {
124126
Lifetime,
125127
Parameter,
126128
Type,
129+
Drop,
127130
}
128131

129132
#[derive(Debug)]
@@ -503,7 +506,10 @@ fn hints(
503506
ast::Item(it) => match it {
504507
// FIXME: record impl lifetimes so they aren't being reused in assoc item lifetime inlay hints
505508
ast::Item::Impl(_) => None,
506-
ast::Item::Fn(it) => fn_lifetime_fn::hints(hints, config, it),
509+
ast::Item::Fn(it) => {
510+
implicit_drop::hints(hints, sema, config, &it);
511+
fn_lifetime_fn::hints(hints, config, it)
512+
},
507513
// static type elisions
508514
ast::Item::Static(it) => implicit_static::hints(hints, config, Either::Left(it)),
509515
ast::Item::Const(it) => implicit_static::hints(hints, config, Either::Right(it)),
@@ -591,6 +597,7 @@ mod tests {
591597
max_length: None,
592598
closing_brace_hints_min_lines: None,
593599
fields_to_resolve: InlayFieldsToResolve::empty(),
600+
implicit_drop_hints: false,
594601
};
595602
pub(super) const TEST_CONFIG: InlayHintsConfig = InlayHintsConfig {
596603
type_hints: true,
Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
//! Implementation of "implicit drop" inlay hints:
2+
//! ```no_run
3+
//! fn main() {
4+
//! let x = vec![2];
5+
//! if some_condition() {
6+
//! /* drop(x) */return;
7+
//! }
8+
//! }
9+
//! ```
10+
use hir::{
11+
db::{DefDatabase as _, HirDatabase as _},
12+
mir::{MirSpan, TerminatorKind},
13+
ChalkTyInterner, DefWithBody, Semantics,
14+
};
15+
use ide_db::{base_db::FileRange, RootDatabase};
16+
17+
use syntax::{
18+
ast::{self, AstNode},
19+
match_ast,
20+
};
21+
22+
use crate::{InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, InlayKind};
23+
24+
pub(super) fn hints(
25+
acc: &mut Vec<InlayHint>,
26+
sema: &Semantics<'_, RootDatabase>,
27+
config: &InlayHintsConfig,
28+
def: &ast::Fn,
29+
) -> Option<()> {
30+
if !config.implicit_drop_hints {
31+
return None;
32+
}
33+
34+
let def = sema.to_def(def)?;
35+
let def: DefWithBody = def.into();
36+
37+
let source_map = sema.db.body_with_source_map(def.into()).1;
38+
39+
let hir = sema.db.body(def.into());
40+
let mir = sema.db.mir_body(def.into()).ok()?;
41+
42+
let local_to_binding = mir.local_to_binding_map();
43+
44+
for (_, bb) in mir.basic_blocks.iter() {
45+
let terminator = bb.terminator.as_ref()?;
46+
if let TerminatorKind::Drop { place, .. } = terminator.kind {
47+
if !place.projection.is_empty() {
48+
continue; // Ignore complex cases for now
49+
}
50+
if mir.locals[place.local].ty.adt_id(ChalkTyInterner).is_none() {
51+
continue; // Arguably only ADTs have significant drop impls
52+
}
53+
let Some(binding) = local_to_binding.get(place.local) else {
54+
continue; // Ignore temporary values
55+
};
56+
let range = match terminator.span {
57+
MirSpan::ExprId(e) => match source_map.expr_syntax(e) {
58+
Ok(s) => {
59+
let root = &s.file_syntax(sema.db);
60+
let expr = s.value.to_node(root);
61+
let expr = expr.syntax();
62+
match_ast! {
63+
match expr {
64+
ast::BlockExpr(x) => x.stmt_list().and_then(|x| x.r_curly_token()).map(|x| x.text_range()).unwrap_or_else(|| expr.text_range()),
65+
_ => expr.text_range(),
66+
}
67+
}
68+
}
69+
Err(_) => continue,
70+
},
71+
MirSpan::PatId(p) => match source_map.pat_syntax(p) {
72+
Ok(s) => s.value.text_range(),
73+
Err(_) => continue,
74+
},
75+
MirSpan::Unknown => continue,
76+
};
77+
let binding = &hir.bindings[*binding];
78+
let binding_source = binding
79+
.definitions
80+
.first()
81+
.and_then(|d| source_map.pat_syntax(*d).ok())
82+
.and_then(|d| {
83+
Some(FileRange { file_id: d.file_id.file_id()?, range: d.value.text_range() })
84+
});
85+
let name = binding.name.to_smol_str();
86+
if name.starts_with("<ra@") {
87+
continue; // Ignore desugared variables
88+
}
89+
let mut label = InlayHintLabel::simple(
90+
name,
91+
Some(crate::InlayTooltip::String("moz".into())),
92+
binding_source,
93+
);
94+
label.prepend_str("drop(");
95+
label.append_str(")");
96+
acc.push(InlayHint {
97+
range,
98+
position: InlayHintPosition::Before,
99+
pad_left: true,
100+
pad_right: true,
101+
kind: InlayKind::Drop,
102+
needs_resolve: label.needs_resolve(),
103+
label,
104+
text_edit: None,
105+
})
106+
}
107+
}
108+
109+
Some(())
110+
}
111+
112+
#[cfg(test)]
113+
mod tests {
114+
use crate::{
115+
inlay_hints::tests::{check_with_config, DISABLED_CONFIG},
116+
InlayHintsConfig,
117+
};
118+
119+
const ONLY_DROP_CONFIG: InlayHintsConfig =
120+
InlayHintsConfig { implicit_drop_hints: true, ..DISABLED_CONFIG };
121+
122+
#[test]
123+
fn basic() {
124+
check_with_config(
125+
ONLY_DROP_CONFIG,
126+
r#"
127+
struct X;
128+
fn f() {
129+
let x = X;
130+
if 2 == 5 {
131+
return;
132+
//^^^^^^ drop(x)
133+
}
134+
}
135+
//^ drop(x)
136+
"#,
137+
);
138+
}
139+
140+
#[test]
141+
fn no_hint_for_copy_types_and_mutable_references() {
142+
// `T: Copy` and `T = &mut U` types do nothing on drop, so we should hide drop inlay hint for them.
143+
check_with_config(
144+
ONLY_DROP_CONFIG,
145+
r#"
146+
//- minicore: copy, derive
147+
148+
struct X(i32, i32);
149+
#[derive(Clone, Copy)]
150+
struct Y(i32, i32);
151+
fn f() {
152+
let a = 2;
153+
let b = a + 4;
154+
let mut x = X(a, b);
155+
let mut y = Y(a, b);
156+
let mx = &mut x;
157+
let my = &mut y;
158+
let c = a + b;
159+
}
160+
//^ drop(x)
161+
"#,
162+
);
163+
}
164+
165+
#[test]
166+
fn try_operator() {
167+
// We currently show drop inlay hint for every `?` operator that may potentialy drop something. We probably need to
168+
// make it configurable as it doesn't seem very useful.
169+
check_with_config(
170+
ONLY_DROP_CONFIG,
171+
r#"
172+
//- minicore: copy, try, option
173+
174+
struct X;
175+
fn f() -> Option<()> {
176+
let x = X;
177+
let t_opt = Some(2);
178+
let t = t_opt?;
179+
//^^^^^^ drop(x)
180+
Some(())
181+
}
182+
//^ drop(x)
183+
"#,
184+
);
185+
}
186+
187+
#[test]
188+
fn if_let() {
189+
check_with_config(
190+
ONLY_DROP_CONFIG,
191+
r#"
192+
struct X;
193+
fn f() {
194+
let x = X;
195+
if let X = x {
196+
let y = X;
197+
}
198+
//^ drop(y)
199+
}
200+
//^ drop(x)
201+
"#,
202+
);
203+
}
204+
}

‎src/tools/rust-analyzer/crates/ide/src/static_index.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ impl StaticIndex<'_> {
118118
adjustment_hints: crate::AdjustmentHints::Never,
119119
adjustment_hints_mode: AdjustmentHintsMode::Prefix,
120120
adjustment_hints_hide_outside_unsafe: false,
121+
implicit_drop_hints: false,
121122
hide_named_constructor_hints: false,
122123
hide_closure_initialization_hints: false,
123124
closure_style: hir::ClosureStyle::ImplFn,

‎src/tools/rust-analyzer/crates/parser/src/grammar.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,16 @@ fn error_block(p: &mut Parser<'_>, message: &str) {
376376
m.complete(p, ERROR);
377377
}
378378

379+
// test_err top_level_let
380+
// let ref foo: fn() = 1 + 3;
381+
fn error_let_stmt(p: &mut Parser<'_>, message: &str) {
382+
assert!(p.at(T![let]));
383+
let m = p.start();
384+
p.error(message);
385+
expressions::let_stmt(p, expressions::Semicolon::Optional);
386+
m.complete(p, ERROR);
387+
}
388+
379389
/// The `parser` passed this is required to at least consume one token if it returns `true`.
380390
/// If the `parser` returns false, parsing will stop.
381391
fn delimited(

‎src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs

Lines changed: 53 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@ pub(super) fn stmt(p: &mut Parser<'_>, semicolon: Semicolon) {
5959
attributes::outer_attrs(p);
6060

6161
if p.at(T![let]) {
62-
let_stmt(p, m, semicolon);
62+
let_stmt(p, semicolon);
63+
m.complete(p, LET_STMT);
6364
return;
6465
}
6566

@@ -109,54 +110,53 @@ pub(super) fn stmt(p: &mut Parser<'_>, semicolon: Semicolon) {
109110
m.complete(p, EXPR_STMT);
110111
}
111112
}
113+
}
112114

113-
// test let_stmt
114-
// fn f() { let x: i32 = 92; }
115-
fn let_stmt(p: &mut Parser<'_>, m: Marker, with_semi: Semicolon) {
116-
p.bump(T![let]);
117-
patterns::pattern(p);
118-
if p.at(T![:]) {
119-
// test let_stmt_ascription
120-
// fn f() { let x: i32; }
121-
types::ascription(p);
122-
}
115+
// test let_stmt
116+
// fn f() { let x: i32 = 92; }
117+
pub(super) fn let_stmt(p: &mut Parser<'_>, with_semi: Semicolon) {
118+
p.bump(T![let]);
119+
patterns::pattern(p);
120+
if p.at(T![:]) {
121+
// test let_stmt_ascription
122+
// fn f() { let x: i32; }
123+
types::ascription(p);
124+
}
123125

124-
let mut expr_after_eq: Option<CompletedMarker> = None;
125-
if p.eat(T![=]) {
126-
// test let_stmt_init
127-
// fn f() { let x = 92; }
128-
expr_after_eq = expressions::expr(p);
129-
}
126+
let mut expr_after_eq: Option<CompletedMarker> = None;
127+
if p.eat(T![=]) {
128+
// test let_stmt_init
129+
// fn f() { let x = 92; }
130+
expr_after_eq = expressions::expr(p);
131+
}
130132

131-
if p.at(T![else]) {
132-
// test_err let_else_right_curly_brace
133-
// fn func() { let Some(_) = {Some(1)} else { panic!("h") };}
134-
if let Some(expr) = expr_after_eq {
135-
if BlockLike::is_blocklike(expr.kind()) {
136-
p.error(
137-
"right curly brace `}` before `else` in a `let...else` statement not allowed",
138-
)
139-
}
133+
if p.at(T![else]) {
134+
// test_err let_else_right_curly_brace
135+
// fn func() { let Some(_) = {Some(1)} else { panic!("h") };}
136+
if let Some(expr) = expr_after_eq {
137+
if BlockLike::is_blocklike(expr.kind()) {
138+
p.error(
139+
"right curly brace `}` before `else` in a `let...else` statement not allowed",
140+
)
140141
}
141-
142-
// test let_else
143-
// fn f() { let Some(x) = opt else { return }; }
144-
let m = p.start();
145-
p.bump(T![else]);
146-
block_expr(p);
147-
m.complete(p, LET_ELSE);
148142
}
149143

150-
match with_semi {
151-
Semicolon::Forbidden => (),
152-
Semicolon::Optional => {
153-
p.eat(T![;]);
154-
}
155-
Semicolon::Required => {
156-
p.expect(T![;]);
157-
}
144+
// test let_else
145+
// fn f() { let Some(x) = opt else { return }; }
146+
let m = p.start();
147+
p.bump(T![else]);
148+
block_expr(p);
149+
m.complete(p, LET_ELSE);
150+
}
151+
152+
match with_semi {
153+
Semicolon::Forbidden => (),
154+
Semicolon::Optional => {
155+
p.eat(T![;]);
156+
}
157+
Semicolon::Required => {
158+
p.expect(T![;]);
158159
}
159-
m.complete(p, LET_STMT);
160160
}
161161
}
162162

@@ -693,6 +693,17 @@ pub(crate) fn record_expr_field_list(p: &mut Parser<'_>) {
693693
// We permit `.. }` on the left-hand side of a destructuring assignment.
694694
if !p.at(T!['}']) {
695695
expr(p);
696+
697+
if p.at(T![,]) {
698+
// test_err comma_after_functional_update_syntax
699+
// fn foo() {
700+
// S { ..x, };
701+
// S { ..x, a: 0 }
702+
// }
703+
704+
// Do not bump, so we can support additional fields after this comma.
705+
p.error("cannot use a comma after the base struct");
706+
}
696707
}
697708
}
698709
T!['{'] => {

‎src/tools/rust-analyzer/crates/parser/src/grammar/items.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ pub(super) fn item_or_macro(p: &mut Parser<'_>, stop_on_r_curly: bool) {
7979
e.complete(p, ERROR);
8080
}
8181
EOF | T!['}'] => p.error("expected an item"),
82+
T![let] => error_let_stmt(p, "expected an item"),
8283
_ => p.err_and_bump("expected an item"),
8384
}
8485
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
SOURCE_FILE
2+
FN
3+
FN_KW "fn"
4+
WHITESPACE " "
5+
NAME
6+
IDENT "foo"
7+
PARAM_LIST
8+
L_PAREN "("
9+
R_PAREN ")"
10+
WHITESPACE " "
11+
BLOCK_EXPR
12+
STMT_LIST
13+
L_CURLY "{"
14+
WHITESPACE "\n "
15+
EXPR_STMT
16+
RECORD_EXPR
17+
PATH
18+
PATH_SEGMENT
19+
NAME_REF
20+
IDENT "S"
21+
WHITESPACE " "
22+
RECORD_EXPR_FIELD_LIST
23+
L_CURLY "{"
24+
WHITESPACE " "
25+
DOT2 ".."
26+
PATH_EXPR
27+
PATH
28+
PATH_SEGMENT
29+
NAME_REF
30+
IDENT "x"
31+
COMMA ","
32+
WHITESPACE " "
33+
R_CURLY "}"
34+
SEMICOLON ";"
35+
WHITESPACE "\n "
36+
RECORD_EXPR
37+
PATH
38+
PATH_SEGMENT
39+
NAME_REF
40+
IDENT "S"
41+
WHITESPACE " "
42+
RECORD_EXPR_FIELD_LIST
43+
L_CURLY "{"
44+
WHITESPACE " "
45+
DOT2 ".."
46+
PATH_EXPR
47+
PATH
48+
PATH_SEGMENT
49+
NAME_REF
50+
IDENT "x"
51+
COMMA ","
52+
WHITESPACE " "
53+
RECORD_EXPR_FIELD
54+
NAME_REF
55+
IDENT "a"
56+
COLON ":"
57+
WHITESPACE " "
58+
LITERAL
59+
INT_NUMBER "0"
60+
WHITESPACE " "
61+
R_CURLY "}"
62+
WHITESPACE "\n"
63+
R_CURLY "}"
64+
WHITESPACE "\n"
65+
error 22: cannot use a comma after the base struct
66+
error 38: cannot use a comma after the base struct
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
fn foo() {
2+
S { ..x, };
3+
S { ..x, a: 0 }
4+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
SOURCE_FILE
2+
ERROR
3+
LET_KW "let"
4+
WHITESPACE " "
5+
IDENT_PAT
6+
REF_KW "ref"
7+
WHITESPACE " "
8+
NAME
9+
IDENT "foo"
10+
COLON ":"
11+
WHITESPACE " "
12+
FN_PTR_TYPE
13+
FN_KW "fn"
14+
PARAM_LIST
15+
L_PAREN "("
16+
R_PAREN ")"
17+
WHITESPACE " "
18+
EQ "="
19+
WHITESPACE " "
20+
BIN_EXPR
21+
LITERAL
22+
INT_NUMBER "1"
23+
WHITESPACE " "
24+
PLUS "+"
25+
WHITESPACE " "
26+
LITERAL
27+
INT_NUMBER "3"
28+
SEMICOLON ";"
29+
WHITESPACE "\n"
30+
error 0: expected an item
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
let ref foo: fn() = 1 + 3;

‎src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -783,6 +783,7 @@ impl flags::AnalysisStats {
783783
closure_return_type_hints: ide::ClosureReturnTypeHints::Always,
784784
closure_capture_hints: true,
785785
binding_mode_hints: true,
786+
implicit_drop_hints: true,
786787
lifetime_elision_hints: ide::LifetimeElisionHints::Always,
787788
param_names_for_lifetime_elision_hints: true,
788789
hide_named_constructor_hints: false,

‎src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,8 @@ config_data! {
381381
inlayHints_expressionAdjustmentHints_hideOutsideUnsafe: bool = "false",
382382
/// Whether to show inlay hints as postfix ops (`.*` instead of `*`, etc).
383383
inlayHints_expressionAdjustmentHints_mode: AdjustmentHintsModeDef = "\"prefix\"",
384+
/// Whether to show implicit drop hints.
385+
inlayHints_implicitDrops_enable: bool = "false",
384386
/// Whether to show inlay type hints for elided lifetimes in function signatures.
385387
inlayHints_lifetimeElisionHints_enable: LifetimeElisionDef = "\"never\"",
386388
/// Whether to prefer using parameter names as the name for elided lifetime hints if possible.
@@ -1391,6 +1393,7 @@ impl Config {
13911393
type_hints: self.data.inlayHints_typeHints_enable,
13921394
parameter_hints: self.data.inlayHints_parameterHints_enable,
13931395
chaining_hints: self.data.inlayHints_chainingHints_enable,
1396+
implicit_drop_hints: self.data.inlayHints_implicitDrops_enable,
13941397
discriminant_hints: match self.data.inlayHints_discriminantHints_enable {
13951398
DiscriminantHintsDef::Always => ide::DiscriminantHints::Always,
13961399
DiscriminantHintsDef::Never => ide::DiscriminantHints::Never,

‎src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use ide_db::{
2222
base_db::{salsa::Durability, CrateGraph, ProcMacroPaths, ProcMacros},
2323
FxHashMap,
2424
};
25+
use itertools::Itertools;
2526
use load_cargo::{load_proc_macro, ProjectFolders};
2627
use proc_macro_api::ProcMacroServer;
2728
use project_model::{ProjectWorkspace, WorkspaceBuildScripts};
@@ -227,16 +228,12 @@ impl GlobalState {
227228
let mut i = 0;
228229
while i < workspaces.len() {
229230
if let Ok(w) = &workspaces[i] {
230-
let dupes: Vec<_> = workspaces
231+
let dupes: Vec<_> = workspaces[i + 1..]
231232
.iter()
232-
.enumerate()
233-
.skip(i + 1)
234-
.filter_map(|(i, it)| {
235-
it.as_ref().ok().filter(|ws| ws.eq_ignore_build_data(w)).map(|_| i)
236-
})
233+
.positions(|it| it.as_ref().is_ok_and(|ws| ws.eq_ignore_build_data(w)))
237234
.collect();
238235
dupes.into_iter().rev().for_each(|d| {
239-
_ = workspaces.remove(d);
236+
_ = workspaces.remove(d + i + 1);
240237
});
241238
}
242239
i += 1;

‎src/tools/rust-analyzer/crates/test-utils/src/minicore.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1054,6 +1054,10 @@ pub mod option {
10541054
Some(T),
10551055
}
10561056

1057+
// region:copy
1058+
impl<T: Copy> Copy for Option<T> {}
1059+
// endregion:copy
1060+
10571061
impl<T> Option<T> {
10581062
pub const fn unwrap(self) -> T {
10591063
match self {

‎src/tools/rust-analyzer/docs/user/generated_config.adoc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -564,6 +564,11 @@ Whether to hide inlay hints for type adjustments outside of `unsafe` blocks.
564564
--
565565
Whether to show inlay hints as postfix ops (`.*` instead of `*`, etc).
566566
--
567+
[[rust-analyzer.inlayHints.implicitDrops.enable]]rust-analyzer.inlayHints.implicitDrops.enable (default: `false`)::
568+
+
569+
--
570+
Whether to show implicit drop hints.
571+
--
567572
[[rust-analyzer.inlayHints.lifetimeElisionHints.enable]]rust-analyzer.inlayHints.lifetimeElisionHints.enable (default: `"never"`)::
568573
+
569574
--

‎src/tools/rust-analyzer/editors/code/package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1264,6 +1264,11 @@
12641264
"Show prefix or postfix depending on which uses less parenthesis, preferring postfix."
12651265
]
12661266
},
1267+
"rust-analyzer.inlayHints.implicitDrops.enable": {
1268+
"markdownDescription": "Whether to show implicit drop hints.",
1269+
"default": false,
1270+
"type": "boolean"
1271+
},
12671272
"rust-analyzer.inlayHints.lifetimeElisionHints.enable": {
12681273
"markdownDescription": "Whether to show inlay type hints for elided lifetimes in function signatures.",
12691274
"default": "never",

‎src/tools/rust-analyzer/editors/code/src/debug.ts

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import * as vscode from "vscode";
33
import * as path from "path";
44
import type * as ra from "./lsp_ext";
55

6-
import { Cargo, getRustcId, getSysroot } from "./toolchain";
6+
import { Cargo, type ExecutableInfo, getRustcId, getSysroot } from "./toolchain";
77
import type { Ctx } from "./ctx";
88
import { prepareEnv } from "./run";
99
import { unwrapUndefinable } from "./undefinable";
@@ -12,6 +12,7 @@ const debugOutput = vscode.window.createOutputChannel("Debug");
1212
type DebugConfigProvider = (
1313
config: ra.Runnable,
1414
executable: string,
15+
cargoWorkspace: string,
1516
env: Record<string, string>,
1617
sourceFileMap?: Record<string, string>,
1718
) => vscode.DebugConfiguration;
@@ -130,7 +131,7 @@ async function getDebugConfiguration(
130131
}
131132

132133
const env = prepareEnv(runnable, ctx.config.runnablesExtraEnv);
133-
const executable = await getDebugExecutable(runnable, env);
134+
const { executable, workspace: cargoWorkspace } = await getDebugExecutableInfo(runnable, env);
134135
let sourceFileMap = debugOptions.sourceFileMap;
135136
if (sourceFileMap === "auto") {
136137
// let's try to use the default toolchain
@@ -142,7 +143,13 @@ async function getDebugConfiguration(
142143
}
143144

144145
const provider = unwrapUndefinable(knownEngines[debugEngine.id]);
145-
const debugConfig = provider(runnable, simplifyPath(executable), env, sourceFileMap);
146+
const debugConfig = provider(
147+
runnable,
148+
simplifyPath(executable),
149+
cargoWorkspace,
150+
env,
151+
sourceFileMap,
152+
);
146153
if (debugConfig.type in debugOptions.engineSettings) {
147154
const settingsMap = (debugOptions.engineSettings as any)[debugConfig.type];
148155
for (var key in settingsMap) {
@@ -164,20 +171,21 @@ async function getDebugConfiguration(
164171
return debugConfig;
165172
}
166173

167-
async function getDebugExecutable(
174+
async function getDebugExecutableInfo(
168175
runnable: ra.Runnable,
169176
env: Record<string, string>,
170-
): Promise<string> {
177+
): Promise<ExecutableInfo> {
171178
const cargo = new Cargo(runnable.args.workspaceRoot || ".", debugOutput, env);
172-
const executable = await cargo.executableFromArgs(runnable.args.cargoArgs);
179+
const executableInfo = await cargo.executableInfoFromArgs(runnable.args.cargoArgs);
173180

174181
// if we are here, there were no compilation errors.
175-
return executable;
182+
return executableInfo;
176183
}
177184

178185
function getLldbDebugConfig(
179186
runnable: ra.Runnable,
180187
executable: string,
188+
cargoWorkspace: string,
181189
env: Record<string, string>,
182190
sourceFileMap?: Record<string, string>,
183191
): vscode.DebugConfiguration {
@@ -187,7 +195,7 @@ function getLldbDebugConfig(
187195
name: runnable.label,
188196
program: executable,
189197
args: runnable.args.executableArgs,
190-
cwd: runnable.args.workspaceRoot,
198+
cwd: cargoWorkspace || runnable.args.workspaceRoot,
191199
sourceMap: sourceFileMap,
192200
sourceLanguages: ["rust"],
193201
env,
@@ -197,6 +205,7 @@ function getLldbDebugConfig(
197205
function getCppvsDebugConfig(
198206
runnable: ra.Runnable,
199207
executable: string,
208+
cargoWorkspace: string,
200209
env: Record<string, string>,
201210
sourceFileMap?: Record<string, string>,
202211
): vscode.DebugConfiguration {
@@ -206,7 +215,7 @@ function getCppvsDebugConfig(
206215
name: runnable.label,
207216
program: executable,
208217
args: runnable.args.executableArgs,
209-
cwd: runnable.args.workspaceRoot,
218+
cwd: cargoWorkspace || runnable.args.workspaceRoot,
210219
sourceFileMap,
211220
env,
212221
};

‎src/tools/rust-analyzer/editors/code/src/toolchain.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,17 @@ import { unwrapUndefinable } from "./undefinable";
99

1010
interface CompilationArtifact {
1111
fileName: string;
12+
workspace: string;
1213
name: string;
1314
kind: string;
1415
isTest: boolean;
1516
}
1617

18+
export interface ExecutableInfo {
19+
executable: string;
20+
workspace: string;
21+
}
22+
1723
export interface ArtifactSpec {
1824
cargoArgs: string[];
1925
filter?: (artifacts: CompilationArtifact[]) => CompilationArtifact[];
@@ -68,6 +74,7 @@ export class Cargo {
6874
artifacts.push({
6975
fileName: message.executable,
7076
name: message.target.name,
77+
workspace: message.manifest_path.replace(/\/Cargo\.toml$/, ""),
7178
kind: message.target.kind[0],
7279
isTest: message.profile.test,
7380
});
@@ -86,7 +93,7 @@ export class Cargo {
8693
return spec.filter?.(artifacts) ?? artifacts;
8794
}
8895

89-
async executableFromArgs(args: readonly string[]): Promise<string> {
96+
async executableInfoFromArgs(args: readonly string[]): Promise<ExecutableInfo> {
9097
const artifacts = await this.getArtifacts(Cargo.artifactSpec(args));
9198

9299
if (artifacts.length === 0) {
@@ -96,7 +103,10 @@ export class Cargo {
96103
}
97104

98105
const artifact = unwrapUndefinable(artifacts[0]);
99-
return artifact.fileName;
106+
return {
107+
executable: artifact.fileName,
108+
workspace: artifact.workspace,
109+
};
100110
}
101111

102112
private async runCargo(

0 commit comments

Comments
 (0)
Please sign in to comment.