diff options
-rw-r--r-- | crates/assists/src/handlers/fill_match_arms.rs | 21 | ||||
-rw-r--r-- | crates/assists/src/handlers/replace_if_let_with_match.rs | 73 | ||||
-rw-r--r-- | crates/assists/src/utils.rs | 19 | ||||
-rw-r--r-- | crates/hir_def/src/db.rs | 15 | ||||
-rw-r--r-- | crates/hir_def/src/lib.rs | 15 | ||||
-rw-r--r-- | crates/hir_def/src/nameres.rs | 73 | ||||
-rw-r--r-- | crates/hir_def/src/nameres/path_resolution.rs | 41 | ||||
-rw-r--r-- | crates/hir_def/src/nameres/tests.rs | 62 | ||||
-rw-r--r-- | crates/hir_def/src/nameres/tests/block.rs | 26 | ||||
-rw-r--r-- | crates/ide/src/display/navigation_target.rs | 11 | ||||
-rw-r--r-- | crates/ide/src/references.rs | 16 | ||||
-rw-r--r-- | crates/ide_db/src/ty_filter.rs | 15 | ||||
-rw-r--r-- | crates/paths/src/lib.rs | 51 | ||||
-rw-r--r-- | docs/dev/README.md | 4 | ||||
-rw-r--r-- | editors/code/src/main.ts | 2 | ||||
-rw-r--r-- | editors/code/src/net.ts | 9 |
16 files changed, 342 insertions, 111 deletions
diff --git a/crates/assists/src/handlers/fill_match_arms.rs b/crates/assists/src/handlers/fill_match_arms.rs index da47187e4..7663d211d 100644 --- a/crates/assists/src/handlers/fill_match_arms.rs +++ b/crates/assists/src/handlers/fill_match_arms.rs | |||
@@ -8,7 +8,7 @@ use syntax::ast::{self, make, AstNode, MatchArm, NameOwner, Pat}; | |||
8 | use test_utils::mark; | 8 | use test_utils::mark; |
9 | 9 | ||
10 | use crate::{ | 10 | use crate::{ |
11 | utils::{render_snippet, Cursor}, | 11 | utils::{does_pat_match_variant, render_snippet, Cursor}, |
12 | AssistContext, AssistId, AssistKind, Assists, | 12 | AssistContext, AssistId, AssistKind, Assists, |
13 | }; | 13 | }; |
14 | 14 | ||
@@ -147,25 +147,6 @@ fn is_variant_missing(existing_arms: &mut Vec<MatchArm>, var: &Pat) -> bool { | |||
147 | }) | 147 | }) |
148 | } | 148 | } |
149 | 149 | ||
150 | fn does_pat_match_variant(pat: &Pat, var: &Pat) -> bool { | ||
151 | let first_node_text = |pat: &Pat| pat.syntax().first_child().map(|node| node.text()); | ||
152 | |||
153 | let pat_head = match pat { | ||
154 | Pat::IdentPat(bind_pat) => { | ||
155 | if let Some(p) = bind_pat.pat() { | ||
156 | first_node_text(&p) | ||
157 | } else { | ||
158 | return false; | ||
159 | } | ||
160 | } | ||
161 | pat => first_node_text(pat), | ||
162 | }; | ||
163 | |||
164 | let var_head = first_node_text(var); | ||
165 | |||
166 | pat_head == var_head | ||
167 | } | ||
168 | |||
169 | fn resolve_enum_def(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<hir::Enum> { | 150 | fn resolve_enum_def(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<hir::Enum> { |
170 | sema.type_of_expr(&expr)?.autoderef(sema.db).find_map(|ty| match ty.as_adt() { | 151 | sema.type_of_expr(&expr)?.autoderef(sema.db).find_map(|ty| match ty.as_adt() { |
171 | Some(Adt::Enum(e)) => Some(e), | 152 | Some(Adt::Enum(e)) => Some(e), |
diff --git a/crates/assists/src/handlers/replace_if_let_with_match.rs b/crates/assists/src/handlers/replace_if_let_with_match.rs index aee3397ab..aee880625 100644 --- a/crates/assists/src/handlers/replace_if_let_with_match.rs +++ b/crates/assists/src/handlers/replace_if_let_with_match.rs | |||
@@ -10,7 +10,10 @@ use syntax::{ | |||
10 | AstNode, | 10 | AstNode, |
11 | }; | 11 | }; |
12 | 12 | ||
13 | use crate::{utils::unwrap_trivial_block, AssistContext, AssistId, AssistKind, Assists}; | 13 | use crate::{ |
14 | utils::{does_pat_match_variant, unwrap_trivial_block}, | ||
15 | AssistContext, AssistId, AssistKind, Assists, | ||
16 | }; | ||
14 | 17 | ||
15 | // Assist: replace_if_let_with_match | 18 | // Assist: replace_if_let_with_match |
16 | // | 19 | // |
@@ -66,7 +69,13 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext) | |||
66 | .sema | 69 | .sema |
67 | .type_of_pat(&pat) | 70 | .type_of_pat(&pat) |
68 | .and_then(|ty| TryEnum::from_ty(&ctx.sema, &ty)) | 71 | .and_then(|ty| TryEnum::from_ty(&ctx.sema, &ty)) |
69 | .map(|it| it.sad_pattern()) | 72 | .map(|it| { |
73 | if does_pat_match_variant(&pat, &it.sad_pattern()) { | ||
74 | it.happy_pattern() | ||
75 | } else { | ||
76 | it.sad_pattern() | ||
77 | } | ||
78 | }) | ||
70 | .unwrap_or_else(|| make::wildcard_pat().into()); | 79 | .unwrap_or_else(|| make::wildcard_pat().into()); |
71 | let else_expr = unwrap_trivial_block(else_block); | 80 | let else_expr = unwrap_trivial_block(else_block); |
72 | make::match_arm(vec![pattern], else_expr) | 81 | make::match_arm(vec![pattern], else_expr) |
@@ -279,6 +288,36 @@ fn foo(x: Option<i32>) { | |||
279 | } | 288 | } |
280 | 289 | ||
281 | #[test] | 290 | #[test] |
291 | fn special_case_inverted_option() { | ||
292 | check_assist( | ||
293 | replace_if_let_with_match, | ||
294 | r#" | ||
295 | enum Option<T> { Some(T), None } | ||
296 | use Option::*; | ||
297 | |||
298 | fn foo(x: Option<i32>) { | ||
299 | $0if let None = x { | ||
300 | println!("none") | ||
301 | } else { | ||
302 | println!("some") | ||
303 | } | ||
304 | } | ||
305 | "#, | ||
306 | r#" | ||
307 | enum Option<T> { Some(T), None } | ||
308 | use Option::*; | ||
309 | |||
310 | fn foo(x: Option<i32>) { | ||
311 | match x { | ||
312 | None => println!("none"), | ||
313 | Some(_) => println!("some"), | ||
314 | } | ||
315 | } | ||
316 | "#, | ||
317 | ); | ||
318 | } | ||
319 | |||
320 | #[test] | ||
282 | fn special_case_result() { | 321 | fn special_case_result() { |
283 | check_assist( | 322 | check_assist( |
284 | replace_if_let_with_match, | 323 | replace_if_let_with_match, |
@@ -309,6 +348,36 @@ fn foo(x: Result<i32, ()>) { | |||
309 | } | 348 | } |
310 | 349 | ||
311 | #[test] | 350 | #[test] |
351 | fn special_case_inverted_result() { | ||
352 | check_assist( | ||
353 | replace_if_let_with_match, | ||
354 | r#" | ||
355 | enum Result<T, E> { Ok(T), Err(E) } | ||
356 | use Result::*; | ||
357 | |||
358 | fn foo(x: Result<i32, ()>) { | ||
359 | $0if let Err(x) = x { | ||
360 | println!("{}", x) | ||
361 | } else { | ||
362 | println!("ok") | ||
363 | } | ||
364 | } | ||
365 | "#, | ||
366 | r#" | ||
367 | enum Result<T, E> { Ok(T), Err(E) } | ||
368 | use Result::*; | ||
369 | |||
370 | fn foo(x: Result<i32, ()>) { | ||
371 | match x { | ||
372 | Err(x) => println!("{}", x), | ||
373 | Ok(_) => println!("ok"), | ||
374 | } | ||
375 | } | ||
376 | "#, | ||
377 | ); | ||
378 | } | ||
379 | |||
380 | #[test] | ||
312 | fn nested_indent() { | 381 | fn nested_indent() { |
313 | check_assist( | 382 | check_assist( |
314 | replace_if_let_with_match, | 383 | replace_if_let_with_match, |
diff --git a/crates/assists/src/utils.rs b/crates/assists/src/utils.rs index 44c35bafa..4e762e18b 100644 --- a/crates/assists/src/utils.rs +++ b/crates/assists/src/utils.rs | |||
@@ -248,3 +248,22 @@ fn invert_special_case(expr: &ast::Expr) -> Option<ast::Expr> { | |||
248 | pub(crate) fn next_prev() -> impl Iterator<Item = Direction> { | 248 | pub(crate) fn next_prev() -> impl Iterator<Item = Direction> { |
249 | [Direction::Next, Direction::Prev].iter().copied() | 249 | [Direction::Next, Direction::Prev].iter().copied() |
250 | } | 250 | } |
251 | |||
252 | pub(crate) fn does_pat_match_variant(pat: &ast::Pat, var: &ast::Pat) -> bool { | ||
253 | let first_node_text = |pat: &ast::Pat| pat.syntax().first_child().map(|node| node.text()); | ||
254 | |||
255 | let pat_head = match pat { | ||
256 | ast::Pat::IdentPat(bind_pat) => { | ||
257 | if let Some(p) = bind_pat.pat() { | ||
258 | first_node_text(&p) | ||
259 | } else { | ||
260 | return pat.syntax().text() == var.syntax().text(); | ||
261 | } | ||
262 | } | ||
263 | pat => first_node_text(pat), | ||
264 | }; | ||
265 | |||
266 | let var_head = first_node_text(var); | ||
267 | |||
268 | pat_head == var_head | ||
269 | } | ||
diff --git a/crates/hir_def/src/db.rs b/crates/hir_def/src/db.rs index a87c80b8a..aef7e1f6c 100644 --- a/crates/hir_def/src/db.rs +++ b/crates/hir_def/src/db.rs | |||
@@ -2,9 +2,9 @@ | |||
2 | use std::sync::Arc; | 2 | use std::sync::Arc; |
3 | 3 | ||
4 | use base_db::{salsa, CrateId, SourceDatabase, Upcast}; | 4 | use base_db::{salsa, CrateId, SourceDatabase, Upcast}; |
5 | use hir_expand::{db::AstDatabase, AstId, HirFileId}; | 5 | use hir_expand::{db::AstDatabase, HirFileId}; |
6 | use la_arena::ArenaMap; | 6 | use la_arena::ArenaMap; |
7 | use syntax::{ast, SmolStr}; | 7 | use syntax::SmolStr; |
8 | 8 | ||
9 | use crate::{ | 9 | use crate::{ |
10 | adt::{EnumData, StructData}, | 10 | adt::{EnumData, StructData}, |
@@ -16,9 +16,10 @@ use crate::{ | |||
16 | item_tree::ItemTree, | 16 | item_tree::ItemTree, |
17 | lang_item::{LangItemTarget, LangItems}, | 17 | lang_item::{LangItemTarget, LangItems}, |
18 | nameres::DefMap, | 18 | nameres::DefMap, |
19 | AttrDefId, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, FunctionId, FunctionLoc, | 19 | AttrDefId, BlockId, BlockLoc, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, FunctionId, |
20 | GenericDefId, ImplId, ImplLoc, LocalEnumVariantId, LocalFieldId, StaticId, StaticLoc, StructId, | 20 | FunctionLoc, GenericDefId, ImplId, ImplLoc, LocalEnumVariantId, LocalFieldId, StaticId, |
21 | StructLoc, TraitId, TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, UnionLoc, VariantId, | 21 | StaticLoc, StructId, StructLoc, TraitId, TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, |
22 | UnionLoc, VariantId, | ||
22 | }; | 23 | }; |
23 | 24 | ||
24 | #[salsa::query_group(InternDatabaseStorage)] | 25 | #[salsa::query_group(InternDatabaseStorage)] |
@@ -41,6 +42,8 @@ pub trait InternDatabase: SourceDatabase { | |||
41 | fn intern_type_alias(&self, loc: TypeAliasLoc) -> TypeAliasId; | 42 | fn intern_type_alias(&self, loc: TypeAliasLoc) -> TypeAliasId; |
42 | #[salsa::interned] | 43 | #[salsa::interned] |
43 | fn intern_impl(&self, loc: ImplLoc) -> ImplId; | 44 | fn intern_impl(&self, loc: ImplLoc) -> ImplId; |
45 | #[salsa::interned] | ||
46 | fn intern_block(&self, loc: BlockLoc) -> BlockId; | ||
44 | } | 47 | } |
45 | 48 | ||
46 | #[salsa::query_group(DefDatabaseStorage)] | 49 | #[salsa::query_group(DefDatabaseStorage)] |
@@ -56,7 +59,7 @@ pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> { | |||
56 | fn crate_def_map_query(&self, krate: CrateId) -> Arc<DefMap>; | 59 | fn crate_def_map_query(&self, krate: CrateId) -> Arc<DefMap>; |
57 | 60 | ||
58 | #[salsa::invoke(DefMap::block_def_map_query)] | 61 | #[salsa::invoke(DefMap::block_def_map_query)] |
59 | fn block_def_map(&self, krate: CrateId, block: AstId<ast::BlockExpr>) -> Arc<DefMap>; | 62 | fn block_def_map(&self, block: BlockId) -> Arc<DefMap>; |
60 | 63 | ||
61 | #[salsa::invoke(StructData::struct_data_query)] | 64 | #[salsa::invoke(StructData::struct_data_query)] |
62 | fn struct_data(&self, id: StructId) -> Arc<StructData>; | 65 | fn struct_data(&self, id: StructId) -> Arc<StructData>; |
diff --git a/crates/hir_def/src/lib.rs b/crates/hir_def/src/lib.rs index cf09ebd3f..42b50b5b7 100644 --- a/crates/hir_def/src/lib.rs +++ b/crates/hir_def/src/lib.rs | |||
@@ -74,12 +74,16 @@ use stdx::impl_from; | |||
74 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 74 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
75 | pub struct ModuleId { | 75 | pub struct ModuleId { |
76 | krate: CrateId, | 76 | krate: CrateId, |
77 | block: Option<BlockId>, | ||
77 | pub local_id: LocalModuleId, | 78 | pub local_id: LocalModuleId, |
78 | } | 79 | } |
79 | 80 | ||
80 | impl ModuleId { | 81 | impl ModuleId { |
81 | pub fn def_map(&self, db: &dyn db::DefDatabase) -> Arc<DefMap> { | 82 | pub fn def_map(&self, db: &dyn db::DefDatabase) -> Arc<DefMap> { |
82 | db.crate_def_map(self.krate) | 83 | match self.block { |
84 | Some(block) => db.block_def_map(block), | ||
85 | None => db.crate_def_map(self.krate), | ||
86 | } | ||
83 | } | 87 | } |
84 | 88 | ||
85 | pub fn krate(&self) -> CrateId { | 89 | pub fn krate(&self) -> CrateId { |
@@ -230,6 +234,15 @@ pub struct ImplId(salsa::InternId); | |||
230 | type ImplLoc = ItemLoc<Impl>; | 234 | type ImplLoc = ItemLoc<Impl>; |
231 | impl_intern!(ImplId, ImplLoc, intern_impl, lookup_intern_impl); | 235 | impl_intern!(ImplId, ImplLoc, intern_impl, lookup_intern_impl); |
232 | 236 | ||
237 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)] | ||
238 | pub struct BlockId(salsa::InternId); | ||
239 | #[derive(Debug, Hash, PartialEq, Eq, Clone)] | ||
240 | pub struct BlockLoc { | ||
241 | ast_id: AstId<ast::BlockExpr>, | ||
242 | module: ModuleId, | ||
243 | } | ||
244 | impl_intern!(BlockId, BlockLoc, intern_block, lookup_intern_block); | ||
245 | |||
233 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 246 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
234 | pub struct TypeParamId { | 247 | pub struct TypeParamId { |
235 | pub parent: GenericDefId, | 248 | pub parent: GenericDefId, |
diff --git a/crates/hir_def/src/nameres.rs b/crates/hir_def/src/nameres.rs index 4fbbecb38..199771e9a 100644 --- a/crates/hir_def/src/nameres.rs +++ b/crates/hir_def/src/nameres.rs | |||
@@ -62,7 +62,7 @@ use la_arena::Arena; | |||
62 | use profile::Count; | 62 | use profile::Count; |
63 | use rustc_hash::FxHashMap; | 63 | use rustc_hash::FxHashMap; |
64 | use stdx::format_to; | 64 | use stdx::format_to; |
65 | use syntax::{ast, AstNode}; | 65 | use syntax::ast; |
66 | 66 | ||
67 | use crate::{ | 67 | use crate::{ |
68 | db::DefDatabase, | 68 | db::DefDatabase, |
@@ -70,14 +70,14 @@ use crate::{ | |||
70 | nameres::{diagnostics::DefDiagnostic, path_resolution::ResolveMode}, | 70 | nameres::{diagnostics::DefDiagnostic, path_resolution::ResolveMode}, |
71 | path::ModPath, | 71 | path::ModPath, |
72 | per_ns::PerNs, | 72 | per_ns::PerNs, |
73 | AstId, LocalModuleId, ModuleDefId, ModuleId, | 73 | AstId, BlockId, BlockLoc, LocalModuleId, ModuleDefId, ModuleId, |
74 | }; | 74 | }; |
75 | 75 | ||
76 | /// Contains all top-level defs from a macro-expanded crate | 76 | /// Contains all top-level defs from a macro-expanded crate |
77 | #[derive(Debug, PartialEq, Eq)] | 77 | #[derive(Debug, PartialEq, Eq)] |
78 | pub struct DefMap { | 78 | pub struct DefMap { |
79 | _c: Count<Self>, | 79 | _c: Count<Self>, |
80 | parent: Option<Arc<DefMap>>, | 80 | block: Option<BlockInfo>, |
81 | root: LocalModuleId, | 81 | root: LocalModuleId, |
82 | modules: Arena<ModuleData>, | 82 | modules: Arena<ModuleData>, |
83 | krate: CrateId, | 83 | krate: CrateId, |
@@ -91,6 +91,13 @@ pub struct DefMap { | |||
91 | diagnostics: Vec<DefDiagnostic>, | 91 | diagnostics: Vec<DefDiagnostic>, |
92 | } | 92 | } |
93 | 93 | ||
94 | #[derive(Debug, PartialEq, Eq)] | ||
95 | struct BlockInfo { | ||
96 | block: BlockId, | ||
97 | parent: Arc<DefMap>, | ||
98 | parent_module: LocalModuleId, | ||
99 | } | ||
100 | |||
94 | impl std::ops::Index<LocalModuleId> for DefMap { | 101 | impl std::ops::Index<LocalModuleId> for DefMap { |
95 | type Output = ModuleData; | 102 | type Output = ModuleData; |
96 | fn index(&self, id: LocalModuleId) -> &ModuleData { | 103 | fn index(&self, id: LocalModuleId) -> &ModuleData { |
@@ -190,15 +197,12 @@ impl DefMap { | |||
190 | Arc::new(def_map) | 197 | Arc::new(def_map) |
191 | } | 198 | } |
192 | 199 | ||
193 | pub(crate) fn block_def_map_query( | 200 | pub(crate) fn block_def_map_query(db: &dyn DefDatabase, block_id: BlockId) -> Arc<DefMap> { |
194 | db: &dyn DefDatabase, | 201 | let block: BlockLoc = db.lookup_intern_block(block_id); |
195 | krate: CrateId, | 202 | let item_tree = db.item_tree(block.ast_id.file_id); |
196 | block: AstId<ast::BlockExpr>, | 203 | let block_items = item_tree.inner_items_of_block(block.ast_id.value); |
197 | ) -> Arc<DefMap> { | ||
198 | let item_tree = db.item_tree(block.file_id); | ||
199 | let block_items = item_tree.inner_items_of_block(block.value); | ||
200 | 204 | ||
201 | let parent = parent_def_map(db, krate, block); | 205 | let parent = block.module.def_map(db); |
202 | 206 | ||
203 | if block_items.is_empty() { | 207 | if block_items.is_empty() { |
204 | // If there are no inner items, nothing new is brought into scope, so we can just return | 208 | // If there are no inner items, nothing new is brought into scope, so we can just return |
@@ -206,10 +210,13 @@ impl DefMap { | |||
206 | return parent; | 210 | return parent; |
207 | } | 211 | } |
208 | 212 | ||
209 | let mut def_map = DefMap::empty(krate, parent.edition); | 213 | let block_info = |
210 | def_map.parent = Some(parent); | 214 | BlockInfo { block: block_id, parent, parent_module: block.module.local_id }; |
215 | |||
216 | let mut def_map = DefMap::empty(block.module.krate, block_info.parent.edition); | ||
217 | def_map.block = Some(block_info); | ||
211 | 218 | ||
212 | let def_map = collector::collect_defs(db, def_map, Some(block.value)); | 219 | let def_map = collector::collect_defs(db, def_map, Some(block.ast_id.value)); |
213 | Arc::new(def_map) | 220 | Arc::new(def_map) |
214 | } | 221 | } |
215 | 222 | ||
@@ -218,7 +225,7 @@ impl DefMap { | |||
218 | let root = modules.alloc(ModuleData::default()); | 225 | let root = modules.alloc(ModuleData::default()); |
219 | DefMap { | 226 | DefMap { |
220 | _c: Count::new(), | 227 | _c: Count::new(), |
221 | parent: None, | 228 | block: None, |
222 | krate, | 229 | krate, |
223 | edition, | 230 | edition, |
224 | extern_prelude: FxHashMap::default(), | 231 | extern_prelude: FxHashMap::default(), |
@@ -266,7 +273,8 @@ impl DefMap { | |||
266 | } | 273 | } |
267 | 274 | ||
268 | pub fn module_id(&self, local_id: LocalModuleId) -> ModuleId { | 275 | pub fn module_id(&self, local_id: LocalModuleId) -> ModuleId { |
269 | ModuleId { krate: self.krate, local_id } | 276 | let block = self.block.as_ref().map(|b| b.block); |
277 | ModuleId { krate: self.krate, local_id, block } | ||
270 | } | 278 | } |
271 | 279 | ||
272 | pub(crate) fn resolve_path( | 280 | pub(crate) fn resolve_path( |
@@ -286,9 +294,9 @@ impl DefMap { | |||
286 | pub fn dump(&self) -> String { | 294 | pub fn dump(&self) -> String { |
287 | let mut buf = String::new(); | 295 | let mut buf = String::new(); |
288 | let mut current_map = self; | 296 | let mut current_map = self; |
289 | while let Some(parent) = ¤t_map.parent { | 297 | while let Some(block) = ¤t_map.block { |
290 | go(&mut buf, current_map, "block scope", current_map.root); | 298 | go(&mut buf, current_map, "block scope", current_map.root); |
291 | current_map = &**parent; | 299 | current_map = &*block.parent; |
292 | } | 300 | } |
293 | go(&mut buf, current_map, "crate", current_map.root); | 301 | go(&mut buf, current_map, "crate", current_map.root); |
294 | return buf; | 302 | return buf; |
@@ -342,35 +350,6 @@ impl ModuleData { | |||
342 | } | 350 | } |
343 | } | 351 | } |
344 | 352 | ||
345 | fn parent_def_map( | ||
346 | db: &dyn DefDatabase, | ||
347 | krate: CrateId, | ||
348 | block: AstId<ast::BlockExpr>, | ||
349 | ) -> Arc<DefMap> { | ||
350 | // FIXME: store this info in the item tree instead of reparsing here | ||
351 | let ast_id_map = db.ast_id_map(block.file_id); | ||
352 | let block_ptr = ast_id_map.get(block.value); | ||
353 | let root = match db.parse_or_expand(block.file_id) { | ||
354 | Some(it) => it, | ||
355 | None => { | ||
356 | return Arc::new(DefMap::empty(krate, Edition::Edition2018)); | ||
357 | } | ||
358 | }; | ||
359 | let ast = block_ptr.to_node(&root); | ||
360 | |||
361 | for ancestor in ast.syntax().ancestors().skip(1) { | ||
362 | if let Some(block_expr) = ast::BlockExpr::cast(ancestor) { | ||
363 | let ancestor_id = ast_id_map.ast_id(&block_expr); | ||
364 | let ast_id = InFile::new(block.file_id, ancestor_id); | ||
365 | let parent_map = db.block_def_map(krate, ast_id); | ||
366 | return parent_map; | ||
367 | } | ||
368 | } | ||
369 | |||
370 | // No enclosing block scope, so the parent is the crate-level DefMap. | ||
371 | db.crate_def_map(krate) | ||
372 | } | ||
373 | |||
374 | #[derive(Debug, Clone, PartialEq, Eq)] | 353 | #[derive(Debug, Clone, PartialEq, Eq)] |
375 | pub enum ModuleSource { | 354 | pub enum ModuleSource { |
376 | SourceFile(ast::SourceFile), | 355 | SourceFile(ast::SourceFile), |
diff --git a/crates/hir_def/src/nameres/path_resolution.rs b/crates/hir_def/src/nameres/path_resolution.rs index c1eded5f2..419e465ed 100644 --- a/crates/hir_def/src/nameres/path_resolution.rs +++ b/crates/hir_def/src/nameres/path_resolution.rs | |||
@@ -10,8 +10,6 @@ | |||
10 | //! | 10 | //! |
11 | //! `ReachedFixedPoint` signals about this. | 11 | //! `ReachedFixedPoint` signals about this. |
12 | 12 | ||
13 | use std::iter::successors; | ||
14 | |||
15 | use base_db::Edition; | 13 | use base_db::Edition; |
16 | use hir_expand::name; | 14 | use hir_expand::name; |
17 | use hir_expand::name::Name; | 15 | use hir_expand::name::Name; |
@@ -131,8 +129,8 @@ impl DefMap { | |||
131 | result.krate = result.krate.or(new.krate); | 129 | result.krate = result.krate.or(new.krate); |
132 | result.segment_index = result.segment_index.min(new.segment_index); | 130 | result.segment_index = result.segment_index.min(new.segment_index); |
133 | 131 | ||
134 | match ¤t_map.parent { | 132 | match ¤t_map.block { |
135 | Some(map) => current_map = map, | 133 | Some(block) => current_map = &block.parent, |
136 | None => return result, | 134 | None => return result, |
137 | } | 135 | } |
138 | } | 136 | } |
@@ -193,14 +191,35 @@ impl DefMap { | |||
193 | self.resolve_name_in_module(db, original_module, &segment, prefer_module) | 191 | self.resolve_name_in_module(db, original_module, &segment, prefer_module) |
194 | } | 192 | } |
195 | PathKind::Super(lvl) => { | 193 | PathKind::Super(lvl) => { |
196 | let m = successors(Some(original_module), |m| self.modules[*m].parent) | 194 | let mut module = original_module; |
197 | .nth(lvl as usize); | 195 | for i in 0..lvl { |
198 | if let Some(local_id) = m { | 196 | match self.modules[module].parent { |
199 | PerNs::types(self.module_id(local_id).into(), Visibility::Public) | 197 | Some(it) => module = it, |
200 | } else { | 198 | None => match &self.block { |
201 | log::debug!("super path in root module"); | 199 | Some(block) => { |
202 | return ResolvePathResult::empty(ReachedFixedPoint::Yes); | 200 | // Look up remaining path in parent `DefMap` |
201 | let new_path = ModPath { | ||
202 | kind: PathKind::Super(lvl - i), | ||
203 | segments: path.segments.clone(), | ||
204 | }; | ||
205 | log::debug!("`super` path: {} -> {} in parent map", path, new_path); | ||
206 | return block.parent.resolve_path_fp_with_macro( | ||
207 | db, | ||
208 | mode, | ||
209 | block.parent_module, | ||
210 | &new_path, | ||
211 | shadow, | ||
212 | ); | ||
213 | } | ||
214 | None => { | ||
215 | log::debug!("super path in root module"); | ||
216 | return ResolvePathResult::empty(ReachedFixedPoint::Yes); | ||
217 | } | ||
218 | }, | ||
219 | } | ||
203 | } | 220 | } |
221 | |||
222 | PerNs::types(self.module_id(module).into(), Visibility::Public) | ||
204 | } | 223 | } |
205 | PathKind::Abs => { | 224 | PathKind::Abs => { |
206 | // 2018-style absolute path -- only extern prelude | 225 | // 2018-style absolute path -- only extern prelude |
diff --git a/crates/hir_def/src/nameres/tests.rs b/crates/hir_def/src/nameres/tests.rs index 73e3a4702..b36d0b59b 100644 --- a/crates/hir_def/src/nameres/tests.rs +++ b/crates/hir_def/src/nameres/tests.rs | |||
@@ -8,12 +8,12 @@ mod block; | |||
8 | 8 | ||
9 | use std::sync::Arc; | 9 | use std::sync::Arc; |
10 | 10 | ||
11 | use base_db::{fixture::WithFixture, SourceDatabase}; | 11 | use base_db::{fixture::WithFixture, FilePosition, SourceDatabase}; |
12 | use expect_test::{expect, Expect}; | 12 | use expect_test::{expect, Expect}; |
13 | use hir_expand::db::AstDatabase; | 13 | use syntax::AstNode; |
14 | use test_utils::mark; | 14 | use test_utils::mark; |
15 | 15 | ||
16 | use crate::{db::DefDatabase, nameres::*, test_db::TestDB}; | 16 | use crate::{db::DefDatabase, nameres::*, test_db::TestDB, Lookup}; |
17 | 17 | ||
18 | fn compute_crate_def_map(ra_fixture: &str) -> Arc<DefMap> { | 18 | fn compute_crate_def_map(ra_fixture: &str) -> Arc<DefMap> { |
19 | let db = TestDB::with_files(ra_fixture); | 19 | let db = TestDB::with_files(ra_fixture); |
@@ -23,14 +23,58 @@ fn compute_crate_def_map(ra_fixture: &str) -> Arc<DefMap> { | |||
23 | 23 | ||
24 | fn compute_block_def_map(ra_fixture: &str) -> Arc<DefMap> { | 24 | fn compute_block_def_map(ra_fixture: &str) -> Arc<DefMap> { |
25 | let (db, position) = TestDB::with_position(ra_fixture); | 25 | let (db, position) = TestDB::with_position(ra_fixture); |
26 | |||
27 | // FIXME: perhaps we should make this use body lowering tests instead? | ||
28 | |||
26 | let module = db.module_for_file(position.file_id); | 29 | let module = db.module_for_file(position.file_id); |
27 | let ast_map = db.ast_id_map(position.file_id.into()); | 30 | let mut def_map = db.crate_def_map(module.krate); |
28 | let ast = db.parse(position.file_id); | 31 | while let Some(new_def_map) = descend_def_map_at_position(&db, position, def_map.clone()) { |
29 | let block: ast::BlockExpr = | 32 | def_map = new_def_map; |
30 | syntax::algo::find_node_at_offset(&ast.syntax_node(), position.offset).unwrap(); | 33 | } |
31 | let block_id = ast_map.ast_id(&block); | 34 | |
35 | // FIXME: select the right module, not the root | ||
36 | |||
37 | def_map | ||
38 | } | ||
39 | |||
40 | fn descend_def_map_at_position( | ||
41 | db: &dyn DefDatabase, | ||
42 | position: FilePosition, | ||
43 | def_map: Arc<DefMap>, | ||
44 | ) -> Option<Arc<DefMap>> { | ||
45 | for (local_id, module_data) in def_map.modules() { | ||
46 | let mod_def = module_data.origin.definition_source(db); | ||
47 | let ast_map = db.ast_id_map(mod_def.file_id); | ||
48 | let item_tree = db.item_tree(mod_def.file_id); | ||
49 | let root = db.parse_or_expand(mod_def.file_id).unwrap(); | ||
50 | for item in module_data.scope.declarations() { | ||
51 | match item { | ||
52 | ModuleDefId::FunctionId(it) => { | ||
53 | // Technically blocks can be inside any type (due to arrays and const generics), | ||
54 | // and also in const/static initializers. For tests we only really care about | ||
55 | // functions though. | ||
56 | |||
57 | let ast = ast_map.get(item_tree[it.lookup(db).id.value].ast_id).to_node(&root); | ||
58 | |||
59 | if ast.syntax().text_range().contains(position.offset) { | ||
60 | // Cursor inside function, descend into its body's DefMap. | ||
61 | // Note that we don't handle block *expressions* inside function bodies. | ||
62 | let ast_map = db.ast_id_map(position.file_id.into()); | ||
63 | let ast_id = ast_map.ast_id(&ast.body().unwrap()); | ||
64 | let block = BlockLoc { | ||
65 | ast_id: InFile::new(position.file_id.into(), ast_id), | ||
66 | module: def_map.module_id(local_id), | ||
67 | }; | ||
68 | let block_id = db.intern_block(block); | ||
69 | return Some(db.block_def_map(block_id)); | ||
70 | } | ||
71 | } | ||
72 | _ => continue, | ||
73 | } | ||
74 | } | ||
75 | } | ||
32 | 76 | ||
33 | db.block_def_map(module.krate, InFile::new(position.file_id.into(), block_id)) | 77 | None |
34 | } | 78 | } |
35 | 79 | ||
36 | fn check(ra_fixture: &str, expect: Expect) { | 80 | fn check(ra_fixture: &str, expect: Expect) { |
diff --git a/crates/hir_def/src/nameres/tests/block.rs b/crates/hir_def/src/nameres/tests/block.rs index 01d6326a7..470ca593e 100644 --- a/crates/hir_def/src/nameres/tests/block.rs +++ b/crates/hir_def/src/nameres/tests/block.rs | |||
@@ -95,3 +95,29 @@ fn outer() { | |||
95 | "#]], | 95 | "#]], |
96 | ); | 96 | ); |
97 | } | 97 | } |
98 | |||
99 | #[test] | ||
100 | fn super_imports() { | ||
101 | check_at( | ||
102 | r#" | ||
103 | mod module { | ||
104 | fn f() { | ||
105 | use super::Struct; | ||
106 | $0 | ||
107 | } | ||
108 | } | ||
109 | |||
110 | struct Struct {} | ||
111 | "#, | ||
112 | expect![[r#" | ||
113 | block scope | ||
114 | Struct: t | ||
115 | crate | ||
116 | Struct: t | ||
117 | module: t | ||
118 | |||
119 | crate::module | ||
120 | f: v | ||
121 | "#]], | ||
122 | ); | ||
123 | } | ||
diff --git a/crates/ide/src/display/navigation_target.rs b/crates/ide/src/display/navigation_target.rs index f178dd744..23d885218 100644 --- a/crates/ide/src/display/navigation_target.rs +++ b/crates/ide/src/display/navigation_target.rs | |||
@@ -435,13 +435,16 @@ impl TryToNav for hir::TypeParam { | |||
435 | fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> { | 435 | fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> { |
436 | let src = self.source(db)?; | 436 | let src = self.source(db)?; |
437 | let full_range = match &src.value { | 437 | let full_range = match &src.value { |
438 | Either::Left(it) => it.syntax().text_range(), | 438 | Either::Left(it) => it |
439 | .name() | ||
440 | .map_or_else(|| it.syntax().text_range(), |name| name.syntax().text_range()), | ||
439 | Either::Right(it) => it.syntax().text_range(), | 441 | Either::Right(it) => it.syntax().text_range(), |
440 | }; | 442 | }; |
441 | let focus_range = match &src.value { | 443 | let focus_range = match &src.value { |
442 | Either::Left(_) => None, | 444 | Either::Left(it) => it.name(), |
443 | Either::Right(it) => it.name().map(|it| it.syntax().text_range()), | 445 | Either::Right(it) => it.name(), |
444 | }; | 446 | } |
447 | .map(|it| it.syntax().text_range()); | ||
445 | Some(NavigationTarget { | 448 | Some(NavigationTarget { |
446 | file_id: src.file_id.original_file(db), | 449 | file_id: src.file_id.original_file(db), |
447 | name: self.name(db).to_string().into(), | 450 | name: self.name(db).to_string().into(), |
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs index 3a4f4d80b..40d9487eb 100644 --- a/crates/ide/src/references.rs +++ b/crates/ide/src/references.rs | |||
@@ -1098,4 +1098,20 @@ fn foo<const FOO$0: usize>() -> usize { | |||
1098 | "#]], | 1098 | "#]], |
1099 | ); | 1099 | ); |
1100 | } | 1100 | } |
1101 | |||
1102 | #[test] | ||
1103 | fn test_find_self_ty_in_trait_def() { | ||
1104 | check( | ||
1105 | r#" | ||
1106 | trait Foo { | ||
1107 | fn f() -> Self$0; | ||
1108 | } | ||
1109 | "#, | ||
1110 | expect![[r#" | ||
1111 | Self TypeParam FileId(0) 6..9 6..9 Other | ||
1112 | |||
1113 | FileId(0) 26..30 Other | ||
1114 | "#]], | ||
1115 | ); | ||
1116 | } | ||
1101 | } | 1117 | } |
diff --git a/crates/ide_db/src/ty_filter.rs b/crates/ide_db/src/ty_filter.rs index 63a945282..f8406851b 100644 --- a/crates/ide_db/src/ty_filter.rs +++ b/crates/ide_db/src/ty_filter.rs | |||
@@ -49,6 +49,21 @@ impl TryEnum { | |||
49 | } | 49 | } |
50 | } | 50 | } |
51 | 51 | ||
52 | pub fn happy_pattern(self) -> ast::Pat { | ||
53 | match self { | ||
54 | TryEnum::Result => make::tuple_struct_pat( | ||
55 | make::path_unqualified(make::path_segment(make::name_ref("Ok"))), | ||
56 | iter::once(make::wildcard_pat().into()), | ||
57 | ) | ||
58 | .into(), | ||
59 | TryEnum::Option => make::tuple_struct_pat( | ||
60 | make::path_unqualified(make::path_segment(make::name_ref("Some"))), | ||
61 | iter::once(make::wildcard_pat().into()), | ||
62 | ) | ||
63 | .into(), | ||
64 | } | ||
65 | } | ||
66 | |||
52 | fn type_name(self) -> &'static str { | 67 | fn type_name(self) -> &'static str { |
53 | match self { | 68 | match self { |
54 | TryEnum::Result => "Result", | 69 | TryEnum::Result => "Result", |
diff --git a/crates/paths/src/lib.rs b/crates/paths/src/lib.rs index 1b259682d..22011cb33 100644 --- a/crates/paths/src/lib.rs +++ b/crates/paths/src/lib.rs | |||
@@ -6,6 +6,7 @@ use std::{ | |||
6 | path::{Component, Path, PathBuf}, | 6 | path::{Component, Path, PathBuf}, |
7 | }; | 7 | }; |
8 | 8 | ||
9 | /// Wrapper around an absolute [`PathBuf`]. | ||
9 | #[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] | 10 | #[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] |
10 | pub struct AbsPathBuf(PathBuf); | 11 | pub struct AbsPathBuf(PathBuf); |
11 | 12 | ||
@@ -58,18 +59,33 @@ impl PartialEq<AbsPath> for AbsPathBuf { | |||
58 | } | 59 | } |
59 | 60 | ||
60 | impl AbsPathBuf { | 61 | impl AbsPathBuf { |
62 | /// Wrap the given absolute path in `AbsPathBuf` | ||
63 | /// | ||
64 | /// # Panics | ||
65 | /// | ||
66 | /// Panics if `path` is not absolute. | ||
61 | pub fn assert(path: PathBuf) -> AbsPathBuf { | 67 | pub fn assert(path: PathBuf) -> AbsPathBuf { |
62 | AbsPathBuf::try_from(path) | 68 | AbsPathBuf::try_from(path) |
63 | .unwrap_or_else(|path| panic!("expected absolute path, got {}", path.display())) | 69 | .unwrap_or_else(|path| panic!("expected absolute path, got {}", path.display())) |
64 | } | 70 | } |
71 | |||
72 | /// Coerces to a `AbsPath` slice. | ||
73 | /// | ||
74 | /// Equivalent of [`PathBuf::as_path`] for `AbsPathBuf`. | ||
65 | pub fn as_path(&self) -> &AbsPath { | 75 | pub fn as_path(&self) -> &AbsPath { |
66 | AbsPath::assert(self.0.as_path()) | 76 | AbsPath::assert(self.0.as_path()) |
67 | } | 77 | } |
78 | |||
79 | /// Equivalent of [`PathBuf::pop`] for `AbsPathBuf`. | ||
80 | /// | ||
81 | /// Note that this won't remove the root component, so `self` will still be | ||
82 | /// absolute. | ||
68 | pub fn pop(&mut self) -> bool { | 83 | pub fn pop(&mut self) -> bool { |
69 | self.0.pop() | 84 | self.0.pop() |
70 | } | 85 | } |
71 | } | 86 | } |
72 | 87 | ||
88 | /// Wrapper around an absolute [`Path`]. | ||
73 | #[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash)] | 89 | #[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash)] |
74 | #[repr(transparent)] | 90 | #[repr(transparent)] |
75 | pub struct AbsPath(Path); | 91 | pub struct AbsPath(Path); |
@@ -98,28 +114,56 @@ impl<'a> TryFrom<&'a Path> for &'a AbsPath { | |||
98 | } | 114 | } |
99 | 115 | ||
100 | impl AbsPath { | 116 | impl AbsPath { |
117 | /// Wrap the given absolute path in `AbsPath` | ||
118 | /// | ||
119 | /// # Panics | ||
120 | /// | ||
121 | /// Panics if `path` is not absolute. | ||
101 | pub fn assert(path: &Path) -> &AbsPath { | 122 | pub fn assert(path: &Path) -> &AbsPath { |
102 | assert!(path.is_absolute()); | 123 | assert!(path.is_absolute()); |
103 | unsafe { &*(path as *const Path as *const AbsPath) } | 124 | unsafe { &*(path as *const Path as *const AbsPath) } |
104 | } | 125 | } |
105 | 126 | ||
127 | /// Equivalent of [`Path::parent`] for `AbsPath`. | ||
106 | pub fn parent(&self) -> Option<&AbsPath> { | 128 | pub fn parent(&self) -> Option<&AbsPath> { |
107 | self.0.parent().map(AbsPath::assert) | 129 | self.0.parent().map(AbsPath::assert) |
108 | } | 130 | } |
131 | |||
132 | /// Equivalent of [`Path::join`] for `AbsPath`. | ||
109 | pub fn join(&self, path: impl AsRef<Path>) -> AbsPathBuf { | 133 | pub fn join(&self, path: impl AsRef<Path>) -> AbsPathBuf { |
110 | self.as_ref().join(path).try_into().unwrap() | 134 | self.as_ref().join(path).try_into().unwrap() |
111 | } | 135 | } |
136 | |||
137 | /// Normalize the given path: | ||
138 | /// - Removes repeated separators: `/a//b` becomes `/a/b` | ||
139 | /// - Removes occurrences of `.` and resolves `..`. | ||
140 | /// - Removes trailing slashes: `/a/b/` becomes `/a/b`. | ||
141 | /// | ||
142 | /// # Example | ||
143 | /// ``` | ||
144 | /// # use paths::AbsPathBuf; | ||
145 | /// let abs_path_buf = AbsPathBuf::assert("/a/../../b/.//c//".into()); | ||
146 | /// let normalized = abs_path_buf.normalize(); | ||
147 | /// assert_eq!(normalized, AbsPathBuf::assert("/b/c".into())); | ||
148 | /// ``` | ||
112 | pub fn normalize(&self) -> AbsPathBuf { | 149 | pub fn normalize(&self) -> AbsPathBuf { |
113 | AbsPathBuf(normalize_path(&self.0)) | 150 | AbsPathBuf(normalize_path(&self.0)) |
114 | } | 151 | } |
152 | |||
153 | /// Equivalent of [`Path::to_path_buf`] for `AbsPath`. | ||
115 | pub fn to_path_buf(&self) -> AbsPathBuf { | 154 | pub fn to_path_buf(&self) -> AbsPathBuf { |
116 | AbsPathBuf::try_from(self.0.to_path_buf()).unwrap() | 155 | AbsPathBuf::try_from(self.0.to_path_buf()).unwrap() |
117 | } | 156 | } |
157 | |||
158 | /// Equivalent of [`Path::strip_prefix`] for `AbsPath`. | ||
159 | /// | ||
160 | /// Returns a relative path. | ||
118 | pub fn strip_prefix(&self, base: &AbsPath) -> Option<&RelPath> { | 161 | pub fn strip_prefix(&self, base: &AbsPath) -> Option<&RelPath> { |
119 | self.0.strip_prefix(base).ok().map(RelPath::new_unchecked) | 162 | self.0.strip_prefix(base).ok().map(RelPath::new_unchecked) |
120 | } | 163 | } |
121 | } | 164 | } |
122 | 165 | ||
166 | /// Wrapper around a relative [`PathBuf`]. | ||
123 | #[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] | 167 | #[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] |
124 | pub struct RelPathBuf(PathBuf); | 168 | pub struct RelPathBuf(PathBuf); |
125 | 169 | ||
@@ -160,11 +204,15 @@ impl TryFrom<&str> for RelPathBuf { | |||
160 | } | 204 | } |
161 | 205 | ||
162 | impl RelPathBuf { | 206 | impl RelPathBuf { |
207 | /// Coerces to a `RelPath` slice. | ||
208 | /// | ||
209 | /// Equivalent of [`PathBuf::as_path`] for `RelPathBuf`. | ||
163 | pub fn as_path(&self) -> &RelPath { | 210 | pub fn as_path(&self) -> &RelPath { |
164 | RelPath::new_unchecked(self.0.as_path()) | 211 | RelPath::new_unchecked(self.0.as_path()) |
165 | } | 212 | } |
166 | } | 213 | } |
167 | 214 | ||
215 | /// Wrapper around a relative [`Path`]. | ||
168 | #[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash)] | 216 | #[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash)] |
169 | #[repr(transparent)] | 217 | #[repr(transparent)] |
170 | pub struct RelPath(Path); | 218 | pub struct RelPath(Path); |
@@ -183,12 +231,13 @@ impl AsRef<Path> for RelPath { | |||
183 | } | 231 | } |
184 | 232 | ||
185 | impl RelPath { | 233 | impl RelPath { |
234 | /// Creates a new `RelPath` from `path`, without checking if it is relative. | ||
186 | pub fn new_unchecked(path: &Path) -> &RelPath { | 235 | pub fn new_unchecked(path: &Path) -> &RelPath { |
187 | unsafe { &*(path as *const Path as *const RelPath) } | 236 | unsafe { &*(path as *const Path as *const RelPath) } |
188 | } | 237 | } |
189 | } | 238 | } |
190 | 239 | ||
191 | // https://github.com/rust-lang/cargo/blob/79c769c3d7b4c2cf6a93781575b7f592ef974255/src/cargo/util/paths.rs#L60-L85 | 240 | /// Taken from https://github.com/rust-lang/cargo/blob/79c769c3d7b4c2cf6a93781575b7f592ef974255/src/cargo/util/paths.rs#L60-L85 |
192 | fn normalize_path(path: &Path) -> PathBuf { | 241 | fn normalize_path(path: &Path) -> PathBuf { |
193 | let mut components = path.components().peekable(); | 242 | let mut components = path.components().peekable(); |
194 | let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().cloned() { | 243 | let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().cloned() { |
diff --git a/docs/dev/README.md b/docs/dev/README.md index 6a6ba1443..6bce38e56 100644 --- a/docs/dev/README.md +++ b/docs/dev/README.md | |||
@@ -43,6 +43,10 @@ https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Fwg-rls-2.2E0 | |||
43 | while unactionable ones are effectively wont-fix. Each triaged issue should have one of these labels. | 43 | while unactionable ones are effectively wont-fix. Each triaged issue should have one of these labels. |
44 | * [fun](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3Afun) | 44 | * [fun](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3Afun) |
45 | is for cool, but probably hard stuff. | 45 | is for cool, but probably hard stuff. |
46 | * [Design](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%Design) | ||
47 | is for moderate/large scale architecture discussion. | ||
48 | Also a kind of fun. | ||
49 | These issues should generally include a link to a Zulip discussion thread. | ||
46 | 50 | ||
47 | # CI | 51 | # CI |
48 | 52 | ||
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts index 1edb7713d..1900d900a 100644 --- a/editors/code/src/main.ts +++ b/editors/code/src/main.ts | |||
@@ -208,7 +208,6 @@ async function bootstrapExtension(config: Config, state: PersistentState): Promi | |||
208 | url: artifact.browser_download_url, | 208 | url: artifact.browser_download_url, |
209 | dest, | 209 | dest, |
210 | progressTitle: "Downloading rust-analyzer extension", | 210 | progressTitle: "Downloading rust-analyzer extension", |
211 | overwrite: true, | ||
212 | }); | 211 | }); |
213 | }); | 212 | }); |
214 | 213 | ||
@@ -340,7 +339,6 @@ async function getServer(config: Config, state: PersistentState): Promise<string | |||
340 | progressTitle: "Downloading rust-analyzer server", | 339 | progressTitle: "Downloading rust-analyzer server", |
341 | gunzip: true, | 340 | gunzip: true, |
342 | mode: 0o755, | 341 | mode: 0o755, |
343 | overwrite: true, | ||
344 | }); | 342 | }); |
345 | }); | 343 | }); |
346 | 344 | ||
diff --git a/editors/code/src/net.ts b/editors/code/src/net.ts index 3e50d352c..d39dc1baf 100644 --- a/editors/code/src/net.ts +++ b/editors/code/src/net.ts | |||
@@ -73,23 +73,16 @@ interface DownloadOpts { | |||
73 | dest: string; | 73 | dest: string; |
74 | mode?: number; | 74 | mode?: number; |
75 | gunzip?: boolean; | 75 | gunzip?: boolean; |
76 | overwrite?: boolean; | ||
77 | } | 76 | } |
78 | 77 | ||
79 | export async function download(opts: DownloadOpts) { | 78 | export async function download(opts: DownloadOpts) { |
80 | // Put artifact into a temporary file (in the same dir for simplicity) | 79 | // Put artifact into a temporary file (in the same dir for simplicity) |
81 | // to prevent partially downloaded files when user kills vscode | 80 | // to prevent partially downloaded files when user kills vscode |
81 | // This also avoids overwriting running executables | ||
82 | const dest = path.parse(opts.dest); | 82 | const dest = path.parse(opts.dest); |
83 | const randomHex = crypto.randomBytes(5).toString("hex"); | 83 | const randomHex = crypto.randomBytes(5).toString("hex"); |
84 | const tempFile = path.join(dest.dir, `${dest.name}${randomHex}`); | 84 | const tempFile = path.join(dest.dir, `${dest.name}${randomHex}`); |
85 | 85 | ||
86 | if (opts.overwrite) { | ||
87 | // Unlinking the exe file before moving new one on its place should prevent ETXTBSY error. | ||
88 | await fs.promises.unlink(opts.dest).catch(err => { | ||
89 | if (err.code !== "ENOENT") throw err; | ||
90 | }); | ||
91 | } | ||
92 | |||
93 | await vscode.window.withProgress( | 86 | await vscode.window.withProgress( |
94 | { | 87 | { |
95 | location: vscode.ProgressLocation.Notification, | 88 | location: vscode.ProgressLocation.Notification, |