diff options
47 files changed, 619 insertions, 305 deletions
diff --git a/Cargo.lock b/Cargo.lock index bf1b9e417..efd1c6d33 100644 --- a/Cargo.lock +++ b/Cargo.lock | |||
@@ -275,9 +275,9 @@ checksum = "28b9d6de7f49e22cf97ad17fc4036ece69300032f45f78f30b4a4482cdc3f4a6" | |||
275 | 275 | ||
276 | [[package]] | 276 | [[package]] |
277 | name = "countme" | 277 | name = "countme" |
278 | version = "2.0.0-pre.2" | 278 | version = "2.0.0" |
279 | source = "registry+https://github.com/rust-lang/crates.io-index" | 279 | source = "registry+https://github.com/rust-lang/crates.io-index" |
280 | checksum = "c5716604cba7c02a846ecad3f4a3fd2d2b641faccc2a24a51efb21aff0d01f35" | 280 | checksum = "4f8038ded86523aa26c1321dfe8bb432707d0a3e2608f9d0108803727546e089" |
281 | dependencies = [ | 281 | dependencies = [ |
282 | "dashmap", | 282 | "dashmap", |
283 | "once_cell", | 283 | "once_cell", |
@@ -1400,9 +1400,9 @@ checksum = "b5eb417147ba9860a96cfe72a0b93bf88fee1744b5636ec99ab20c1aa9376581" | |||
1400 | 1400 | ||
1401 | [[package]] | 1401 | [[package]] |
1402 | name = "rowan" | 1402 | name = "rowan" |
1403 | version = "0.12.1" | 1403 | version = "0.12.2" |
1404 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1404 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1405 | checksum = "24c2d78254049413f9d73495f883e7fa0b7a7d4b88468cd72a3bbbd0ad585cd1" | 1405 | checksum = "5e05df24c035422fb2b845d66ef3542d6c4f8572eafe077175a30787b0254207" |
1406 | dependencies = [ | 1406 | dependencies = [ |
1407 | "countme", | 1407 | "countme", |
1408 | "hashbrown", | 1408 | "hashbrown", |
@@ -1663,9 +1663,9 @@ dependencies = [ | |||
1663 | 1663 | ||
1664 | [[package]] | 1664 | [[package]] |
1665 | name = "syn" | 1665 | name = "syn" |
1666 | version = "1.0.58" | 1666 | version = "1.0.59" |
1667 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1667 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1668 | checksum = "cc60a3d73ea6594cd712d830cc1f0390fd71542d8c8cd24e70cc54cdfd5e05d5" | 1668 | checksum = "07cb8b1b4ebf86a89ee88cbd201b022b94138c623644d035185c84d3f41b7e66" |
1669 | dependencies = [ | 1669 | dependencies = [ |
1670 | "proc-macro2", | 1670 | "proc-macro2", |
1671 | "quote", | 1671 | "quote", |
@@ -1742,11 +1742,11 @@ dependencies = [ | |||
1742 | 1742 | ||
1743 | [[package]] | 1743 | [[package]] |
1744 | name = "thread_local" | 1744 | name = "thread_local" |
1745 | version = "1.1.0" | 1745 | version = "1.1.1" |
1746 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1746 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1747 | checksum = "bb9bc092d0d51e76b2b19d9d85534ffc9ec2db959a2523cdae0697e2972cd447" | 1747 | checksum = "301bdd13d23c49672926be451130892d274d3ba0b410c18e00daa7990ff38d99" |
1748 | dependencies = [ | 1748 | dependencies = [ |
1749 | "lazy_static", | 1749 | "once_cell", |
1750 | ] | 1750 | ] |
1751 | 1751 | ||
1752 | [[package]] | 1752 | [[package]] |
@@ -1897,9 +1897,9 @@ checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" | |||
1897 | 1897 | ||
1898 | [[package]] | 1898 | [[package]] |
1899 | name = "ungrammar" | 1899 | name = "ungrammar" |
1900 | version = "1.9.3" | 1900 | version = "1.10.0" |
1901 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1901 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1902 | checksum = "f5901372c0f3a6a1a9d880aef134c8eaf5e54409343637508c0a344270b42d7b" | 1902 | checksum = "ce24866975a8858d3a35eba845efc6b42962c5067afd2bc1a07b9ce0108d335c" |
1903 | 1903 | ||
1904 | [[package]] | 1904 | [[package]] |
1905 | name = "unicase" | 1905 | name = "unicase" |
diff --git a/crates/completion/src/render.rs b/crates/completion/src/render.rs index 6eb20df2b..e11b881ca 100644 --- a/crates/completion/src/render.rs +++ b/crates/completion/src/render.rs | |||
@@ -201,8 +201,7 @@ impl<'a> Render<'a> { | |||
201 | ScopeDef::ModuleDef(Module(..)) => CompletionItemKind::SymbolKind(SymbolKind::Module), | 201 | ScopeDef::ModuleDef(Module(..)) => CompletionItemKind::SymbolKind(SymbolKind::Module), |
202 | ScopeDef::ModuleDef(Adt(adt)) => CompletionItemKind::SymbolKind(match adt { | 202 | ScopeDef::ModuleDef(Adt(adt)) => CompletionItemKind::SymbolKind(match adt { |
203 | hir::Adt::Struct(_) => SymbolKind::Struct, | 203 | hir::Adt::Struct(_) => SymbolKind::Struct, |
204 | // FIXME: add CompletionItemKind::Union | 204 | hir::Adt::Union(_) => SymbolKind::Union, |
205 | hir::Adt::Union(_) => SymbolKind::Struct, | ||
206 | hir::Adt::Enum(_) => SymbolKind::Enum, | 205 | hir::Adt::Enum(_) => SymbolKind::Enum, |
207 | }), | 206 | }), |
208 | ScopeDef::ModuleDef(Const(..)) => CompletionItemKind::SymbolKind(SymbolKind::Const), | 207 | ScopeDef::ModuleDef(Const(..)) => CompletionItemKind::SymbolKind(SymbolKind::Const), |
diff --git a/crates/hir/src/code_model.rs b/crates/hir/src/code_model.rs index d9b4cdfce..e9bb4f541 100644 --- a/crates/hir/src/code_model.rs +++ b/crates/hir/src/code_model.rs | |||
@@ -18,8 +18,8 @@ use hir_def::{ | |||
18 | type_ref::{Mutability, TypeRef}, | 18 | type_ref::{Mutability, TypeRef}, |
19 | AdtId, AssocContainerId, AssocItemId, AssocItemLoc, AttrDefId, ConstId, ConstParamId, | 19 | AdtId, AssocContainerId, AssocItemId, AssocItemLoc, AttrDefId, ConstId, ConstParamId, |
20 | DefWithBodyId, EnumId, FunctionId, GenericDefId, HasModule, ImplId, LifetimeParamId, | 20 | DefWithBodyId, EnumId, FunctionId, GenericDefId, HasModule, ImplId, LifetimeParamId, |
21 | LocalEnumVariantId, LocalFieldId, LocalModuleId, Lookup, ModuleId, StaticId, StructId, TraitId, | 21 | LocalEnumVariantId, LocalFieldId, Lookup, ModuleId, StaticId, StructId, TraitId, TypeAliasId, |
22 | TypeAliasId, TypeParamId, UnionId, | 22 | TypeParamId, UnionId, |
23 | }; | 23 | }; |
24 | use hir_def::{find_path::PrefixKind, item_scope::ItemInNs, visibility::Visibility}; | 24 | use hir_def::{find_path::PrefixKind, item_scope::ItemInNs, visibility::Visibility}; |
25 | use hir_expand::{ | 25 | use hir_expand::{ |
@@ -90,8 +90,8 @@ impl Crate { | |||
90 | } | 90 | } |
91 | 91 | ||
92 | pub fn root_module(self, db: &dyn HirDatabase) -> Module { | 92 | pub fn root_module(self, db: &dyn HirDatabase) -> Module { |
93 | let module_id = db.crate_def_map(self.id).root(); | 93 | let def_map = db.crate_def_map(self.id); |
94 | Module::new(self, module_id) | 94 | Module { id: def_map.module_id(def_map.root()) } |
95 | } | 95 | } |
96 | 96 | ||
97 | pub fn root_file(self, db: &dyn HirDatabase) -> FileId { | 97 | pub fn root_file(self, db: &dyn HirDatabase) -> FileId { |
@@ -275,10 +275,6 @@ impl ModuleDef { | |||
275 | } | 275 | } |
276 | 276 | ||
277 | impl Module { | 277 | impl Module { |
278 | pub(crate) fn new(krate: Crate, crate_module_id: LocalModuleId) -> Module { | ||
279 | Module { id: ModuleId::top_level(krate.id, crate_module_id) } | ||
280 | } | ||
281 | |||
282 | /// Name of this module. | 278 | /// Name of this module. |
283 | pub fn name(self, db: &dyn HirDatabase) -> Option<Name> { | 279 | pub fn name(self, db: &dyn HirDatabase) -> Option<Name> { |
284 | let def_map = self.id.def_map(db.upcast()); | 280 | let def_map = self.id.def_map(db.upcast()); |
@@ -302,7 +298,7 @@ impl Module { | |||
302 | /// in the module tree of any target in `Cargo.toml`. | 298 | /// in the module tree of any target in `Cargo.toml`. |
303 | pub fn crate_root(self, db: &dyn HirDatabase) -> Module { | 299 | pub fn crate_root(self, db: &dyn HirDatabase) -> Module { |
304 | let def_map = db.crate_def_map(self.id.krate()); | 300 | let def_map = db.crate_def_map(self.id.krate()); |
305 | self.with_module_id(def_map.root()) | 301 | Module { id: def_map.module_id(def_map.root()) } |
306 | } | 302 | } |
307 | 303 | ||
308 | /// Iterates over all child modules. | 304 | /// Iterates over all child modules. |
@@ -311,7 +307,7 @@ impl Module { | |||
311 | let children = def_map[self.id.local_id] | 307 | let children = def_map[self.id.local_id] |
312 | .children | 308 | .children |
313 | .iter() | 309 | .iter() |
314 | .map(|(_, module_id)| self.with_module_id(*module_id)) | 310 | .map(|(_, module_id)| Module { id: def_map.module_id(*module_id) }) |
315 | .collect::<Vec<_>>(); | 311 | .collect::<Vec<_>>(); |
316 | children.into_iter() | 312 | children.into_iter() |
317 | } | 313 | } |
@@ -321,7 +317,7 @@ impl Module { | |||
321 | // FIXME: handle block expressions as modules (their parent is in a different DefMap) | 317 | // FIXME: handle block expressions as modules (their parent is in a different DefMap) |
322 | let def_map = self.id.def_map(db.upcast()); | 318 | let def_map = self.id.def_map(db.upcast()); |
323 | let parent_id = def_map[self.id.local_id].parent?; | 319 | let parent_id = def_map[self.id.local_id].parent?; |
324 | Some(self.with_module_id(parent_id)) | 320 | Some(Module { id: def_map.module_id(parent_id) }) |
325 | } | 321 | } |
326 | 322 | ||
327 | pub fn path_to_root(self, db: &dyn HirDatabase) -> Vec<Module> { | 323 | pub fn path_to_root(self, db: &dyn HirDatabase) -> Vec<Module> { |
@@ -406,10 +402,6 @@ impl Module { | |||
406 | def_map[self.id.local_id].scope.impls().map(Impl::from).collect() | 402 | def_map[self.id.local_id].scope.impls().map(Impl::from).collect() |
407 | } | 403 | } |
408 | 404 | ||
409 | pub(crate) fn with_module_id(self, module_id: LocalModuleId) -> Module { | ||
410 | Module::new(self.krate(), module_id) | ||
411 | } | ||
412 | |||
413 | /// Finds a path that can be used to refer to the given item from within | 405 | /// Finds a path that can be used to refer to the given item from within |
414 | /// this module, if possible. | 406 | /// this module, if possible. |
415 | pub fn find_use_path(self, db: &dyn DefDatabase, item: impl Into<ItemInNs>) -> Option<ModPath> { | 407 | pub fn find_use_path(self, db: &dyn DefDatabase, item: impl Into<ItemInNs>) -> Option<ModPath> { |
@@ -1013,8 +1005,9 @@ impl MacroDef { | |||
1013 | /// early, in `hir_expand`, where modules simply do not exist yet. | 1005 | /// early, in `hir_expand`, where modules simply do not exist yet. |
1014 | pub fn module(self, db: &dyn HirDatabase) -> Option<Module> { | 1006 | pub fn module(self, db: &dyn HirDatabase) -> Option<Module> { |
1015 | let krate = self.id.krate; | 1007 | let krate = self.id.krate; |
1016 | let module_id = db.crate_def_map(krate).root(); | 1008 | let def_map = db.crate_def_map(krate); |
1017 | Some(Module::new(Crate { id: krate }, module_id)) | 1009 | let module_id = def_map.root(); |
1010 | Some(Module { id: def_map.module_id(module_id) }) | ||
1018 | } | 1011 | } |
1019 | 1012 | ||
1020 | /// XXX: this parses the file | 1013 | /// XXX: this parses the file |
diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs index 447faa04f..5343a036c 100644 --- a/crates/hir/src/diagnostics.rs +++ b/crates/hir/src/diagnostics.rs | |||
@@ -5,5 +5,5 @@ pub use hir_expand::diagnostics::{ | |||
5 | }; | 5 | }; |
6 | pub use hir_ty::diagnostics::{ | 6 | pub use hir_ty::diagnostics::{ |
7 | IncorrectCase, MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkOrSomeInTailExpr, | 7 | IncorrectCase, MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkOrSomeInTailExpr, |
8 | NoSuchField, RemoveThisSemicolon, | 8 | NoSuchField, RemoveThisSemicolon, ReplaceFilterMapNextWithFindMap, |
9 | }; | 9 | }; |
diff --git a/crates/hir/src/semantics/source_to_def.rs b/crates/hir/src/semantics/source_to_def.rs index faede3269..6c612ee86 100644 --- a/crates/hir/src/semantics/source_to_def.rs +++ b/crates/hir/src/semantics/source_to_def.rs | |||
@@ -30,13 +30,12 @@ pub(super) struct SourceToDefCtx<'a, 'b> { | |||
30 | impl SourceToDefCtx<'_, '_> { | 30 | impl SourceToDefCtx<'_, '_> { |
31 | pub(super) fn file_to_def(&mut self, file: FileId) -> Option<ModuleId> { | 31 | pub(super) fn file_to_def(&mut self, file: FileId) -> Option<ModuleId> { |
32 | let _p = profile::span("SourceBinder::to_module_def"); | 32 | let _p = profile::span("SourceBinder::to_module_def"); |
33 | let (krate, local_id) = self.db.relevant_crates(file).iter().find_map(|&crate_id| { | 33 | self.db.relevant_crates(file).iter().find_map(|&crate_id| { |
34 | // FIXME: inner items | 34 | // FIXME: inner items |
35 | let crate_def_map = self.db.crate_def_map(crate_id); | 35 | let crate_def_map = self.db.crate_def_map(crate_id); |
36 | let local_id = crate_def_map.modules_for_file(file).next()?; | 36 | let local_id = crate_def_map.modules_for_file(file).next()?; |
37 | Some((crate_id, local_id)) | 37 | Some(crate_def_map.module_id(local_id)) |
38 | })?; | 38 | }) |
39 | Some(ModuleId::top_level(krate, local_id)) | ||
40 | } | 39 | } |
41 | 40 | ||
42 | pub(super) fn module_to_def(&mut self, src: InFile<ast::Module>) -> Option<ModuleId> { | 41 | pub(super) fn module_to_def(&mut self, src: InFile<ast::Module>) -> Option<ModuleId> { |
@@ -63,8 +62,7 @@ impl SourceToDefCtx<'_, '_> { | |||
63 | let child_name = src.value.name()?.as_name(); | 62 | let child_name = src.value.name()?.as_name(); |
64 | let def_map = parent_module.def_map(self.db.upcast()); | 63 | let def_map = parent_module.def_map(self.db.upcast()); |
65 | let child_id = *def_map[parent_module.local_id].children.get(&child_name)?; | 64 | let child_id = *def_map[parent_module.local_id].children.get(&child_name)?; |
66 | // FIXME: handle block expression modules | 65 | Some(def_map.module_id(child_id)) |
67 | Some(ModuleId::top_level(parent_module.krate(), child_id)) | ||
68 | } | 66 | } |
69 | 67 | ||
70 | pub(super) fn trait_to_def(&mut self, src: InFile<ast::Trait>) -> Option<TraitId> { | 68 | pub(super) fn trait_to_def(&mut self, src: InFile<ast::Trait>) -> Option<TraitId> { |
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/find_path.rs b/crates/hir_def/src/find_path.rs index c01b6daf2..94a1d567d 100644 --- a/crates/hir_def/src/find_path.rs +++ b/crates/hir_def/src/find_path.rs | |||
@@ -53,12 +53,8 @@ fn check_self_super(def_map: &DefMap, item: ItemInNs, from: ModuleId) -> Option< | |||
53 | Some(ModPath::from_segments(PathKind::Super(0), Vec::new())) | 53 | Some(ModPath::from_segments(PathKind::Super(0), Vec::new())) |
54 | } else if let Some(parent_id) = def_map[from.local_id].parent { | 54 | } else if let Some(parent_id) = def_map[from.local_id].parent { |
55 | // - if the item is the parent module, use `super` (this is not used recursively, since `super::super` is ugly) | 55 | // - if the item is the parent module, use `super` (this is not used recursively, since `super::super` is ugly) |
56 | if item | 56 | let parent_id = def_map.module_id(parent_id); |
57 | == ItemInNs::Types(ModuleDefId::ModuleId(ModuleId { | 57 | if item == ItemInNs::Types(ModuleDefId::ModuleId(parent_id)) { |
58 | krate: from.krate, | ||
59 | local_id: parent_id, | ||
60 | })) | ||
61 | { | ||
62 | Some(ModPath::from_segments(PathKind::Super(1), Vec::new())) | 58 | Some(ModPath::from_segments(PathKind::Super(1), Vec::new())) |
63 | } else { | 59 | } else { |
64 | None | 60 | None |
@@ -120,12 +116,8 @@ fn find_path_inner( | |||
120 | } | 116 | } |
121 | 117 | ||
122 | // - if the item is the crate root, return `crate` | 118 | // - if the item is the crate root, return `crate` |
123 | if item | 119 | let root = def_map.module_id(def_map.root()); |
124 | == ItemInNs::Types(ModuleDefId::ModuleId(ModuleId { | 120 | if item == ItemInNs::Types(ModuleDefId::ModuleId(root)) { |
125 | krate: from.krate, | ||
126 | local_id: def_map.root(), | ||
127 | })) | ||
128 | { | ||
129 | return Some(ModPath::from_segments(PathKind::Crate, Vec::new())); | 121 | return Some(ModPath::from_segments(PathKind::Crate, Vec::new())); |
130 | } | 122 | } |
131 | 123 | ||
@@ -175,7 +167,7 @@ fn find_path_inner( | |||
175 | 167 | ||
176 | // - otherwise, look for modules containing (reexporting) it and import it from one of those | 168 | // - otherwise, look for modules containing (reexporting) it and import it from one of those |
177 | 169 | ||
178 | let crate_root = ModuleId { local_id: def_map.root(), krate: from.krate }; | 170 | let crate_root = def_map.module_id(def_map.root()); |
179 | let crate_attrs = db.attrs(crate_root.into()); | 171 | let crate_attrs = db.attrs(crate_root.into()); |
180 | let prefer_no_std = crate_attrs.by_key("no_std").exists(); | 172 | let prefer_no_std = crate_attrs.by_key("no_std").exists(); |
181 | let mut best_path = None; | 173 | let mut best_path = None; |
@@ -288,14 +280,11 @@ fn find_local_import_locations( | |||
288 | // Compute the initial worklist. We start with all direct child modules of `from` as well as all | 280 | // Compute the initial worklist. We start with all direct child modules of `from` as well as all |
289 | // of its (recursive) parent modules. | 281 | // of its (recursive) parent modules. |
290 | let data = &def_map[from.local_id]; | 282 | let data = &def_map[from.local_id]; |
291 | let mut worklist = data | 283 | let mut worklist = |
292 | .children | 284 | data.children.values().map(|child| def_map.module_id(*child)).collect::<Vec<_>>(); |
293 | .values() | ||
294 | .map(|child| ModuleId { krate: from.krate, local_id: *child }) | ||
295 | .collect::<Vec<_>>(); | ||
296 | let mut parent = data.parent; | 285 | let mut parent = data.parent; |
297 | while let Some(p) = parent { | 286 | while let Some(p) = parent { |
298 | worklist.push(ModuleId { krate: from.krate, local_id: p }); | 287 | worklist.push(def_map.module_id(p)); |
299 | parent = def_map[p].parent; | 288 | parent = def_map[p].parent; |
300 | } | 289 | } |
301 | 290 | ||
diff --git a/crates/hir_def/src/import_map.rs b/crates/hir_def/src/import_map.rs index 0b7830445..0a3dc7956 100644 --- a/crates/hir_def/src/import_map.rs +++ b/crates/hir_def/src/import_map.rs | |||
@@ -75,7 +75,7 @@ impl ImportMap { | |||
75 | 75 | ||
76 | // We look only into modules that are public(ly reexported), starting with the crate root. | 76 | // We look only into modules that are public(ly reexported), starting with the crate root. |
77 | let empty = ImportPath { segments: vec![] }; | 77 | let empty = ImportPath { segments: vec![] }; |
78 | let root = ModuleId { krate, local_id: def_map.root() }; | 78 | let root = def_map.module_id(def_map.root()); |
79 | let mut worklist = vec![(root, empty)]; | 79 | let mut worklist = vec![(root, empty)]; |
80 | while let Some((module, mod_path)) = worklist.pop() { | 80 | while let Some((module, mod_path)) = worklist.pop() { |
81 | let ext_def_map; | 81 | let ext_def_map; |
diff --git a/crates/hir_def/src/item_tree.rs b/crates/hir_def/src/item_tree.rs index b8d7608e7..1e5c94660 100644 --- a/crates/hir_def/src/item_tree.rs +++ b/crates/hir_def/src/item_tree.rs | |||
@@ -66,7 +66,7 @@ impl GenericParamsId { | |||
66 | } | 66 | } |
67 | 67 | ||
68 | /// The item tree of a source file. | 68 | /// The item tree of a source file. |
69 | #[derive(Debug, Eq, PartialEq)] | 69 | #[derive(Debug, Default, Eq, PartialEq)] |
70 | pub struct ItemTree { | 70 | pub struct ItemTree { |
71 | _c: Count<Self>, | 71 | _c: Count<Self>, |
72 | 72 | ||
@@ -82,7 +82,7 @@ impl ItemTree { | |||
82 | let syntax = if let Some(node) = db.parse_or_expand(file_id) { | 82 | let syntax = if let Some(node) = db.parse_or_expand(file_id) { |
83 | node | 83 | node |
84 | } else { | 84 | } else { |
85 | return Arc::new(Self::empty()); | 85 | return Default::default(); |
86 | }; | 86 | }; |
87 | 87 | ||
88 | let hygiene = Hygiene::new(db.upcast(), file_id); | 88 | let hygiene = Hygiene::new(db.upcast(), file_id); |
@@ -118,15 +118,6 @@ impl ItemTree { | |||
118 | Arc::new(item_tree) | 118 | Arc::new(item_tree) |
119 | } | 119 | } |
120 | 120 | ||
121 | fn empty() -> Self { | ||
122 | Self { | ||
123 | _c: Count::new(), | ||
124 | top_level: Default::default(), | ||
125 | attrs: Default::default(), | ||
126 | data: Default::default(), | ||
127 | } | ||
128 | } | ||
129 | |||
130 | fn shrink_to_fit(&mut self) { | 121 | fn shrink_to_fit(&mut self) { |
131 | if let Some(data) = &mut self.data { | 122 | if let Some(data) = &mut self.data { |
132 | let ItemTreeData { | 123 | let ItemTreeData { |
diff --git a/crates/hir_def/src/item_tree/lower.rs b/crates/hir_def/src/item_tree/lower.rs index ce470fc3b..8a71376b9 100644 --- a/crates/hir_def/src/item_tree/lower.rs +++ b/crates/hir_def/src/item_tree/lower.rs | |||
@@ -43,7 +43,7 @@ pub(super) struct Ctx { | |||
43 | impl Ctx { | 43 | impl Ctx { |
44 | pub(super) fn new(db: &dyn DefDatabase, hygiene: Hygiene, file: HirFileId) -> Self { | 44 | pub(super) fn new(db: &dyn DefDatabase, hygiene: Hygiene, file: HirFileId) -> Self { |
45 | Self { | 45 | Self { |
46 | tree: ItemTree::empty(), | 46 | tree: ItemTree::default(), |
47 | hygiene, | 47 | hygiene, |
48 | file, | 48 | file, |
49 | source_ast_id_map: db.ast_id_map(file), | 49 | source_ast_id_map: db.ast_id_map(file), |
diff --git a/crates/hir_def/src/lib.rs b/crates/hir_def/src/lib.rs index c8dbb2aeb..42b50b5b7 100644 --- a/crates/hir_def/src/lib.rs +++ b/crates/hir_def/src/lib.rs | |||
@@ -74,16 +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 top_level(krate: CrateId, local_id: LocalModuleId) -> Self { | ||
82 | Self { krate, local_id } | ||
83 | } | ||
84 | |||
85 | pub fn def_map(&self, db: &dyn db::DefDatabase) -> Arc<DefMap> { | 82 | pub fn def_map(&self, db: &dyn db::DefDatabase) -> Arc<DefMap> { |
86 | 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 | } | ||
87 | } | 87 | } |
88 | 88 | ||
89 | pub fn krate(&self) -> CrateId { | 89 | pub fn krate(&self) -> CrateId { |
@@ -234,6 +234,15 @@ pub struct ImplId(salsa::InternId); | |||
234 | type ImplLoc = ItemLoc<Impl>; | 234 | type ImplLoc = ItemLoc<Impl>; |
235 | impl_intern!(ImplId, ImplLoc, intern_impl, lookup_intern_impl); | 235 | impl_intern!(ImplId, ImplLoc, intern_impl, lookup_intern_impl); |
236 | 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 | |||
237 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 246 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
238 | pub struct TypeParamId { | 247 | pub struct TypeParamId { |
239 | pub parent: GenericDefId, | 248 | pub parent: GenericDefId, |
diff --git a/crates/hir_def/src/nameres.rs b/crates/hir_def/src/nameres.rs index bd3ea9b8b..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(), |
@@ -265,6 +272,11 @@ impl DefMap { | |||
265 | self.extern_prelude.iter() | 272 | self.extern_prelude.iter() |
266 | } | 273 | } |
267 | 274 | ||
275 | pub fn module_id(&self, local_id: LocalModuleId) -> ModuleId { | ||
276 | let block = self.block.as_ref().map(|b| b.block); | ||
277 | ModuleId { krate: self.krate, local_id, block } | ||
278 | } | ||
279 | |||
268 | pub(crate) fn resolve_path( | 280 | pub(crate) fn resolve_path( |
269 | &self, | 281 | &self, |
270 | db: &dyn DefDatabase, | 282 | db: &dyn DefDatabase, |
@@ -282,9 +294,9 @@ impl DefMap { | |||
282 | pub fn dump(&self) -> String { | 294 | pub fn dump(&self) -> String { |
283 | let mut buf = String::new(); | 295 | let mut buf = String::new(); |
284 | let mut current_map = self; | 296 | let mut current_map = self; |
285 | while let Some(parent) = ¤t_map.parent { | 297 | while let Some(block) = ¤t_map.block { |
286 | go(&mut buf, current_map, "block scope", current_map.root); | 298 | go(&mut buf, current_map, "block scope", current_map.root); |
287 | current_map = &**parent; | 299 | current_map = &*block.parent; |
288 | } | 300 | } |
289 | go(&mut buf, current_map, "crate", current_map.root); | 301 | go(&mut buf, current_map, "crate", current_map.root); |
290 | return buf; | 302 | return buf; |
@@ -338,35 +350,6 @@ impl ModuleData { | |||
338 | } | 350 | } |
339 | } | 351 | } |
340 | 352 | ||
341 | fn parent_def_map( | ||
342 | db: &dyn DefDatabase, | ||
343 | krate: CrateId, | ||
344 | block: AstId<ast::BlockExpr>, | ||
345 | ) -> Arc<DefMap> { | ||
346 | // FIXME: store this info in the item tree instead of reparsing here | ||
347 | let ast_id_map = db.ast_id_map(block.file_id); | ||
348 | let block_ptr = ast_id_map.get(block.value); | ||
349 | let root = match db.parse_or_expand(block.file_id) { | ||
350 | Some(it) => it, | ||
351 | None => { | ||
352 | return Arc::new(DefMap::empty(krate, Edition::Edition2018)); | ||
353 | } | ||
354 | }; | ||
355 | let ast = block_ptr.to_node(&root); | ||
356 | |||
357 | for ancestor in ast.syntax().ancestors().skip(1) { | ||
358 | if let Some(block_expr) = ast::BlockExpr::cast(ancestor) { | ||
359 | let ancestor_id = ast_id_map.ast_id(&block_expr); | ||
360 | let ast_id = InFile::new(block.file_id, ancestor_id); | ||
361 | let parent_map = db.block_def_map(krate, ast_id); | ||
362 | return parent_map; | ||
363 | } | ||
364 | } | ||
365 | |||
366 | // No enclosing block scope, so the parent is the crate-level DefMap. | ||
367 | db.crate_def_map(krate) | ||
368 | } | ||
369 | |||
370 | #[derive(Debug, Clone, PartialEq, Eq)] | 353 | #[derive(Debug, Clone, PartialEq, Eq)] |
371 | pub enum ModuleSource { | 354 | pub enum ModuleSource { |
372 | SourceFile(ast::SourceFile), | 355 | SourceFile(ast::SourceFile), |
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs index adfcf879a..393170b32 100644 --- a/crates/hir_def/src/nameres/collector.rs +++ b/crates/hir_def/src/nameres/collector.rs | |||
@@ -37,8 +37,8 @@ use crate::{ | |||
37 | per_ns::PerNs, | 37 | per_ns::PerNs, |
38 | visibility::{RawVisibility, Visibility}, | 38 | visibility::{RawVisibility, Visibility}, |
39 | AdtId, AsMacroCall, AstId, AstIdWithPath, ConstLoc, ContainerId, EnumLoc, EnumVariantId, | 39 | AdtId, AsMacroCall, AstId, AstIdWithPath, ConstLoc, ContainerId, EnumLoc, EnumVariantId, |
40 | FunctionLoc, ImplLoc, Intern, LocalModuleId, ModuleDefId, ModuleId, StaticLoc, StructLoc, | 40 | FunctionLoc, ImplLoc, Intern, LocalModuleId, ModuleDefId, StaticLoc, StructLoc, TraitLoc, |
41 | TraitLoc, TypeAliasLoc, UnionLoc, | 41 | TypeAliasLoc, UnionLoc, |
42 | }; | 42 | }; |
43 | 43 | ||
44 | const GLOB_RECURSION_LIMIT: usize = 100; | 44 | const GLOB_RECURSION_LIMIT: usize = 100; |
@@ -56,10 +56,9 @@ pub(super) fn collect_defs( | |||
56 | for dep in &crate_graph[def_map.krate].dependencies { | 56 | for dep in &crate_graph[def_map.krate].dependencies { |
57 | log::debug!("crate dep {:?} -> {:?}", dep.name, dep.crate_id); | 57 | log::debug!("crate dep {:?} -> {:?}", dep.name, dep.crate_id); |
58 | let dep_def_map = db.crate_def_map(dep.crate_id); | 58 | let dep_def_map = db.crate_def_map(dep.crate_id); |
59 | def_map.extern_prelude.insert( | 59 | def_map |
60 | dep.as_name(), | 60 | .extern_prelude |
61 | ModuleId { krate: dep.crate_id, local_id: dep_def_map.root }.into(), | 61 | .insert(dep.as_name(), dep_def_map.module_id(dep_def_map.root).into()); |
62 | ); | ||
63 | 62 | ||
64 | // look for the prelude | 63 | // look for the prelude |
65 | // If the dependency defines a prelude, we overwrite an already defined | 64 | // If the dependency defines a prelude, we overwrite an already defined |
@@ -332,11 +331,9 @@ impl DefCollector<'_> { | |||
332 | // exported in type/value namespace. This function reduces the visibility of all items | 331 | // exported in type/value namespace. This function reduces the visibility of all items |
333 | // in the crate root that aren't proc macros. | 332 | // in the crate root that aren't proc macros. |
334 | let root = self.def_map.root; | 333 | let root = self.def_map.root; |
334 | let module_id = self.def_map.module_id(root); | ||
335 | let root = &mut self.def_map.modules[root]; | 335 | let root = &mut self.def_map.modules[root]; |
336 | root.scope.censor_non_proc_macros(ModuleId { | 336 | root.scope.censor_non_proc_macros(module_id); |
337 | krate: self.def_map.krate, | ||
338 | local_id: self.def_map.root, | ||
339 | }); | ||
340 | } | 337 | } |
341 | } | 338 | } |
342 | 339 | ||
@@ -1029,8 +1026,7 @@ impl ModCollector<'_, '_> { | |||
1029 | continue; | 1026 | continue; |
1030 | } | 1027 | } |
1031 | } | 1028 | } |
1032 | let module = | 1029 | let module = self.def_collector.def_map.module_id(self.module_id); |
1033 | ModuleId { krate: self.def_collector.def_map.krate, local_id: self.module_id }; | ||
1034 | let container = ContainerId::ModuleId(module); | 1030 | let container = ContainerId::ModuleId(module); |
1035 | 1031 | ||
1036 | let mut def = None; | 1032 | let mut def = None; |
@@ -1097,10 +1093,7 @@ impl ModCollector<'_, '_> { | |||
1097 | } | 1093 | } |
1098 | } | 1094 | } |
1099 | ModItem::Impl(imp) => { | 1095 | ModItem::Impl(imp) => { |
1100 | let module = ModuleId { | 1096 | let module = self.def_collector.def_map.module_id(self.module_id); |
1101 | krate: self.def_collector.def_map.krate, | ||
1102 | local_id: self.module_id, | ||
1103 | }; | ||
1104 | let container = ContainerId::ModuleId(module); | 1097 | let container = ContainerId::ModuleId(module); |
1105 | let impl_id = ImplLoc { container, id: ItemTreeId::new(self.file_id, imp) } | 1098 | let impl_id = ImplLoc { container, id: ItemTreeId::new(self.file_id, imp) } |
1106 | .intern(self.def_collector.db); | 1099 | .intern(self.def_collector.db); |
@@ -1343,7 +1336,7 @@ impl ModCollector<'_, '_> { | |||
1343 | modules[res].scope.define_legacy_macro(name, mac) | 1336 | modules[res].scope.define_legacy_macro(name, mac) |
1344 | } | 1337 | } |
1345 | modules[self.module_id].children.insert(name.clone(), res); | 1338 | modules[self.module_id].children.insert(name.clone(), res); |
1346 | let module = ModuleId { krate: self.def_collector.def_map.krate, local_id: res }; | 1339 | let module = self.def_collector.def_map.module_id(res); |
1347 | let def: ModuleDefId = module.into(); | 1340 | let def: ModuleDefId = module.into(); |
1348 | self.def_collector.def_map.modules[self.module_id].scope.define_def(def); | 1341 | self.def_collector.def_map.modules[self.module_id].scope.define_def(def); |
1349 | self.def_collector.update( | 1342 | self.def_collector.update( |
diff --git a/crates/hir_def/src/nameres/path_resolution.rs b/crates/hir_def/src/nameres/path_resolution.rs index 82528b792..419e465ed 100644 --- a/crates/hir_def/src/nameres/path_resolution.rs +++ b/crates/hir_def/src/nameres/path_resolution.rs | |||
@@ -10,9 +10,8 @@ | |||
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; |
14 | use hir_expand::name; | ||
16 | use hir_expand::name::Name; | 15 | use hir_expand::name::Name; |
17 | use test_utils::mark; | 16 | use test_utils::mark; |
18 | 17 | ||
@@ -23,7 +22,7 @@ use crate::{ | |||
23 | path::{ModPath, PathKind}, | 22 | path::{ModPath, PathKind}, |
24 | per_ns::PerNs, | 23 | per_ns::PerNs, |
25 | visibility::{RawVisibility, Visibility}, | 24 | visibility::{RawVisibility, Visibility}, |
26 | AdtId, CrateId, EnumVariantId, LocalModuleId, ModuleDefId, ModuleId, | 25 | AdtId, CrateId, EnumVariantId, LocalModuleId, ModuleDefId, |
27 | }; | 26 | }; |
28 | 27 | ||
29 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | 28 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
@@ -63,6 +62,10 @@ impl ResolvePathResult { | |||
63 | 62 | ||
64 | impl DefMap { | 63 | impl DefMap { |
65 | pub(super) fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs { | 64 | pub(super) fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs { |
65 | if name == &name!(self) { | ||
66 | mark::hit!(extern_crate_self_as); | ||
67 | return PerNs::types(self.module_id(self.root).into(), Visibility::Public); | ||
68 | } | ||
66 | self.extern_prelude | 69 | self.extern_prelude |
67 | .get(name) | 70 | .get(name) |
68 | .map_or(PerNs::none(), |&it| PerNs::types(it, Visibility::Public)) | 71 | .map_or(PerNs::none(), |&it| PerNs::types(it, Visibility::Public)) |
@@ -126,8 +129,8 @@ impl DefMap { | |||
126 | result.krate = result.krate.or(new.krate); | 129 | result.krate = result.krate.or(new.krate); |
127 | result.segment_index = result.segment_index.min(new.segment_index); | 130 | result.segment_index = result.segment_index.min(new.segment_index); |
128 | 131 | ||
129 | match ¤t_map.parent { | 132 | match ¤t_map.block { |
130 | Some(map) => current_map = map, | 133 | Some(block) => current_map = &block.parent, |
131 | None => return result, | 134 | None => return result, |
132 | } | 135 | } |
133 | } | 136 | } |
@@ -146,21 +149,15 @@ impl DefMap { | |||
146 | PathKind::DollarCrate(krate) => { | 149 | PathKind::DollarCrate(krate) => { |
147 | if krate == self.krate { | 150 | if krate == self.krate { |
148 | mark::hit!(macro_dollar_crate_self); | 151 | mark::hit!(macro_dollar_crate_self); |
149 | PerNs::types( | 152 | PerNs::types(self.module_id(self.root).into(), Visibility::Public) |
150 | ModuleId { krate: self.krate, local_id: self.root }.into(), | ||
151 | Visibility::Public, | ||
152 | ) | ||
153 | } else { | 153 | } else { |
154 | let def_map = db.crate_def_map(krate); | 154 | let def_map = db.crate_def_map(krate); |
155 | let module = ModuleId { krate, local_id: def_map.root }; | 155 | let module = def_map.module_id(def_map.root); |
156 | mark::hit!(macro_dollar_crate_other); | 156 | mark::hit!(macro_dollar_crate_other); |
157 | PerNs::types(module.into(), Visibility::Public) | 157 | PerNs::types(module.into(), Visibility::Public) |
158 | } | 158 | } |
159 | } | 159 | } |
160 | PathKind::Crate => PerNs::types( | 160 | PathKind::Crate => PerNs::types(self.module_id(self.root).into(), Visibility::Public), |
161 | ModuleId { krate: self.krate, local_id: self.root }.into(), | ||
162 | Visibility::Public, | ||
163 | ), | ||
164 | // plain import or absolute path in 2015: crate-relative with | 161 | // plain import or absolute path in 2015: crate-relative with |
165 | // fallback to extern prelude (with the simplification in | 162 | // fallback to extern prelude (with the simplification in |
166 | // rust-lang/rust#57745) | 163 | // rust-lang/rust#57745) |
@@ -194,17 +191,35 @@ impl DefMap { | |||
194 | self.resolve_name_in_module(db, original_module, &segment, prefer_module) | 191 | self.resolve_name_in_module(db, original_module, &segment, prefer_module) |
195 | } | 192 | } |
196 | PathKind::Super(lvl) => { | 193 | PathKind::Super(lvl) => { |
197 | let m = successors(Some(original_module), |m| self.modules[*m].parent) | 194 | let mut module = original_module; |
198 | .nth(lvl as usize); | 195 | for i in 0..lvl { |
199 | if let Some(local_id) = m { | 196 | match self.modules[module].parent { |
200 | PerNs::types( | 197 | Some(it) => module = it, |
201 | ModuleId { krate: self.krate, local_id }.into(), | 198 | None => match &self.block { |
202 | Visibility::Public, | 199 | Some(block) => { |
203 | ) | 200 | // Look up remaining path in parent `DefMap` |
204 | } else { | 201 | let new_path = ModPath { |
205 | log::debug!("super path in root module"); | 202 | kind: PathKind::Super(lvl - i), |
206 | return ResolvePathResult::empty(ReachedFixedPoint::Yes); | 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 | } | ||
207 | } | 220 | } |
221 | |||
222 | PerNs::types(self.module_id(module).into(), Visibility::Public) | ||
208 | } | 223 | } |
209 | PathKind::Abs => { | 224 | PathKind::Abs => { |
210 | // 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/hir_def/src/nameres/tests/diagnostics.rs b/crates/hir_def/src/nameres/tests/diagnostics.rs index 58d69d3c6..e8e72e5ef 100644 --- a/crates/hir_def/src/nameres/tests/diagnostics.rs +++ b/crates/hir_def/src/nameres/tests/diagnostics.rs | |||
@@ -62,6 +62,22 @@ fn unresolved_extern_crate() { | |||
62 | } | 62 | } |
63 | 63 | ||
64 | #[test] | 64 | #[test] |
65 | fn extern_crate_self_as() { | ||
66 | mark::check!(extern_crate_self_as); | ||
67 | check_diagnostics( | ||
68 | r" | ||
69 | //- /lib.rs | ||
70 | extern crate doesnotexist; | ||
71 | //^^^^^^^^^^^^^^^^^^^^^^^^^^ unresolved extern crate | ||
72 | // Should not error. | ||
73 | extern crate self as foo; | ||
74 | struct Foo; | ||
75 | use foo::Foo as Bar; | ||
76 | ", | ||
77 | ); | ||
78 | } | ||
79 | |||
80 | #[test] | ||
65 | fn dedup_unresolved_import_from_unresolved_crate() { | 81 | fn dedup_unresolved_import_from_unresolved_crate() { |
66 | check_diagnostics( | 82 | check_diagnostics( |
67 | r" | 83 | r" |
diff --git a/crates/hir_def/src/path.rs b/crates/hir_def/src/path.rs index e34cd7f2f..84ea09b53 100644 --- a/crates/hir_def/src/path.rs +++ b/crates/hir_def/src/path.rs | |||
@@ -304,6 +304,7 @@ pub use hir_expand::name as __name; | |||
304 | #[macro_export] | 304 | #[macro_export] |
305 | macro_rules! __known_path { | 305 | macro_rules! __known_path { |
306 | (core::iter::IntoIterator) => {}; | 306 | (core::iter::IntoIterator) => {}; |
307 | (core::iter::Iterator) => {}; | ||
307 | (core::result::Result) => {}; | 308 | (core::result::Result) => {}; |
308 | (core::option::Option) => {}; | 309 | (core::option::Option) => {}; |
309 | (core::ops::Range) => {}; | 310 | (core::ops::Range) => {}; |
diff --git a/crates/hir_def/src/resolver.rs b/crates/hir_def/src/resolver.rs index 130c074f0..9021ea712 100644 --- a/crates/hir_def/src/resolver.rs +++ b/crates/hir_def/src/resolver.rs | |||
@@ -459,7 +459,7 @@ impl Resolver { | |||
459 | 459 | ||
460 | pub fn module(&self) -> Option<ModuleId> { | 460 | pub fn module(&self) -> Option<ModuleId> { |
461 | let (def_map, local_id) = self.module_scope()?; | 461 | let (def_map, local_id) = self.module_scope()?; |
462 | Some(ModuleId { krate: def_map.krate(), local_id }) | 462 | Some(def_map.module_id(local_id)) |
463 | } | 463 | } |
464 | 464 | ||
465 | pub fn krate(&self) -> Option<CrateId> { | 465 | pub fn krate(&self) -> Option<CrateId> { |
diff --git a/crates/hir_def/src/test_db.rs b/crates/hir_def/src/test_db.rs index 4ff219fb7..c4e36eda5 100644 --- a/crates/hir_def/src/test_db.rs +++ b/crates/hir_def/src/test_db.rs | |||
@@ -15,7 +15,7 @@ use rustc_hash::FxHashSet; | |||
15 | use syntax::{TextRange, TextSize}; | 15 | use syntax::{TextRange, TextSize}; |
16 | use test_utils::extract_annotations; | 16 | use test_utils::extract_annotations; |
17 | 17 | ||
18 | use crate::{db::DefDatabase, ModuleDefId}; | 18 | use crate::{db::DefDatabase, ModuleDefId, ModuleId}; |
19 | 19 | ||
20 | #[salsa::database( | 20 | #[salsa::database( |
21 | base_db::SourceDatabaseExtStorage, | 21 | base_db::SourceDatabaseExtStorage, |
@@ -72,12 +72,12 @@ impl FileLoader for TestDB { | |||
72 | } | 72 | } |
73 | 73 | ||
74 | impl TestDB { | 74 | impl TestDB { |
75 | pub(crate) fn module_for_file(&self, file_id: FileId) -> crate::ModuleId { | 75 | pub(crate) fn module_for_file(&self, file_id: FileId) -> ModuleId { |
76 | for &krate in self.relevant_crates(file_id).iter() { | 76 | for &krate in self.relevant_crates(file_id).iter() { |
77 | let crate_def_map = self.crate_def_map(krate); | 77 | let crate_def_map = self.crate_def_map(krate); |
78 | for (local_id, data) in crate_def_map.modules() { | 78 | for (local_id, data) in crate_def_map.modules() { |
79 | if data.origin.file_id() == Some(file_id) { | 79 | if data.origin.file_id() == Some(file_id) { |
80 | return crate::ModuleId { krate, local_id }; | 80 | return crate_def_map.module_id(local_id); |
81 | } | 81 | } |
82 | } | 82 | } |
83 | } | 83 | } |
diff --git a/crates/hir_expand/src/builtin_macro.rs b/crates/hir_expand/src/builtin_macro.rs index 80b60d59f..57bc6fbd7 100644 --- a/crates/hir_expand/src/builtin_macro.rs +++ b/crates/hir_expand/src/builtin_macro.rs | |||
@@ -327,17 +327,14 @@ fn concat_expand( | |||
327 | // concat works with string and char literals, so remove any quotes. | 327 | // concat works with string and char literals, so remove any quotes. |
328 | // It also works with integer, float and boolean literals, so just use the rest | 328 | // It also works with integer, float and boolean literals, so just use the rest |
329 | // as-is. | 329 | // as-is. |
330 | 330 | let component = unquote_str(&it).unwrap_or_else(|| it.text.to_string()); | |
331 | text += it | 331 | text.push_str(&component); |
332 | .text | 332 | } |
333 | .trim_start_matches(|c| match c { | 333 | // handle boolean literals |
334 | 'r' | '#' | '\'' | '"' => true, | 334 | tt::TokenTree::Leaf(tt::Leaf::Ident(id)) |
335 | _ => false, | 335 | if i % 2 == 0 && (id.text == "true" || id.text == "false") => |
336 | }) | 336 | { |
337 | .trim_end_matches(|c| match c { | 337 | text.push_str(id.text.as_str()); |
338 | '#' | '\'' | '"' => true, | ||
339 | _ => false, | ||
340 | }); | ||
341 | } | 338 | } |
342 | tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) if i % 2 == 1 && punct.char == ',' => (), | 339 | tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) if i % 2 == 1 && punct.char == ',' => (), |
343 | _ => { | 340 | _ => { |
@@ -345,7 +342,6 @@ fn concat_expand( | |||
345 | } | 342 | } |
346 | } | 343 | } |
347 | } | 344 | } |
348 | |||
349 | ExpandResult { value: Some((quote!(#text), FragmentKind::Expr)), err } | 345 | ExpandResult { value: Some((quote!(#text), FragmentKind::Expr)), err } |
350 | } | 346 | } |
351 | 347 | ||
@@ -745,12 +741,10 @@ mod tests { | |||
745 | r##" | 741 | r##" |
746 | #[rustc_builtin_macro] | 742 | #[rustc_builtin_macro] |
747 | macro_rules! concat {} | 743 | macro_rules! concat {} |
748 | concat!("foo", 0, r#"bar"#); | 744 | concat!("foo", "r", 0, r#"bar"#, false); |
749 | "##, | 745 | "##, |
750 | ); | 746 | ); |
751 | 747 | ||
752 | assert_eq!(expanded, r#""foo0bar""#); | 748 | assert_eq!(expanded, r#""foor0barfalse""#); |
753 | |||
754 | // FIXME: `true`/`false` literals don't work. | ||
755 | } | 749 | } |
756 | } | 750 | } |
diff --git a/crates/hir_expand/src/name.rs b/crates/hir_expand/src/name.rs index d692cec14..c7609e90d 100644 --- a/crates/hir_expand/src/name.rs +++ b/crates/hir_expand/src/name.rs | |||
@@ -186,6 +186,9 @@ pub mod known { | |||
186 | Neg, | 186 | Neg, |
187 | Not, | 187 | Not, |
188 | Index, | 188 | Index, |
189 | // Components of known path (function name) | ||
190 | filter_map, | ||
191 | next, | ||
189 | // Builtin macros | 192 | // Builtin macros |
190 | file, | 193 | file, |
191 | column, | 194 | column, |
diff --git a/crates/hir_ty/src/diagnostics.rs b/crates/hir_ty/src/diagnostics.rs index 247da43f2..323c5f963 100644 --- a/crates/hir_ty/src/diagnostics.rs +++ b/crates/hir_ty/src/diagnostics.rs | |||
@@ -247,7 +247,7 @@ impl Diagnostic for RemoveThisSemicolon { | |||
247 | 247 | ||
248 | // Diagnostic: break-outside-of-loop | 248 | // Diagnostic: break-outside-of-loop |
249 | // | 249 | // |
250 | // This diagnostic is triggered if `break` keyword is used outside of a loop. | 250 | // This diagnostic is triggered if the `break` keyword is used outside of a loop. |
251 | #[derive(Debug)] | 251 | #[derive(Debug)] |
252 | pub struct BreakOutsideOfLoop { | 252 | pub struct BreakOutsideOfLoop { |
253 | pub file: HirFileId, | 253 | pub file: HirFileId, |
@@ -271,7 +271,7 @@ impl Diagnostic for BreakOutsideOfLoop { | |||
271 | 271 | ||
272 | // Diagnostic: missing-unsafe | 272 | // Diagnostic: missing-unsafe |
273 | // | 273 | // |
274 | // This diagnostic is triggered if operation marked as `unsafe` is used outside of `unsafe` function or block. | 274 | // This diagnostic is triggered if an operation marked as `unsafe` is used outside of an `unsafe` function or block. |
275 | #[derive(Debug)] | 275 | #[derive(Debug)] |
276 | pub struct MissingUnsafe { | 276 | pub struct MissingUnsafe { |
277 | pub file: HirFileId, | 277 | pub file: HirFileId, |
@@ -295,7 +295,7 @@ impl Diagnostic for MissingUnsafe { | |||
295 | 295 | ||
296 | // Diagnostic: mismatched-arg-count | 296 | // Diagnostic: mismatched-arg-count |
297 | // | 297 | // |
298 | // This diagnostic is triggered if function is invoked with an incorrect amount of arguments. | 298 | // This diagnostic is triggered if a function is invoked with an incorrect amount of arguments. |
299 | #[derive(Debug)] | 299 | #[derive(Debug)] |
300 | pub struct MismatchedArgCount { | 300 | pub struct MismatchedArgCount { |
301 | pub file: HirFileId, | 301 | pub file: HirFileId, |
@@ -347,7 +347,7 @@ impl fmt::Display for CaseType { | |||
347 | 347 | ||
348 | // Diagnostic: incorrect-ident-case | 348 | // Diagnostic: incorrect-ident-case |
349 | // | 349 | // |
350 | // This diagnostic is triggered if item name doesn't follow https://doc.rust-lang.org/1.0.0/style/style/naming/README.html[Rust naming convention]. | 350 | // This diagnostic is triggered if an item name doesn't follow https://doc.rust-lang.org/1.0.0/style/style/naming/README.html[Rust naming convention]. |
351 | #[derive(Debug)] | 351 | #[derive(Debug)] |
352 | pub struct IncorrectCase { | 352 | pub struct IncorrectCase { |
353 | pub file: HirFileId, | 353 | pub file: HirFileId, |
@@ -386,6 +386,31 @@ impl Diagnostic for IncorrectCase { | |||
386 | } | 386 | } |
387 | } | 387 | } |
388 | 388 | ||
389 | // Diagnostic: replace-filter-map-next-with-find-map | ||
390 | // | ||
391 | // This diagnostic is triggered when `.filter_map(..).next()` is used, rather than the more concise `.find_map(..)`. | ||
392 | #[derive(Debug)] | ||
393 | pub struct ReplaceFilterMapNextWithFindMap { | ||
394 | pub file: HirFileId, | ||
395 | /// This expression is the whole method chain up to and including `.filter_map(..).next()`. | ||
396 | pub next_expr: AstPtr<ast::Expr>, | ||
397 | } | ||
398 | |||
399 | impl Diagnostic for ReplaceFilterMapNextWithFindMap { | ||
400 | fn code(&self) -> DiagnosticCode { | ||
401 | DiagnosticCode("replace-filter-map-next-with-find-map") | ||
402 | } | ||
403 | fn message(&self) -> String { | ||
404 | "replace filter_map(..).next() with find_map(..)".to_string() | ||
405 | } | ||
406 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
407 | InFile { file_id: self.file, value: self.next_expr.clone().into() } | ||
408 | } | ||
409 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
410 | self | ||
411 | } | ||
412 | } | ||
413 | |||
389 | #[cfg(test)] | 414 | #[cfg(test)] |
390 | mod tests { | 415 | mod tests { |
391 | use base_db::{fixture::WithFixture, FileId, SourceDatabase, SourceDatabaseExt}; | 416 | use base_db::{fixture::WithFixture, FileId, SourceDatabase, SourceDatabaseExt}; |
@@ -644,4 +669,87 @@ fn foo() { break; } | |||
644 | "#, | 669 | "#, |
645 | ); | 670 | ); |
646 | } | 671 | } |
672 | |||
673 | // Register the required standard library types to make the tests work | ||
674 | fn add_filter_map_with_find_next_boilerplate(body: &str) -> String { | ||
675 | let prefix = r#" | ||
676 | //- /main.rs crate:main deps:core | ||
677 | use core::iter::Iterator; | ||
678 | use core::option::Option::{self, Some, None}; | ||
679 | "#; | ||
680 | let suffix = r#" | ||
681 | //- /core/lib.rs crate:core | ||
682 | pub mod option { | ||
683 | pub enum Option<T> { Some(T), None } | ||
684 | } | ||
685 | pub mod iter { | ||
686 | pub trait Iterator { | ||
687 | type Item; | ||
688 | fn filter_map<B, F>(self, f: F) -> FilterMap where F: FnMut(Self::Item) -> Option<B> { FilterMap } | ||
689 | fn next(&mut self) -> Option<Self::Item>; | ||
690 | } | ||
691 | pub struct FilterMap {} | ||
692 | impl Iterator for FilterMap { | ||
693 | type Item = i32; | ||
694 | fn next(&mut self) -> i32 { 7 } | ||
695 | } | ||
696 | } | ||
697 | "#; | ||
698 | format!("{}{}{}", prefix, body, suffix) | ||
699 | } | ||
700 | |||
701 | #[test] | ||
702 | fn replace_filter_map_next_with_find_map2() { | ||
703 | check_diagnostics(&add_filter_map_with_find_next_boilerplate( | ||
704 | r#" | ||
705 | fn foo() { | ||
706 | let m = [1, 2, 3].iter().filter_map(|x| if *x == 2 { Some (4) } else { None }).next(); | ||
707 | //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ replace filter_map(..).next() with find_map(..) | ||
708 | } | ||
709 | "#, | ||
710 | )); | ||
711 | } | ||
712 | |||
713 | #[test] | ||
714 | fn replace_filter_map_next_with_find_map_no_diagnostic_without_next() { | ||
715 | check_diagnostics(&add_filter_map_with_find_next_boilerplate( | ||
716 | r#" | ||
717 | fn foo() { | ||
718 | let m = [1, 2, 3] | ||
719 | .iter() | ||
720 | .filter_map(|x| if *x == 2 { Some (4) } else { None }) | ||
721 | .len(); | ||
722 | } | ||
723 | "#, | ||
724 | )); | ||
725 | } | ||
726 | |||
727 | #[test] | ||
728 | fn replace_filter_map_next_with_find_map_no_diagnostic_with_intervening_methods() { | ||
729 | check_diagnostics(&add_filter_map_with_find_next_boilerplate( | ||
730 | r#" | ||
731 | fn foo() { | ||
732 | let m = [1, 2, 3] | ||
733 | .iter() | ||
734 | .filter_map(|x| if *x == 2 { Some (4) } else { None }) | ||
735 | .map(|x| x + 2) | ||
736 | .len(); | ||
737 | } | ||
738 | "#, | ||
739 | )); | ||
740 | } | ||
741 | |||
742 | #[test] | ||
743 | fn replace_filter_map_next_with_find_map_no_diagnostic_if_not_in_chain() { | ||
744 | check_diagnostics(&add_filter_map_with_find_next_boilerplate( | ||
745 | r#" | ||
746 | fn foo() { | ||
747 | let m = [1, 2, 3] | ||
748 | .iter() | ||
749 | .filter_map(|x| if *x == 2 { Some (4) } else { None }); | ||
750 | let n = m.next(); | ||
751 | } | ||
752 | "#, | ||
753 | )); | ||
754 | } | ||
647 | } | 755 | } |
diff --git a/crates/hir_ty/src/diagnostics/expr.rs b/crates/hir_ty/src/diagnostics/expr.rs index 107417c27..d740b7265 100644 --- a/crates/hir_ty/src/diagnostics/expr.rs +++ b/crates/hir_ty/src/diagnostics/expr.rs | |||
@@ -2,8 +2,10 @@ | |||
2 | 2 | ||
3 | use std::sync::Arc; | 3 | use std::sync::Arc; |
4 | 4 | ||
5 | use hir_def::{expr::Statement, path::path, resolver::HasResolver, AdtId, DefWithBodyId}; | 5 | use hir_def::{ |
6 | use hir_expand::diagnostics::DiagnosticSink; | 6 | expr::Statement, path::path, resolver::HasResolver, AdtId, AssocItemId, DefWithBodyId, |
7 | }; | ||
8 | use hir_expand::{diagnostics::DiagnosticSink, name}; | ||
7 | use rustc_hash::FxHashSet; | 9 | use rustc_hash::FxHashSet; |
8 | use syntax::{ast, AstPtr}; | 10 | use syntax::{ast, AstPtr}; |
9 | 11 | ||
@@ -24,6 +26,8 @@ pub(crate) use hir_def::{ | |||
24 | LocalFieldId, VariantId, | 26 | LocalFieldId, VariantId, |
25 | }; | 27 | }; |
26 | 28 | ||
29 | use super::ReplaceFilterMapNextWithFindMap; | ||
30 | |||
27 | pub(super) struct ExprValidator<'a, 'b: 'a> { | 31 | pub(super) struct ExprValidator<'a, 'b: 'a> { |
28 | owner: DefWithBodyId, | 32 | owner: DefWithBodyId, |
29 | infer: Arc<InferenceResult>, | 33 | infer: Arc<InferenceResult>, |
@@ -40,6 +44,8 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
40 | } | 44 | } |
41 | 45 | ||
42 | pub(super) fn validate_body(&mut self, db: &dyn HirDatabase) { | 46 | pub(super) fn validate_body(&mut self, db: &dyn HirDatabase) { |
47 | self.check_for_filter_map_next(db); | ||
48 | |||
43 | let body = db.body(self.owner.into()); | 49 | let body = db.body(self.owner.into()); |
44 | 50 | ||
45 | for (id, expr) in body.exprs.iter() { | 51 | for (id, expr) in body.exprs.iter() { |
@@ -150,20 +156,76 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
150 | } | 156 | } |
151 | } | 157 | } |
152 | 158 | ||
153 | fn validate_call(&mut self, db: &dyn HirDatabase, call_id: ExprId, expr: &Expr) -> Option<()> { | 159 | fn check_for_filter_map_next(&mut self, db: &dyn HirDatabase) { |
160 | // Find the FunctionIds for Iterator::filter_map and Iterator::next | ||
161 | let iterator_path = path![core::iter::Iterator]; | ||
162 | let resolver = self.owner.resolver(db.upcast()); | ||
163 | let iterator_trait_id = match resolver.resolve_known_trait(db.upcast(), &iterator_path) { | ||
164 | Some(id) => id, | ||
165 | None => return, | ||
166 | }; | ||
167 | let iterator_trait_items = &db.trait_data(iterator_trait_id).items; | ||
168 | let filter_map_function_id = | ||
169 | match iterator_trait_items.iter().find(|item| item.0 == name![filter_map]) { | ||
170 | Some((_, AssocItemId::FunctionId(id))) => id, | ||
171 | _ => return, | ||
172 | }; | ||
173 | let next_function_id = match iterator_trait_items.iter().find(|item| item.0 == name![next]) | ||
174 | { | ||
175 | Some((_, AssocItemId::FunctionId(id))) => id, | ||
176 | _ => return, | ||
177 | }; | ||
178 | |||
179 | // Search function body for instances of .filter_map(..).next() | ||
180 | let body = db.body(self.owner.into()); | ||
181 | let mut prev = None; | ||
182 | for (id, expr) in body.exprs.iter() { | ||
183 | if let Expr::MethodCall { receiver, .. } = expr { | ||
184 | let function_id = match self.infer.method_resolution(id) { | ||
185 | Some(id) => id, | ||
186 | None => continue, | ||
187 | }; | ||
188 | |||
189 | if function_id == *filter_map_function_id { | ||
190 | prev = Some(id); | ||
191 | continue; | ||
192 | } | ||
193 | |||
194 | if function_id == *next_function_id { | ||
195 | if let Some(filter_map_id) = prev { | ||
196 | if *receiver == filter_map_id { | ||
197 | let (_, source_map) = db.body_with_source_map(self.owner.into()); | ||
198 | if let Ok(next_source_ptr) = source_map.expr_syntax(id) { | ||
199 | self.sink.push(ReplaceFilterMapNextWithFindMap { | ||
200 | file: next_source_ptr.file_id, | ||
201 | next_expr: next_source_ptr.value, | ||
202 | }); | ||
203 | } | ||
204 | } | ||
205 | } | ||
206 | } | ||
207 | } | ||
208 | prev = None; | ||
209 | } | ||
210 | } | ||
211 | |||
212 | fn validate_call(&mut self, db: &dyn HirDatabase, call_id: ExprId, expr: &Expr) { | ||
154 | // Check that the number of arguments matches the number of parameters. | 213 | // Check that the number of arguments matches the number of parameters. |
155 | 214 | ||
156 | // FIXME: Due to shortcomings in the current type system implementation, only emit this | 215 | // FIXME: Due to shortcomings in the current type system implementation, only emit this |
157 | // diagnostic if there are no type mismatches in the containing function. | 216 | // diagnostic if there are no type mismatches in the containing function. |
158 | if self.infer.type_mismatches.iter().next().is_some() { | 217 | if self.infer.type_mismatches.iter().next().is_some() { |
159 | return None; | 218 | return; |
160 | } | 219 | } |
161 | 220 | ||
162 | let is_method_call = matches!(expr, Expr::MethodCall { .. }); | 221 | let is_method_call = matches!(expr, Expr::MethodCall { .. }); |
163 | let (sig, args) = match expr { | 222 | let (sig, args) = match expr { |
164 | Expr::Call { callee, args } => { | 223 | Expr::Call { callee, args } => { |
165 | let callee = &self.infer.type_of_expr[*callee]; | 224 | let callee = &self.infer.type_of_expr[*callee]; |
166 | let sig = callee.callable_sig(db)?; | 225 | let sig = match callee.callable_sig(db) { |
226 | Some(sig) => sig, | ||
227 | None => return, | ||
228 | }; | ||
167 | (sig, args.clone()) | 229 | (sig, args.clone()) |
168 | } | 230 | } |
169 | Expr::MethodCall { receiver, args, .. } => { | 231 | Expr::MethodCall { receiver, args, .. } => { |
@@ -175,22 +237,25 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
175 | // if the receiver is of unknown type, it's very likely we | 237 | // if the receiver is of unknown type, it's very likely we |
176 | // don't know enough to correctly resolve the method call. | 238 | // don't know enough to correctly resolve the method call. |
177 | // This is kind of a band-aid for #6975. | 239 | // This is kind of a band-aid for #6975. |
178 | return None; | 240 | return; |
179 | } | 241 | } |
180 | 242 | ||
181 | // FIXME: note that we erase information about substs here. This | 243 | // FIXME: note that we erase information about substs here. This |
182 | // is not right, but, luckily, doesn't matter as we care only | 244 | // is not right, but, luckily, doesn't matter as we care only |
183 | // about the number of params | 245 | // about the number of params |
184 | let callee = self.infer.method_resolution(call_id)?; | 246 | let callee = match self.infer.method_resolution(call_id) { |
247 | Some(callee) => callee, | ||
248 | None => return, | ||
249 | }; | ||
185 | let sig = db.callable_item_signature(callee.into()).value; | 250 | let sig = db.callable_item_signature(callee.into()).value; |
186 | 251 | ||
187 | (sig, args) | 252 | (sig, args) |
188 | } | 253 | } |
189 | _ => return None, | 254 | _ => return, |
190 | }; | 255 | }; |
191 | 256 | ||
192 | if sig.is_varargs { | 257 | if sig.is_varargs { |
193 | return None; | 258 | return; |
194 | } | 259 | } |
195 | 260 | ||
196 | let params = sig.params(); | 261 | let params = sig.params(); |
@@ -213,8 +278,6 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
213 | }); | 278 | }); |
214 | } | 279 | } |
215 | } | 280 | } |
216 | |||
217 | None | ||
218 | } | 281 | } |
219 | 282 | ||
220 | fn validate_match( | 283 | fn validate_match( |
diff --git a/crates/hir_ty/src/test_db.rs b/crates/hir_ty/src/test_db.rs index 09696fcf4..381b98ba8 100644 --- a/crates/hir_ty/src/test_db.rs +++ b/crates/hir_ty/src/test_db.rs | |||
@@ -83,7 +83,7 @@ impl TestDB { | |||
83 | let crate_def_map = self.crate_def_map(krate); | 83 | let crate_def_map = self.crate_def_map(krate); |
84 | for (local_id, data) in crate_def_map.modules() { | 84 | for (local_id, data) in crate_def_map.modules() { |
85 | if data.origin.file_id() == Some(file_id) { | 85 | if data.origin.file_id() == Some(file_id) { |
86 | return ModuleId::top_level(krate, local_id); | 86 | return crate_def_map.module_id(local_id); |
87 | } | 87 | } |
88 | } | 88 | } |
89 | } | 89 | } |
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs index b35bc2bae..8607139ba 100644 --- a/crates/ide/src/diagnostics.rs +++ b/crates/ide/src/diagnostics.rs | |||
@@ -136,6 +136,9 @@ pub(crate) fn diagnostics( | |||
136 | .on::<hir::diagnostics::IncorrectCase, _>(|d| { | 136 | .on::<hir::diagnostics::IncorrectCase, _>(|d| { |
137 | res.borrow_mut().push(warning_with_fix(d, &sema)); | 137 | res.borrow_mut().push(warning_with_fix(d, &sema)); |
138 | }) | 138 | }) |
139 | .on::<hir::diagnostics::ReplaceFilterMapNextWithFindMap, _>(|d| { | ||
140 | res.borrow_mut().push(warning_with_fix(d, &sema)); | ||
141 | }) | ||
139 | .on::<hir::diagnostics::InactiveCode, _>(|d| { | 142 | .on::<hir::diagnostics::InactiveCode, _>(|d| { |
140 | // If there's inactive code somewhere in a macro, don't propagate to the call-site. | 143 | // If there's inactive code somewhere in a macro, don't propagate to the call-site. |
141 | if d.display_source().file_id.expansion_info(db).is_some() { | 144 | if d.display_source().file_id.expansion_info(db).is_some() { |
diff --git a/crates/ide/src/diagnostics/fixes.rs b/crates/ide/src/diagnostics/fixes.rs index 579d5a308..cbfc66ab3 100644 --- a/crates/ide/src/diagnostics/fixes.rs +++ b/crates/ide/src/diagnostics/fixes.rs | |||
@@ -4,7 +4,7 @@ use hir::{ | |||
4 | db::AstDatabase, | 4 | db::AstDatabase, |
5 | diagnostics::{ | 5 | diagnostics::{ |
6 | Diagnostic, IncorrectCase, MissingFields, MissingOkOrSomeInTailExpr, NoSuchField, | 6 | Diagnostic, IncorrectCase, MissingFields, MissingOkOrSomeInTailExpr, NoSuchField, |
7 | RemoveThisSemicolon, UnresolvedModule, | 7 | RemoveThisSemicolon, ReplaceFilterMapNextWithFindMap, UnresolvedModule, |
8 | }, | 8 | }, |
9 | HasSource, HirDisplay, InFile, Semantics, VariantDef, | 9 | HasSource, HirDisplay, InFile, Semantics, VariantDef, |
10 | }; | 10 | }; |
@@ -15,8 +15,8 @@ use ide_db::{ | |||
15 | }; | 15 | }; |
16 | use syntax::{ | 16 | use syntax::{ |
17 | algo, | 17 | algo, |
18 | ast::{self, edit::IndentLevel, make}, | 18 | ast::{self, edit::IndentLevel, make, ArgListOwner}, |
19 | AstNode, | 19 | AstNode, TextRange, |
20 | }; | 20 | }; |
21 | use text_edit::TextEdit; | 21 | use text_edit::TextEdit; |
22 | 22 | ||
@@ -144,6 +144,33 @@ impl DiagnosticWithFix for IncorrectCase { | |||
144 | } | 144 | } |
145 | } | 145 | } |
146 | 146 | ||
147 | impl DiagnosticWithFix for ReplaceFilterMapNextWithFindMap { | ||
148 | fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Fix> { | ||
149 | let root = sema.db.parse_or_expand(self.file)?; | ||
150 | let next_expr = self.next_expr.to_node(&root); | ||
151 | let next_call = ast::MethodCallExpr::cast(next_expr.syntax().clone())?; | ||
152 | |||
153 | let filter_map_call = ast::MethodCallExpr::cast(next_call.receiver()?.syntax().clone())?; | ||
154 | let filter_map_name_range = filter_map_call.name_ref()?.ident_token()?.text_range(); | ||
155 | let filter_map_args = filter_map_call.arg_list()?; | ||
156 | |||
157 | let range_to_replace = | ||
158 | TextRange::new(filter_map_name_range.start(), next_expr.syntax().text_range().end()); | ||
159 | let replacement = format!("find_map{}", filter_map_args.syntax().text()); | ||
160 | let trigger_range = next_expr.syntax().text_range(); | ||
161 | |||
162 | let edit = TextEdit::replace(range_to_replace, replacement); | ||
163 | |||
164 | let source_change = SourceChange::from_text_edit(self.file.original_file(sema.db), edit); | ||
165 | |||
166 | Some(Fix::new( | ||
167 | "Replace filter_map(..).next() with find_map()", | ||
168 | source_change, | ||
169 | trigger_range, | ||
170 | )) | ||
171 | } | ||
172 | } | ||
173 | |||
147 | fn missing_record_expr_field_fix( | 174 | fn missing_record_expr_field_fix( |
148 | sema: &Semantics<RootDatabase>, | 175 | sema: &Semantics<RootDatabase>, |
149 | usage_file_id: FileId, | 176 | usage_file_id: FileId, |
diff --git a/crates/ide/src/display/navigation_target.rs b/crates/ide/src/display/navigation_target.rs index 16fa828ad..23d885218 100644 --- a/crates/ide/src/display/navigation_target.rs +++ b/crates/ide/src/display/navigation_target.rs | |||
@@ -173,6 +173,7 @@ impl ToNav for FileSymbol { | |||
173 | FileSymbolKind::Const => SymbolKind::Const, | 173 | FileSymbolKind::Const => SymbolKind::Const, |
174 | FileSymbolKind::Static => SymbolKind::Static, | 174 | FileSymbolKind::Static => SymbolKind::Static, |
175 | FileSymbolKind::Macro => SymbolKind::Macro, | 175 | FileSymbolKind::Macro => SymbolKind::Macro, |
176 | FileSymbolKind::Union => SymbolKind::Union, | ||
176 | }), | 177 | }), |
177 | full_range: self.range, | 178 | full_range: self.range, |
178 | focus_range: self.name_range, | 179 | focus_range: self.name_range, |
@@ -434,13 +435,16 @@ impl TryToNav for hir::TypeParam { | |||
434 | fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> { | 435 | fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> { |
435 | let src = self.source(db)?; | 436 | let src = self.source(db)?; |
436 | let full_range = match &src.value { | 437 | let full_range = match &src.value { |
437 | 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()), | ||
438 | Either::Right(it) => it.syntax().text_range(), | 441 | Either::Right(it) => it.syntax().text_range(), |
439 | }; | 442 | }; |
440 | let focus_range = match &src.value { | 443 | let focus_range = match &src.value { |
441 | Either::Left(_) => None, | 444 | Either::Left(it) => it.name(), |
442 | Either::Right(it) => it.name().map(|it| it.syntax().text_range()), | 445 | Either::Right(it) => it.name(), |
443 | }; | 446 | } |
447 | .map(|it| it.syntax().text_range()); | ||
444 | Some(NavigationTarget { | 448 | Some(NavigationTarget { |
445 | file_id: src.file_id.original_file(db), | 449 | file_id: src.file_id.original_file(db), |
446 | 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/symbol_index.rs b/crates/ide_db/src/symbol_index.rs index e954bd72e..9ed9568ce 100644 --- a/crates/ide_db/src/symbol_index.rs +++ b/crates/ide_db/src/symbol_index.rs | |||
@@ -356,15 +356,16 @@ pub struct FileSymbol { | |||
356 | 356 | ||
357 | #[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)] | 357 | #[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)] |
358 | pub enum FileSymbolKind { | 358 | pub enum FileSymbolKind { |
359 | Const, | ||
360 | Enum, | ||
359 | Function, | 361 | Function, |
362 | Macro, | ||
363 | Module, | ||
364 | Static, | ||
360 | Struct, | 365 | Struct, |
361 | Enum, | ||
362 | Trait, | 366 | Trait, |
363 | Module, | ||
364 | TypeAlias, | 367 | TypeAlias, |
365 | Const, | 368 | Union, |
366 | Static, | ||
367 | Macro, | ||
368 | } | 369 | } |
369 | 370 | ||
370 | impl FileSymbolKind { | 371 | impl FileSymbolKind { |
@@ -375,6 +376,7 @@ impl FileSymbolKind { | |||
375 | | FileSymbolKind::Enum | 376 | | FileSymbolKind::Enum |
376 | | FileSymbolKind::Trait | 377 | | FileSymbolKind::Trait |
377 | | FileSymbolKind::TypeAlias | 378 | | FileSymbolKind::TypeAlias |
379 | | FileSymbolKind::Union | ||
378 | ) | 380 | ) |
379 | } | 381 | } |
380 | } | 382 | } |
@@ -425,6 +427,7 @@ fn to_symbol(node: &SyntaxNode) -> Option<(SmolStr, SyntaxNodePtr, TextRange)> { | |||
425 | ast::Const(it) => decl(it), | 427 | ast::Const(it) => decl(it), |
426 | ast::Static(it) => decl(it), | 428 | ast::Static(it) => decl(it), |
427 | ast::MacroRules(it) => decl(it), | 429 | ast::MacroRules(it) => decl(it), |
430 | ast::Union(it) => decl(it), | ||
428 | _ => None, | 431 | _ => None, |
429 | } | 432 | } |
430 | } | 433 | } |
@@ -443,6 +446,7 @@ fn to_file_symbol(node: &SyntaxNode, file_id: FileId) -> Option<FileSymbol> { | |||
443 | CONST => FileSymbolKind::Const, | 446 | CONST => FileSymbolKind::Const, |
444 | STATIC => FileSymbolKind::Static, | 447 | STATIC => FileSymbolKind::Static, |
445 | MACRO_RULES => FileSymbolKind::Macro, | 448 | MACRO_RULES => FileSymbolKind::Macro, |
449 | UNION => FileSymbolKind::Union, | ||
446 | kind => unreachable!("{:?}", kind), | 450 | kind => unreachable!("{:?}", kind), |
447 | }, | 451 | }, |
448 | range: node.text_range(), | 452 | range: node.text_range(), |
diff --git a/crates/parser/src/grammar/items/consts.rs b/crates/parser/src/grammar/items/consts.rs index eb7d1f828..12130df40 100644 --- a/crates/parser/src/grammar/items/consts.rs +++ b/crates/parser/src/grammar/items/consts.rs | |||
@@ -13,7 +13,7 @@ pub(super) fn konst(p: &mut Parser, m: Marker) { | |||
13 | fn const_or_static(p: &mut Parser, m: Marker, kw: SyntaxKind, def: SyntaxKind) { | 13 | fn const_or_static(p: &mut Parser, m: Marker, kw: SyntaxKind, def: SyntaxKind) { |
14 | assert!(p.at(kw)); | 14 | assert!(p.at(kw)); |
15 | p.bump(kw); | 15 | p.bump(kw); |
16 | p.eat(T![mut]); // FIXME: validator to forbid const mut | 16 | p.eat(T![mut]); |
17 | 17 | ||
18 | // Allow `_` in place of an identifier in a `const`. | 18 | // Allow `_` in place of an identifier in a `const`. |
19 | let is_const_underscore = kw == T![const] && p.eat(T![_]); | 19 | let is_const_underscore = kw == T![const] && p.eat(T![_]); |
diff --git a/crates/profile/Cargo.toml b/crates/profile/Cargo.toml index cc7da27f7..4dd9acc98 100644 --- a/crates/profile/Cargo.toml +++ b/crates/profile/Cargo.toml | |||
@@ -14,7 +14,7 @@ once_cell = "1.3.1" | |||
14 | cfg-if = "1" | 14 | cfg-if = "1" |
15 | libc = "0.2.73" | 15 | libc = "0.2.73" |
16 | la-arena = { version = "0.2.0", path = "../../lib/arena" } | 16 | la-arena = { version = "0.2.0", path = "../../lib/arena" } |
17 | countme = { version = "2.0.0-pre.2", features = ["enable"] } | 17 | countme = { version = "2.0.0", features = ["enable"] } |
18 | jemalloc-ctl = { version = "0.3.3", optional = true } | 18 | jemalloc-ctl = { version = "0.3.3", optional = true } |
19 | 19 | ||
20 | [target.'cfg(target_os = "linux")'.dependencies] | 20 | [target.'cfg(target_os = "linux")'.dependencies] |
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 3ddb9e19a..247bfe71e 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs | |||
@@ -147,6 +147,9 @@ config_data! { | |||
147 | /// Whether to show `Method References` lens. Only applies when | 147 | /// Whether to show `Method References` lens. Only applies when |
148 | /// `#rust-analyzer.lens.enable#` is set. | 148 | /// `#rust-analyzer.lens.enable#` is set. |
149 | lens_methodReferences: bool = "false", | 149 | lens_methodReferences: bool = "false", |
150 | /// Whether to show `References` lens. Only applies when | ||
151 | /// `#rust-analyzer.lens.enable#` is set. | ||
152 | lens_references: bool = "false", | ||
150 | 153 | ||
151 | /// Disable project auto-discovery in favor of explicitly specified set | 154 | /// Disable project auto-discovery in favor of explicitly specified set |
152 | /// of projects.\n\nElements must be paths pointing to `Cargo.toml`, | 155 | /// of projects.\n\nElements must be paths pointing to `Cargo.toml`, |
@@ -221,6 +224,7 @@ pub struct LensConfig { | |||
221 | pub debug: bool, | 224 | pub debug: bool, |
222 | pub implementations: bool, | 225 | pub implementations: bool, |
223 | pub method_refs: bool, | 226 | pub method_refs: bool, |
227 | pub refs: bool, // for Struct, Enum, Union and Trait | ||
224 | } | 228 | } |
225 | 229 | ||
226 | impl LensConfig { | 230 | impl LensConfig { |
@@ -237,7 +241,7 @@ impl LensConfig { | |||
237 | } | 241 | } |
238 | 242 | ||
239 | pub fn references(&self) -> bool { | 243 | pub fn references(&self) -> bool { |
240 | self.method_refs | 244 | self.method_refs || self.refs |
241 | } | 245 | } |
242 | } | 246 | } |
243 | 247 | ||
@@ -593,6 +597,7 @@ impl Config { | |||
593 | debug: self.data.lens_enable && self.data.lens_debug, | 597 | debug: self.data.lens_enable && self.data.lens_debug, |
594 | implementations: self.data.lens_enable && self.data.lens_implementations, | 598 | implementations: self.data.lens_enable && self.data.lens_implementations, |
595 | method_refs: self.data.lens_enable && self.data.lens_methodReferences, | 599 | method_refs: self.data.lens_enable && self.data.lens_methodReferences, |
600 | refs: self.data.lens_enable && self.data.lens_references, | ||
596 | } | 601 | } |
597 | } | 602 | } |
598 | pub fn hover(&self) -> HoverConfig { | 603 | pub fn hover(&self) -> HoverConfig { |
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index 809452e6d..07204436c 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs | |||
@@ -1112,42 +1112,48 @@ pub(crate) fn handle_code_lens( | |||
1112 | } | 1112 | } |
1113 | } | 1113 | } |
1114 | 1114 | ||
1115 | if lens_config.implementations { | 1115 | if lens_config.implementations || lens_config.refs { |
1116 | // Handle impls | 1116 | snap.analysis |
1117 | lenses.extend( | 1117 | .file_structure(file_id)? |
1118 | snap.analysis | 1118 | .into_iter() |
1119 | .file_structure(file_id)? | 1119 | .filter(|it| { |
1120 | .into_iter() | 1120 | matches!( |
1121 | .filter(|it| { | 1121 | it.kind, |
1122 | matches!( | 1122 | SymbolKind::Trait | SymbolKind::Struct | SymbolKind::Enum | SymbolKind::Union |
1123 | it.kind, | 1123 | ) |
1124 | SymbolKind::Trait | 1124 | }) |
1125 | | SymbolKind::Struct | 1125 | .for_each(|it| { |
1126 | | SymbolKind::Enum | 1126 | let range = to_proto::range(&line_index, it.node_range); |
1127 | | SymbolKind::Union | 1127 | let position = to_proto::position(&line_index, it.navigation_range.start()); |
1128 | ) | 1128 | let doc_pos = lsp_types::TextDocumentPositionParams::new( |
1129 | }) | 1129 | params.text_document.clone(), |
1130 | .map(|it| { | 1130 | position, |
1131 | let range = to_proto::range(&line_index, it.node_range); | 1131 | ); |
1132 | let pos = range.start; | 1132 | let goto_params = lsp_types::request::GotoImplementationParams { |
1133 | let lens_params = lsp_types::request::GotoImplementationParams { | 1133 | text_document_position_params: doc_pos.clone(), |
1134 | text_document_position_params: lsp_types::TextDocumentPositionParams::new( | 1134 | work_done_progress_params: Default::default(), |
1135 | params.text_document.clone(), | 1135 | partial_result_params: Default::default(), |
1136 | pos, | 1136 | }; |
1137 | ), | 1137 | |
1138 | work_done_progress_params: Default::default(), | 1138 | if lens_config.implementations { |
1139 | partial_result_params: Default::default(), | 1139 | lenses.push(CodeLens { |
1140 | }; | ||
1141 | CodeLens { | ||
1142 | range, | 1140 | range, |
1143 | command: None, | 1141 | command: None, |
1144 | data: Some(to_value(CodeLensResolveData::Impls(lens_params)).unwrap()), | 1142 | data: Some(to_value(CodeLensResolveData::Impls(goto_params)).unwrap()), |
1145 | } | 1143 | }) |
1146 | }), | 1144 | } |
1147 | ); | 1145 | |
1146 | if lens_config.refs { | ||
1147 | lenses.push(CodeLens { | ||
1148 | range, | ||
1149 | command: None, | ||
1150 | data: Some(to_value(CodeLensResolveData::References(doc_pos)).unwrap()), | ||
1151 | }) | ||
1152 | } | ||
1153 | }); | ||
1148 | } | 1154 | } |
1149 | 1155 | ||
1150 | if lens_config.references() { | 1156 | if lens_config.method_refs { |
1151 | lenses.extend(snap.analysis.find_all_methods(file_id)?.into_iter().map(|it| { | 1157 | lenses.extend(snap.analysis.find_all_methods(file_id)?.into_iter().map(|it| { |
1152 | let range = to_proto::range(&line_index, it.range); | 1158 | let range = to_proto::range(&line_index, it.range); |
1153 | let position = to_proto::position(&line_index, it.range.start()); | 1159 | let position = to_proto::position(&line_index, it.range.start()); |
diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml index 24298fbfa..e70fbba9c 100644 --- a/crates/syntax/Cargo.toml +++ b/crates/syntax/Cargo.toml | |||
@@ -12,7 +12,7 @@ doctest = false | |||
12 | 12 | ||
13 | [dependencies] | 13 | [dependencies] |
14 | itertools = "0.10.0" | 14 | itertools = "0.10.0" |
15 | rowan = "0.12" | 15 | rowan = "0.12.2" |
16 | rustc_lexer = { version = "700.0.0", package = "rustc-ap-rustc_lexer" } | 16 | rustc_lexer = { version = "700.0.0", package = "rustc-ap-rustc_lexer" } |
17 | rustc-hash = "1.1.0" | 17 | rustc-hash = "1.1.0" |
18 | arrayvec = "0.5.1" | 18 | arrayvec = "0.5.1" |
diff --git a/crates/syntax/src/validation.rs b/crates/syntax/src/validation.rs index 7694e8834..3e216fb70 100644 --- a/crates/syntax/src/validation.rs +++ b/crates/syntax/src/validation.rs | |||
@@ -1,4 +1,6 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! This module implements syntax validation that the parser doesn't handle. |
2 | //! | ||
3 | //! A failed validation emits a diagnostic. | ||
2 | 4 | ||
3 | mod block; | 5 | mod block; |
4 | 6 | ||
@@ -92,6 +94,7 @@ pub(crate) fn validate(root: &SyntaxNode) -> Vec<SyntaxError> { | |||
92 | match_ast! { | 94 | match_ast! { |
93 | match node { | 95 | match node { |
94 | ast::Literal(it) => validate_literal(it, &mut errors), | 96 | ast::Literal(it) => validate_literal(it, &mut errors), |
97 | ast::Const(it) => validate_const(it, &mut errors), | ||
95 | ast::BlockExpr(it) => block::validate_block_expr(it, &mut errors), | 98 | ast::BlockExpr(it) => block::validate_block_expr(it, &mut errors), |
96 | ast::FieldExpr(it) => validate_numeric_name(it.name_ref(), &mut errors), | 99 | ast::FieldExpr(it) => validate_numeric_name(it.name_ref(), &mut errors), |
97 | ast::RecordExprField(it) => validate_numeric_name(it.name_ref(), &mut errors), | 100 | ast::RecordExprField(it) => validate_numeric_name(it.name_ref(), &mut errors), |
@@ -362,3 +365,14 @@ fn validate_macro_rules(mac: ast::MacroRules, errors: &mut Vec<SyntaxError>) { | |||
362 | )); | 365 | )); |
363 | } | 366 | } |
364 | } | 367 | } |
368 | |||
369 | fn validate_const(const_: ast::Const, errors: &mut Vec<SyntaxError>) { | ||
370 | if let Some(mut_token) = const_ | ||
371 | .const_token() | ||
372 | .and_then(|t| t.next_token()) | ||
373 | .and_then(|t| algo::skip_trivia_token(t, Direction::Next)) | ||
374 | .filter(|t| t.kind() == T![mut]) | ||
375 | { | ||
376 | errors.push(SyntaxError::new("const globals cannot be mutable", mut_token.text_range())); | ||
377 | } | ||
378 | } | ||
diff --git a/crates/syntax/test_data/parser/err/0047_mutable_const_item.rast b/crates/syntax/test_data/parser/err/0047_mutable_const_item.rast new file mode 100644 index 000000000..c7eb312c9 --- /dev/null +++ b/crates/syntax/test_data/parser/err/0047_mutable_const_item.rast | |||
@@ -0,0 +1,22 @@ | |||
1 | [email protected] | ||
2 | [email protected] | ||
3 | [email protected] "const" | ||
4 | [email protected] " " | ||
5 | [email protected] "mut" | ||
6 | [email protected] " " | ||
7 | [email protected] | ||
8 | [email protected] "FOO" | ||
9 | [email protected] ":" | ||
10 | [email protected] " " | ||
11 | [email protected] | ||
12 | [email protected] "(" | ||
13 | [email protected] ")" | ||
14 | [email protected] " " | ||
15 | [email protected] "=" | ||
16 | [email protected] " " | ||
17 | [email protected] | ||
18 | [email protected] "(" | ||
19 | [email protected] ")" | ||
20 | [email protected] ";" | ||
21 | [email protected] "\n" | ||
22 | error 6..9: const globals cannot be mutable | ||
diff --git a/crates/syntax/test_data/parser/err/0047_mutable_const_item.rs b/crates/syntax/test_data/parser/err/0047_mutable_const_item.rs new file mode 100644 index 000000000..b34336f3f --- /dev/null +++ b/crates/syntax/test_data/parser/err/0047_mutable_const_item.rs | |||
@@ -0,0 +1 @@ | |||
const mut FOO: () = (); | |||
diff --git a/crates/syntax/test_data/parser/ok/0024_const_item.rast b/crates/syntax/test_data/parser/ok/0024_const_item.rast index dd1b9c9a0..b89ed6f98 100644 --- a/crates/syntax/test_data/parser/ok/0024_const_item.rast +++ b/crates/syntax/test_data/parser/ok/0024_const_item.rast | |||
@@ -1,4 +1,4 @@ | |||
1 | SOURCE_FILE@0..64 | 1 | SOURCE_FILE@0..39 |
2 | [email protected] | 2 | [email protected] |
3 | [email protected] "const" | 3 | [email protected] "const" |
4 | [email protected] " " | 4 | [email protected] " " |
@@ -36,24 +36,3 @@ [email protected] | |||
36 | [email protected] "92" | 36 | [email protected] "92" |
37 | [email protected] ";" | 37 | [email protected] ";" |
38 | [email protected] "\n" | 38 | [email protected] "\n" |
39 | [email protected] | ||
40 | [email protected] "const" | ||
41 | [email protected] " " | ||
42 | [email protected] "mut" | ||
43 | [email protected] " " | ||
44 | [email protected] | ||
45 | [email protected] "BAR" | ||
46 | [email protected] ":" | ||
47 | [email protected] " " | ||
48 | [email protected] | ||
49 | [email protected] | ||
50 | [email protected] | ||
51 | [email protected] | ||
52 | [email protected] "u32" | ||
53 | [email protected] " " | ||
54 | [email protected] "=" | ||
55 | [email protected] " " | ||
56 | [email protected] | ||
57 | [email protected] "62" | ||
58 | [email protected] ";" | ||
59 | [email protected] "\n" | ||
diff --git a/crates/syntax/test_data/parser/ok/0024_const_item.rs b/crates/syntax/test_data/parser/ok/0024_const_item.rs index a806a209d..1f2ffa0da 100644 --- a/crates/syntax/test_data/parser/ok/0024_const_item.rs +++ b/crates/syntax/test_data/parser/ok/0024_const_item.rs | |||
@@ -1,3 +1,2 @@ | |||
1 | const _: u32 = 0; | 1 | const _: u32 = 0; |
2 | const FOO: u32 = 92; | 2 | const FOO: u32 = 92; |
3 | const mut BAR: u32 = 62; | ||
diff --git a/docs/dev/README.md b/docs/dev/README.md index 24197b332..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 | ||
@@ -212,7 +216,7 @@ To log all communication between the server and the client, there are two choice | |||
212 | 216 | ||
213 | * you can log on the server side, by running something like | 217 | * you can log on the server side, by running something like |
214 | ``` | 218 | ``` |
215 | env RA_LOG=gen_lsp_server=trace code . | 219 | env RA_LOG=lsp_server=debug code . |
216 | ``` | 220 | ``` |
217 | 221 | ||
218 | * you can log on the client side, by enabling `"rust-analyzer.trace.server": | 222 | * you can log on the client side, by enabling `"rust-analyzer.trace.server": |
diff --git a/docs/dev/style.md b/docs/dev/style.md index 428cee3ad..6dc6868c2 100644 --- a/docs/dev/style.md +++ b/docs/dev/style.md | |||
@@ -294,8 +294,9 @@ Prefer `Default` even it has to be implemented manually. | |||
294 | 294 | ||
295 | **Rationale:** less typing in the common case, uniformity. | 295 | **Rationale:** less typing in the common case, uniformity. |
296 | 296 | ||
297 | Use `Vec::new` rather than `vec![]`. **Rationale:** uniformity, strength | 297 | Use `Vec::new` rather than `vec![]`. |
298 | reduction. | 298 | |
299 | **Rationale:** uniformity, strength reduction. | ||
299 | 300 | ||
300 | ## Functions Over Objects | 301 | ## Functions Over Objects |
301 | 302 | ||
@@ -479,7 +480,7 @@ pub fn reachable_nodes(node: Node) -> FxHashSet<Node> { | |||
479 | } | 480 | } |
480 | ``` | 481 | ``` |
481 | 482 | ||
482 | **Rational:** re-use allocations, accumulator style is more concise for complex cases. | 483 | **Rationale:** re-use allocations, accumulator style is more concise for complex cases. |
483 | 484 | ||
484 | # Style | 485 | # Style |
485 | 486 | ||
diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc index a76c99d1e..2f681b01a 100644 --- a/docs/user/generated_config.adoc +++ b/docs/user/generated_config.adoc | |||
@@ -86,6 +86,8 @@ | |||
86 | Whether to show `Run` lens. Only applies when `#rust-analyzer.lens.enable#` is set. | 86 | Whether to show `Run` lens. Only applies when `#rust-analyzer.lens.enable#` is set. |
87 | [[rust-analyzer.lens.methodReferences]]rust-analyzer.lens.methodReferences (default: `false`):: | 87 | [[rust-analyzer.lens.methodReferences]]rust-analyzer.lens.methodReferences (default: `false`):: |
88 | Whether to show `Method References` lens. Only applies when `#rust-analyzer.lens.enable#` is set. | 88 | Whether to show `Method References` lens. Only applies when `#rust-analyzer.lens.enable#` is set. |
89 | [[rust-analyzer.lens.references]]rust-analyzer.lens.references (default: `false`):: | ||
90 | Whether to show `References` lens. Only applies when `#rust-analyzer.lens.enable#` is set. | ||
89 | [[rust-analyzer.linkedProjects]]rust-analyzer.linkedProjects (default: `[]`):: | 91 | [[rust-analyzer.linkedProjects]]rust-analyzer.linkedProjects (default: `[]`):: |
90 | Disable project auto-discovery in favor of explicitly specified set of projects.\n\nElements must be paths pointing to `Cargo.toml`, `rust-project.json`, or JSON objects in `rust-project.json` format. | 92 | Disable project auto-discovery in favor of explicitly specified set of projects.\n\nElements must be paths pointing to `Cargo.toml`, `rust-project.json`, or JSON objects in `rust-project.json` format. |
91 | [[rust-analyzer.lruCapacity]]rust-analyzer.lruCapacity (default: `null`):: | 93 | [[rust-analyzer.lruCapacity]]rust-analyzer.lruCapacity (default: `null`):: |
diff --git a/editors/code/package.json b/editors/code/package.json index 3e6ebd7ed..2225cf1dd 100644 --- a/editors/code/package.json +++ b/editors/code/package.json | |||
@@ -633,6 +633,11 @@ | |||
633 | "default": false, | 633 | "default": false, |
634 | "type": "boolean" | 634 | "type": "boolean" |
635 | }, | 635 | }, |
636 | "rust-analyzer.lens.references": { | ||
637 | "markdownDescription": "Whether to show `References` lens. Only applies when `#rust-analyzer.lens.enable#` is set.", | ||
638 | "default": false, | ||
639 | "type": "boolean" | ||
640 | }, | ||
636 | "rust-analyzer.linkedProjects": { | 641 | "rust-analyzer.linkedProjects": { |
637 | "markdownDescription": "Disable project auto-discovery in favor of explicitly specified set of projects.\\n\\nElements must be paths pointing to `Cargo.toml`, `rust-project.json`, or JSON objects in `rust-project.json` format.", | 642 | "markdownDescription": "Disable project auto-discovery in favor of explicitly specified set of projects.\\n\\nElements must be paths pointing to `Cargo.toml`, `rust-project.json`, or JSON objects in `rust-project.json` format.", |
638 | "default": [], | 643 | "default": [], |
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index ebe4de1ea..ddb5cfbd3 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts | |||
@@ -144,6 +144,7 @@ export class Config { | |||
144 | debug: this.get<boolean>("lens.debug"), | 144 | debug: this.get<boolean>("lens.debug"), |
145 | implementations: this.get<boolean>("lens.implementations"), | 145 | implementations: this.get<boolean>("lens.implementations"), |
146 | methodReferences: this.get<boolean>("lens.methodReferences"), | 146 | methodReferences: this.get<boolean>("lens.methodReferences"), |
147 | references: this.get<boolean>("lens.references"), | ||
147 | }; | 148 | }; |
148 | } | 149 | } |
149 | 150 | ||
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 1ab21e726..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, |
@@ -99,13 +92,15 @@ export async function download(opts: DownloadOpts) { | |||
99 | async (progress, _cancellationToken) => { | 92 | async (progress, _cancellationToken) => { |
100 | let lastPercentage = 0; | 93 | let lastPercentage = 0; |
101 | await downloadFile(opts.url, tempFile, opts.mode, !!opts.gunzip, (readBytes, totalBytes) => { | 94 | await downloadFile(opts.url, tempFile, opts.mode, !!opts.gunzip, (readBytes, totalBytes) => { |
102 | const newPercentage = (readBytes / totalBytes) * 100; | 95 | const newPercentage = Math.round((readBytes / totalBytes) * 100); |
103 | progress.report({ | 96 | if (newPercentage !== lastPercentage) { |
104 | message: newPercentage.toFixed(0) + "%", | 97 | progress.report({ |
105 | increment: newPercentage - lastPercentage | 98 | message: `${newPercentage.toFixed(0)}%`, |
106 | }); | 99 | increment: newPercentage - lastPercentage |
107 | 100 | }); | |
108 | lastPercentage = newPercentage; | 101 | |
102 | lastPercentage = newPercentage; | ||
103 | } | ||
109 | }); | 104 | }); |
110 | } | 105 | } |
111 | ); | 106 | ); |