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